Sdílet prostřednictvím


Za oponou firewallu na ochranu osobních údajů

Poznámka:

Úrovně ochrany osobních údajů jsou v tocích dat Power Platform momentálně nedostupné, ale produktový tým pracuje na povolení této funkce.

Pokud jste Power Query používali nějakou dobu, pravděpodobně jste ji zaznamenali. Jak se tak věnujete dotazování, náhle se objeví chyba, kterou ani žádné množství online vyhledávání, úprav dotazů ani mlácení do klávesnice nedokáže napravit. Například chyba

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.

Nebo možná:

Formula.Firewall: Query 'Query1' (step 'Source') is accessing data sources that have privacy levels which cannot be used together. Please rebuild this data combination.

Tyto Formula.Firewall chyby jsou výsledkem brány ochrany osobních údajů Power Query (známé také jako Firewall), která může někdy působit tak, že existuje výhradně pro to, aby frustrovala datové analytiky po celém světě. Věřte tomu nebo ne, ale firewall plní důležitou úlohu. V tomto článku se ponoříme pod kapuce, abychom lépe pochopili, jak to funguje. S větším pochopením vám to doufejme umožní lépe diagnostikovat a opravovat chyby firewallu v budoucnu.

Co je to?

Účel brány firewall pro ochranu osobních údajů je jednoduchý: je zaměřen na to, aby Power Query zabránil neúmyslnému úniku dat mezi zdroji.

Proč je to potřeba? Určitě byste mohli vytvořit nějaký M, který by předal hodnotu SQL datovému kanálu OData. To by ale bylo záměrné úniky dat. Autor mashupu by (nebo alespoň měl) vědět, že to dělá. Proč je potřeba zajistit ochranu před neúmyslným únikem dat?

Odpověď? Skládání.

Skládací?

Skládání je termín, který odkazuje na převod výrazů v jazyce M (například filtry, přejmenování, spojení atd.) na operace s nezpracovaným zdrojem dat (například SQL, OData atd.). Obrovská část výkonu Power Query pochází ze skutečnosti, že Power Query dokáže převést operace, které uživatel provádí prostřednictvím svého uživatelského rozhraní, na složité jazyky SQL nebo jiného back-endového zdroje dat, aniž by uživatel musel znát uvedené jazyky. Uživatelé získají výhodu výkonu nativních operací zdroje dat s jednoduchým použitím uživatelského rozhraní, ve kterém lze všechny zdroje dat transformovat pomocí společné sady příkazů.

V rámci skládání může Power Query někdy určit, že nejúčinnější způsob, jak spustit daný mashup, je vzít data z jednoho zdroje a předat je jinému. Pokud například připojujete malý soubor CSV k obrovské tabulce SQL, pravděpodobně nechcete, aby Power Query přečetl soubor CSV, přečetl celou tabulku SQL a pak je spojte na místním počítači. Pravděpodobně chcete, aby Power Query vložil data CSV do příkazu SQL a požádal databázi SQL o provedení spojení.

To je způsob, jakým může dojít k neúmyslnému úniku dat.

Představte si, že jste se připojili k datům SQL, která zahrnovala čísla sociálního pojištění zaměstnanců s výsledky externího datového kanálu OData, a najednou jste zjistili, že se čísla sociálního zabezpečení z SQL odesílala do služby OData. Špatná zpráva, že?

Toto je druh scénáře, kterému má brána firewall zabránit.

Jak to funguje?

Brána firewall existuje, aby se zabránilo neúmyslnému odesílání dat z jednoho zdroje do jiného zdroje. Je to dost jednoduché.

Jak to tedy dosahuje tohoto poslání?

Provede to tak, že vaše dotazy M rozdělí na něco, co se nazývá oddíly, a pak vynucuje následující pravidlo:

  • Oddíl může buď přistupovat k kompatibilním zdrojům dat, nebo odkazovat na jiné oddíly, ale ne na oba oddíly.

Jednoduchý... ale matoucí. Co je oddíl? Co dělá dva zdroje dat "kompatibilní"? A proč by se brána firewall měla starat, pokud oddíl chce získat přístup ke zdroji dat a odkazovat na oddíl?

Pojďme si to rozebrat a prozkoumat předchozí pravidlo krok za krokem.

Co je oddíl?

Základním způsobem je oddíl pouze soubor jednoho nebo více kroků dotazu. Nejpodrobnější možná část (alespoň v současné implementaci) je jeden krok. Největší oddíly můžou někdy zahrnovat více dotazů. (Další informace o tomto postupu najdete později.)

Pokud neznáte kroky, můžete je po výběru dotazu zobrazit na pravé straně okna Editoru Power Query v podokně Použitý postup . Kroky udržují přehled o všem, co děláte pro transformaci dat do konečného tvaru.

Oddíly, které odkazují na jiné oddíly

Když se dotaz vyhodnocuje s firewallem zapnutým, firewall rozdělí dotaz a všechny jeho závislosti do oddílů, tedy do skupin kroků. Kdykoli jeden oddíl odkazuje na něco v jiném oddílu, firewall nahradí odkaz voláním speciální funkce volanou Value.Firewall. Jinými slovy, brána firewall neumožňuje oddílům přímý přístup k sobě navzájem. Všechny odkazy se upraví tak, aby procházely bránou firewall. Bránu firewall si můžete představit jako vrátného. Oddíl, který odkazuje na jiný oddíl, musí k tomu získat oprávnění brány firewall a brána firewall určuje, jestli jsou odkazovaná data do oddílu povolená nebo ne.

Může to vypadat docela abstraktně, takže se podívejme na příklad.

Předpokládejme, že máte dotaz s názvem Zaměstnanci, který načítá některá data z databáze SQL. Předpokládejme, že máte také jiný dotaz (EmployeesReference), který jednoduše odkazuje na zaměstnance.

shared Employees = let
    Source = Sql.Database(…),
    EmployeesTable = …
in
    EmployeesTable;

shared EmployeesReference = let
    Source = Employees
in
    Source;

Tyto dotazy jsou rozdělené do dvou oddílů: jeden pro dotaz Zaměstnanci a jeden pro dotaz EmployeesReference (který odkazuje na oddíl Zaměstnanci). Při vyhodnocování s aktivovanou bránou firewall jsou tyto dotazy přepsány následovně:

shared Employees = let
    Source = Sql.Database(…),
    EmployeesTable = …
in
    EmployeesTable;

shared EmployeesReference = let
    Source = Value.Firewall("Section1/Employees")
in
    Source;

Všimněte si, že jednoduchý odkaz na dotaz Zaměstnanci je nahrazen voláním Value.Firewall, které je poskytnuto s úplným názvem dotazu Zaměstnanci.

Když se vyhodnocuje EmployeesReference, Firewall zachytí volání na Value.Firewall("Section1/Employees"), které teď má šanci určit, zda (a jak) požadovaná data proudí do oddílu EmployeesReference. Může provádět libovolný počet věcí: zamítnout požadavek, ukládat požadovaná data do vyrovnávací paměti (což brání dalšímu posouvání do původního zdroje dat), atd.

Takto firewall udržuje kontrolu nad datovým tokem mezi oddíly.

Oddíly, které přímo přistupují ke zdrojům dat

Řekněme, že definujete dotaz Query1 jedním krokem (všimněte si, že tento jednokrokový dotaz odpovídá jednomu oddílu brány firewall) a že tento jediný krok přistupuje ke dvěma zdrojům dat: k tabulce databáze SQL a souboru CSV. Jak se s tím firewall vypořádá, když neexistuje žádný odkaz na oddíly, a tudíž neexistuje žádné volání Value.Firewall, které by mohl zachytit? Pojďme se podívat na dříve uvedené pravidlo:

  • Oddíl může přistupovat ke kompatibilním zdrojům dat nebo odkazovat na jiné oddíly, ale ne na oba oddíly.

Aby bylo možné spustit dotaz pro jeden oddíl, ale se dvěma zdroji dat, musí být jeho dva zdroje dat „kompatibilní“. Jinými slovy, musí umožňovat obousměrné sdílení dat mezi nimi. To znamená, že úrovně ochrany osobních údajů obou zdrojů musí být veřejné nebo obě organizační, protože jde o jediné dvě kombinace, které umožňují sdílení v obou směrech. Pokud jsou oba zdroje označené jako soukromé nebo jeden je označený jako veřejný a jeden je označený jako organizační nebo jsou označené pomocí jiné kombinace úrovní ochrany osobních údajů, pak obousměrné sdílení není povolené. Není bezpečné, aby byly obě vyhodnocovány ve stejném oddílu. To by znamenalo, že by mohlo dojít k nebezpečnému úniku dat (kvůli posouvání) a brána firewall by neměla způsob, jak tomu zabránit.

Co se stane, když se pokusíte získat přístup k nekompatibilním zdrojům dat ve stejném oddílu?

Formula.Firewall: Query 'Query1' (step 'Source') is accessing data sources that have privacy levels which cannot be used together. Please rebuild this data combination.

Doufejme, že teď lépe rozumíte jedné z chybových zpráv uvedených na začátku tohoto článku.

Tento požadavek na kompatibilitu platí jenom v rámci daného oddílu. Pokud oddíl odkazuje na jiné oddíly, zdroje dat z odkazovaných oddílů nemusí být vzájemně kompatibilní. Je to proto, že brána firewall může pufrovat data, což brání dalšímu skládání vůči původnímu zdroji dat. Data se načtou do paměti a zachází se s nimi, jako by byly neznámého původu.

Proč nedělat obojí?

Řekněme, že definujete dotaz jedním krokem (který opět odpovídá jednomu oddílu), který přistupuje ke dvěma dalším dotazům (tedy dvěma dalšími oddíly). Co kdybyste chtěli v jednom kroku získat přímý přístup k databázi SQL? Proč nemůže oddíl odkazovat na jiné oddíly a získat přímý přístup k kompatibilním zdrojům dat?

Jak jste viděli dříve, když jeden oddíl odkazuje na jiný oddíl, firewall funguje jako strážce pro všechna data proudící do oddílu. Aby to bylo možné, musí mít možnost řídit, jaká data jsou povolená. Pokud existují zdroje dat, ke kterým se přistupuje v rámci oddílu, a data proudí z jiných oddílů, ztrácí schopnost fungovat jako správce, protože data proudící mohou být přesměrována do jednoho z interně přístupných zdrojů dat, aniž by o tom vědělo. Brána firewall proto brání oddílu, který přistupuje k jiným oddílům, aby mohl přímo přistupovat k jakýmkoli zdrojům dat.

Co se stane, když se oddíl pokusí odkazovat na jiné oddíly a také přímo přistupovat ke zdrojům dat?

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.

Teď snad lépe pochopíte další chybovou zprávu uvedenou na začátku tohoto článku.

Podrobné oddíly

Jak pravděpodobně můžete odhadnout z předchozích informací, způsob dělení dotazů je nakonec neuvěřitelně důležitý. Pokud máte nějaké kroky, které odkazují na jiné dotazy, a další kroky, které přistupují ke zdrojům dat, teď snad rozpoznáte, že kreslení hranic oddílů na určitých místech způsobuje chyby brány firewall, zatímco jejich kreslení na jiných místech umožňuje, aby váš dotaz běžel bez problémů.

Jak se tedy přesně rozdělí dotazy?

Tato část je pravděpodobně nejdůležitější pro pochopení, proč se zobrazují chyby brány firewall a jak je vyřešit (kde je to možné).

Tady je souhrnný přehled logiky dělení.

  • Počáteční dělení
    • Vytvoří oddíl pro každý krok v každém dotazu.
  • Statická fáze
    • Tato fáze nezávisí na výsledcích vyhodnocení. Místo toho spoléhá na to, jak jsou dotazy strukturované.
      • Oříznutí parametrů
        • Oříznou oddíly založené na parametrech, to znamená jakýkoli oddíl, který:
          • Neodkazuje na žádné jiné oddíly.
          • Neobsahuje žádné vyvolání funkce.
          • Není cyklické (to znamená, že se neodkazuje na sebe)
        • Všimněte si, že "odebrání" oddílu ve skutečnosti znamená jeho začlenění do všech ostatních oddílů, které na něj odkazují.
        • Oříznutí oddílů parametrů umožňuje, aby odkazy na parametry používané ve voláních funkcí zdroje dat (například Web.Contents(myUrl)) fungovaly, místo vyvolání chyb "oddíl nemůže odkazovat na zdroje dat a další kroky".
      • Seskupování (statické)
        • Oddíly se sloučí v pořadí závislostí zdola nahoru. Ve výsledných sloučených oddílech zůstávají následující položky oddělené:
          • Oddíly v různých dotazech
          • Oddíly, které neodkazují na jiné oddíly (a mají tak povolený přístup ke zdroji dat)
          • Oddíly, které odkazují na jiné oddíly (a proto nemají přístup ke zdroji dat)
  • Dynamická fáze
    • Tato fáze závisí na výsledcích vyhodnocení, včetně informací o zdrojích dat, ke které přistupují různé oddíly.
    • Ořezávání
      • Oříznou oddíly, které splňují všechny následující požadavky:
        • Nemá přístup k žádným zdrojům dat.
        • Neodkazuje na žádné oddíly, které přistupují ke zdrojům dat.
        • Není cyklické
    • Seskupování (dynamické)
      • Teď, když jsou nadbytečné oddíly oříznuté, zkuste vytvořit zdrojové oddíly, které jsou co největší. Vytvoření probíhá sloučením oddílů pomocí těch samých pravidel popsaných v předchozí fázi statického seskupení.

Co to všechno znamená?

Pojďme si projít příklad, abychom si ukázali, jak funguje dříve popsaná složitá logika.

Tady je ukázkový scénář. Je to poměrně jednoduché sloučení textového souboru (Kontakty) s databází SQL (Zaměstnanci), kde sql server je parametr (DbServer).

Tři dotazy

Tady je kód M pro tři dotazy použité v tomto příkladu.

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";

Tady je zobrazení na vyšší úrovni zobrazující závislosti.

Dialogové okno závislosti dotazů

Pojďme rozdělit

Pojďme se trochu přiblížit, zahrnout kroky do obrázku a začít si procházet logikou dělení. Tady je diagram tří dotazů zobrazující počáteční oddíly brány firewall zeleně. Všimněte si, že každý krok začíná ve svém přiřazeném oddílu.

Diagram znázorňující počáteční oddíly brány firewall

Dále zredukujeme oddíly parametrů. DbServer se tedy implicitně zahrne do zdrojového oddílu.

Diagram znázorňující oříznuté oddíly firewallu

Teď provedeme statické seskupení. Toto seskupení udržuje oddělení mezi oddíly v samostatných dotazech (všimněte si například, že poslední dva kroky zaměstnanců nejsou seskupeny s kroky kontaktů) a mezi oddíly, které odkazují na jiné oddíly (například poslední dva kroky zaměstnanců), a ty, které neodkazují na jiné oddíly (například první tři kroky zaměstnanců).

Diagram znázorňující sekce firewallu po statickém uspořádání

Teď vstoupíme do dynamické fáze. V této fázi se vyhodnotí výše uvedené statické oddíly. Oddíly, které nemají přístup k žádným zdrojům dat, jsou oříznuty. Oddíly se pak seskupí a vytvoří zdrojové oddíly, které jsou co největší. V tomto ukázkovém scénáři ale všechny zbývající oddíly přistupují ke zdrojům dat a není možné provádět žádné další seskupení. Oddíly v našem vzorku se tak během této fáze nemění.

Pojďme předstírat

Pro ilustraci si ale pojďme prohlédnout, co by se stalo, když by dotaz Kontakty, nikoli z textového souboru, byl pevně zakódovaný v jazyce M (možná prostřednictvím dialogového okna Zadat data ).

V tomto případě by dotaz Kontakty neměl přístup k žádným zdrojům dat. Během první části dynamické fáze by tedy došlo k oříznutí.

Diagram znázorňující sekci brány firewall po trimování dynamické fáze.

Když oddíl Kontakty odeberete, poslední dva kroky zaměstnanců už neodkazují na žádné oddíly s výjimkou oddílu, který obsahuje první tři kroky zaměstnanců. Proto by se dva oddíly seskupily.

Výsledný oddíl by vypadal takto.

Diagram znázorňující konečné oddíly brány firewall

Příklad: Předávání dat z jednoho zdroje dat do jiného

Dobře, dost abstraktní vysvětlení. Pojďme se podívat na běžný scénář, ve kterém se pravděpodobně setkáte s chybou brány firewall a postupem, jak ji vyřešit.

Představte si, že chcete vyhledat název společnosti ze služby Northwind OData a pak pomocí názvu společnosti provést vyhledávání Bingem.

Nejprve vytvoříte dotaz společnosti , který načte název společnosti.

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

Dále vytvoříte vyhledávací dotaz, který odkazuje na společnost a předá ho Bingu.

let
    Source = Text.FromBinary(Web.Contents("https://www.bing.com/search?q=" & Company))
in
    Source

V tuto chvíli narazíte na potíže. Při vyhodnocení Search se objeví chyba firewallu.

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.

K této chybě dochází, protože zdrojový krok hledání odkazuje na zdroj dat (bing.com) a odkazuje také na jiný dotaz nebo oddíl (společnost). Porušuje dříve zmíněné pravidlo (oddíl může přistupovat ke kompatibilním zdrojům dat nebo odkazovat na jiné oddíly, ale ne obojí).

Co dělat? Jednou z možností je úplně zakázat bránu firewall (prostřednictvím možnosti Ochrana osobních údajů s popiskem Ignorovat úrovně ochrany osobních údajů a potenciálně zvýšit výkon). Ale co když chcete bránu firewall nechat povolenou?

Pokud chcete chybu vyřešit bez zakázání brány firewall, můžete kombinovat Company a Search do jednoho požadavku, například takto:

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

Všechno se teď děje uvnitř jednoho oddílu. Za předpokladu, že úrovně ochrany osobních údajů pro oba zdroje dat jsou kompatibilní, brána firewall by nyní měla fungovat správně a už se nezobrazí chyba.

Tak a je hotovo.

I když je na tomto tématu toho možné říci mnohem víc, tento úvodní článek už je dost dlouhý. Doufejme, že vám to pomůže lépe porozumět bráně firewall a pomůže vám pochopit a opravit chyby brány firewall, když na ně v budoucnu narazíte.