RoleProvider dosn't work with custom IIdentity and IPrincipal on server - asp.net-mvc-3

I'm using a custom IIdentity and IPrincipal in my ASP.NET MVC application via EF 4.3 as expalined here (and follow accepted answer's solution). Also, I have a custom RoleProvider. In local (using IIS Express), it works currectly. But now, when I upload the application on a real host, it seems all users are in "admin" role! e.g. I create a user that is not in role "admin", but it can access to all protected pages (that need "admin" role). e.g. Role.IsUserInRole always returns true. Have you any idea please? Can you help me? Is there any setting that I should to do in IIS?

I explain that solution and it works for me. I don't now, may be you should rollback to the AuthenticateRequest event.If you want to try this way, you have to remove RoleManagerModule completely from your project. Try this and let me know if works or nop:
// in your module:
public void Init(HttpApplication context) {
_application = context;
// rollback this line:
_application.AuthenticateRequest += ApplicationAuthenticateRequest;
}
// and in web.config
<!-- in system.web section: -->
</system.web>
<!-- other stufs -->
<httpModules>
<remove name="RoleManager"/>
</httpModules>
</system.web>
<!-- and in system.webServer section: -->
<system.webServer>
<!-- other stufs -->
<modules runAllManagedModulesForAllRequests="true">
<remove name="RoleManager"/>
</modules>
<system.webServer>

If you want to keep using the default RoleManager, it gets difficult. I tried creating my own RoleManager by deriving from the default, without any luck.
After 2 days trying several things, I ended up creating some extension methods for RolePrincipal:
public static bool IsEmployee(this RolePrincipal principal)
{
if (IsAuthenticated())
return principal.IsInRole("Employee");
return false;
}
public static bool IsAdmin(this RolePrincipal principal)
{
if (IsAuthenticated())
return principal.IsInRole("Admin");
return false;
}
Created a new WebViewPage class:
public abstract class BaseViewPage : WebViewPage
{
public virtual new RolePrincipal User
{
get
{
if (base.User == null)
return null;
return (RolePrincipal)base.User; //Hard casting: If it goes wrong, it better goes wrong here
}
}
}
public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
public virtual new RolePrincipal User
{
get
{
if (base.User == null)
return null;
return (RolePrincipal)base.User; //Hard casting: If it goes wrong, it better goes wrong here
}
}
}
Modified the web.config in the views folder:
<pages pageBaseType="MyCompany.MyProject.BaseViewPage">
And all my Controllers derive from my BaseController:
public abstract class BaseController : Controller
{
protected virtual new RolePrincipal User
{
get { return HttpContext.User as RolePrincipal; }
}
}
Downside is that the methods query my database everytime they get called.
I'm using MVC 4 btw
Hope this helps anyone

Related

SimpleMembershipProvider vs WebSecurity

I have a WebAPI project with a Repository layer. For methods related to accounts, I'm not sure which method to use here:
public class RepositoryService : IRepositoryService
{
private readonly SimpleMembershipProvider _membership;
public RepositoryService()
{
_membership = (SimpleMembershipProvider)Membership.Provider;
}
To check if a user exists:
public bool UserExists(string userName)
{
return WebSecurity.UserExists(userName);
}
OR
public bool UserExists(string userName)
{
return _membership.GetUser(userName, true) != null;
}
I'm wondering because in other places I think I have to use _membership (to get a specific user, for example):
public MembershipUser GetUser(string userName)
{
return _membership.GetUser(userName, false);
}
But it seems odd that I have to use _membership and WebSecurity. Is there anything "smelly" about using both?
The following seems to suggests that you are OK:
From the MSDN docs http://msdn.microsoft.com/en-us/library/gg547905%28v=vs.111%29.aspx on the WebSecurity class:
Behind the scenes, the WebSecurity class interacts with an ASP.NET
membership provider that in turn performs the lower-level work that is
required in order to perform security tasks. The default membership
provider in ASP.NET Web Pages is the SimpleMembershipProvider class.

ASP.Net MVC 3 IF Statement outside Controller Action

I am developing an ASP.Net MVC 3 Web Application. I need to have my website secured with an SSL certificate, however, I only want this used when the application is on my live server, NOT on my test server.
Therefore, I setup an AppSetting in my Web Config like so
<appSettings>
<add key="SSL" value="false" />
</appSettings>
Then in my Account Controller I get this value (either True or False) and using the value, decide whether or not to set the RequiresHttps attribute on my LogOn Action. I would like to do something like so
public class AccountController : Controller
{
public string SSL = System.Configuration.ConfigurationManager.AppSettings["SSL"];
if (SSL.Equals("true"))
{
[RequireHttps]
}
public ActionResult LogOn()
{
return View();
}
}
But I know I can't put my IF statement where it currently is, however, hopefully you get the idea of what I am trying to achieve.
Does anyone have any suggestions as to how I can implement my idea?
Thanks.
Subclass the RequireHttpAttribute (note this code is changed from my original answer - this new version will be more efficient):
public class RequireHttpsIfEnabledAttribute : RequireHttpsAttribute
{
//this setting can't be changed without a recycle, so get it once and cache it.
private static readonly Lazy<bool> HttpsRequired = new Lazy<bool>(() => {
//if the AppSettings["SSL"] returns null you raise an exception if you do a
//.Equals on it - so do it on the constant instead. And make sure it's case
//insensitive!
return "true".Equals(System.Configuration.ConfigurationManager.AppSettings["SSL"],
StringComparison.OrdinalIgnoreCase);
});
public override void OnAuthorization(AuthorizationContext filterContext)
{
//calling the base will fire the HTTPS check. Not calling it will allow
//non-SSL requests through
if (HttpsRequired.Value)
base.OnAuthorization(filterContext);
}
}
Now you just decorate your controllers/actions as before - but with your new attribute:
[RequireHttpsIfEnabled]
public class AccountController : Controller
{
//....
}

global authorization not working - results in blank page rendered

I am trying to implement a very basic login scheme for my MVC3 site. If I understand correctly, instead of adding the [Authorize] markup to each of my controller classes, I should be able to simply implement a global setting. To accomplish this, I have added the following into global.asax:
protected void Application_Start()
{
RegisterGlobalFilters(GlobalFilters.Filters);
}
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new AuthorizeAttribute());
}
and in my webconfig, I added:
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880" />
</authentication>
The result is that the resulting page is totally blank. Looking at the url, it seems that mvc is redirecting to my login route as expected except the page empty. If I comment out the code in global.asax and just place the [Authorize] markup directly in each contoller, it works as expected.
As a workaround, I have implemented what I have read the MVC2 best practice to be, which was to create a BaseController:Controller class, add the [Authorize] markup to it, and then change the inherentences of all of my controllers to inheret from BaseController instead of Controller.
That seems to work well enough for now.
But why isn't the global.asax implementation working?
Let's see what's happening here:
You are navigating to /
Your global authorize attribute kicks in and since the user is not authenticated he is redirected to ~/Account/LogOn (as instructed in your web.config file) for authentication
Your global authorize attribute kicks in and since the user is not authenticated he is redirected to ~/Account/LogOn (as instructed in your web.config file) for authentication
Same as 3.
Same as 4.
...
I think you get the point. The LogOn action should be excluded from authentication otherwise the user can never get a chance to login to your web site.
Since you have applied the Authorize attribute globally this cannot be done. One possible way is to write a custom AuthorizeAttribute that will be applied globally and which will exclude this action from authentication.
So you could write a marker attribute:
public class AllowAnonymousAttribute : Attribute
{
}
and a global custom authorize attribute:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
var exclude = ((AllowAnonymousAttribute[])filterContext.ActionDescriptor.GetCustomAttributes(typeof(AllowAnonymousAttribute), false)).Any();
if (!exclude)
{
base.OnAuthorization(filterContext);
}
}
}
that will be registered:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new MyAuthorizeAttribute());
}
Now all that's left for you is to decorate the controller actions that you want to be excluded from authentication with our marker attribute:
public class AccountController : Controller
{
[AllowAnonymous]
public ActionResult LogOn()
{
return View();
}
[AllowAnonymous]
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
...
}
}

How do I inject into some generic asp.net http handler using Ninject?

I'm a newbie using Ninject and I can't figure out how to inject into my generic http handler. I have a MVC3 project and I'm injecting my services into controllers with no problem at all.
This is what I got in my Ninject App_start class for registering services:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<NLSubscriber.Core.Service.Repository.INLUserRepository>().To<NLSubscriber.Core.Service.Repository.EFDAL.EFNLUserRepository>().InRequestScope();
kernel.Bind<Neticon.Mvc.Helpers.IConfigHelper>().To<Neticon.Mvc.Helpers.AzureEnabledConfigHelper>().InSingletonScope();
kernel.Bind<Neticon.Security.Service.IAuthenticationService>().ToMethod(m => Neticon.Security.Service.SecurityServiceFactory.CreateAuthenticationService()).InRequestScope();
kernel.Bind<Neticon.Security.Service.IMembershipService>().ToMethod(m => Neticon.Security.Service.SecurityServiceFactory.CreateMembershipService()).InRequestScope();
kernel.Bind<Neticon.Security.Service.IRoleManagerService>().ToMethod(m => Neticon.Security.Service.SecurityServiceFactory.CreateRoleManagerService()).InRequestScope();
When I try to get some service from my generic handler by using property injection (with [inject] attribute) I always get null. This is how my handler looks like:
public class SubscriberHandler : IHttpHandler
{
[Inject]
public INLUserRepository userRep { get; set;}
public void ProcessRequest(HttpContext context)
{
var users = userRep.GetUsers(); //userRep is always null here
}
public bool IsReusable
{
get
{
return false;
}
}
}
I have also tried doing it like this:
readonly INLUserRepository userRep;
public SubscriberHandler()
{
using (IKernel kernel = new StandardKernel(new App_Start.NJRepositoryModule()))
{
userRep = kernel.Get<INLUserRepository>();
}
}
but I'm getting an exception: "Error loading Ninject component ICache. No such component has been registered in the kernel's component container.
Suggestions:
1) If you have created a custom subclass for KernelBase, ensure that you have properly
implemented the AddComponents() method.
2) Ensure that you have not removed the component from the container via a call to RemoveAll().
3) Ensure you have not accidentally created more than one kernel."
That's suggesting me that I'm not supposed to instantiate more than one kernel in my application, right?
What am I doing wrong?
Thanks
You could use the dependency resolver:
public class SubscriberHandler : IHttpHandler
{
public INLUserRepository userRep { get; private set; }
public SubscriberHandler()
{
userRep = DependencyResolver.Current.GetService<INLUserRepository>();
}
public void ProcessRequest(HttpContext context)
{
var users = userRep.GetUsers(); //userRep is always null here
}
public bool IsReusable
{
get
{
return false;
}
}
}
I am expecting to get negative feedback from this answer because the service locator pattern is considered by many as an anti-pattern.
But I am not sure whether NInject allows you to use constructor injection for HTTP handlers because they are instantiated by the ASP.NET runtime.
The composition root for IHttpHandlers is the IHttpHandlerFactory. You can create a custom IHttpHandlerFactory that uses Ninject to create an instance of your IHttpHandler. That way you can use constructor injection.
I see you have a "RegisterServices" method in your snippet which suggests you're already using Ninject.Web.Common. What you might not know about NinjectWebCommon.cs is it uses a Bootstrapper class which contains a singleton instance of the Ninject kernel.
As Remo mentioned above, IHttpHandlerFactory is the composition root for IHttpHandler instances and as such you will need to create an implementation of this interface and add the necessary configuration elements to your web.config.
MyHandlerFactory.cs:
public class MyHandlerFactory : IHttpHandlerFactory
{
public bool IsReusable => false;
public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
{
// the bootstrapper class uses the singleton pattern to share the Ninject Kernel across your web app's ApplicationDomain
var kernel = new Bootstrapper().Kernel;
// assuming you have only one IHttpHandler binding in your NinjectWebCommon.cs
return kernel.Get<IHttpHandler>();
}
public void ReleaseHandler(IHttpHandler handler)
{
// nothing to release
}
}
Now, add the necessary config elements for your new handler factory...
Web.config:
<system.web>
<httpHandlers>
<add verb="GET" path="*.customThingImade" type="MyNamespace.MyHandlerFactory, MyAssemblyWhereIPutMyHandlerFactory, Version=1.0.0.0, Culture=neutral" />
</httpHandlers>
</system.web>
<system.webServer>
<handlers>
<add name="MyHandlerFactory" verb="GET" path="*.customThingImade" type="MyNamespace.MyHandlerFactory, MyAssemblyWhereIPutMyHandlerFactory, Version=1.0.0.0, Culture=neutral" preCondition="integratedMode" />
</handlers>
</system.webServer>
Finally, add a binding for your IHttpHandler implementation...
NinjectWebCommon.cs:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<NLSubscriber.Core.Service.Repository.INLUserRepository>().To<NLSubscriber.Core.Service.Repository.EFDAL.EFNLUserRepository>().InRequestScope();
kernel.Bind<Neticon.Mvc.Helpers.IConfigHelper>().To<Neticon.Mvc.Helpers.AzureEnabledConfigHelper>().InSingletonScope();
kernel.Bind<Neticon.Security.Service.IAuthenticationService>().ToMethod(m => Neticon.Security.Service.SecurityServiceFactory.CreateAuthenticationService()).InRequestScope();
kernel.Bind<Neticon.Security.Service.IMembershipService>().ToMethod(m => Neticon.Security.Service.SecurityServiceFactory.CreateMembershipService()).InRequestScope();
kernel.Bind<Neticon.Security.Service.IRoleManagerService>().ToMethod(m => Neticon.Security.Service.SecurityServiceFactory.CreateRoleManagerService()).InRequestScope();
// the binding for your handler factory
Bind<IHttpHandler>().To<SubscriberHandler>();
}

ASP.NET MVC3, How to Use Different OutputCacheProfile to Different User Role?

I know I can setup OutputCacheProfiles at web.config file.
I like to know how to apply different cache profile to different user role on page (controller) level?
You can decorate a controller with the OutputCache attribute which allows arguments to be passed as parameters. For example;
[OutputCache(Duration = 3600, VaryByParam = "None")]
There is no reason why you couldn't extend the attribute to take a further argument "RoleName" and perform a "Roles.IsUserInRole(RoleName)" and load different settings based upon each role.
EDIT
After comments from the author, I have reviewed my solution.
Firstly, you can define you cache profiles within the Web.config;
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<clear />
<add name="Default" duration="60" />
<add name="Admin" duration="10" />
</outputCacheProfiles>
</outputCacheSettings>
</caching>
I have extended the OutputCacheAttribute to account for authorisation of a user, and if the user authenticates, it loads that CacheProfile;
public class AuthorisedOutputCache : OutputCacheAttribute
{
public string RoleName { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Default Profile.
CacheProfile = "Default";
if (HttpContext.Current.Request.IsAuthenticated)
{
if (Roles.IsUserInRole(RoleName))
{
CacheProfile = RoleName;
}
}
base.OnActionExecuting(filterContext);
}
}
Here is the Index.cshtml file for completeness;
#model DateTime
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
The time is #Model.TimeOfDay.ToString()
</p>
Note: You will have to make sure to define a cacheprofile for each of your roles, aswell as a default for when no role is found.
EDIT
The author wished to know how to set the cache profile within the controller, I have posted a viable solution, but I don't like it because of the use of HttpContext.Items - so if anyone can suggest alternatives?
Firstly, you must change the OnActionExecuting to OnActionExecuted;
public class AuthorisedOutputCache : OutputCacheAttribute
{
public string RoleName { get; set; }
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
// Do you wish to force the profile?
if (HttpContext.Current.Items["Cache.Force"] != null)
{
// Force the profile and remove the flag.
CacheProfile = HttpContext.Current.Items["Cache.Force"].ToString();
HttpContext.Current.Items.Remove("Cache.Force");
}
else
{
// If the profile has not been set - use the role based authorisation -
// otherwise, carry on as normal.
if (string.IsNullOrEmpty(CacheProfile))
{
CacheProfile = "Default";
if (HttpContext.Current.Request.IsAuthenticated)
{
if (Roles.IsUserInRole(RoleName))
{
CacheProfile = "Admin";
}
}
}
}
base.OnActionExecuted(filterContext);
}
}
The following line allows you to set the profile within the controller;
public ActionResult Index()
{
// Forces the cache profile to one of the name of "Mandatory".
HttpContext.Items["Cache.Force"] = "Mandatory";
return View(IndexViewName, DateTime.Now);
}
Let me know if I can be of further assistance,
Matt

Resources