This post assumes that the reader has working knowledge of version 2.0 of the Web Client Software Factory.
1. Every request is run through the HandleAuthorization method in the WebClientAuthorizationModule. A delegate is specified that is hooked into the AuthorizeRequest event; this delegate calls the HandleAuthorization method.
public void Init(HttpApplication httpApplication)
{
EventHandler handler = null;
ICompositionContainer rootContainer = httpApplication.Application["__RootContainer__"] as ICompositionContainer;
if (rootContainer != null)
{
if (handler == null)
{
handler = delegate (object sender, EventArgs e) {
IHttpContext context = new HttpContext(httpApplication.Context);
this.HandleAuthorization(rootContainer, context);
};
}
httpApplication.AuthorizeRequest += handler;
}
}
2. The HandleAuthorization() method uses the AuthorizationRulesService to get all authorization rules associated with the request. That list is enumerated, and for each the AuthorizationService is used to check to make sure that the user is able to view the requested URL.
protected virtual void HandleAuthorization(ICompositionContainer rootContainer, IHttpContext context)Question: Where does the concrete implementation of the IAuthorizationService get set? Answer: in the ShellModuleInitializer. Developers are directed to set this to EnterpriseLibraryAuthorizationService.
{
if (!context.SkipAuthorization)
{
IAuthorizationRulesService service = rootContainer.Services.Get<IAuthorizationRulesService>();
IVirtualPathUtilityService service2 = rootContainer.Services.Get<IVirtualPathUtilityService>();
if (service != null)
{
string[] authorizationRules = service.GetAuthorizationRules(service2.ToAppRelative(context.Request.Path));
if ((authorizationRules != null) && (authorizationRules.Length != 0))
{
IAuthorizationService service3 = rootContainer.Services.Get<IAuthorizationService>(true);
foreach (string str in authorizationRules)
{
if (!service3.IsAuthorized(str))
{
throw new HttpException(0x193, Resources.UserDoesntHaveAccessToTheRequestedResource);
}
}
}
}
}
}
Question: Where do the concrete implementations of the IAuthorizationRulesService and IVirtualPathUtilityService get set?
Answer: in the AddRequiredServices() method of the WebClientApplication class.
protected virtual void AddRequiredServices()CUSTOMIZATION POINT:
{
AddServiceIfMissing<ModuleConfigurationLocatorService, IModuleConfigurationLocatorService>(this.RootContainer);
AddServiceIfMissing<VirtualPathUtilityService, IVirtualPathUtilityService>(this.RootContainer);
AddServiceIfMissing<AuthorizationRulesService, IAuthorizationRulesService>(this.RootContainer);
AddServiceIfMissing<SessionStateLocatorService, ISessionStateLocatorService>(this.RootContainer);
AddServiceIfMissing<HttpContextLocatorService, IHttpContextLocatorService>(this.RootContainer);
AddServiceIfMissing<ModuleLoaderService, IModuleLoaderService>(this.RootContainer);
AddServiceIfMissing<WebConfigModuleInfoStore, IModuleInfoStore>(this.RootContainer);
AddServiceIfMissing<WebModuleEnumerator, IModuleEnumerator>(this.RootContainer);
AddServiceIfMissing<ModuleContainerLocatorService, IModuleContainerLocatorService>(this.RootContainer);
}
If the developer wishes to change the specific implementations of these or any services defined in the AddRequiredServices() method, the developer may simply override the method and make the specified changes. Note however that this requires that the developer does one of the following:
A. Duplicate all of the code above and makes changes based on the specific implementations they want to use, OR
B. Call the base method and then use the RootContainer.Services.Remove() method to remove the ones to be replaced, and then use the AddServiceIfMissing(ICompositionContainer container) method to add the specific implementations needed.
3. The AuthorizationService calls the Authorize method on the AuthorizationProvider, passing the current principal and a string representing the URL of the request, to determine if the user is authorized.
public bool IsAuthorized(string context)Question: How does the concrete implementation of the authorizationProvider get set?
{
try
{
return this._authorizationProvider.Authorize(Thread.CurrentPrincipal, context);
}
catch (InvalidOperationException)
{
return true;
}
}
Answer: a factory generates it based on configuration; the default configuration for WCSF applications is set for the AuthorizationRuleProvider.
CUSTOMIZATION POINT:
A different AuthorizationProvider can be specified in configuration. See the snippet from a typical WCSF web.config file. The highlighted section is where a different AuthorizationProvider can be specified. Be sure that the name specified in the “defaultAuthorizationInstance” is the same as the one that is specified under the “name” attribute for the new AuthorizationProvider configuration. While the specific rules do not have to be specified in configuration if another provider is used, all classes inheriting from AuthorizationProvider must provide for a constructor that takes a collection of rules as a parameter. In this case, the rules element can be simply left blank ().
<securityConfiguration defaultAuthorizationInstance="RuleProvider" defaultSecurityCacheInstance="">
<authorizationProviders>
<add type="Microsoft.Practices.EnterpriseLibrary.Security.AuthorizationRuleProvider, Microsoft.Practices.EnterpriseLibrary.Security, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" name="RuleProvider">
<rules>
<add expression="R:Approver" name="AllowApprovals"/>
<add expression="R:User" name="AllowAutocomplete"/>
<add expression="R:User" name="AllowCreateOrders"/>
<add expression="R:User" name="AllowBrowseOrders"/>
<add expression="R:User" name="AllowSearchCustomers"/>
</rules>
</add>
</authorizationProviders>
</securityConfiguration>
4. The AuthorizationProvider compares the user and the rule, to see if the user has access.
public override bool Authorize(IPrincipal principal, string ruleName)
{
if (principal == null)
{
throw new ArgumentNullException("principal");
}
if ((ruleName == null) (ruleName.Length == 0))
{
throw new ArgumentNullException("ruleName");
}
base.InstrumentationProvider.FireAuthorizationCheckPerformed(principal.Identity.Name, ruleName);
BooleanExpression parsedExpression = this.GetParsedExpression(ruleName);
if (parsedExpression == null)
{
throw new InvalidOperationException(string.Format(Resources.AuthorizationRuleNotFoundMsg, ruleName));
}
bool flag = parsedExpression.Evaluate(principal);
if (!flag)
{
base.InstrumentationProvider.FireAuthorizationCheckFailed(principal.Identity.Name, ruleName);
}
return flag;
}
For more on how WCSF showcases the ASP.NET security architecture, see this screencast. NOTE: screencast showcases ASP.NET version 2.0 architecture.