public interface IUsersApiController { // List all users. IEnumerable<User> GetAllUsers(); // Lookup single user. User GetUserById(int id); // Create user. HttpResponseMessage Post(RegisterModel user); // Restore user's password. HttpResponseMessage RestorePassword(string email); // Update user. HttpResponseMessage Put(int id, UpdateUserModel value); // Delete user. HttpResponseMessage Delete(string email); }
public class UsersController : ApiController { //... public HttpResponseMessage Post([FromBody]RegisterModel user) { if (ModelState.IsValid) { return Request.CreateResponse(HttpStatusCode.OK, "Created!"); } else { return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState); } } //... }
ClaimsAuthorizationManager
is the WIF component that allows you to intercept incoming requests in one place and execute arbitrary logic, which, based on the current user's claims, * decides on granting or denying access. /// <summary> /// Overloads the base class method to load the custom policies from the config file /// </summary> /// <param name="nodelist">XmlNodeList containing the policy information read from the config file</param> public override void LoadCustomConfiguration(XmlNodeList nodelist) {...} /// <summary> /// Checks if the principal specified in the authorization context is authorized /// to perform action specified in the authorization context on the specified resource /// </summary> /// <param name="pec">Authorization context</param> /// <returns>true if authorized, false otherwise</returns> public override bool CheckAccess(AuthorizationContext pec) {...}
... <policy resource="http://localhost:28491/Developers.aspx" action="GET"> <or> <claim claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" claimValue="developer" /> <claim claimType="http://schemas.xmlsoap.org/claims/Group" claimValue="Administrator" /> </or> </policy> <policy resource="http://localhost:28491/Administrators.aspx" action="GET"> <and> <claim claimType="http://schemas.xmlsoap.org/claims/Group" claimValue="Administrator" /> <claim claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country" claimValue="USA" /> </and> </policy> <policy resource="http://localhost:28491/Default.aspx" action="GET"> </policy> ...
XmlBasedAuthorizationManager
If you try to knock down a project, it turns out that we lack the PolicyReader
class, you can take it from the full source code of the MSDN example. <configSections> <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" /> <section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" /> <!-- Others sections--> </configSections>
<system.identityModel> <identityConfiguration> <claimsAuthorizationManager type="YourProject.WebApi.Security.XmlBasedAuthorizationManager, YourProject.WebApi, Version=1.0.0.0, Culture=neutral"> <!-- Policies --> </claimsAuthorizationManager> <claimsAuthenticationManager type="YourProject.WebApi.Security.AuthenticationManager, YourProject.WebApi, Version=1.0.0.0, Culture=neutral" /> </identityConfiguration> </system.identityModel>
claimsAuthenticationManager
", which I did not mention earlier. <policy resource="Users" action="GetAllUsers"> <and> <claim claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/system" claimValue="WebApplication" /> <claim claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" claimValue="Admin" /> </and> </policy> <policy resource="Users" action="Post"> <claim claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/system" claimValue="WPhoneApplication" /> </policy> <policy resource="Users" action="RestorePassword"> <claim claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/system" claimValue="WPhoneApplication" /> </policy> <policy resource="Users" action="GetUserById"> <or> <and> <claim claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/system" claimValue="WebApplication" /> <claim claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" claimValue="Admin" /> </and> <and> <claim claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" claimValue="User" /> <!-- {0} "request" ? --> <!-- <claim claimType="UserId" claimValue="{0}" /> --> </and> </or> </policy> <policy resource="Users" action="Put"> <or> <and> <claim claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/system" claimValue="WebApplication" /> <claim claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" claimValue="Admin" /> </and> <and> <claim claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" claimValue="User" /> <!-- {0} "request" ? --> <!-- <claim claimType="UserId" claimValue="{0}" /> --> </and> </or> </policy>
<policy resource="Users" action="GetAllUsers"> <and> <claim claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/system" claimValue="WebApplication" /> <claim claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" claimValue="Admin" /> </and> </policy>
<policy resource="Users" action="GetUserById"> <or> <and> <claim claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/system" claimValue="WebApplication" /> <claim claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" claimValue="Admin" /> </and> <and> <claim claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" claimValue="User" /> <!-- {0} "request" ? --> <!-- <claim claimType="UserId" claimValue="{0}" /> --> </and> </or> </policy>
..
block. But what about the users?and/or
" blocks. If we are to be extremely honest, this implementation rigidly sets the number of “claim” elements equal to two inside “ and/or
” blocks.GetUserById
method looks like a throw new NotImplementedException()
.PolicyReader
implementation of the PolicyReader
class: /// <summary> /// Read the Or Node /// </summary> /// <param name="rdr">XmlDictionaryReader of the policy Xml</param> /// <param name="subject">ClaimsPrincipal subject</param> /// <returns>A LINQ expression created from the Or node</returns> private Expression<Func<ClaimsPrincipal, bool>> ReadOr(XmlDictionaryReader rdr, ParameterExpression subject) { Expression defaultExpr = Expression.Invoke((Expression<Func<bool>>)(() => false)); while (rdr.Read()) { if (rdr.NodeType != XmlNodeType.EndElement && rdr.Name != "or") { defaultExpr = Expression.OrElse(defaultExpr, Expression.Invoke(ReadNode(rdr, subject), subject)); } else break; } rdr.ReadEndElement(); Expression<Func<ClaimsPrincipal, bool>> resultExpr = Expression.Lambda<Func<ClaimsPrincipal, bool>>(defaultExpr, subject); return resultExpr; } /// <summary> /// Read the And Node /// </summary> /// <param name="rdr">XmlDictionaryReader of the policy Xml</param> /// <param name="subject">ClaimsPrincipal subject</param> /// <returns>A LINQ expression created from the And node</returns> private Expression<Func<ClaimsPrincipal, bool>> ReadAnd(XmlDictionaryReader rdr, ParameterExpression subject) { Expression defaultExpr = Expression.Invoke((Expression<Func<bool>>)(() => true)); while (rdr.Read()) { if (rdr.NodeType != XmlNodeType.EndElement && rdr.Name != "and") { defaultExpr = Expression.AndAlso(defaultExpr, Expression.Invoke(ReadNode(rdr, subject), subject)); } else break; } rdr.ReadEndElement(); Expression<Func<ClaimsPrincipal, bool>> resultExpr = Expression.Lambda<Func<ClaimsPrincipal, bool>>(defaultExpr, subject); return resultExpr; }
Install-Package Thinktecture.IdentityModel
WebApiConfig
code of the WebApiConfig
class to approximate the following: public static class WebApiConfig { public static void Register(HttpConfiguration config) { var authentication = CreateAuthenticationConfiguration(); config.MessageHandlers.Add(new AuthenticationHandler(authentication)); config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); config.EnableSystemDiagnosticsTracing(); config.Filters.Add(new ClaimsAuthorizeAttribute()); } private static AuthenticationConfiguration CreateAuthenticationConfiguration() { var authentication = new AuthenticationConfiguration { ClaimsAuthenticationManager = new AuthenticationManager(), RequireSsl = false //only for testing }; #region Basic Authentication authentication.AddBasicAuthentication((username, password) => { var webSecurityService = ServiceLocator.Current.GetInstance<IWebSecurityService>(); return webSecurityService.Login(username, password); }); #endregion return authentication; } }
IWebSecurityService
. You can use your own logic here, for example: return username == password;
ClaimsAuthenticationManager
, or rather our successor to this class, which we have already registered : public class AuthenticationManager : ClaimsAuthenticationManager { public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal) { if (!incomingPrincipal.Identity.IsAuthenticated) { return base.Authenticate(resourceName, incomingPrincipal); } var claimsService = ServiceLocator.Current.GetInstance<IUsersClaimsService>(); var claims = claimsService.GetUserClaims(incomingPrincipal.Identity.Name); foreach (var userClaim in claims) { incomingPrincipal.Identities.First().AddClaim(new Claim(userClaim.Type, userClaim.Value)); } return incomingPrincipal; } }
IUsersClaimsService
by using the newly created instance of IUsersClaimsService
. After the "transformation", an instance of ClaimsPrincipal returns further to the pipeline for later use, for example, by authorization.AuthenticationManager
for testing purposes. Instead of using IUsersClaimsService
following code: public class AuthenticationManager : ClaimsAuthenticationManager { public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal) { ... if (incomingPrincipal.Identity.Name.ToLower().Contains("user")) { incomingPrincipal.Identities.First().AddClaim(new Claim(ClaimTypes.Role, "User")); } return incomingPrincipal; } }
Source: https://habr.com/ru/post/198424/
All Articles