otrdiena, 2010. gada 9. novembris

Pieteikšanās (logon) trigeri

Rakstā aprakstīts pieteikšanās (Logon) trigeris, apskatītas subjektīvi galvenās nianses, kas būtu jāņem vērā tos veidojot. Raksta beigās atsauces uz citiem avotiem, kur par tiem var palasīt vairāk.

Jau seniem laikiem ir zināmi trigeri, kas tika izmantoti datu manipulācijas vaicājumiem (DML insert/udpate/delete). Trigerus bija (un ir) iespējums izveidot tabulām uz skatiem, tos var veidot kā reaģēt uz attiecīgajiem notikumiem, vai kā rīkoties šo notikumu vietā (instead of trigeri un after trigeri).

SQL Server 2005 versijā ir nākuši klāt arī DDL (Data Definition Language) trigeri, kurus var izmantot lai reaģētu uz tādiem notikumiem kā Create/Alter/Drop. Ar to palīdzību, piemēram, var reģistrēt kādas izmaiņas veiktas skatos/procedūrās vai, piemēram, liegt tabulas izveidi. Tos var izmantot gan servera gan datu bāzes līmenī.

Papildus tam SQL Server 2005 ir nācis klāt vēl viens speciāls trigeris- pieteikšanās (logon) trigeris. Trigeris tiek izsaukts brīdī, kad tiek izveidota lietotāja sesija, bet vēl pirms ar to ir sākts strādāt. Tātad- ja lietotāja autentifikācija nenotiek (piemēram, nepareiza parole) trigeris netiek izsaukts. Šo trigeri var izmantot lai ierobežotu sesiju skaitu kādam lietotājam, reģistrēt pieslēgšanās notikumus, ...

Nianse starp šo trigeri un notikumu informāciju (event notification) ir tā, ka trigeris tiek izsaukts sinhroni, bet notikuma informācija ir asinhrona (sīkāk neiedziļinoties šajā rakstā). Uz šo niansi ir norādīts MSDN rakstā un plašāk aprakstīts cita emuāra rakstā par pieslēgšanās trigeriem.

Trigera darbības ilustrācijai izveidošu piemēru, kas reģistrē visus pieteikšanās (logon) notikumus. Notikumu reģistra tabulu atradīsies "myDB" datu bāzē. Tabulas izveides skripts:
Create Table myDB.dbo.ManiNotikumi
(
    ManiNotikumiID int primary key identity(1,1),
    LietotajaVards nvarchar(50),
    DatmsLaiks datetime not null default (getdate()),
    NotikumsEventData xml
)
Servera trigera izveides skripts ir šāds:
Create TRIGGER tr_LietotajsPiesledzas
ON ALL SERVER WITH EXECUTE AS SELF
FOR LOGON
AS
BEGIN
        Insert Into myDB.dbo.ManiNotikumi(NotikumsEventData, LietotajaVards)
            Values (EventData(), ORIGINAL_LOGIN())
END;
Tātad par niansēm, kurmā vajadzētu pievērst uzmanību:
  • tr_LietotajsPiesledzas ir vienkārši trigera nosaukums. lai apskatītu visus trigerus, kas ir izveidota servera līmenī, var izpildīt šādu vaicājumu:
    select * from sys.server_triggers
  • ON ALL SERVER- nozīmē to, ka trigeris tiek veidots visam serverim. Respektīvi visi lietotāji, kas pieslēdzas serverim izsauks šo trigeri. 
  • EXECUTE AS SELF Svarīgi! Uz servera esmu ar sysadmin tiesībām (lai nu cik laba prakse tā arī nebūtu..), līdz ar to- veidojot šo trigeri un norādot šos atslēgas vārdus- trigera iekšienē notiekošie notikumi izpildīsies tā, it kā tos izpildītu trigera izveidotājs. Tādēļ arī tabulas izveides skriptā nav dotas nekādas tiesības. (Šķiet laba alternatīva būtu veidot datu bāzi notikumu reģistrēšanai, kurā izveidot procedūru notikumu reģistrēšanai  un "public" lietotājam nodrošināt tiesības izpildīt šo procedūru.)
    Jāņem vērā- ja trigerī tiks ievietoti dati tabulā, kurā attiecīgajam lietotājam nav piekļuves tiesību- notiks kļūda, kļūda izsauks transakcijas atcelšanu, tas savukārt nozīmēs to, ka lietotājam neizdosies pieslēgties! Gadījumā ja izdodas šādu situāciju izveidot (šādiem vai savādākiem līdzekļiem)- raksta beigās literatūras avotos ir atsauce uz rakstu, kur skaidrots kā tikt ar problēmu galā.
  • EventData() satur informāciju par notikumu XML formātā. Šajā gadījumā es reģistra tabulā reģistrēju visu XML informāciju, kas saistīta ar pieteikšanās notikumu. Bet ir iespējams paņemt tikai daļu no šīs infromācijas.
  • ORIGINAL_LOGIN()- kā iepriekš minēts, "execute as self" nodrošina to, ka trigera izpildes iekšienē visas komandas izpildās tā, it kā tās izpildītu sa (vispārīgā gadījumā- lietotājs kas izveidoja trigeri). līdz ar to izpildot, piemēram "suser_sname()" tiks atgriezts trigera izveidotāja lietotāja vārds. Šajā gadījumā, protams, interesē tas lietotājs, kas trigeri ir izsaucis.
  • Vēl dažas, manuprāt noderīgas, funkcijas, kuras var var izmantot trigerī (un ne tikai): SYSTEM_USER, HOST_NAME(), USER, @@SPID, GETDATE(), APP_NAME() (no šī emuāra rakstiņš šajā sakarā- Programmas nosaukuma norādīšana), DB_NAME()

Trūkums šai realizācijai (kas vienlaicīgi arī ir "iespēja") ir tā, ka .NET programmas pašas "izdomā", kad atvērt/aizvērt konekciju. Līdz ar to pie pieslēgšanās šis trigeris var tikt (visdrīzāk tiek) izsaukts daudz reizes. mazliet var palasīt šeit. Kāpēc trūkums? Tāpēc ka realitātē vairāk gribētos zināt- kad lietotājs sāk strādāt ar jebkuru programmu (1 notikumus) un viss. Alternatīva- veidot pieslēgšanās notikumu reģistrēšanu programmā. Bet ja programma nav paša veidota.. Ar altenatīvām tā grūtāk.

Pēc sevis var satīrīt:
Drop Trigger tr_LietotajsPiesledzas on all server
Drop Table MyDb.dbo.ManiNotikumi
Papildus literatūra:

2 komentāri:

  1. Palasīju to norādīto pythian saiti un izskatās, ka, ja SQL Serverī salaiž dēlī logon trigeri, tad pieslēgties un to izlabot var būt vēl problemātiskāk, nekā Oraclē :)

    AtbildētDzēst
  2. Nē, īstenībā nav tik traki.
    Vienkāršākais veids (jābūt sysadmin'am)-
    (1) Management Studio uz tā datora, uz kura ir serveris (pieņemot ka nav atļautas Dedicated Administrator Connections no citurienes), bet NEPIESLĒGTIES (to uzreiz prasa palaižot SSMS).
    (2) paņemt jaunu vaicājumu logu (New Query) un tad, kad prasa konekciju- servera nosaukumam priekšā pielikt "Admin:"
    (3) vaicājuma logā var veikt visas vajadzīgās darbības. DAC konekcija trigeri "nešauj".

    var uzmest aci šeit http://msdn.microsoft.com/en-us/library/ms178068%28SQL.90%29.aspx

    AtbildētDzēst