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
Related
I have a scenario in which my application connects to different databases with respect to subdomains in url.
I have tried this code:
public class Provider : SqlMembershipProvider
{
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(name, config);
// Update the private connection string field in the base class.
SqlConnectionStringBuilder sqlBuilder = new SqlConnectionStringBuilder();
sqlBuilder["Data Source"] = "My-PC\\SQLEXPRESS";
string dbName;
if (!string.IsNullOrEmpty(Helpers.RouteManager.GetSubDomain()))
{
dbName = Helpers.RouteManager.GetSubDomain().ToString()+"_db";
}
else
{
dbName = "dbName";
}
bool isEqual = string.Equals(name, dbName);
sqlBuilder["Initial Catalog"] = dbName;
sqlBuilder["Integrated Security"] = true;
sqlBuilder["Application Name"] = "EntityFramework";
FieldInfo connectionStringField = GetType().BaseType.GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic);
connectionStringField.SetValue(this, sqlBuilder.ConnectionString);
}
}
And I am calling this function from Application_BeginRequest() method.
The issue I am facing is that when I build application and it hits url with specific sub-domain, it connects to the specified database but I try different sub-domain it sticks with the same database for asp.net membership.
But when i rebuild the application and try another sub domain it works.
It seems that my code is working only for the first time after I build the solution.
Please someone guide me.
Thanx
Try to configure different Membership providers in web config file.
<membership>
<providers>
<add name="ProviderA" connectionStringName="ConnectionA" ... />
<add name="ProviderB" connectionStringName="ConnectionB" ... />
</providers>
</membership>
<connectionStrings>
<add name="ConnectionA" .... />
<add name="ConnectionB" .... />
</connectionStrings>
And then use this providers in code
var user = Membership.Providers["ProviderA"].GetUser(username, false);
var user = Membership.Providers["ProviderB"].GetUser(username, false);
sorry this isn't really an answer but I'd like to question your architecture here. Why do you need your application to contain membership data in multiple databases? It's nature is to maintain account details (authentication/authorization) for your application as a whole. If you want the ability to control access to certain areas of your application based on the Url you could do this by using Role based Forms Authentication. There are lots of articles on the interweb explaining this. Hope this sets you off on the right direction.
Update:
I've just found this answer on stackoverflow. I'm afraid it seems what you want to do is not possible as the membership provider is a global static object shared between all users of your application. Changing it based on a url would not just change it for that particular user but for everyone that is logged in
check it out here
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.
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/
I'm looking for a walkthrough on how to create and use a custom provider for ASP.Net Healthmonitoring.
So far I've only worked with the e-mail provider that generates e-mails on errors. Basically I want to do the same, but with more flexibility:
I want to use the HealthMonitoring features (I don't want to use the Application_OnError event in the global.asax) in a way that allows me have access to an event, that gets thrown like "OnNewHealthMonitoringEntry" with all the information provided in the e-mail, to run custom code.
Edit:
Based on the source code provided here http://www.asp.net/general/videos/how-do-i-create-a-custom-provider-for-logging-health-monitoring-events I was able to build my own custom provider and implement it. Now I want to add some new attributes to configure my custom provider.
Here is what the web.config looks like:
<healthMonitoring>
<bufferModes>
<add name="Log Notification" maxBufferSize="1" maxFlushSize="1" urgentFlushThreshold="1" regularFlushInterval="Infinite" urgentFlushInterval="00:00:10"/>
</bufferModes>
<providers>
<add name="FileEventProvider" buffer="true" bufferMode="Log Notification" type="healthmonitoringtest.FileHealthMonitorEventProvider"/>
</providers>
<profiles>
<add name="Custom" minInstances="1" maxLimit="Infinite" minInterval="00:00:00"/>
</profiles>
<rules>
<add name="File Event Provider" eventName="All Errors" provider="FileEventProvider" profile="Custom"/>
</rules>
</healthMonitoring>
If I attempt to add an attribute to the provider, like this
<providers>
<add name="FileEventProvider" buffer="true" bufferMode="Log Notification" foo="bar" type="healthmonitoringtest.FileHealthMonitorEventProvider"/>
</providers>
I'll get an error saying:
An exception of type
'System.Configuration.ConfigurationErrorsException'
occurred in System.Web.dll but was not
handled in user code Additional
information: Unexpected attribute foo
in the configuration of the
FileEventProvider.
Is it possible to store configuration necessary for custom provider close to the healthMonitoring section? I guess I could include the settings into the appSettings node, but I'd like to configure it somehow with attributes (inside the healthMonitoring node). Is that possible?
Edit2:
You might take a look at this article: http://www.tomot.de/en-us/article/6/asp.net/how-to-create-a-custom-healthmonitoring-provider-that-sends-e-mails
The following series of articles will take you through the basics of using the Health Monitoring System upto creating Custom Events.
Then the following 26 minute video will take you through creating a custom provider that records events to a text-based log file.
UPDATE Based on Comment
Looking at your update and using Reflector to look at the source for the BufferedWebEventProvider class that you base your custom provider on, I have found that the Initialize method in BufferedWebEventProvider does a check at the end to see if there are any attributes that it doesn't recognize. This is done by removing values from the config NameValueCollection parameter as soon as they are assigned to the properties or fields of the BufferedWebEventProvider. Then a check is done to see if the config parameter is empty and if not that means that there are extra attributes added, which causes an exception to be thrown.
As to how to fix this problem, one option is to:
Move the call to base.Initialize to the end of the method
Remove the additional attributes as soon as you assign them to variables just like the provider does.
Something like the following would work:
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
foo = config["foo"];
if (String.IsNullOrEmpty(foo))
{
// You can set a default value for foo
}
//remove foo from the config just like BufferedWebEventProvider with the other
//attributes. Note that it doesn't matter if someone didn't proivde a foo attribute
//because the NameValueCollection remains unchanged if you call its Remove method
//and the name doesn't exist.
config.Remove("foo");
base.Initialize(name, config);
}
Hopefully this works out for you.
I'm trying to unit test values that will eventually wind up in a web.config file. In my test project, I created an app.config file with a web.config section to hold the settings. In a normal situation, I would call System.Configuration.ConfigurationSettings.AppSettings, but in this case, that doesn't work. I saw this question, which is very similar, but doesn't address how to get the NameValueCollection out of the config file. Here is an example of the config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<membership defaultProvider="CustomMembershipProvider">
<providers>
<clear/>
<add
name="CustomMembershipProvider"
applicationName="SettlementInfo"
enablePasswordRetrieval="false"
enablePasswordReset="false"
requiresQuestionAndAnswer="true"
writeExceptionsToEventLog="true" />
</providers>
</membership>
</system.web>
</configuration>
Has anyone dealt with this before?
I guess I'm confused here; it looks like you're trying to test that ASP.NET is using your custom membership provider appropriately. Correct?
If so, I'm 99.999% sure that you cannot unit test this using the MS framework; you must integration test it by deploying it to the webserver (or running Cassini in VS) and typing a username/password into your login page.
Now, it's possible I've misunderstood your request. If so, let me know and I'll edit my answer accordingly.
Edit:
For right now, I'm really just trying
to test the NameValue pairs coming out
of the config file, to make sure that
if the values aren't present, my
defaults are being applied. In other
words, I want to try to pull
applicationName, and verify that it
equals "SettlementInfo", and so on.
After that, I will be using
integration testing to ensure that
ASP.NET is using the custom framework
in place of the default one. Does that
make sense?
I need more than a comment to reply, so I'm editing. If I read you correctly, you are wanting to unit test your program to ensure that it deals with configuration correctly, yes? Meaning you want to ensure that your code grabs, for example, the correct AppSettings key and handles a null value therein, correct?
If that's the case, you're in luck; you don't need an app.config or web.config at all, you can set the values you need as part of your test setup.
For example:
[TestMethod]
public void Test_Configuration_Used_Correctly()
{
ConfigurationManager.AppSettings["MyConfigName"] = "MyConfigValue";
MyClass testObject = new MyClass();
testObject.ConfigurationHandler();
Assert.AreEqual(testObject.ConfigurationItemOrDefault, "MyConfigValue");
}
[TestMethod]
public void Test_Configuration_Defaults_Used_Correctly()
{
// you don't need to set AppSettings for a non-existent value...
// ConfigurationManager.AppSettings["MyConfigName"] = "MyConfigValue";
MyClass testObject = new MyClass();
testObject.ConfigurationHandler();
Assert.AreEqual(testObject.ConfigurationItemOrDefault, "MyConfigDefaultValue");
}
I believe you only have access to the webconfig file while your application is actually beeing started up. The solution is rather easy -> "Fake" your config. Use a NameValueCollection and use that instead:
private static NameValueCollection CreateConfig()
{
NameValueCollection config = new NameValueCollection();
config.Add("applicationName", "TestApp");
config.Add("enablePasswordReset", "false");
config.Add("enablePasswordRetrieval", "true");
config.Add("maxInvalidPasswordAttempts", "5");
config.Add("minRequiredNonalphanumericCharacters", "2");
config.Add("minRequiredPasswordLength", "6");
config.Add("requiresQuestionAndAnswer", "true");
config.Add("requiresUniqueEmail", "true");
config.Add("passwordAttemptWindow", "10");
return config;
}
Now you could easily pass that collection into your class that parses data from it.
You should be able to use the ConfigurationManager.GetSection() method to pull out whatever you want.
Actually, if you are using NUnit, you can stick that in an App.config in your test project.
Then add this line to your Post-build event:
copy /Y “$(ProjectDir)App.config” “$(TargetDir)$(TargetFileName).config”
When you create the new provider in your tests, NUnit will pass the values in your app.config to the provider in the initialize method.
Why not just stick it in the web.config file?