За кулисами брандмауэра конфиденциальности данных
Примечание.
Уровни конфиденциальности в настоящее время недоступны в потоках данных Power Platform, но команда продуктов работает над включением этой функции.
Если вы использовали Power Query в течение любого времени, скорее всего, вы столкнулись с ним. Там вы, запрашивая оттуда, когда вы внезапно получите ошибку, что никакого количества поиска в Интернете, настройки запросов или клавиатуры bashing может исправить. Ошибка, например:
Formula.Firewall: Query 'Query1' (step 'Source') references other queries or steps, so it may not directly access a data source. Please rebuild this data combination.
Или может быть:
Formula.Firewall: Query 'Query1' (step 'Source') is accessing data sources that have privacy levels which cannot be used together. Please rebuild this data combination.
Эти Formula.Firewall
ошибки являются результатом брандмауэра конфиденциальности данных Power Query (также известного как брандмауэр), который иногда может показаться, что он существует исключительно для разочарования аналитиков данных во всем мире. Поверьте, что это или нет, брандмауэр служит важной целью. В этой статье мы углубимся под капотом, чтобы лучше понять, как это работает. Вооружившись более большим пониманием, вы, надеюсь, сможете лучше диагностировать и устранять ошибки брандмауэра в будущем.
Что это такое?
Цель брандмауэра конфиденциальности данных проста: она существует, чтобы предотвратить непреднамеренно утечку данных между источниками Power Query.
Почему? Я имею в виду, вы, безусловно, можете создать некоторые M, которые будут передавать значение SQL в веб-канал OData. Но это было бы преднамеренной утечкой данных. Автор mashup (или, по крайней мере, должен) знать, что они делают это. Почему тогда необходимость защиты от непреднамеренной утечки данных?
Ответ? Складной.
Складной?
Свертывание — это термин, который относится к преобразованию выражений в M (например, фильтрам, переименованиям, соединениям и т. д.) в операции с необработанным источником данных (например, SQL, OData и т. д.). Огромная часть возможностей Power Query исходит от того, что PQ может преобразовать операции, выполняемые пользователем через его пользовательский интерфейс в сложные языки SQL или других внутренних языков источника данных, без необходимости знать указанные языки. Пользователи получают преимущество производительности собственных операций источника данных с легкостью использования пользовательского интерфейса, где все источники данных можно преобразовать с помощью общего набора команд.
В рамках свертывания PQ иногда может определить, что наиболее эффективный способ выполнения заданного mashup заключается в том, чтобы принимать данные из одного источника и передавать его другому. Например, если вы присоединяете небольшой CSV-файл к огромной таблице SQL, возможно, не хотите, чтобы PQ считывал CSV-файл, считывал всю таблицу SQL, а затем присоединял их вместе на локальном компьютере. Возможно, вам нужно, чтобы PQ встроит данные CSV в инструкцию SQL и попросите базу данных SQL выполнить присоединение.
Это то, как может произойти непреднамеренное утечка данных.
Представьте, что вы присоединялись к данным SQL, которые включали номера социального страхования сотрудников с результатами внешнего веб-канала OData, и вы вдруг обнаружили, что номера социального страхования из SQL отправляются в службу OData. Плохие новости, да?
Это тип сценария, который брандмауэр предназначен для предотвращения.
Как это работает?
Брандмауэр существует, чтобы предотвратить непреднамеренные отправки данных из одного источника в другой источник. Достаточно просто.
Так как он выполняет эту миссию?
Это делается путем разделения запросов M на нечто называемое секциями, а затем применив следующее правило:
- Секция может получить доступ к совместимым источникам данных или ссылать на другие секции, но не оба.
Простой... тем не менее, запутанный. Что такое секция? Что делает два источника данных совместимыми? И почему следует заботиться о брандмауэре, если секция хочет получить доступ к источнику данных и ссылаться на секцию?
Давайте разберем это вниз и рассмотрим приведенное выше правило по одному элементу за раз.
Что такое секция?
На самом базовом уровне секция — это только коллекция одного или нескольких шагов запроса. Наиболее детализированный раздел (по крайней мере в текущей реализации) является одним шагом. Самые большие секции иногда могут охватывать несколько запросов. (Подробнее об этом позже.)
Если вы не знакомы с шагами, их можно просмотреть справа от окна Редактор Power Query после выбора запроса на панели "Примененные шаги". Шаги отслеживают все, что вы сделали, чтобы преобразовать данные в свою окончательную форму.
Разделы, ссылающиеся на другие секции
При оценке запроса с помощью брандмауэра брандмауэр разделяет запрос и все его зависимости на секции (то есть группы шагов). В любой момент, когда одна секция ссылается на что-то в другой секции, брандмауэр заменяет ссылку вызовом специальной функции Value.Firewall
. Другими словами, брандмауэр не позволяет секциям напрямую обращаться друг к другу. Все ссылки изменяются для прохождения через брандмауэр. Подумайте о брандмауэре как вратарь. Раздел, ссылающийся на другую секцию, должен получить разрешение брандмауэра для этого, и брандмауэр управляет тем, разрешены ли указанные данные в секцию.
Все это может показаться довольно абстрактным, поэтому рассмотрим пример.
Предположим, что у вас есть запрос с именем Employees, который извлекает некоторые данные из базы данных SQL. Предположим, что у вас также есть другой запрос (EmployeesReference), который просто ссылается на сотрудников.
shared Employees = let
Source = Sql.Database(…),
EmployeesTable = …
in
EmployeesTable;
shared EmployeesReference = let
Source = Employees
in
Source;
Эти запросы будут разделены на две секции: один для запроса Employees и один для запроса EmployeesReference (который будет ссылаться на раздел EmployeesReference). При оценке брандмауэра эти запросы будут перезаписаны следующим образом:
shared Employees = let
Source = Sql.Database(…),
EmployeesTable = …
in
EmployeesTable;
shared EmployeesReference = let
Source = Value.Firewall("Section1/Employees")
in
Source;
Обратите внимание, что простая ссылка на запрос Employees заменена вызовом Value.Firewall
, который предоставляет полное имя запроса Employees.
При оценке EmployeesReference вызов Value.Firewall("Section1/Employees")
перехватывается брандмауэром, который теперь имеет возможность контролировать ли (и как) запрошенные данные передаются в секцию EmployeesReference. Это может сделать любое количество вещей: запретить запрос, буферировать запрошенные данные (что предотвращает дальнейшее свертывание в исходный источник данных) и т. д.
Это то, как брандмауэр поддерживает контроль над потоком данных между секциями.
Секции, которые напрямую обращаются к источникам данных
Предположим, что вы определите запрос Query1 с одним шагом (обратите внимание, что этот одношаговый запрос соответствует одному разделу брандмауэра), и что этот один шаг обращается к двум источникам данных: таблице базы данных SQL и CSV-файлу. Как брандмауэр имеет дело с этим, так как нет ссылки на секции, и, следовательно, никаких вызовов для Value.Firewall
его перехвата? Давайте рассмотрим правило, указанное ранее:
- Секция может получить доступ к совместимым источникам данных или ссылать на другие секции, но не оба.
Чтобы разрешить выполнение запроса односекционных, но двух источников данных, два источника данных должны быть совместимыми. Другими словами, данные должны быть общими двунаправленно между ними. Это означает, что уровни конфиденциальности обоих источников должны быть общедоступными или как организационными, так как это только два сочетания, которые позволяют совместно использовать оба направления. Если оба источника помечены как частные, либо один помечен как общедоступный, и один помечен как организационный, или они помечены с помощью некоторого другого сочетания уровней конфиденциальности, то двунаправленный общий доступ не допускается, и поэтому они небезопасны для них, чтобы их оба были оценены в одном разделе. Это означает, что утечка небезопасных данных может произойти (из-за свертывания), и брандмауэр не сможет предотвратить его.
Что произойдет, если вы пытаетесь получить доступ к несовместимным источникам данных в той же секции?
Formula.Firewall: Query 'Query1' (step 'Source') is accessing data sources that have privacy levels which cannot be used together. Please rebuild this data combination.
Надеюсь, теперь вы лучше понимаете одно из сообщений об ошибках, перечисленных в начале этой статьи.
Обратите внимание, что это требование совместимости применяется только в заданной секции . Если секция ссылается на другие секции, источники данных из ссылочных секций не должны быть совместимы друг с другом. Это связано с тем, что брандмауэр может буферизать данные, что приведет к предотвращению дальнейшего свертывания в исходном источнике данных. Данные будут загружены в память и обрабатываются так, как если бы они не были откуда угодно.
Почему бы и то, и другое?
Предположим, что вы определите запрос с одним шагом (который снова будет соответствовать одному разделу), который обращается к двум другим запросам (т. е. двум другим секциям). Что делать, если вы хотите, на том же шаге, чтобы также напрямую получить доступ к базе данных SQL? Почему не удается ссылаться на другие секции и напрямую обращаться к совместимым источникам данных?
Как вы видели ранее, когда одна секция ссылается на другую секцию, брандмауэр выступает в качестве воротника для всех данных, поступающих в секцию. Для этого он должен иметь возможность контролировать, в каких данных разрешены данные. Если в секции осуществляется доступ к источникам данных, и данные, поступающие из других разделов, теряют возможность быть привратниками, так как потоки данных могут быть утечкой в одном из внутренних источников данных, не зная об этом. Таким образом, брандмауэр предотвращает доступ к секции, которая обращается к другим секциям, разрешать напрямую доступ к любым источникам данных.
Так что происходит, если секция пытается ссылаться на другие секции, а также напрямую обращаться к источникам данных?
Formula.Firewall: Query 'Query1' (step 'Source') references other queries or steps, so it may not directly access a data source. Please rebuild this data combination.
Теперь вы надеемся лучше понять другое сообщение об ошибке, указанное в начале этой статьи.
Подробные секции
Как вы, вероятно, можете угадать из приведенной выше информации, как секционированные запросы становятся невероятно важными. Если у вас есть некоторые шаги, ссылающиеся на другие запросы, и другие шаги, которые обращаются к источникам данных, теперь вы узнаете, что рисование границ секции в определенных местах приведет к ошибкам брандмауэра, а их рисование в других местах позволит выполнять запрос просто хорошо.
Так как именно запросы будут секционированы?
Этот раздел, вероятно, является наиболее важным для понимания причин возникновения ошибок брандмауэра и понимания их разрешения (где это возможно).
Ниже приведена сводка по логике секционирования.
- Начальное секционирование
- Создает секцию для каждого шага в каждом запросе
- Статический этап
- Этот этап не зависит от результатов оценки. Вместо этого он зависит от структуры запросов.
- Обрезка параметров
- Обрезает секции esque параметров, т. е. любой из них:
- Не ссылается на другие разделы
- Не содержит вызовов функций
- Не циклический (т. е. он не относится к себе)
- Обратите внимание, что "удаление" секции эффективно включает его в любые другие разделы, ссылающиеся на него.
- Секции параметров обрезки позволяют использовать ссылки на параметры, используемые в вызовах функций источника данных (например,
Web.Contents(myUrl)
) для работы, а не вызывать ошибки "секции не могут ссылаться на источники данных и другие шаги".
- Обрезает секции esque параметров, т. е. любой из них:
- Группирование (статический)
- Секции объединяются в порядке зависимостей вниз. В результате объединенные секции будут разделены следующим образом:
- Секции в разных запросах
- Секции, которые не ссылаются на другие секции (и таким образом разрешены доступ к источнику данных)
- Секции, ссылающиеся на другие секции (и поэтому запрещены доступ к источнику данных)
- Секции объединяются в порядке зависимостей вниз. В результате объединенные секции будут разделены следующим образом:
- Обрезка параметров
- Этот этап не зависит от результатов оценки. Вместо этого он зависит от структуры запросов.
- Динамический этап
- Этот этап зависит от результатов оценки, включая сведения о источниках данных, к которым обращается различные секции.
- Обрезка
- Обрезка секций, удовлетворяющих всем следующим требованиям:
- Не обращается к источникам данных
- Не ссылается на разделы, которые обращаются к источникам данных
- Не циклический
- Обрезка секций, удовлетворяющих всем следующим требованиям:
- Группирование (динамическое)
- Теперь, когда ненужные секции были обрезаны, попробуйте создать исходные секции, которые максимально большие. Это делается путем объединения секций с использованием одинаковых правил, описанных на этапе статической группировки выше.
Что означает все это?
Рассмотрим пример работы сложной логики, описанной выше.
Ниже приведен пример сценария. Это довольно простое слияние текстового файла (контакты) с базой данных SQL (сотрудники), где СЕРВЕР SQL является параметром (DbServer).
Три запроса
Ниже приведен код M для трех запросов, используемых в этом примере.
shared DbServer = "MySqlServer" meta [IsParameterQuery=true, Type="Text", IsParameterQueryRequired=true];
shared Contacts = let
Source = Csv.Document(File.Contents("C:\contacts.txt"),[Delimiter=" ", Columns=15, Encoding=1252, QuoteStyle=QuoteStyle.None]),
#"Promoted Headers" = Table.PromoteHeaders(Source, [PromoteAllScalars=true]),
#"Changed Type" = Table.TransformColumnTypes(#"Promoted Headers",{{"ContactID", Int64.Type}, {"NameStyle", type logical}, {"Title", type text}, {"FirstName", type text}, {"MiddleName", type text}, {"LastName", type text}, {"Suffix", type text}, {"EmailAddress", type text}, {"EmailPromotion", Int64.Type}, {"Phone", type text}, {"PasswordHash", type text}, {"PasswordSalt", type text}, {"AdditionalContactInfo", type text}, {"rowguid", type text}, {"ModifiedDate", type datetime}})
in
#"Changed Type";
shared Employees = let
Source = Sql.Databases(DbServer),
AdventureWorks = Source{[Name="AdventureWorks"]}[Data],
HumanResources_Employee = AdventureWorks{[Schema="HumanResources",Item="Employee"]}[Data],
#"Removed Columns" = Table.RemoveColumns(HumanResources_Employee,{"HumanResources.Employee(EmployeeID)", "HumanResources.Employee(ManagerID)", "HumanResources.EmployeeAddress", "HumanResources.EmployeeDepartmentHistory", "HumanResources.EmployeePayHistory", "HumanResources.JobCandidate", "Person.Contact", "Purchasing.PurchaseOrderHeader", "Sales.SalesPerson"}),
#"Merged Queries" = Table.NestedJoin(#"Removed Columns",{"ContactID"},Contacts,{"ContactID"},"Contacts",JoinKind.LeftOuter),
#"Expanded Contacts" = Table.ExpandTableColumn(#"Merged Queries", "Contacts", {"EmailAddress"}, {"EmailAddress"})
in
#"Expanded Contacts";
Ниже приведено представление более высокого уровня, показывающее зависимости.
Давайте секционируем
Давайте немного масштабируем и добавим шаги в рисунок и начнем с логики секционирования. Ниже приведена схема трех запросов, показывающая начальные секции брандмауэра зеленым цветом. Обратите внимание, что каждый шаг начинается в отдельном разделе.
Затем мы обрезаем секции параметров. Таким образом, DbServer неявно включается в раздел Source.
Теперь мы выполняем статическую группирование. Это поддерживает разделение между секциями в отдельных запросах (обратите внимание, что последние два шага сотрудников не группируются по шагам контактов) и между секциями, ссылающимися на другие секции (например, последние два шага сотрудников) и не (например, первые три шага сотрудников).
Теперь мы входим в динамический этап. На этом этапе вычисляются указанные выше статические секции. Секции, не имеющие доступа к источникам данных, обрезаются. Затем секции группируются для создания исходных секций, которые как можно больше. Однако в этом примере все остальные секции получают доступ к источникам данных, и для этого невозможно выполнить дальнейшую группировку. Секции в нашем примере, таким образом, не будут изменяться на этом этапе.
Давайте притворимся
Для иллюстрации, однако, давайте рассмотрим, что произойдет, если запрос "Контакты", а не из текстового файла, был жестко закодирован в M (возможно, через диалоговое окно ввод данных ).
В этом случае запрос "Контакты" не будет получать доступ к источникам данных. Таким образом, она будет обрезана во время первой части динамического этапа.
После удаления раздела "Контакты" последние два шага сотрудников больше не будут ссылаться на разделы, кроме первых трех шагов сотрудников. Таким образом, две секции будут сгруппированы.
Результирующий раздел будет выглядеть следующим образом.
Пример. Передача данных из одного источника данных в другой
Хорошо, достаточно абстрактного объяснения. Давайте рассмотрим распространенный сценарий, в котором, скорее всего, возникает ошибка брандмауэра и действия по ее устранению.
Представьте, что вы хотите найти имя компании из службы Northwind OData, а затем использовать имя компании для поиска Bing.
Сначала вы создадите запрос компании для получения имени компании.
let
Source = OData.Feed("https://services.odata.org/V4/Northwind/Northwind.svc/", null, [Implementation="2.0"]),
Customers_table = Source{[Name="Customers",Signature="table"]}[Data],
CHOPS = Customers_table{[CustomerID="CHOPS"]}[CompanyName]
in
CHOPS
Затем вы создадите запрос поиска , который ссылается на компанию и передает его в Bing.
let
Source = Text.FromBinary(Web.Contents("https://www.bing.com/search?q=" & Company))
in
Source
На этом этапе возникают проблемы. При оценке поиска возникает ошибка брандмауэра.
Formula.Firewall: Query 'Search' (step 'Source') references other queries or steps, so it may not directly access a data source. Please rebuild this data combination.
Это связано с тем, что исходный шаг поиска ссылается на источник данных (bing.com), а также ссылается на другой запрос или секцию (компания). Это нарушает правило, упоминание указанное выше ("секция может получить доступ к совместимым источникам данных или ссылать на другие разделы, но не оба").
Что делать? Одним из вариантов является отключение брандмауэра (с помощью параметра "Конфиденциальность" с меткой "Игнорировать уровни конфиденциальности" и потенциально повысить производительность). Но что делать, если вы хотите оставить брандмауэр включено?
Чтобы устранить ошибку без отключения брандмауэра, можно объединить компанию и поиск в один запрос, как показано ниже.
let
Source = OData.Feed("https://services.odata.org/V4/Northwind/Northwind.svc/", null, [Implementation="2.0"]),
Customers_table = Source{[Name="Customers",Signature="table"]}[Data],
CHOPS = Customers_table{[CustomerID="CHOPS"]}[CompanyName],
Search = Text.FromBinary(Web.Contents("https://www.bing.com/search?q=" & CHOPS))
in
Search
Все происходит внутри одной секции. Если уровни конфиденциальности для двух источников данных совместимы, брандмауэр теперь должен быть счастлив, и вы больше не получите ошибку.
Это обертка
Хотя есть гораздо больше, что можно сказать по этой теме, эта вводная статья уже достаточно длинна. Надеюсь, это даст вам лучшее понимание брандмауэра, и поможет вам понять и исправить ошибки брандмауэра при их возникновении в будущем.