I'm building a Asp.net MVC3 aplication (with Razor) and I have a Data Base that have information about users and roles.
This is simplified scheme of my DB.
User(IDUser, Login, Password);
Role(IDRole, Name);
UserInRole(IDUser, IDRole); //Many to Many
Looks like this:
I read about use AuthorizeAttribute, to control pages for loged users, and with specific roles and I research about use My DB to control users and roles. So my questions is:
Is possible use my DB to manage users and roles and use [Authorize] in my actions? [If yes how i do that?]
Is possible use session in the place of cookie to manage login and use the Authorization native Asp.net MVC3? [if yes, how i do that? if no how use session otherwise?]
If possible please post code examples.
Not sure if I understood correctly, but you want to use the [Authorize] attribute to work with your custom users database?
If that's the case, there are somethings to check:
To simply allow/deny based whether the user is authorized or not, the stock [Authorize] attribute will work just fine. The custom logic goes in your Login action, where you will check the database with the given credentials and issue the cookie accordingly. Something like:
public ActionResult Login(string username, string password)
{
bool isValid = //check the database with the given username and password
if(isValid)
{
FormsAuthentication.SetAuthCookie(username, false);
return RedirectToAction("...");
}else
{
return View();
}
}
If you want to also control access based on roles, I would say there are 2 immediate ways:
Implement a custom Membership and Role providers, something I don't like as I find them pretty useless, and always end up redoing the logic in my respositories
Implement a custom AuthorizeAttribute, like
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//Check based on these 2 properties:
// this.Roles
// this.Users
//against httpContext.User
//return true or false to indicate access or deny
}
}
Thanks Pedro. Based in your post I build this to use SESSION:
public class CustomAutorizeAttribute : AuthorizeAttribute
{
public List<string> Roles { get; set; }
public CustomAutorizeAttribute()
{
}
public CustomAutorizeAttribute(params string[] Roles)
{
this.Roles = new List<string>();
this.Roles.AddRange(Roles);
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
User user = (User)httpContext.Session["user"];
if (user != null)
{
if (Roles != null)
{
foreach (var role in user.Roles)
{
if (Roles.Exists(e => e == role)) return true;
}
return false; // User don't have any hole
}
else
{
return true; // The Attribute is used without roles.
}
}
else return false; // Not logged.
}
}
Post here to hope others.
Related
I have created an enum with security access levels, an example:
public enum AccessLevel
{
Total,
DeletionPrivileges,
MaintainUsers,
MaintainInventory,
QueriesOnly,
None
}
I can manage the site so certain features eg delete, are not presented to someone without deletion privileges. But I am also wanting to use some kind of authorisation within the code.
Within the default framework, there is the facility to prevent access to certain areas of a project using [Authorize], how can I create differing levels of authority to tag each method?
You could use claim based authentication feature of Identity to aim this purpose easily. First you need add proper claim per user in log in action method to do this change your log in action method like this:
[HttpPost]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var userManager=HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
var user = userManager.Find(model.UserName, model.Password);
if (user != null)
{
var ident = userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
// imaging you have a custom class which return user access levels
var userAccessLevels=_accessLevelManager.GetAccessLevels(user.Id);
// now we are going to add our custom claims
ident.AddClaims(new[]
{
// add each access level as a separate claim
new Claim("AccessLevel",userAccessLevels[0].ToString()),
new Claim("AccessLevel",userAccessLevels[1].ToString()),
// and so on
});
HttpContext.GetOwinContext().Authentication.SignIn(new AuthenticationProperties { IsPersistent = false }, ident);
// authentication succeed do what you want
return Redirect(login.ReturnUrl ?? Url.Action("Index", "Home"));
}
}
ModelState.AddModelError("", "Invalid username or password");
return View(login);
}
Now we have successfully injected our claims to Identity. But you need a custom authorize attribute to check your claims like this:
public class ClaimsAccessAttribute : AuthorizeAttribute
{
public string ClaimType { get; set; }
public string Value { get; set; }
protected override bool AuthorizeCore(HttpContextBase context)
{
return context.User.Identity.IsAuthenticated
&& context.User.Identity is ClaimsIdentity
&& ((ClaimsIdentity)context.User.Identity).HasClaim(x =>
x.Type == ClaimType && x.Value == Value);
}
}
Now you could easily use your attribute in your action methods:
[ClaimsAccess(CliamType="AccessLevel",Value="DeletionPrivileges")]
public ActionResult MyAction()
{
// also you have access the authenticated user's claims
// simply by casting User.Identity to ClaimsIdentity
// ((ClaimsIdentity)User.Identity).Claims
}
Using ASP.NET MVC 5 and Entity Framework. How can I secure my application so I cant access other users data?
To do CRUD stuff I have index, create, edit, delete methods in FooController so I can use:
/Foo/
to view my information I click one and get
/Foo/Details/5
When I type in 3 in the browser I get someone else's information.
/Foo/Details/3
How can I secure this from being accessed everywhere? I use Owin Identity and are logged into the application.
You could write a custom authorization filter by deriving from the AuthorizeAttribute class and inside you could check whether the currently authenticated user has access to the requested resource.
For example:
public class MyAuthorizeAttribute: AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool authorized = base.AuthorizeCore(httpContext);
if (!authorized)
{
return false;
}
string id = httpContext.Request["id"];
string currentUser = httpContext.User.Identity.Name;
return HasAccessToResource(id, currentUser);
}
private bool HasAccessToResource(string id, string currentUser)
{
// You know what to do here => check in your backend whether the
// current user is authorized to access the specified resource id
throw new NotImplementedException();
}
}
and then decorate your controllers/actions with this custom attribute:
[MyAuthorize]
public ActionResult Delete(string id)
{
// if you get that far, the current user is owner of the requested resource
...
}
I have the following structure in my DB:
DomainEntities:
+EntityID
+Name
+ParentID
+...
Users:
+UserID
+Username
+...
Roles:
+RoleID
+Name
UserRolesAssociation:
+RoleID
+UserID
+EntityID
So i want to use MVC's built in authorization attribute to filter action in my controllers that are made by different members.
I what to be able to say if user1 makes a delete action on entity1 or any entity under it i can see if he has the right role to do that and filter the action accordingly.
What would be the best practice to tackle that topic ?
Should i create my own permissions engine that will provide me the answers i need or can i use the existing capabilities ?
What would be the best practice to tackle that topic ?
A custom [Authorize] seems like a good place to implement this logic.
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var authorized = base.AuthorizeCore(httpContext);
if (!authorized)
{
// the use ris not authenticated or not authorized - no need to continue
return false;
}
string username = httpContext.User.Identity.Name;
// read the entity id that this user is attempting to manipulate
string entityId = (string)httpContext.Request.RequestContext.RouteData.Values["id"] ?? httpContext.Request["id"];
return IsAllowed(username, entityId);
}
private bool IsAllowed(string username, string entityId)
{
// You know what to do here - hit the database and check whether
// the current user is the owner of the entity
throw new NotImplementedException();
}
}
and then:
[HttpDelete]
[MyAuthorize]
public ActionResult Delete(int id)
{
...
}
I am implementing the security part of an ASP MVC application and I am getting confused in how to implement a custom membership provider since there seems to be a lot of functionallity that I wouldn't use.
The application I'm porting usually managed the security through an object stored in session called "SecurityManager" which contained the current user and a form permission collection. Each item of this collection contains the permission per form of the logged user and permissions per field of that form (in case they were needed if not full control of the form is assumed).
However, when I see the methods of the MembershipProvider and the AuthorizeAttribute tag they assume that I will use roles which my application does not use, we just have permission groups which are just the permissions grouped together for certain user groups, but they tend to change in time.
So basically the only thing I would need would be something that when a request is made would check if the security manager is stored in session (if it is not the user is not authenticated and will be redirected to the login page) and then get that object from session and perform operation with it to know if the user can or cannot access the view.
What would be the best approach for this? I've read that bypassing the custom membership was not a good idea.
Update: I recently came up against this and figured out how to use the AuthorizeAttribute to do exactly as you wanted. My attribute, that verifies if the user is an administrator, works as follows:
public class AuthorizeAdminAttribute : AuthorizeAttribute
{
public bool IsValidUser { get; protected set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null) { throw new ArgumentNullException("httpContext"); }
// Make sure Forms authentication shows the user as authenticated
if (httpContext.User.Identity.IsAuthenticated == false) return false;
// Retrieve the unit of work from Windsor, and determine if the current user is an admin
var unitOfWork = Bootstrapper.WindsorContainer.Resolve<IUnitOfWork>();
var user = new UserByIdQuery(unitOfWork).WithUserId((int)Membership.GetUser().ProviderUserKey).Execute();
if (user == null)
return false;
// Otherwise the logged in user is a real user in the system
IsValidUser = true;
return user.IsAdmin;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext == null) { throw new ArgumentNullException("filterContext"); }
// If this user is a valid user but not an admin, redirect to the homepage
if (IsValidUser)
{
// Redirect them to the homepage
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary
{
{ "area", "" },
{ "action", "Index" },
{ "controller", "Home" }
});
}
else
{
// User isn't logged in, perform normal operations
base.HandleUnauthorizedRequest(filterContext);
}
}
}
Essentially you have to user the AuthorizeCore() to determine if the user is logged in, store that result, and then perform authorization for the roles in your system. Then in your HandleUnauthorizedRequest you have to figure out if the request was unauthorized because the user wasn't logged in, or if it was because they weren't authorized.
Old Answer
I accomplish using the Authorize attribute by creating a subclass of the AuthorizeAttribute class. So for example:
public class MyCustomAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null) { throw new ArgumentNullException("httpContext"); }
// Make sure Forms authentication shows the user as authenticated
if (httpContext.User.Identity.IsAuthenticated == false) return false;
// replace with whatever code you need to call to determine if the user is authorized
return IsUserAuthorized();
}
}
Now when a controller or action is called, and is decorated with [MyCustomAuthorize] it will run this code to determine if the user is authorized based on your custom logic, and if not will redirect them exactly how the [Authorize] attribute would.
I don't know if this is the best approach, but it is what I came up with.
I am building a simple CMS in which roles are set dynamically in the admin panel. The existing way of authorizing a controller method, adding [Authorize(Roles="admin")] for example, is therefore no longer sufficient. The role-action relationship must be stored in the database, so that end users can easily give/take permissions to/from others in the admin panel. How can I implement this?
If you want to take control of the authorization process, you should subclass AuthorizeAttribute and override the AuthorizeCore method. Then simply decorate your controllers with your CmsAuthorizeAttribute instead of the default.
public class CmsAuthorizeAttribute : AuthorizeAttribute
{
public override virtual bool AuthorizeCore(HttpContextBase httpContext)
{
IPrincipal user = httpContext.User;
IIdentity identity = user.Identity;
if (!identity.IsAuthenticated) {
return false;
}
bool isAuthorized = true;
// TODO: perform custom authorization against the CMS
return isAuthorized;
}
}
The downside to this is that you won't have access to ctor-injected IoC, so you'll have to request any dependencies from the container directly.
That is exactly what the ASP.NET membership / profile stuff does for you. And it works with the Authorize attribute.
If you want to roll your own you could create a custom action filter that mimics the behavior of the standard Authorize action filter does. Pseudo code below.
public MyAuthorizeAttribute : ActionFilterAttribute
{
public string MyRole { get; set; }
public void OnActionExecuting(ControllerContext context)
{
if (!(bool)Session["userIsAuthenticated"])
{
throw new AuthenticationException("Must log in.");
}
if (!Session["userRoles"].Contains(MyRole))
{
throw new AuthenticationException("Must have role " + MyRole);
}
}
}
The role - action relationship must be
stored in the database
You will have to check your security within the controller method, unless you want to subclass AuthorizeAttribute so that it looks up the roles from the database for you.