Is the following API of a claims authorization service ok, from the point of view of usability?
/* before UPDATE it was like this:
var canEdit = Authz.ForNewRequest()
.WithActionName("edit")
.WithResourceName("project")
.WithResourceClaim("DepartmentId","21")
.CheckAccess(); */
//UPDATE:
var deptId = SomehowGetDepartmentIdAtRuntime(targetProject);
var canEdit = Authz.ForNewRequest()
.WithActionName("edit")
.WithResourceName("project")
.WithResourceClaim("DepartmentId",deptId)
.CheckAccess();
if (canEdit)
{
//edit project
}
And configuration like this:
var authorizationModel = Authorization.ConfigNamespace.AuthzConfig
.LoadAuthorizationModelFromXml("authz.xml");
Authorization.ConfigNamespace.AuthzConfig
.SetApplicationAuthorization(authorizationModel);
Or custom configuration like this:
var authzCustomConfig = Authorization.ConfigNamespace.AuthzConfig
.NewCustomConfiguration()
.WithCustomClaimBasedFactFunctions(claimBasedFunctions)
.WithCustomClaimProviders(claimProviders)
.WithCustomCompositeFactFunctions(compositeFactFunctions)
.WithCustomObligations(obligations);
var authorizationModel = Authorization.ConfigNamespace.AuthzConfig
.LoadAuthorizationModelFromXml("authz.xml", authzCustomConfig);
Authorization.ConfigNamespace.AuthzConfig
.SetApplicationAuthorization(authorizationModel);
Basically, the question is about the top part of the iceberg, i.e. how to use the service, but not how to implement or design the inner part. But just in case, here are a couple of general words about this service:
This service gives an answer of true/false for the given authorization request.
Authorization request has information about:
Action (action name, for example)
Resource (resource name, resource properties)
Subject (user name, user id, user roles, user properties)
Due to the fact that i use Microsoft.IdentityModel:
The properties of an action, resource, or a subject are presented as Claims. Approximately, a claim is an object, which has a value, value name, and value type. For example, a claim for a user could have the following info: ("UserName", "Andrey", "string").
The authorization request is the AuthorizationContext class from the Microsoft.IdentityModel.Claims namespace.
Two more things to consider:
I'd like this service to be a general solution, which could fit not only to one certain project.
Logic could be involved in order to understand if a condition for the permission decision is met or not.
That's why custom logic might be injected. Those claimBasedFunctions, claimProviders, compositeFactFunctions, and obligations are the custom logic. But they don't really matter for the question, just some custom confuguration elements, implementations of the interfaces, which are defined in the authorization assembly. The question is not about what they should be, or how they should work. You can think of them as of any interface implementatons that have to be injected.
Thanks!
P.S. this question is off-topic for the Code Review site.
If I interpret your description correctly, the following code means: "If you want to edit a project, you need a claim with name DepartmentId that has a value of 21.
var canEdit = Authz.ForNewRequest()
.WithActionName("edit")
.WithResourceName("project")
.WithResourceClaim("DepartmentId","21")
.CheckAccess();
if (canEdit)
{
//edit project
}
This statement would be at the start of your Edit action in your Project controller in case you were building an MVC application.
If my interpretation is correct, I would advise you to remove the WithActionName and WithResourceName methods. Those things can be retrieved from the context in which this code is executing. Your fluent API is too easy to copy from one method to another and forget to update those strings. I would look at a custom authorize attribute that you attach to an action in which you checks the claims.
UPDATE:
I was thinking something like this:
public class ProjectController : ApiController
{
[HttpPost]
[MyAuthorize("DepartmentId","21")
public void Edit(string applicationName)
{
// business logic
}
}
Inside the MyAuthorize attribute implementation you can retrieve the controller- and action names. If the developer using the attribute doesn't have to specify this, he/she can't get it wrong.
Related
Just to be clear: I am NOT talking about claims-based identity validation.
I am building an app in which I make fine use of Identity 2.2 to provide validation. It is sufficient for my needs.
My problem is that once a user logs in, only the first page is widely accessible without storing additional information in the user’s “session”. In particular, when the user clicks on a major internal item (for sake of convenience, let’s call this a “customer module”, the Guid for that customer is stored in a claim held by the user. That way, the user can move from page to page and still have that same customer’s data brought up on every page regardless of what chunk of data the page was meant to display. This claim is only refreshed with something different when they return to the main page and click on another customer.
For security’s sake I would like to ensure that if a claim gets accidentally dropped or set to empty, the user gets shunted back to the main page regardless of where they are in the system, and preferably without having to put code in each and every page action of every controller.
Suggestions? Or am I completely wrong by making use of claims? Because it’s still early enough in the project to make a u-turn if the advantages of a different method are compelling enough.
EDIT:
Just to let people know my solution: Because only one group of people will be accessing this application (the users that interact with companies, this app is to record the interactions and “company information”), I decided to use a base controller. The users would be able to log on and view lists of companies without coming across any page that derived from BaseController, but once they chose a Company to work with, they needed to have Claims set to be able to maintain page-by-page contact with this company’s information. This information would be reset only when they chose a different company, but since there was always a chance that a claim could be disabled, I needed something to automagically redirect them back to the list of companies to re-set the claims. A BaseController that was employed by only those pages where information specific to one company would be displayed was the perfect solution.
A base controller is simple. Just create a controller called BaseController and you’re off to the races. Change any controller that needs to work with this base controller such that they are public class YourOtherController : BaseController.
I initially tried to make an Initialize method to handle everything, but ran into a rather big problem: I was unable to successfully both access and write to my Claims. As in, I was able to either read my claims but not make use of my ClaimWriter extension, or I was able to make use of my ClaimWriter extension but be unable to read claims in the first place. Since Initialize is wayyyy too low in the stack to actually do both these things, I abandoned it and went for an OnActionExecuted method, which ended up being successful. My code ended up being this:
public class BaseController : Controller {
private ApplicationDbContext db = new ApplicationDbContext();
protected override void OnActionExecuted(ActionExecutedContext filterContext) {
base.OnActionExecuted(filterContext);
var principal = ClaimsPrincipal.Current.Identities.First();
var company = User.GetClaimValue("CWD-Company");
var prospect = User.GetClaimValue("CWD-Prospect");
if(string.IsNullOrEmpty(company)) {
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.Redirect("/");
filterContext.HttpContext.Response.End();
}
if(!string.IsNullOrEmpty(company) && string.IsNullOrEmpty(prospect)) {
var id = new Guid(company);
var prospecting = db.Prospecting
.Where(x => x.CompanyId.Equals(id))
.Select(x => x.ProspectingId)
.ToList().SingleOrDefault();
if(prospecting.Equals(Guid.Empty)) { // null prospecting
User.AddUpdateClaim("CWD-Prospecting", "");
} else { // fill prospecting
User.AddUpdateClaim("CWD-Prospecting", Convert.ToString(prospecting));
}
}
}
}
I am probably going to change the if(prospecting.Equals(Guid.Empty) part of the Prospecting section to automagically create the first entry in the db (with all null values except for the ProspectingId and the CompanyId, of course), but this is what works for now.
That's a fine use of claims you describe, no need a u-turn. What you need is a MVC filter, authorisation filter. Something like this:
public class MyAuthorisationFilter : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
var principal = HttpContext.Current.User as ClaimsPrincipal;
if(!principal.Claims.Any(c => c.Type == "My Claim Name"))
{
// user has no claim - do redirection
// you need to create 'AuthenticateAgain' route to your table of routes
// or you can do other means of redirection
filterContext.Result = new RedirectToRouteResult("AuthenticateAgain", new RouteValueDictionary());
}
}
}
Then you can add it globally in your filters configuration, but you'll have to exclude your authorisation page from this filter. Or apply on per controller basis - whenever this needs to happen.
This is very basic form of filter - a lot of checks are stripped out, but it gives a general direction how to proceed.
Update
This is a good article about Authorise attribute.
Here use of AllowAnonymous attribute is explained
The way you use it - depends on your scenario. In most cases when you only expose a login page to the world - it is sufficient to add this attribute as a global filter (see second link, part about RegisterGlobalFilters) and then sprinkle [AllowAnonymous] on top of controllers/actions which should be exposed without authentication.
Another approach is to have a base controller that has your attribute applied. And then all your controllers inherit from this base controller. This is more sufficient when global filter does not cut it: cases when you expose different pages to different users - think companies and customers. Your controllers for companies will inherit CompaniesBaseController that has [CompaniesAuthFilter] and customers will be inheriting from CustomersBaseController with [CustomersAuthFilter].
So I have two separate projects (one Web Api 2 and one MVC) like this diagram:
The MVC has controllers and a service layer. The services from the MVC app call to the web api controllers. For example:
await _service.GetGiftCards("/api/GiftCards/ViewAllByUser", email);
The Web Api controllers have their routes defined like so:
[RoutePrefix("api/giftcards")]
[Route("ViewAllByUser")]
public async Task<List<GiftCard>> GetGiftCardsForUser(string email){}
So to define the endpoint route in the MVC app I simply pass a string like "/api/GiftCards/ViewAllByUser" above.
My question is, is there a better way to sort of "strongly type" the endpoints of the Web Api routes that are defined so I can do something like?:
await _service.GetGiftCards(GiftCardController.ViewAllByUser, email);
I guess at a minimum I could always just store the endpoint strings in a static class like so, so they at least can all be updated in one place:
public static class ApiEndpoints(){
public string GetAllGiftCards = "api/GiftCards/ViewAllByUser";
}
but I'm looking to know if there are better ways or other suggestions. Thanks!
API routes shouldn't specify actions. You want your routes to be logical paths to a record or group of records. Example, in your case the route should look something like this:
GET
api/giftcards/{userID:int:min(1)}
You want to be able to walk up the url basically and get what you would expect. In the case of the example route you would get gift cards based on the user id. If you were to take off the user id page and just call api/giftcards you would expect to get all gift cards by all users. I'm using an ID here but you would do the same with email.
Pleas try with 'ActionName' Attribute on action like this :
[ActionName("SelectAll")]
public IEnumerable<Customer> Get()
{
...
}
calling this action name like:
$("#getdata").click(function () {
var options = {};
options.url = "/api/customer/SelectAll";
options.type = "GET";
...
...
$.ajax(options);
});
note: 'getdata' is id of control which click event will be fired and calling 'api method' and getdata from api
Here's a library that may be what you're looking for.
Although I like static strings so you don't always have to show future developers on your team how to use said library when updates are needed on the clients.
I'm working on an ASP.NET MVC application where administrators can add new users and flag them to complete additional information before they can use other features of the site. For example, we have a "ForcePasswordReset" bool, and a requirement to complete Security Questions. We're not using Active Directory for these users.
Ultimately this is the behavior I'd like to implement:
Direct any logged in user who is required to change password to the
ChangePassword view. And if that user clicks on other links, funnel
him back to the ChangePassword view.
Same scenario for users who must change their security questions.
Initially I placed the checks directly into a Login Controller ActionResult. But this only accounts for the Login action. An abbreviated code sample is below.
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid)
{
// ...
// Does the user need to complete some missing information?
if (externalUser.IsSecurityQuestionInfoComplete == false)
return RedirectToAction("ChangeSecurityQuestions", "MyInfo");
if (externalUser.ForcePasswordReset)
return RedirectToAction("ChangePassword", "MyInfo");
// Login was successful
return RedirectToLocal(returnUrl);
}
}
One problem with this approach is that there are other hyperlinks presented in those targeted views, where the user could navigate away from the targeted interface. So for example, a user directed to the ChangeSecurityQuestions view could just click away from it.
Logged-in users can change those settings at any time. I could create duplicate views for changing passwords and security questions that are fashioned just for this scenario, where the user is being forced to update these values. In the interest of staying DRY and reducing maintenance, I'd like to use the same views for both scenarios (users who just want to edit that info, and users who are being forced to edit that info). But, trying to stay DRY in this respect may be wrongheaded if the alternative is worse.
I started to write a method within a helper class to divert these users, trying something like this.
/// <summary>
/// Check scenarios where the ExternalUser needs to complete some missing information, and divert as necessary.
/// </summary>
public static void divertExternalUserAsRequired(Controller controller, ExternalUser externalUser)
{
if (externalUser.IsSecurityQuestionInfoComplete == false)
return controller.RedirectToAction("ChangeSecurityQuestions", "MyInfo");
if (externalUser.ForcePasswordReset)
return controller.RedirectToAction("ChangePassword", "MyInfo");
}
But that RedirectToAction is inaccessible due to protection level. Moreover, that doesn't appear to be recommended (Is it possible to use RedirectToAction() inside a custom AuthorizeAttribute class?). And I don't like the idea of junking up my controllers by pasting this check all over the place.
UtilityHelpers.divertExternalUserAsRequired(this, externalUser);
What is the recommended approach to handling this scenario? I would perfer something that's more globally implemented, where the check can run when any relevant view loads.
Thanks for your help.
If I'm understanding your question correctly then you've got a few options available to you.
One option is to check the necessary conditions within Application_BeginRequest in your Global.asax.cs class. This method is called at the very beginning of every request and if the condition fails then you can load a different controller action like so:
protected void Application_BeginRequest(object sender, EventArgs e)
{
if (!externalUser.IsSecurityQuestionInfoComplete)
{
var routeData = new RouteData();
routeData.Values["action"] = "MyInfo";
routeData.Values["controller"] = "ChangeSecurityQuestions";
RequestContext requestContext = new RequestContext(new HttpContextWrapper(Context), routeData);
IController errorController = new ChangeSecurityQuestionsController();
errorController.Execute(requestContext);
requestContext.HttpContext.Response.End();
}
}
Another option available to you is to create and register a global action filter. As you mentioned in your question, you don't like the idea of littering your controllers with these condition checks. By registering a global action filter your controllers can remain completely unaware of the action filter being performed against it. All you need to do is register your action filter within Global.asax.cs like so:
protected void Application_Start()
{
...
GlobalFilters.Filters.Add(new SecurityQuestionCompleteFilter());
...
}
I hope this helps.
Right now I have the following website structure:
Front end: website\user1, website\user2
Back end: website\account\user1, website\account\user2
Where website\account controller has generic [Authorize] attribute applied. Which is not enough since any authorized user can access other user's backed functionality simply by going to website\account\ url (if he knows his name).
What is the best way to resolve this issue?
I have two approaches so far:
Create custom Authorize attribute, inspect controller context, extract user information from there and compare it against current authorized user in ASP.NET:
var currerntUserId = (long)System.Web.Security.Membership.GetUser().ProviderUserKey;
return ExtractCurrentUserId(filterContext) == currerntUserId;
Remove part from back-end URLs and have all users access \account. Current user information will be provided by ASP.NET framework.
How about this?
Route /website/my-account
[Authorize]
public class MyAccountController : Controller
{
public ActionResult Index()
{
var userData = System.Web.Security.Membership.GetUser();
// note you could also get this from db using this.User.Identity.Name
return View(userData);
}
}
It is much easier to control the authorization this way because we are not passing the userid to the action method via a route parameter. The only way someone can get to the backend for a particular user account is by being logged in as that user.
Reply to comments:
To answer your questions in comments about what is easier / harder / better / what my preference is, I am going to go ahead and make my final answer "It depends."
It depends on the sensitivity of the data, what things admins can do that users aren't allowed to (or vice versa), how many controller actions needed to be secured, how similar the views are for public / account / admin perspectives on the data, etc. Pretty much everything stated in your question and all the answers here are valid approaches. You can certainly do it with an ActionFilter and keep the user URL's, or you could do it directly in the action method (if there aren't a lot of them), change your URL schema, implement impersonation (or not), etc.
When you are retrieving a user's data from your datastore (most likely a database), you should only retrieve data for the username of the authenticated user. In your controller, this will give you the username of the currently authenticated user:
User.Identity.Name
So you could do something like:
return ExtractCurrentUserId(filterContext) == User.Identity.Name;
If you use Role based authentication with SimpleMembership you can do something like this and give users roles that should be able to access certain controller actions:
public class MyAccountController : Controller
{
[Authorize(Roles = "Admin")]
public ActionResult User1()
{
// do user1 work
}
[Authorize]
public ActionResult User2()
{
// do user2 work
}
}
I have 3 different types of users (with different roles) interacting on my web application, they all perform some task - some can be exactly the same e.g. create a quote others can be unique to that specific user e.g. sign off quote.
For more clarity 3 types of users: Client, Supplier, Customer.
Client or Customer can create a quote, however only the Customer can sign off a quote.
How do I ensure my application allows clients to access client speficic controllers and suppliers to access supplier specific controllers or areas. Via Custom Attributes? Do I store the type of user inside a cookie? Is this safe? or Session state? As soon as someone logs onto the system I send back a LoggedOnDTO object on which I store Username, UserID, and type of user....
NOTE: I went away from asp.net build in way of creating users, I have my own custom tables with my custom mechanism for logging into the system. I have a registered Model Bindiner that looks for the prefix and I send in a strongly typed object to each action...
Sample code:
[HttpGet]
public ActionResult AddComment(int quoteid, ClientUserDTO loggedonclientuser)
{
}
[HttpGet]
public ActionResult AddCommentSupplier(int quoteid, Supplier loggedonsuppluser)
{
}
EDIT: This method for some reason seems so much simpler... Is there something wrong with it? Any possible security issues? Threading?
My session controller is:
if (_authService.isValidUser(model))
{
var data = _authService.GetAuthenticationCookieDetails(model);
AuthenticateCookie.AddDetailsToCookie(data);
return Redirect(Url.Action("Index", "Activity"));
}
When I create my cookie... I can simple store "ClientUser", "Supplier" or whatever role they are inside the cookie.
Then I can create an Attribute and read in the cookie data to see if they are a valid user e.g.
public class ClientAuthorizationAttribute : AuthorizeAttribute
{
public bool AlwaysAllowLocalRequests = false;
protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
{
if (AlwaysAllowLocalRequests && httpContext.Request.IsLocal)
{
bool authorized = false;
var result = UserDetails.GetTypeFromTicket(httpContext.User.Identity as FormsIdentity);
if (result.Equals("client", StringComparison.OrdinalIgnoreCase))
{
authorized = true;
}
//throw no access exception?
return authorized;
}
return base.AuthorizeCore(httpContext);
}
}
Register the attribute under my base controller and I have a simple working solution???
Write a custom MembershipProvider and a Custom RoleProvider then you can decorate your controler class or specific methods with the attribute
<Authorize(Roles:="ROLENAME")>
You can learn how to make that your asp mvc use the custom membershiprovider in this question It's really easy.
Edited:
The way you did it looks right, but I think you take the long way. Implementing your own MembershipProvider and your own Roleprovider will take you no more than 20 minutes... and you will have the benefits of being working with a well tested and documented system and still having the benefits of use your own database tables for the login. In a simple login system, you only have to write two functions in the roleprovider (GetRolesForUser and IsUserInRole) and only one function in the membershipprovider (ValidateUser) and you will get your system working.
If you want, I can put somewhere (maybe pastebin) a well commented versión of a membershipProvider as well of a roleprovider that i'm using in a simple app (they're made in vb.net but i'm sure it will not be a problem)
You can also write generic code in the base controller instead of decorating each action with Authorize attribute. Please refer below url.
Good practice to do common authorization in a custom controller factory?
custom-controller-factory/5361846#5361846