System.Web.Http.AuthorizeAttribute not recognizing custom role provider - c#

In my MVC 4 Web API project, I have a custom role provider that works as designed via System.Web.Mvc.Authorize attribute on my Home System.Web.Mvc.Controller.
On any System.Web.Http.ApiController with System.Web.Http.Authorize the custom role provider never gets called, always returning false. Is there a way to specify that the Web API AuthorizeAttribute pick up my custom role provider like the MVC AuthorizeAttribute?
Role Provider:
public class CustomRoleProvider : RoleProvider
{
//Overriden methods
public override string[] GetRolesForUser(string username)
{
//Always return "Master" for testing purposes
return new string[] { "Master" };
}
public override bool IsUserInRole(string username, string roleName)
{
//Always return true for testing purposes
return true;
}
//Other overridden method stubs...
}
Web.config:
<roleManager defaultProvider="CustomRoleProvider" enabled="true" cacheRolesInCookie="false" >
<providers>
<clear />
<add name="CustomRoleProvider" type="MyApp.SecurityExtensions.CustomRoleProvider, MyApp" />
</providers>
</roleManager>

This is not really an answer, but this might help:
Both attributes work by querying the current pricipal. The MVC attribute uses HTTPContent.User, while the System.Web.http version uses Thread.CurrentPrincipal, but that difference is minor.
I'm not really familar with Web API, but I suspect that the RoleManagerModule is not running by the time the attribute fires, or you have not yet reached the PostAuthenticateRequest event, because in that event the Module replaces the Pricipal.
Are you sure you have some form of ASP authentication required for your WebAPI usage? If you don't have your WebAPI project configured to require some form of authentication, then obviously you will never reach the PostAuthenticateRequest event, and thus the RoleManagerModule will never kick-in.
The last possibility that comes to mind is that someting else is replacing the Principal after the RoleManagerModule does so. If possible, temporarally remove the System.Web.Http.AuthorizeAttribute, set a breakpoint in the controller, and detemine what class Thread.CurrentPrincipal has. That might give you a hint as to where it went wrong.

You would need to use System.Web.Http.AuthorizeAttribute for Web API's controllers. Sample: http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-membership-provider/

Related

How do I stop a custom EF-based RoleProvider from caching results?

I have an ASP.NET MVC 4 application which was required to use a pre-existing membership model of users/roles. The way I did this was to implement a custom ASP.NET RoleProvider to manage access, this uses Entity Framework repositories to read user data from the database. The method to read user roles is shown below, but all the method implementations follow this pattern:
public class OurRoleProvider : RoleProvider
{
private IUserRepository _userRepository;
public OurRoleProvider() : this(Container.Resolve<IUserRepository>())
{
}
public OurRoleProvider(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public override string[] GetRolesForUser(string username)
{
var user = _userRepository.GetUserByUserName(username);
if (user.Roles.IsNullOrEmpty())
return new string[0];
return user.Roles.Select(r => r.RoleName).ToArray();
}
}
I have now come across the problem described in this post. Because a single instance of a RoleProvider is re-used for the lifetime of the application, and all other functionality creates it's own per-request DbContext to persist data, changes made to a User profile are not reflected by the RoleProvider until a restart in the application, because it's underlying DbContext is not being refreshed. This means you can remove a User from a Role, and they will still have access to that Role's functionality until an app restart.
I have tried creating a new repository instance within the RoleProvider methods, i.e. in GetRoleForUser():
var user = _userRepository.GetUserByUserName(username);
becomes
var userRepository = Container.Resolve<IUserRepository>();
var user = userRepository.GetUserByUserName(username);
This fixes the issue but breaks unit tests which don't use the DI container and inject a mock repository via the constructor. There would be a lot of unit tests to re-write.
I'd like to stick with a custom RoleProvider if possible to make use of features such as the Authorize atrribute. What I really need to do is re-instantiate the RoleProvider on a per-request basis OR force the EF repository to always update from the database. So far I haven't found a way to do this. Is this possible? Or is there a better solution?
You shouldn't define EF Services' life time as a singleton (it should be per-request life time). Because DbContext is not thread safe and also it's designed to have a short life. Activating the caching of a role provider is done somewhere else, in the web.config file:
<roleManager defaultProvider="CustomRoleProvider" cacheRolesInCookie="true" enabled="true">
<providers>
<clear />
<add name="CustomRoleProvider" type="Security.CustomRoleProvider" />
</providers>
</roleManager>
The above custom role provider is cached, beacuse it's using cacheRolesInCookie="true" here.

ClaimsAuthenticationManager in parallel with RoleManagerModule

I'm mixing classic Membership/RoleManager security setup with new WIF 4.5 API for testing purposes. I have implemented two classes that I have breakpoints set on:
public class CustomAuthenticationManager : ClaimsAuthenticationManager
{
public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
{
// Breakpoint here is hit 1st
if (!incomingPrincipal.Identity.IsAuthenticated)
{
return base.Authenticate(resourceName, incomingPrincipal);
}
return TransformPrincipal(incomingPrincipal);
}
private ClaimsPrincipal TransformPrincipal(ClaimsPrincipal incomingPrincipal)
{
// this breakpoint is hit last
ClaimsIdentity newIdentity = new ClaimsIdentity("Custom");
newIdentity.AddClaims(incomingPrincipal.Claims);
// I add some additional claims
ClaimsPrincipal newPrincipal = new ClaimsPrincipal(newIdentity);
return newPrincipal;
}
}
public class CustomRoleProvider : RoleProvider
{
public override string[] GetRolesForUser(string username)
{
// breakpoint here is hit 2nd
if(username == "me") return new string [] { "Lead", "Developer" };
return new string[] {};
}
#region Not implemented
// bunch of not implemented methods
#endregion
}
Now result is quite fine, I get mixed ClaimsPrincipal that has both Name claim, Role claims and claims that I've added in TransformPrincipal method.
However, debugging breakpoints are hit in completely weird order:
1) Breakpoint at the beginning of Authenticate method is hit first
2) Breakpoint at the beginning of GetRolesForUser is hit second
3) Breakpoing at the beginning of TransformPrincipal is hit last
Is this just Visual Studio issue or there is an atomic chance that Authenticate might complete before GetRolesForUser is called?
How are RoleManagerModule and ClaimsAuthenticationManager working in the pipeline? In parallel or there is sequential order? Can mixing the two be an issue?
EDIT:
void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
ClaimsPrincipal transformedPrincipal = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager.Authenticate(null, ClaimsPrincipal.Current);
Thread.CurrentPrincipal = transformedPrincipal;
HttpContext.Current.User = transformedPrincipal;
}
EDIT:
<membership defaultProvider="CustomMembershipProvider">
<providers>
<add name="CustomMembershipProvider" type="Tests.CustomMembershipProvider" />
</providers>
</membership>
<roleManager enabled="true" defaultProvider="CustomRoleProvider">
<providers>
<add name="CustomRoleProvider" type="Tests.CustomRoleProvider" />
</providers>
</roleManager>
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>
<system.identityModel>
<identityConfiguration>
<claimsAuthenticationManager type="Tests.CustomAuthenticationManager, Tests"/>
</identityConfiguration>
</system.identityModel>
These two are rather not meant to be used together. SAM role management suppresses forms authentcation role management and as I suspect, your role provider is just enabled in web.config.
I haven't tested this under 4.5 but I don't believe it changed. In 4.0 the authentication manager fires only if a user is not yet authenticated or current user is established with another authentication module. The latter could happen unexpectedly when you issue both cookies, the forms cookie and the sam cookie. Take a look at my blog post on that
http://www.wiktorzychla.com/2012/09/sessionauthenticationmodule-and-dynamic.html
Although I don't know why the breakpoint at the authenticate gets hit first, SAM would notice that your user is RolePrincipal with FormsIdentity and would try to make a ClaimsPrincipal out of it and then call the authentication manager.
Edit: after all you provided, I still have a slight problem regarding the order. Although Dominick suspects roles are lazy loaded, I can't confirm this by recompiling the BCL. Rather, the RolePrincipal seems to eagerly add role claims in the constructor (the internal AttachRoleClaims method in the RolePrincipal class).
No matter how I look into it, I believe the GetRolesForUser should be called first, as this is ClaimsPrincipal.Current passed to the Authenticate.
If your RoleProvider creates the RolePrincipal in advance, the AttachRoleClaims would be called and roles would be enumerated with the RoleProvider.GetRolesForUser.
If your RoleProvider doesn't create the RolePrincipal in advance, the ClaimsPrincipal.Current would and this leads to the same execution path.
Edit2: I've found the possible culprit, it is the RoleClaimProvider::Claims method that is internally used by the AttachRoleClaims. It is implemented lazily with yield which means that until someone actually enumerates role claims, they are not created. This "someone" is your code from the TransformPrincipal:
ClaimsIdentity newIdentity = new ClaimsIdentity("Custom");
/* this forces the enumeration */
newIdentity.AddClaims(incomingPrincipal.Claims);
However, this would also mean that the GetRolesForUser would be called last, from within the TransformPrincipal.

Custom role provider cache troubleshooting

I've written custom role provider, which internally uses web service methods for getting roles or usernames. This provider inherits from System.Web.Security.RoleProvider. In the web.config file I switched on .NET provided caching feature which uses cookies.
Here is how looks web.config section for this provider:
<system.web>
<roleManager defaultProvider="MyProvider"
enabled="true"
cacheRolesInCookie="true"
cookieName=".MYROLES"
cookieTimeout="30"
cookiePath="/"
cookieRequireSSL="false"
cookieSlidingExpiration="true"
cookieProtection="All">
<providers>
<clear/>
<add name="MYProvider"
type="MYProvider.MyRoleProvider, MYProvider"
Service1URL="http://localhost:54013/service1.asmx"
Service2URL="http://localhost:54013/service2.asmx"
rolesPrefix="ABC_"
domainName="abc.corp"
specialUserForAllRoles="abc"
applicationURL="http://example.com"
logCommunication="true"/>
</providers>
</roleManager>
</system.web>
Now, it comes to test if the cache is working or not. I've written simple method which looks like that:
public void TestCache()
{
string[] roles = Roles.GetAllRoles();
roles = Roles.GetAllRoles();
string[] rolesForUser1 = Roles.GetRolesForUser("user1");
rolesForUser1 = Roles.GetRolesForUser("user1");
string[] usersInRole = Roles.GetUsersInRole("ABC_DEV");
usersInRole = Roles.GetUsersInRole("ABC_DEV");
Roles.IsUserInRole("user1", "ABC_DEV");
Roles.IsUserInRole("user1", "ABC_DEV");
}
While debugging this piece of code (from test web site), debugger enters each of shown method in provider and executes all logic inside, despite the fact method induction is redundant or not. I thought that second invoke of each method should not execute because the result will be returned without asking of my provider directly from cache.
What I'm doing/thinking wrong and how to fix the caching feature ?
Regards
The role cache works only for roles of the current user. This should be cached:
var isInRole = User.IsInRole("ABC_DEV")
http://msdn.microsoft.com/en-us/library/ms164660(VS.80).aspx

Can I create a custom roleprovider through a WCF service?

I have a web application that accesses a database through a wcf service. The idea is to abstract the data from the web application using the wcf service. All that works fine but I am also using the built in roleprovider using the SqlRoleManager which does access the aspnetdb database directly. I would like to abstract the roleprovider by creating a custom roleprovider in a wcf service and then accessing it through the wcf service.
I have created the custom role provider and it works fine but now I need to place it in a wcf service.
So before I jump headlong into trying to get this to work through the WCF service, I created a second class in the web application that accessed the roleprovider class and changed my web config roleprovider parameters to use that class. So my roleprovider class is called, "UcfCstRoleProvider" and my web.config looks like this:
<roleManager
enabled="true"
defaultProvider="UcfCstRoleProvider">
<providers>
<add
name="UcfCstRoleProvider"
type="Ucf.Security.Wcf.WebTests.UcfCstRoleProvider, Ucf.Security.Wcf.WebTests"
connectionStringName="SqlRoleManagerConnection"
applicationName="SMTP" />
</providers>
</roleManager>
My class starts like this:
public class UcfCstRoleProvider : RoleProvider
{
private readonly WindowsTokenRoleProvider _roleProxy = new WindowsTokenRoleProvider();
public override string ApplicationName
{
get
{
return _roleProxy.ApplicationName;
}
set
{
_roleProxy.ApplicationName = value;
}
}
As I said, this works fine. So the second class is called BlRoleProvider that has identical properties and parameters as the roleprovide but does not implement RoleProvider. I changed the web.config to point to this class like this:
<roleManager
enabled="true"
defaultProvider="BlRoleProvider">
<providers>
<add
name="UcfCstRoleProvider"
type="Ucf.Security.Wcf.WebTests.BlRoleProvider, Ucf.Security.Wcf.WebTests"
connectionStringName="SqlRoleManagerConnection"
applicationName="SMTP" />
</providers>
</roleManager>
But I get this error.
"Provider must implement the class 'System.Web.Security.RoleProvider'."
I hope I have explained well enough to show what I am trying to do. If I can get the roleprovider to work through another class in the same application, I am sure it will work through the WCF service but how do I get past this error?
Or maybe I took a wrong turn and there is a better way to do what I want to do??
I think your best bet is to create a custom role provider and implement each method. In the implementation of each method, call the WCF service to do the data access. Eg:
public class WcfRoleProvider: RoleProvider
{
public bool IsUserInRole(string username, roleName)
{
bool result = false;
using(WcfRoleService roleService = new WcfRoleService())
{
result = roleService.IsUserInRole(username, roleName);
}
return result;
}
}
No, you have to have a class that must implement RoleProvider. That will not work. If you can't have this class inherit from RoleProvider directly, consider creating a RoleProvider wrapper class that implements RoleProvider's props/methods, but utilizes whatever you need to do with this second class.
This error isn't specific to WCF, but is specific to the role provider framework.
HTH.
Looking at your code it appears that you already have your configuration using a custom role provider.
If you want to be able to authentiacate users mking calls through your web service you should implement a custom header that authenticates each request against your configured role provider.
Things work slightly differently in WCF, it's not like you have access to session and application states since each call is considered to be a stateless one, a custom header however will offset that by handling this stuff as the call is made.

How to define endpoint without web.config or httpModule?

I would like to make a RESTful app of HTTPhandlers without having to define every endpoint by making an entry in the web.config, i'd like the style of attaching attributes to a class constructor eg:
public class obj : IHttpHandler
{
[WebGet(UriTemplate = "/accounts/{id}")]
public obj(string id)
{
// this is just an eg, it worild normally include caching and
// a template system
String html = File.ReadAllText("/accounts/accounts.htm");
html.replace("id", id);
httpcontext.current.response.write(html)
}
}
instead of
<httpHandlers>
<clear />
<add verb="GET" path="/accounts/*" type="MyApp.obj" />
</httphandlers>
The way i'm doing it now i have 100's of endpoints in the web.config :( i'd rather define them in the class. And i don't want to make extra files (.asmx) either. I'd like an app of just .htm files with tokens and .cs files
Thanks!
You could automate the registration of the endpoints and so on, with a custom ServiceHost, which overrides the ApplyConfiguration() method, which then virtualizes the configuration so that it does not have to be in the web.config file.
Here's a starting point. It doesn't do exactly what you want, but it illustrates the concept of virtualizing the configuration.

Categories