Korzystając z Web Client Software Factory mamy możliwość zdefiniowania reguł bezpieczeństwa, które kontrolują dostęp użytkowników do zasobów. Uzytkownik należy do ról, role mają (bądź nie) uprawnienia wykonywania reguł, natomiast reguły przypisane są do konkretnych adresów URL. Proste. Przykładowy wpis w web.config (z modułu Customers w Order Management Reference Implementation) wygląda tak:
<authorization> <rule Url="~/Customers/Default.aspx" Rule="AllowSearchCustomers" /> <rule Url="~/Customers/SearchCustomers.aspx" Rule="AllowSearchCustomers" /> <rule Url="~/Customers/CustomerAutoCompleteService.asmx/GetCustomersName" Rule="AllowAutocomplete" /> </authorization>
Co w przypadku, gdybyśmy chcieli jakikolwiek dostęp do wszystkich stron w module Customers ograniczyć do ról posiadających prawa do wykonania reguły “AllowUseCustomers”? Prawdopodobnie należałoby w podobny sposób wylistować wszystkie strony z owego modułu… Ohyda.
Na szczęście architekturę WCSF, podobnie jak wszystkiego ze stajni zespołu Patterns & Practices, przemyślano tak, aby można było właściwie w każde newralgiczne miejsce wprząc własną logikę. Zobaczmy zatem jak poradzić sobie z powyższym problemem. Efekt, który chcemy uzyskać, to plik konfiguracyjny posiadający sekcję authorization wyglądającą następująco:
<authorization> <rule Url="~/Customers/.*" Rule="AllowUseCustomers" /> </authorization>
Na początek trzeba znaleźć kod odpowiedzialny za autoryzację. Po kilkunastowym flircie z Reflectorem wszystko staje się jasne: tą częścią systemu zajmuje się implementacja interfejsu Microsoft.Practices.CompositeWeb.Interfaces.IAuthorizationRulesService. Napiszmy w takim razie własną:
1: public class RegexAuthorizationRulesService : IAuthorizationRulesService 2: { 3: // each url can have multiplerules assigned 4: Dictionary<string, List<string>> _rules = new Dictionary<string, List<string>>(); 5: 6: /// <summary> 7: /// Assigns a new rule name for a given regex-based url. 8: /// </summary> 9: public void RegisterAuthorizationRule(string urlPath, string rule) 10: { 11: // create a new list of rules if a given url has not yet been been registered 12: if (_rules.ContainsKey(urlPath) == false) 13: _rules.Add(urlPath, new List<string>()); 14: 15: _rules[urlPath].Add(rule); 16: } 17: 18: /// <summary> 19: /// Retrieves all rules that were assigned to regexes that match a given url path. 20: /// </summary> 21: public string[] GetAuthorizationRules(string urlPath) 22: { 23: var rulesList = from entry in _rules 24: where Regex.IsMatch(urlPath, entry.Key, RegexOptions.IgnoreCase) 25: select entry.Value; 26: 27: // "flatten" the enumerable of List<string> to a one-dimensional array of strings 28: var allRules = rulesList.SelectMany(list => list) 29: .ToArray(); 30: 31: return allRules; 32: } 33: } 34:
Ślicznie – mamy już komponent gotowy do “wstrzyknięcia”. Tylko gdzie? Po kolejnych kilku minutach analizy WCSF znamy odpowiedź: standardowo wykorzystywana jest Microsoft.Practices.CompositeWeb.Authorization.AuthorizationRulesService
. Fabryka rejestruje ją podczas startu aplikacji w swojej klasie Microsoft.Practices.CompositeWeb.WebClientApplication
. A dokładniej: w metodzie AddRequiredServices wywoływanej z Application_Start. Musimy zmienić to zachowanie. Najprościej będzie odziedziczyć z klasy aplikacji i nadpisać standardową metodę, co wygląda tak:
1: public class CustomWebApplication : WebClientApplication 2: { 3: protected override void AddRequiredServices() 4: { 5: this.RootContainer.Services.AddNew<RegexAuthorizationRulesService, IAuthorizationRulesService>(); 6: 7: base.AddRequiredServices(); 8: } 9: }
Tym sposobem zarejestrujemy własny serwis przed rejestracją swoich usług przez WCSF – i dokładnie o to nam chodzi.
Na koniec zostało jedynie poinstruowanie systemu, że od teraz należy wykorzystywać właśnie stworzoną klasę aplikacji. W tym celu otwieramy plik Global.asax i podmieniamy wymienioną tam klasę na własną:
<%@ Application Language="C#" Inherits="CustomNamespace.CustomWebApplication, CustomAssembly" %>
I to by było na tyle – od teraz możemy dowolnie przypisywać reguły adresom URL przy pomocy wyrażeń regularnych.