Tekninen artikkeli

XLSX-taulun suojaus Delphissä: 15 Allow-vaihtoehtoa

Annat valmiin työkirjan kollegalle ja pyydät häntä suodattamaan sitä, ei kirjoittamaan sitä uudelleen. Siksi suojaat taulukon. Vanhemmissa HotXLS-käännöksissä tämä ele kirjoitti tiedostoon aina saman rivin: <sheetProtection sheet="1" objects="1" scenarios="1"/>, kovakoodattuna joka kerta. Taulukko lukittui, salasanan tiiviste liitettiin mukaan, eikä käyttäjä voinut tehdä yhtään mitään — ei edes lajitella ja suodattaa, jonka olisit oikeasti halunnut jättää avoimeksi. Excelin omassa "Protect Sheet" -valintaikkunassa on juuri tästä syystä viisitoista valintaruutua, eikä moottori pystynyt ilmaisemaan yhtäkään niistä. Tämän aukon sulkee v2.91.0:n suojausmalli

HotXLS on natiivi VCL-laskentataulukkokomponentti Delphille ja C++Builderille, joka lukee ja kirjoittaa XLS- ja XLSX-tiedostoja ilman asennettua Exceliä. Tämä artikkeli käsittelee laskentataulukon suojauksen XLSX-puolta: uutta TXLSXSheetProtectionOption-enumia, AllowOption-ominaisuutta, joka kytkee kunkin oikeuden päälle tai pois, sekä sitä yhtä OOXML-koodaussääntöä, johon kompastuu jokainen, joka kirjoittaa <sheetProtection>-elementin käsin

Mitä laskentataulukon suojaus todella vartioi

Aloitetaan rajanvedosta, sillä se ratkaisee, kuinka paljon tähän kaikkeen kannattaa luottaa. Laskentataulukon suojaus OOXML-taulukkolaskentamuodossa (ECMA-376) on vuorovaikutuskäytäntö, ei salaus. Se kertoo standardia noudattavalle sovellukselle, mitkä muokkaukset on evättävä taulukon ollessa suojattu. Solujen arvot lojuvat edelleen selväkielisenä tiedostossa xl/worksheets/sheetN.xml; pura .xlsx-tiedosto, niin ne ovat suoraan siellä. Valinnainen salasana tallennetaan lyhyenä vanhan mallin tiivisteenä, ei avaimena, joka sekoittaisi mitään. Kuka tahansa, joka nimeää tiedoston uudelleen, avaa osan ja poistaa <sheetProtection>-rivin, pääsee lukemaan ja muokkaamaan kaikkea

Suojaus siis vastaa kysymykseen "estä kollegaani töpeksimästä kaavaa vahingossa", ei kysymykseen "pidä tämä data salassa joltakulta, joka on motivoitunut murtautumaan". Nämä ovat eri ongelmia, ja niihin on eri työkalut. Jos tarvitset luottamuksellisuutta, haluat työkirjatason salauksen, jota käsitellään artikkelissa AES-suojattu XLSX-tuloste ja joka todella salaa paketin. Taulukon suojaus ja työkirjan salaus yhdistyvät saumattomasti, mutta vain jälkimmäinen on lukko. Pidä tämä raja kirkkaana mielessä, niin loppu tästä sivusta on pelkkää putkitusta

Kaavio vertailee Delphi XLSX -taulukkosuojausta HotXLS:ssä vuorovaikutuskäytäntönä ja AES-työkirjasalausta ainoana todellisena lukkona
Taulukkosuoja kieltäytyy muokkauksista, kun taas arvot pysyvät selkotekstinä; vain työkirjan salaus salakirjoittaa paketin

Viisitoista vaihtoehtoa ja AllowOption-ominaisuus

Jokaisella laskentataulukolla on nyt joukko TXLSXSheetProtectionOption-arvoja, jotka kuvaavat, mitä käyttäjä voi yhä tehdä taulukon ollessa suojattu. Jäsenet vastaavat yksi yhteen OOXML-attribuutteja ja Excelin valintaikkunan valintaruutuja:

  • xlsxSpoEditObjects, xlsxSpoEditScenarios — muokkaa piirrosobjekteja ja mitä jos -skenaarioita
  • xlsxSpoFormatCells, xlsxSpoFormatColumns, xlsxSpoFormatRows — muotoile soluja, sarakkeita, rivejä
  • xlsxSpoInsertColumns, xlsxSpoInsertRows, xlsxSpoInsertHyperlinks — lisää sarakkeita, rivejä, linkkejä
  • xlsxSpoDeleteColumns, xlsxSpoDeleteRows — poista sarakkeita, rivejä
  • xlsxSpoSelectLockedCells, xlsxSpoSelectUnlockedCells — siirrä valinta lukittuihin tai lukitsemattomiin soluihin
  • xlsxSpoSort, xlsxSpoAutoFilter, xlsxSpoPivotTables — lajittele alueita, käytä AutoFilter-pudotusvalikoita, työskentele PivotTablejen kanssa

Yksittäisiä bittejä luetaan ja kirjoitetaan TXLSXWorksheet-luokan indeksoidun AllowOption-ominaisuuden kautta. AllowOption[Opt] = True tarkoittaa, että toiminto on sallittu; asettamalla se arvoon False se kielletään. Koko joukkoon pääsee käsiksi myös kerralla SheetProtectionOptions-ominaisuuden kautta, joka on TXLSXSheetProtectionOptions (tavallinen Pascalin set of-joukko), joten voit tallentaa, palauttaa tai korvata sen kokonaan

Oletusarvolla on merkitystä, ja se on tietoinen valinta: vasta luotu laskentataulukko käynnistyy tilassa, jossa kaikki vaihtoehdot ovat sallittuja. Konstruktori alustaa SheetProtectionOptions-joukon koko vaihteluvälillä, [Low(TXLSXSheetProtectionOption)..High(TXLSXSheetProtectionOption)]. Siitä eteenpäin kavennat joukkoa sulkemalla pois ne toiminnot, jotka haluat kieltää, sen sijaan että rakentaisit oikeusjoukon tyhjästä. Juuri tämä valinta saa alla kuvatun kirjoittimen koodaussäännön täsmäämään Excelin käytöksen kanssa

Taulukon suojaus, mutta lajittelu ja suodatus avoinna

Tässä on yleinen tapaus alusta loppuun: suojaa valmis raportti niin, ettei sen asettelua voi muokata, mutta anna lukijan lajitella ja suodattaa sitä. Huomaa, että Protect ja vaihtoehdot ovat toisistaan riippumattomia. Protect kytkee taulukon suojattuun tilaan ja tallentaa valinnaisen salasanan tiivisteen; se ei koske vaihtoehtojoukkoa. AllowOption-arvoja säädetään erikseen, ja kytkimet astuvat voimaan, kun taulukko on suojattu ja tallennettu

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;

    // Suojaa salasanalla. Tämä asettaa vain suojatun tilan ja tiivisteen;
    // vaihtoehtojoukko jää oletusarvoisesti kaikki sallivaksi.
    sh.Protect('HotXLS-2026');

    // Kavenna: säilytä lajittelu ja AutoFilter, kiellä uudelleenmuotoilu ja muotoilu.
    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;

Kaksi asiaa tuosta pätkästä kannattaa huomata. Sort- ja AutoFilter-rivit kirjoitetaan näkyviin, vaikka molempien oletusarvo on True; tämä on dokumentaatiota seuraavalle ylläpitäjälle, ei toiminnallinen vaatimus. Ja koska oletukset ovat salliviksi asetettuja, ainoat rivit, jotka muuttavat tulostiedostoa, ovat ne, jotka asettavat vaihtoehdon arvoksi False. Tämä ei ole tämän API:n sattumaa, vaan OOXML:n siirtomuoto paistaa läpi, mikä on seuraavan osion aihe

Koodaussääntö: puuttuminen tarkoittaa sallittua, attr=0 tarkoittaa kiellettyä

Tämä on koko ominaisuuden ainoa vastaintuitiivinen seikka, ja juuri tähän käsin kirjoitettu <sheetProtection> yleensä kompastuu. OOXML:ssä jokainen toimintokohtainen attribuutti on kielto-lippu, ja sen puuttuminen tarkoittaa lupaa. Puuttuva attribuutti tarkoittaa, että toiminto on sallittu. Attribuutti, jonka arvo on "0", tarkoittaa, että toiminto on kielletty taulukon ollessa suojattu. Kelvollisessa tiedostossa ei ole merkintää formatCells="1" tarkoittamassa "muotoilu on sallittua"; attribuutti yksinkertaisesti jätetään pois. (Puuttuvan attribuutin oletusarvo on OOXML:n totuusarvon oletus tosi, ja nämä attribuutit on nimetty niin, että "tosi" tarkoittaa, että vastaava muokkaus on sallittu.)

HotXLSin kirjoitin peilaa tämän täsmälleen. Se kirjoittaa sheet="1", kun suojaus kytketään päälle, käy sitten läpi vaihtoehtojoukon ja kirjoittaa attr="0" vain niille vaihtoehdoille, jotka on asetettu arvoon False. Sallitut toiminnot eivät tuota tulosteeseen mitään. Näin edellisen osion työkirja serialisoituu suunnilleen tähän tapaan, sisältäen vain kielletyt toiminnot sekä salasanan tiivisteen:

// Käsitteellinen tuloste yllä olevalle pätkälle (attribuutteja lyhennetty selkeyden vuoksi):
// <sheetProtection sheet="1"
//   formatCells="0" formatColumns="0" formatRows="0"
//   insertRows="0" deleteRows="0"
//   password="...4-hex..."/>
// Huomaa, mitä siellä EI ole: ei sort, ei autoFilter, ei selectLockedCells.
// Niiden puuttuminen on juuri se, mikä kertoo Excelille, että nuo toiminnot pysyvät sallittuina.

Jos tulet vanhasta kovakoodatusta merkkijonosta ja odotit näkeväsi jokaisen attribuutin kirjoitettuna auki, tämä näyttää niukalta, lähes väärältä. Se on kuitenkin oikein. Tiedosto, jossa luetellaan sort="1" ja autoFilter="1", tarkoittaisi standardia noudattavalle lukijalle samaa asiaa, mutta Excel itse kirjoittaa minimaalisen, vain kiellot sisältävän muodon, ja sen jäljittely pitää diffit pieninä ja round-tripit tylsinä. objects- ja scenarios-attribuutit noudattavat samaa sääntöä: ne ovat oletuksena sallittuja, joten ne näkyvät vain arvolla "0", kun ne kielletään, mikä on päinvastoin kuin vanha objects="1" scenarios="1", joka kirjoitettiin ehdoitta

Kaavio viidestätoista HotXLS TXLSXSheetProtectionOption -arvosta ryhmiteltynä kuuteen perheeseen ja kytkettynä AllowOption-indeksoituvan ominaisuuden ja SheetProtectionOptions-joukon kautta Delphissä
Viisitoista asetusta ryhmäytyvät kuuteen perheeseen, joita jokainen kytketään bitti bitiltä tai vaihdetaan kokonaisuudessaan Pascal-joukkona

Suojauksen lukeminen takaisin: round-trip-tarkkuus

Oikeusmalli, jota voi kirjoittaa mutta ei lukea takaisin, on yksisuuntainen ovi, ja tavallinen oire on lataa-muokkaa-tallenna-kierto, joka hiljalleen laajentaa oikeuksia. HotXLS sulkee tämän aukon. Kun ParseWorksheetXml osuu <sheetProtection>-elementtiin, se asettaa taulukon suojatuksi, tallentaa salasanan tiivisteen, jos sellainen on, ja purkaa sitten jokaisen toimintokohtaisen attribuutin takaisin AllowOption-arvoiksi käyttäen samaa käytäntöä käänteisesti: läsnä oleva attribuutti, jonka arvo on "0", kieltää toiminnon; puuttuva attribuutti jättää vaihtoehdon oletusarvoisesti sallituksi

var
  wb: TXLSXWorkbook;
  sh: TXLSXWorksheet;
begin
  wb := TXLSXWorkbook.Create;
  try
    wb.Open('protection.xlsx');
    sh := wb.Sheets[1];                  // XLSX-taulukot ovat 1-pohjaisia
    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;

Lataa kirjoittimen tuottama tiedosto, ja saat takaisin Sort- ja AutoFilter-arvot muodossa True, FormatCells-arvon muodossa False — täsmälleen sen joukon, jonka tallensit. Juuri tämä symmetria on koko pointti: muokkaa yhtä solua suojatussa, osittain sallitussa taulukossa ja tallenna uudelleen, ja ne neljätoista oikeutta, joihin et koskenut, säilyvät sen sijaan että romahtaisivat takaisin vanhaan kaikki-tai-ei-mitään-oletukseen

Kaavio HotXLS XLSX -sheetProtection-koodaussäännöstä Delphissä, jossa jätetty attribuutti sallii toimenpiteen ja attr=0 kieltää sen, vertaillen vanhaa kovakoodattua riviä minimaalisen vain-kieltävä-kirjoittajan tuotokseen
Sallittu toiminto ei tuo mukanaan lainkaan attribuuttia; vain kielletyt toiminnot ilmestyvät attr=0-muodossa

Käytännön huomautukset ja rajat

Muutama asia kannattaa tietää, ennen kuin kytket tämän raporttiputkeen:

  • Salasana on tarkoituksella heikko. XLSX-laskentataulukon suojaus tallentaa 16-bittisen vanhan mallin tiivisteen (saman, jota Excel on käyttänyt vuosikymmeniä), säilytettynä täällä yhteensopivuuden vuoksi. Se estää vahingossa tapahtuvat muokkaukset; se ei kestä hyökkääjää. Älä kohtele sitä salaisuuksien vartijana. Todellista suojaa varten salaa työkirja
  • Asetusten määrittäminen ennen suojausta on sallittua. AllowOption voidaan asettaa riippumatta siitä, onko taulukko juuri nyt suojattu; kytkimet vain kuvaavat, mitä suojaus tulee sallimaan, kun Protect astuu voimaan. UnProtect poistaa suojatun tilan ja tiivisteen, mutta jättää vaihtoehtojoukkosi paikalleen seuraavaa kertaa varten
  • Lukittujen solujen semantiikka pätee edelleen. Suojaus estää muokkaukset vain soluihin, joiden Locked-attribuutti on asetettu (työkirjan oletusarvo). Syöttöalueen jättäminen muokattavaksi on solutyylin tehtävä, ei suojausvaihtoehto; nämä kaksi kerrosta yhdistyvät samalla tavalla kuin Excelissä
  • Tämä on XLSX-moottori. Vaihtoehtomalli heijastelee XLS-moottorin vanhempia Allow*-ominaisuuksia, mutta tässä käytetyt enum- ja ominaisuusnimet (xlsxSpo*, AllowOption) kuuluvat TXLSXWorksheet-luokkaan yksikössä lxHandleX. Jos ohjaat myös tulostusasettelua samoilla taulukoilla, suojauksen ja sivun asetusten läpikäynti kertoo, miten nämä asetukset toimivat yhdessä tulostusalueiden ja ylätunnisteiden kanssa, ja tietojen validointi, AutoFilter ja taulukot sopii luontevasti yhteen sen kanssa, että xlsxSpoAutoFilter jätetään auki lukitulla raportilla

Hienojakoinen suojausmalli ja loppu XLSX-luku- ja kirjoitusmoottorista toimitetaan HotXLS-Delphi-komponentissa Delphille ja C++Builderille; tuotesivulla on koko laskentataulukon API mukaan lukien täydellinen suojausvaihtoehtojen viiteopas