I have setup a custom role provider implemented using the code below except it seems it's never being used, and the default provider is being used instead. When decorating the HomeController with the [Authorize(Roles = "Administrator")] attribute the CustomRoleProvider constructor is being called (I only included the constructor in to see whether the breakpoint would be hit) but none of the methods are being called. And then I am left with a HTTP Error 401.0 - Unauthorized page.
Aside from adding the necessary bits to the web.config I haven't done anything else to get Windows authentication to work. I assume it's working though because if I don't include <allow users="*"></allow> (obviously without the inclusion of the Authorize attribute) I get a 401.2.: Unauthorized: Logon failed due to server configuration error, so I assume I'm being authenticated.
I've cleared my browser cookies as per this SO post but that had no effect.
CustomerRoleProvider
public class CustomRoleProvider : RoleProvider
{
public CustomRoleProvider()
{
}
public override bool IsUserInRole(string username, string roleName)
{
bool isUserInRole = false;
// omitted for brevity
return isUserInRole;
}
public override string[] GetRolesForUser(string username)
{
string[] roles = null;
// omitted for brevity
return roles;
}
// omitted for brevity
}
web.config
<authentication mode="Windows">
</authentication>
<authorization>
<allow users="*"></allow>
<deny users="?"/>
</authorization>
<roleManager defaultProvider="CustomRoleProvider" enabled="true">
<providers>
<clear />
<add name="CustomRoleProvider" type="ProjectName.UI.Mvc.Code.Providers.CustomRoleProvider" />
</providers>
</roleManager>
Home Controller
[Authorize(Roles = "Administrator")]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
This was caused from not having IIS Express set to use Windows Authentication. To fix I selected the project in Solution Explorer, opened the Properties window set Windows Authentication = Enabled and Anonymous Authentication = Disabled. Custom role provider now works.
Related
I created an ASP.NET MVC app with the following override via a custom AuthorizeAttribute implementation:
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return base.AuthorizeCore(httpContext);
}
However, within this method, httpContext.User.Identity.Name is "". I need to get a handle to the current Identity.Name so I can retrieve some data based on that value. I have the following entries in web.config:
<authentication mode="Windows" />
<authorization>
<deny users="?"/>
</authorization>
At minimum, I think that the MVC site should prompt me for credentials with the configuration above, right?
I was able to get the network user ID with the following alternate code:
Request.LogonUserIdentity.Name
Are there any implications or impacts of using this code as opposed to:
httpContext.User.Identity.Name
I have a WebAPI project in which I need to use Windows Authentication for the Users but implement a Custom Role Provider for the Authorize attributes above the routes. When I implement it however I always get {"Message":"Authorization has been denied for this request."} as the result for my call. Furthermore none of my breakpoints ever trigger except for the one in the constructor of the Custom Role Provider
Controller
[Authorize(Roles="Administrator")]
[RoutePrefix("api/Constituents")]
public class ConstituentsController : ApiController
{
[Route("Constituents")]
[HttpGet]
public IDataResponse<List<IConstituent>> GetConstituents()
{
return service.GetConstituent();
}
Custom Role Provider
public class CustomRoleProvider : RoleProvider
{
public CustomRoleProvider()
{
this.UserRepo = new UserRepository(); //Breakpoint here triggers
this.RoleRepo = new RoleRepository();
}
public override string[] GetRolesForUser(string username)
{
var roles = UserRepo.GetUser(username)?.Roles?.Select(r => r.Name).ToArray();
return roles;
}
public override bool IsUserInRole(string username, string roleName)
{
var user = UserRepo.GetUser(username);
return user.Roles.Select(r => r.Name).Contains(roleName);
}
Web Config
<authentication mode="Windows"/>
<roleManager cacheRolesInCookie="false"
defaultProvider="CustomRoleProvider"
enabled="true">
<providers>
<clear />
<add name="CustomRoleProvider"
type="Data.CustomRoleProvider, Data" />
</providers>
What piece of the puzzle am I missing? I need to get the current user making the request and then check to see if the have the appropriate role in the database.
Thanks
By default, Visual Studio sets the Windows Authentication property on your project to Disabled. In the Properties pane (not the tab), you need to flip the property to Enabled. That should let you hit the breakpoints on your RoleProvider.
When you put your application on a server, you may have to perform a similar process in IIS to Enable Windows Authentication and Disable Anonymous Authentication.
Below is the code used to login
WebSecurity.Login("abc", "123", true);//return true
return RedirectToAction("afterLogin", #"afterLogin");
After loggin in, I checked the user's id to see if it's -1 by running the below line:
WebSecurity.CurrentUserId
But why whenever I called this, the return value always -1 and CurrentUserName is empty?
edited:
An additional question:
Does the WebSecurity have something like timeout so that the user idle for a specific period and will logged out automatically?
Check your webconfig, you have to enable forms authentication:
Add following snippet inside
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="3600" />
</authentication>
Comment out if it is in your webconfig:
<!--<modules>
<remove name="FormsAuthentication" />
</modules>-->
Now you can check
WebSecurity.CurrentUserName, WebSecurity.CurrentUserId and , WebSecurity.IsAuthenticated flags;
Also add this class in app_start
public static class AuthConfig
{
public static void RegisterAuth()
{
}
}
and call this in AppStart in Global.asax.cs
AuthConfig.RegisterAuth();
I think the default expiration is when the browser session ends. It might be that cookies are not enabled and that's why it is returning -1 cookies need to be enabled.
Normally use authorization settings to interact with the roles.
<location path="Register.aspx">
<system.web>
<authorization>
<allow roles="Administrator"/>
<deny users="?"/>
</authorization>
</system.web>
</location>
But I would like to have this setup in the database in a table
tblAuthorization
-IdAuthorization (Identy(1,1))
-IdCompany=5
-IdRol=5 (Administrator”)
-Path=”Register.aspx”
Is there a setting for a class to do this? There is something like Profile, RoleProvider ..
<roleManager enabled="true" defaultProvider="MyAccessRolProvider">
<providers>
<clear />
<add name="MyAccessRolProvider" type="MyAccessRolProvider" connectionStringName="MyConnectionString" applicationName="/" />
<add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/" />
</providers>
</roleManager>
Right now the only I think that can to implement a validation in the Page_Load event
And if it is invalid making a Redirect but I would do a little more "Professional"
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
if(!ValidateAuthorization())
{
Response.Redirect("Login.aspx");
}
}
}
Could help me with examples?
Thank you very much in advance
Your best bet is to implement a custom RoleProvider - you seem to be heading in this direction as your question includes a configuration section with a custom RoleProvider (MyAccessRolProvider).
You need to specify the fully-qualified name of your RoleProvider, e.g. MyNamespace.MyAccessRoleProvider, MyAssembly.
MSDN describes how to do this, including a sample implementation.
With the understanding that you are taking about authorization at the page level you could add a HTTP module (IHttpModule) and use your module to do the authorization checking. If the authorization failed then you could redirect as appropriate.
Something like this might work:
public class MyAuthorizationModule : IHttpModule
{
public void Init(HttpApplication application)
{
application.AuthorizeRequest += (new EventHandler(AuthorizeRequest));
}
private void AuthorizeRequest(Object source, EventArgs e)
{
bool isAuthorized = //your code here to check page authorization
if (!isAuthorized)
{
var response = //will need to get current response
response.Redirect("Login.aspx", true);
}
}
}
Note: Don't forget you'll need to add the module to your web application.
There is no out of the box support for this as far as I know however I would suggest you implement something as follows:
Create a new class that derives from AuthorizeAttribute, add an ID property and override the "AuthorizeCore" method.
public class CustomAutorizeAttribute : AuthorizeAttribute
{
public Guid ActionId { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//Check user has access to action with ID = ActionId
//implementation omitted
}
}
You can add a new database table to hold on the Actions and their associated ID values. You can either add the record to the table manually or write some code to do this using reflection (I would advise this if you have quite a few actions and are likely to add more in the future).
Once this is done you can assign users or application roles access to these actions.
In you custom Authorize attribute you have to check whether the user has access to the given action returning true or false accordingly from the AuthorizeCore method.
I would advise grabbing a collection of all actions a user has access to when a user logs in and storing them in either the server cache or some kind of distributed cache so that you aren't hitting your database on every request.
UPDATE - Sorry just realised you're using ASP.NET Forms and not MVC. My approach is for an MVC application so its probably not relevant although I'll leave it up here in case any MVC developers happen to stumble across your question.
I am upgrading my project from asp.net web forms to MVC4, step by step. In the first step I changed the login page and few other pages. I am using forms authentication, with my own logic (no membership) - I check the username/password against a database table. If it is OK the user is redirected to its destination.
My login code is:
Web.config:
<authentication mode="Forms">
<forms loginUrl="~/LogIn" name=".ASPXFORMSAUTH" timeout="150" />
</authentication>
<authorization>
<deny users="?" />
</authorization>
Login Controller:
[AllowAnonymous]
[HttpPost]
public ActionResult AjaxLogin(FormCollection postedFormData)
{
try
{
string userName = postedFormData["Login_UserName"];
string password = postedFormData["Login_Password"];
UserEntity userEntity = new UserEntity(Utilities.AuthenticateUser(userName, password, 1));
Session["UserEntity"] = userEntity;
FormsAuthentication.SetAuthCookie(userEntity.Key.Id.ToString(), false);
return Json(new { redirectToUrl = "./AccountSelection", error = "false", lan = Thread.CurrentThread.CurrentUICulture.ToString() });
}
catch (Exception ex)
{
return Json(new { redirectToUrl = "", error = ExceptionHandler.HandleException(ex), lan = Thread.CurrentThread.CurrentUICulture.ToString() });
}
}
When I try to login I get http 302 and redirected back to login.
If I remove the "authorization" section on web.config it will work fine, but now I have two problems:
I have to put [authorize] attribute on every controller
My webforms will not be inside forms authentication (can be accessed directly with no login!!)
What am I doing wrong?
If you're defining your authorization in web.config, you don't need an AllowAnonymousAttribute.
Having said that, you don't appear to be adding AjaxLogin to your authorization list. This is necessary, because the Ajax request will otherwise be blocked. You need both ~/Login and ~/Account/AjaxLogin paths. You may also need a ~/Account/Login path, but i'm not certain of that.