Műszaki cikk

XLSX munkalap-védelem Delphiben: 15 engedélyezési lehetőség

Átad egy elkészült munkafüzetet egy kollégájának, és arra kéri, hogy szűrje azt, ne pedig újraírja. Ezért védi a munkalapot. A régebbi HotXLS verziókban ez a lépés mindig egyetlen dolgot írt a fájlba: <sheetProtection sheet="1" objects="1" scenarios="1"/>, mereven kódolva. A munkalap lezárult, a jelszó-hash csatolásra került, és a felhasználó egyáltalán semmit sem tehetett, még a rendezést és szűrést sem, amit valójában nyitva akart hagyni. Az Excel saját „Munkalap védelme” párbeszédpanele pontosan ezért tartalmaz tizenöt jelölőnégyzetet, és a motor ezek egyikét sem tudta kifejezni. Ezt a hiányt zárja be a v2.91.0 védelmi modellje

A HotXLS egy natív VCL táblázatkezelő komponens Delphihez és C++Builderhez, amely Excel telepítése nélkül olvassa és írja az XLS és XLSX fájlokat. Ez a cikk a munkalap-védelem XLSX oldaláról szól: az új TXLSXSheetProtectionOption felsorolásról (enum), az egyes engedélyeket kapcsoló AllowOption tulajdonságról, valamint arról az egy OOXML kódolási szabályról, amely mindenkit megzavar, aki kézzel ír egy <sheetProtection> elemet

Mit véd valójában a munkalap-védelem

Először is tisztázzuk a határokat, mert ez dönti el, mennyire bízhat ebben a megoldásban. A munkalap-védelem az OOXML táblázatkezelő formátumban (ECMA-376) egy interakciós házirend, nem pedig titkosítás. Megmondja a szabályos alkalmazásnak, hogy mely szerkesztéseket utasítsa el, miközben a munkalap védett. A cellaértékek továbbra is egyszerű szövegként ülnek az xl/worksheets/sheetN.xml fájlban; bontsa ki a .xlsx fájlt, és ott vannak. Az opcionális jelszó rövid, örökölt hash-ként tárolódik, nem pedig olyan kulcsként, amely bármit is összekeverne. Bárki, aki átnevezi a fájlt, megnyitja a részt, és eltávolítja a <sheetProtection> sort, mindent elolvashat és szerkeszthet

Így a védelem arra válaszol, hogyan „akadályozzam meg, hogy a munkatársam véletlenül elrontson egy képletet”, nem pedig arra, hogyan „tartsam titokban ezeket az adatokat egy motivált személlyel szemben”. Ezek különböző problémák különböző eszközökkel. Ha titkosságra van szüksége, akkor a munkafüzet-szintű titkosítást szeretné, amelyről a AES-védett XLSX kimenet című cikk szól, és amely ténylegesen titkosítja a csomagot. A munkalap-védelem és a munkafüzet-titkosítás tisztán összekapcsolódik, de csak a második jelent valódi zárat. Tartsa ezt a határvonalat tisztán, és a lap többi része már csak technikai részlet

A tizenöt lehetőség és az AllowOption tulajdonság

Minden munkalap mostantól a TXLSXSheetProtectionOption értékek készletét hordozza, leírva, hogy a felhasználó mit tehet még, miközben a munkalap védett. A tagok egy az egyben leképeződnek az OOXML attribútumokra és az Excel párbeszédpanel jelölőnégyzeteire:

  • xlsxSpoEditObjects, xlsxSpoEditScenarios — rajzobjektumok és „mi lenne ha” forgatókönyvek szerkesztése
  • xlsxSpoFormatCells, xlsxSpoFormatColumns, xlsxSpoFormatRows — cellák, oszlopok, sorok újraformázása
  • xlsxSpoInsertColumns, xlsxSpoInsertRows, xlsxSpoInsertHyperlinks — oszlopok, sorok, hivatkozások beszúrása
  • xlsxSpoDeleteColumns, xlsxSpoDeleteRows — oszlopok, sorok törlése
  • xlsxSpoSelectLockedCells, xlsxSpoSelectUnlockedCells — kijelölés mozgatása zárolt vagy feloldott cellákra
  • xlsxSpoSort, xlsxSpoAutoFilter, xlsxSpoPivotTables — tartományok rendezése, AutoSzűrő legördülő menük használata, PivotTáblák kezelése

Az egyes biteket a TXLSXWorksheet indexelt AllowOption tulajdonságán keresztül olvashatja és írhatja. Az AllowOption[Opt] = True azt jelenti, hogy a művelet engedélyezett; False-ra állítása megtiltja azt. A teljes készlet egyszerre is elérhető a SheetProtectionOptions tulajdonságon keresztül, amely egy TXLSXSheetProtectionOptions (egy egyszerű Pascal set of), így elmentheti, visszaállíthatja vagy teljes egészében kicserélheti azt

Az alapértelmezés számít és szándékos: a frissen létrehozott munkalap minden engedélyezett opcióval indul. A konstruktor a SheetProtectionOptions tulajdonságot a teljes tartománnyal tölti fel: [Low(TXLSXSheetProtectionOption)..High(TXLSXSheetProtectionOption)]. Innen szűkítheti a kört a megtiltani kívánt műveletek kizárásával, ahelyett, hogy a semmiből építene fel egy engedélykészletet. Ez a választás teszi lehetővé, hogy az író alábbi kódolási szabálya összhangban legyen az Excel viselkedésével

Munkalap védelme, de a rendezés és szűrés nyitva hagyása

Itt a gyakori eset elejétől a végéig: védjen meg egy elkészült jelentést, hogy az elrendezés ne legyen módosítható, de engedje meg az olvasónak a rendezést és a szűrést. Vegye figyelembe, hogy a Protect és az opciók függetlenek egymástól. A Protect védett állapotba helyezi a lapot és eltárolja az opcionális jelszó-hasht; nem nyúl az opciókészlethez. Az AllowOption tulajdonságot külön állíthatja be, és a kapcsolók a munkalap védelme és mentése után lépnek életbe

var
  wb: TXLSXWorkbook;
  sh: TXLSXWorksheet;
begin
  wb := TXLSXWorkbook.Create;
  try
    sh := wb.Sheets.Add('Protected');
    sh.Cells[1, 1].Value := 'Region'; sh.Cells[1, 2].Value := 'Units';
    sh.Cells[2, 1].Value := 'North';  sh.Cells[2, 2].Value := 120;
    sh.Cells[3, 1].Value := 'South';  sh.Cells[3, 2].Value := 98;

    // Védelem jelszóval. Ez csak a védett állapotot + hasht állítja be;
    // az opciókészlet az összes engedélyezett alapértelmezésen marad.
    sh.Protect('HotXLS-2026');

    // Szűkítés: rendezés + AutoSzűrő megtartása, módosítás és formázás megtiltása.
    sh.AllowOption[xlsxSpoSort]          := True;
    sh.AllowOption[xlsxSpoAutoFilter]    := True;
    sh.AllowOption[xlsxSpoFormatCells]   := False;
    sh.AllowOption[xlsxSpoFormatColumns] := False;
    sh.AllowOption[xlsxSpoFormatRows]    := False;
    sh.AllowOption[xlsxSpoInsertRows]    := False;
    sh.AllowOption[xlsxSpoDeleteRows]    := False;

    if wb.SaveAs('protection.xlsx') <> 1 then
      Writeln('SaveAs failed');
  finally
    wb.Free;
  end;
end;

Két dolog olvasható le ebből a kódrészletből. A Sort és AutoFilter sorok kifejezetten le vannak írva, bár mindkettő alapértelmezetten True; ez a dokumentáció a következő karbantartónak szól, nem pedig funkcionális követelmény. És mivel az alapértelmezések megengedőek, az egyetlen sorok, amelyek megváltoztatják a kimeneti fájlt, azok, amelyek egy opciót False értékre állítanak. Ez nem az API véletlene, hanem az OOXML átviteli formátumának megjelenése, amiről a következő szakasz szól

A kódolási szabály: az elhagyás engedélyezést jelent, az attr=0 megtiltást jelent

Ez az egyetlen ellenértelmű tény a teljes funkcióban, és itt szokott a kézzel írt <sheetProtection> elromlani. Az OOXML-ben minden műveletenkénti attribútum egy tiltó jelző, és annak hiánya engedélyezést jelent. A hiányzó attribútum azt jelenti, hogy a művelet engedélyezett. A "0" értékként leírt attribútum azt jelenti, hogy a művelet tilos, miközben a munkalap védett. Egy jól formázott fájlban nincs olyan, hogy formatCells="1", ami azt jelentené, hogy „a formázás engedélyezett”; egyszerűen el kell hagyni az attribútumot. (A hiányzó attribútum alapértelmezett értéke az OOXML logikai true alapértelmezése, és ezek az attribútumok úgy vannak elnevezve, hogy a „true” a megfelelő szerkesztés engedélyezését jelenti)

A HotXLS író pontosan ezt tükrözi. Kibocsátja a sheet="1" értéket a védelem bekapcsolásához, majd végigjárja az opciókészletet, és csak a False értékre állított opciókhoz ír attr="0"-t. Az engedélyezett műveletek semmivel sem járulnak hozzá a kimenethez. Így az előző szakaszban bemutatott munkafüzet valami hasonlóra szerializálódik, csak a tiltott műveleteket és a jelszó-hasht hordozva:

// Elvi kimenet a fenti részlethez (az attribútumok a rövidség kedvéért elhagyva):
// <sheetProtection sheet="1"
//   formatCells="0" formatColumns="0" formatRows="0"
//   insertRows="0" deleteRows="0"
//   password="...4-hex..."/>
// Figyelje meg, mi NINCS ott: nincs sort, nincs autoFilter, no selectLockedCells.
// Hiányuk pontosan az, ami jelzi az Excelnek, hogy ezek a műveletek engedélyezettek maradnak.

Ha a régi mereven kódolt karakterláncból jött, és arra számított, hogy minden attribútumot kiírva lát, ez ritkásnak, szinte hibásnak tűnhet. Ez helyes. Egy olyan fájl, amely a sort="1" és autoFilter="1" értékeket listázná, ugyanazt jelentené a szabályos megjelenítőnek, de az Excel maga is a minimális tiltó formát írja, és ennek követése kis méretű diffeket és unalmas oda-vissza utakat tesz lehetővé. Az objects és scenarios attribútumok ugyanezt a szabályt követik: alapértelmezetten engedélyezettek, így csak "0"-ként jelennek meg, ha megtiltja őket, ami az unconditionally kibocsátott régi objects="1" scenarios="1" fordítottja

A védelem visszaolvasása: oda-vissza hűség

Egy olyan engedélyezési modell, amelyet csak írni lehet, de olvasni nem, egyirányú ajtó, és a szokásos tünet egy olyan betöltés-szerkesztés-mentés ciklus, amely csendben kibővíti az engedélyeket. A HotXLS ezt bezárja. Amikor a ParseWorksheetXml találkozik egy <sheetProtection> elemmel, védetté állítja a lapot, rögzíti a jelszó-hasht, ha jelen van, majd visszafejti az egyes műveletenkénti attribútumokat a AllowOption-ba, ugyanazt a konvenciót alkalmazva fordítva: a jelen lévő és "0" értékű attribútum megtiltja a műveletet; a hiányzó attribútum pedig az engedélyezett alapértelmezésen hagyja az opciót

var
  wb: TXLSXWorkbook;
  sh: TXLSXWorksheet;
begin
  wb := TXLSXWorkbook.Create;
  try
    wb.LoadFromFile('protection.xlsx');
    sh := wb.Sheets[1];                  // Az XLSX munkalapok 1-alapúak
    if sh.IsProtected then
    begin
      Writeln('Protected; password hash present: ',
        sh.SheetProtectHash <> '');
      Writeln('Sort allowed:       ', sh.AllowOption[xlsxSpoSort]);
      Writeln('AutoFilter allowed: ', sh.AllowOption[xlsxSpoAutoFilter]);
      Writeln('FormatCells allowed:', sh.AllowOption[xlsxSpoFormatCells]);
    end;
  finally
    wb.Free;
  end;
end;

Töltse be az író által létrehozott fájlt, és a Sort és AutoFilter értékeket True, a FormatCells értéket pedig False-ként kapja vissza — a mentett készlet sértetlen marad. Ez a szimmetria a lényeg: szerkesszen egyetlen cellát egy védett, részben engedélyezett munkalapon, majd mentse újra, és az a tizennégy engedély, amelyhez nem nyúlt hozzá, fennmarad ahelyett, hogy visszaesne a régi mindent-vagy-semmit alapértelmezésre

Praktikus megjegyzések és korlátok

Néhány dolog, amit érdemes tudni, mielőtt ezt beépítené a jelentéskészítő folyamatba:

  • A jelszó tervezéséből adódóan gyenge. Az XLSX munkalap-védelem egy 16 bites örökölt hasht tárol (ugyanazt, amelyet az Excel évtizedek óta használ), amely az interoperabilitás érdekében maradt meg. Megakadályozza a véletlen szerkesztéseket; nem áll ellen a támadónak. Ne kezelje titoktartóként. Valódi védelemhez titkosítsa a munkafüzetet
  • Az opciók beállítása a védelem előtt is lehetséges. A AllowOption érték hozzárendelhető függetlenül attól, hogy a lap jelenleg védett-e vagy sem; a kapcsolók egyszerűen azt írják le, mit fog engedélyezni a védelem, amint a Protect életbe lép. Az UnProtect törli a védett állapotot és a hasht, de az opciókészletet a helyén hagyja a következő alkalomra
  • A zárolt cella szemantika továbbra is érvényes. A védelem csak azokat a cellákat blokkolja, amelyeknek a Locked attribútuma be van állítva (ez a munkafüzet alapértelmezése). Egy beviteli terület szerkeszthetővé tétele a cellastílus feladata, nem pedig védelmi opció; a kai réteg ugyanúgy kombinálódik, mint az Excelben
  • Ez az XLSX motor. Az opciómodell tükrözi az XLS motor régebbi Allow* tulajdonságait, de az enum és tulajdonságnevek itt (xlsxSpo*, AllowOption) a TXLSXWorksheet-hez tartoznak az lxHandleX-ben. Ha ugyanazokon a lapokon a nyomtatási elrendezést is vezérli, a védelem és oldalbeállítás útmutató bemutatja, hogyan helyezkednek el ezek a beállítások a nyomtatási területek és fejlécek mellett, míg a adatérvényesítés, AutoSzűrő és táblázatok természetes módon párosul a xlsxSpoAutoFilter engedélyezve hagyásával egy zárolt jelentésen

A részletes védelmi modell és az XLSX olvasó/író motor többi része a HotXLS Component csomagban érkezik Delphihez és C++Builderhez; a termékoldal tartalmazza a teljes munkalap API-t, beleértve a teljes védelmi opció referenciát is