Po článcích věnujících se systému zabezpečení CAS se dnes již plnou silou vrhneme na druhý systém zabezpečení, který je obsažen v .NET frameworku. Tímto bezpečnostním systémem je bezpečnost založená na rolích.
Základní koncept
I přesto, že jsme během článků o systému zabezpečení CAS nepoznali úplnou množinu možností, kterou nám tento systém nabízí, myslím, že mohu označit CAS za velmi propracovaný systém zabezpečení. Ovšem ne vždy nám k řízení přístupu ke chráněnému zdroji či operaci stačí systém CAS. V častých případech potřebujeme pro rozhodnutí udělení přístupu informaci o aktuálně pracujícím uživateli. Tuto problematiku řeší systém bezpečnosti založený na rolích. Stejně jako v operačních systémech se zde, jak název naznačuje, pracuje hlavně nikoli s jednotlivými uživateli (i když je to také možné), ale s rolemi.
Tento způsob je doporučen z jednoduchého důvodu. Pokud jsou na úrovni aplikace používány role, je mnohem snazší povolit dalším uživatelům přístup k chráněným operacím a zdrojům. Protože v případě užívání rolí je tohoto úkonu možno dosáhnout obsazením uživatele do dané role namísto doplnění aplikace o konkrétního uživatele a její rekompilaci. V systému zabezpečení založeného na rolích se při vykonávání kontroly role či identity nepoužívá procházení zásobníku (stack walking) jako se tomu děje v případě CAS. Tím pádem jsou také všechny kontroly rychlejší.
Principál
Za účelem reprezentace kontextu zabezpečení uživatele existují v .NET frameworku objekty typu principál. Pomocí těchto objektů, které mají jedno společné rozhraní IPrincipal, je možné zjistit zda-li je uživatel obsazen do určité role. Tyto objekty jsou automaticky modulem CLR připojovány k jednotlivým vláknům aplikace, tím pádem každé vlákno běží s bezpečnostním kontextem určitého uživatele. Typ objektu principálu je závislý na použitém způsobu autentikace. Standardně implementovaná je podpora pro:
• Ověřování pomocí mechanismu systému Windows
• Ověřování pomocí služby .NET passport
• Ověřování pomocí ASP .NET
Pokud by někomu tyto možnosti nestačily (což nevidím jako nereálné), je možné implementovat rozhraní IPrincipal vlastním typem, používaným pro daný způsob autentikace. Na rozhraní IPrincipal je obsažena jediná metoda, kterou je metoda IsInRole, pomocí které zjistíme jestli je uživatel, jehož kontext je reprezentován objektem principálu, obsazen do specifické role.
//nastavime politiku vazani identity AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
//ziskame objekt predstavujici uzivatele
IPrincipal principal = Thread.CurrentPrincipal;
//zjistime jestli je obsazen v roli Administrator
bool isAdmin = principal.IsInRole("Administrator");
if (isAdmin)
{
Console.WriteLine("Uzivatel je obsazen v roli Administrator");
}
else
{
Console.WriteLine("Uzivatel neni obsazen v roli Administrator");
}
Běhové prostředí CLR z bezpečnostních důvodů standardně nenaplní vlastnost CurrentPrincipal vlákna objektem představujícího kontext uživatele. Aby se tak dělo, je potřeba nastavit politiku aplikační domény pro vázání objektů principálů s vlákny, což zařídíme použitím metody SetPrincipalPolicy na objektu aplikační domény. V našem příkladu je použito svázání s účtem systému Windows (PrincipalPolicy.WindowsPrincipal). Poté je z aktuální vlákna vyzvednut objekt principála a použita na něm metoda IsInRole pro zjištění, jestli je uživatel obsazen do role Administrator.
Ke zjišťování rolí, do nichž je aktuální uživatel obsazen, se může hodit výčet WindowsBuiltInRole, který obsahuje názvy častých rolí vyskytujících se v instalacích systémů Windows NT, Windows 2000 a Windows XP.
//ziskani kontextu aktualniho uzivatele
IPrincipal principal = Thread.CurrentPrincipal;
//ziskani hodnot vyctu WindowsBuiltInRole
WindowsBuiltInRole[] roles = (WindowsBuiltInRole[]) Enum.GetValues(typeof(WindowsBuiltInRole));
//testovani roli uzivatele
foreach (WindowsBuiltInRole role in roles)
{
if (principal.IsInRole(role.ToString()))
{
Console.WriteLine("Aktualni uzivatel je obsazen do role {0}", role.ToString());
}
}
Identita uživatele
Kromě zjištění toho, jestli je uživatel obsazen do určité role, je možné získat informace o jeho identitě. Identity jsou podobně jako v případě principálů objekty s jedním společným rozhraním. Tímto rozhraním je rozhraní IIdentity a podobně jako u principálu i zde typ použitého objektu souvisí s použitým způsobem autentikace. K získání objektu identity můžeme použít vlastnosti Identity na objektu principála.
//ziskani objektu aktualniho uzivatele
IPrincipal principal = Thread.CurrentPrincipal;
//ziskani identity aktualniho uzivatele
IIdentity identity = principal.Identity;
//vypsani informaci o identite
Console.WriteLine("Zpusob autentikace: {0}", identity.AuthenticationType);
Console.WriteLine("Je autentikovan: {0}", identity.IsAuthenticated);
Console.WriteLine("Jmeno uzivatele: {0}", identity.Name);
Tímto způsobem bychom postupovali, pokud bychom chtěli používat programování oproti společnému rozhraní IIdentity. Pokud používáme mechanismu ověřování systému Windows, jsou ve skutečnosti používány objekty typu WindowsIdentity, které nám dovolují zjistit specifičtější informace o identitě uživatele. Rozhraní tohoto typu můžeme zajistit buď přetypováním vlastnosti Identity objektu principála, nebo použitím metody GetCurrent na objektu typu WindowsIdentity, což je použito v následujícím příkladu.
//ziskame aktualni identitu uzivatele windows
WindowsIdentity identity = WindowsIdentity.GetCurrent();
//zjistime, zda-li je uzivatel typu guest
Console.WriteLine("Je host? {0}", identity.IsGuest);
//zjistime, zda-li je uzivatel typu system
Console.WriteLine("Je system? {0}", identity.IsSystem);
//zjistime, zda-li je uzivatel anonymni
Console.WriteLine("Je anonymni? {0}", identity.IsAnonymous);
//ziskame token overeni systemu windows
Console.WriteLine("Token : {0}", identity.Token);
Použití deklarativního zabezpečení
Jistě si vzpomínáte, že v systému zabezpečení CAS bylo možné vyžadovat oprávnění jak standardním voláním metod na specifických typech (imperativní), tak pomocí specializovaných atributů (deklarativní). Podobně lze i v systému zabezpečení založeného na rolích využít atributů k realizaci deklarativní kontroly.
Za tímto účelem se nabízí k použití třída atributu PrincipalPermission, která stejně jako v případě deklarativního zabezpečení v CAS používá výčet SecurityAction spolu s dalšími parametry. Důležité je upozornit na skutečnost, že z výčtu SecurityAction mají efekt pouze hodnoty Demand, LinkDemand a InheritanceDemand. Ostatní hodnoty u tohoto typu atributu nefungují.
public class DeclarativePrincipalPermission
{
public static void UseMethods()
{
try
{
//pokud neni aktualni uzivatel clenem role Administrators
//bude vyhozena bezpecnostni vyjimka
MethodForAdministrators();
//pokud neni aktualni uzivatel Bob
//bude vyhozena bezpecnostni vyjimka
MethodForBob();
}
catch (SecurityException ex)
{
Console.WriteLine(ex.Message);
}
}
[PrincipalPermission(SecurityAction.Demand, Role="Administrator")]
public static void MethodForAdministrators()
{
string name = Thread.CurrentPrincipal.Identity.Name;
Console.WriteLine("Uzivatel {0} je clenem skupiny Administrators", name);
}
[PrincipalPermission(SecurityAction.Demand, Name = "PC\\Bob")]
public static void MethodForBob()
{
Console.WriteLine("Aktualni uzivatel je Bob");
}
}
Jak z uvedeného příkladu můžeme vidět, tak je možné deklarativně omezovat vykonání metody v závislosti na roli, ale i na jménu aktuálního uživatele.
Příklady ke článku naleznete zde.