Ostatnio zmagałem się z problemem w miarę eleganckiego opisu uprawnień użytkowników do aplikacji webowej. Chciałem skorzystać z wbudowanych mechanizmów Role Base Security, ale okazało się, że potrzebuję większej ziarnistości uprawnień.
Przykłady potrzebnych mi uprawnień:
- "użytkownicy powinni mieć dostęp do wszystkich ofert złożonych przez siebie, lecz aktualnie mogą edytować tylko oferty określonego typu, natomiast inne mogą tylko czytać",
- "zwykli użytkownicy nie mogą ustalać sygnatur złożonych ofert - to mogą czynić tylko managerowie",
- "różne typy ofert udostępniają do edycji różne zestawy pól" itd.
Przez pewien czas zastanawiałem się w jakiej postaci opisać uprawnienia. Pierwszy pomysł był taki, zdefiniować je w pliku xml, np.:
<Permisions>
<LearningOffers C="True" R="True" U="True" D="True">
<MainData>
<OfferId C="False" U="False" />
<Signature C="False" U="False" />
<Title OfferType="Standardowa" C="False" U="False" />
</MainData>
<Teams OfferType='Konkursowa,Standardowa' />
<TemplateOffers OfferType='Szablon' />
</LearningOffers>
<LearningOffers OwnOffer="True" R="True" />
<LearningOffers OfferType="Konkursowa" C="False" U="False"/>
</Permisions>
To nawet sensownie wygląda, choć powtarzanie gałęzi LearningOffers, tak by wpisać wszystkie możliwe warunki jest IMHO mało xml-owe.
Ale sprawa się komplikuje jak uwzględni się role, do których może przynależeć użytkownik. Wówczas dla każdej roli należałoby zdefiniować osobne uprawnienia, co znacząco wydłuży rozmiar definicji uprawnień dla wszystkich. Poza tym część użytkowników będzie należeć do kilku ról, więc dla nich należałoby wybrać największe dostępne uprawnienia.
Tak więc, nie przekreślając całkiem xml w samej aplikacji, postanowiłem definiować uprawnienia w innej postaci.
Create Table ComponentAuthorizations(
ComponentPath Varchar(1000) Not Null,
Role Varchar(30) Not Null,
Condition Varchar(200),
CanCreate Numeric(1) Not Null,
CanRead Numeric(1) Not Null,
CanUpdate Numeric(1) Not Null,
CanDelete Numeric(1) Not Null,
Constraint Uq_ComponentAuthorizations Unique(
ComponentPath, Role, Condition)
)
I potem przykładowe dane:
| ComponentPath | Role | Condition | CanCreate | CanRead | CanUpdate | CanDelete |
| Teachers | 0 | 0 | 0 | 0 | ||
| Teachers | Manager | 0 | 1 | 1 | 0 | |
| Teachers | Teacher | [OwnOffer] = true | 0 | 1 | 1 | 0 |
| Teachers/OldTeacherCode | Teacher | 0 | 1 | 0 | 0 | |
| Teachers/OldTeacherCode | Manager | 0 | 1 | 1 | 0 | |
| LearningOffers | Teacher | ([OfferType] = 2) and ([OwnOffer] = true) | 1 | 1 | 1 | 1 |
| LearningOffers | Teacher | [OfferType] = 1 | 0 | 1 | 0 | 0 |
| LearningOffers | Manager | 1 | 1 | 1 | 1 |
Założenia wyliczania uprawnień dla określonego komponentu:
- jeżeli użytkownik przynależy do kilku grup, to dla danego poziomu i warunku wybierane są najwyższe uprawnienia (CRUD),
- dla każdego poziomu uprawnienia są sumowane binarnie, czyli jeśli spełniony jest jakikolwiek warunek dla którego uprawnienia są typu 1, to ostateczne uprawnienia też będą wynosić 1,
- uprawnienia komponentów niższego rzędu (dzieci) nie mogą być wyższe niż uprawnienia rodziców.
Potem przemyślałem jeszcze powyższy schemat i postanowiłem obok poziomów uprawnień 0 (Absent, czyli brak uprawnień) i 1 (Grant, czyli są uprawnienia), dodać trzeci poziom uprawnień: -1 (Revoke), który będzie oznaczał wycofanie uprawnień: w przypadku spełnienia jakiegokolwiek warunku z poziomem -1 ostatecznie użytkownik nie będzie miał uprawnień do danego komponentu.
Ostateczna arytmetyka uprawnień:
| Lp. | Warunek pierwszy | Warunek drugi | Łączne uprawnienia |
| 1 | Absent | Absent | Absent |
| 2 | Absent | Grant | Grant |
| 3 | Grant | Absent | Grant |
| 4 | Grant | Grant | Grant |
| 5 | Grant | Revoke | Revoke |
| 6 | Absent | Revoke | Revoke |
I potem przykład wycofania uprawnień:
| LearningOffers | [ReferenceBookId] < 5 | -1 | -1 | -1 | -1 | |
| LearningOffers | Manager | [ReferenceBookId] < 5 | -1 | 1 | -1 | -1 |
Powyższe warunki zabierają wszystkim uprawnienia do odczytu i edycji ofert z ReferenceBookId mniejszym od 5, natomiast Managerowie mają w dalszym ciągu prawa do odczytu tychże ofert.
W następnym artykule napiszę o tym jak wykorzystałem tę definicję uprawnień na poziomie aplikacji.

0 komentarze:
Prześlij komentarz