Is there a way to control access to methods to certain roles in .net. Like
class A
{
//should only be called by Admins**
public void Method1() { }
//should only be called by Admins and PM's**
public void Method2() { }
}
I'm using windows authentication only for retrieving user names and nothing more.User roles are maintained in a different application. I think it's possible through attributes but I'm not really sure how
It it possible, I have used it on an web project that used asp.net and AzMan as the authentication.
Take a look at Code Access Security
From memory all of our methods looked something like
[Permission(SecurityAction.Demand, "Permission")]
public void Method1
It's been a while though so that might not be actually 100% correct.
I'd also highly suggest if you are going to put protection down to this level to look at a task orientated permission approach as this is much more flexible than role based permissions
You can do this as follows:
class A
{
//should only be called by Admins**
[PrincipalPermission(SecurityAction.Demand, Role="Admin")]
public void Method1()
{
}
//should only be called by Admins and PM's**
[PrincipalPermission(SecurityAction.Demand, Role="Admin")]
[PrincipalPermission(SecurityAction.Demand, Role="PM")]
public void Method2()
{
}
}
To do this Thread.CurrentPrincipal must be set to a principal that has the required roles. For example, if you enable roleManager in an ASP.NET application, Thread.CurrentPrincipal will be set to a RolePrincipal with roles from your configured RoleProvider. See this MSDN article for more info.
You can do it using custom validation.
1- Make a method in another public class which take login id as parameter and return roles in form of bits.
2- Call this method on the page_Load event of the required class and save returned bits in the view state.
3- Now validate required method on the basis of roles bits.
Related
In our ASP.NET MVC project we are using Ninject to resolve our dependencies needed by the controllers.
One of these dependencies is the current user HttpContext.Current.User.Identity. If the user is authenticated we would like to instantiate a user object and several services which relies on it. But we would like to do this not manually but let ninject inject these instances to the controller.
So we get into trouble now, since a url can be located without being authenticated of course. Then ninject tries to resolve the instances before asp.net can redirect to the login page.
I can think of the solution, that we configure ninject do just inject when user is authenticated:
kernel.Bind<User>().ToMethod(GetUser).When(context => HttpContext.Current.User.Identity.IsAuthenticated).InRequestScope();
The problem here is that even if the user is not authenticated ninject instantiates a default object, so my services crashes or needs to check the instance anyhow.
Null checks would me much more acceptable but I wouldn't like to activate AllowNullInjection setting of Ninject.
So my question is whats the best practise for doing such conditional things?
Are there Ninject features I could use in these cases or shouldn't I inject these dependencies anyway?
I assume you are talking about a situation where a non-authenticated user could try to navigate to a page that normally requires authentication, but without first going through the login process. Ninject would then be unable to inject the current user object into the controller because it's not yet known and will throw an exception.
I can see 2 options:
The first option is instead of injecting the current user, create a factory or provider that retrieves the current user details and inject this instead. The controller can then call the provider to get the current user and if the user is unavailable you can redirect to the login page.
public OrdersController(IUserProvider userProvider)
{
this.userProvider = userProvider
}
public void DoSomething()
{
var user = this.userProvider.GetCurrentUser();
if (user == null)
RedirectToLogin();
// continue doing something
}
public class UserProvider : IUserProvider
{
public User GetCurrentUser() { ... }
}
The problem with this option is that you'll need to do this potentially in many controllers (it's a "cross cutting concern") and you don't want to have to repeat the code that does the redirect over and over. Instead, a second option would be to use the Decorator design pattern to create an interceptor that checks for logged in users before forwarding on to the real controller.
The way I've done something similar in the past is using the Ninject Interception Extension to create an attribute that marks which controllers require authentication, like this (bit psuedo-codey):
public class AuthenticationInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
bool authenticated = // ... get the current user ...
if (authenticated)
invocation.Proceed();
else
RedirectToLoginPage(); // however you want to do this
}
}
public class RequiresAuthenticationAttribute : InterceptAttribute
{
public override IInterceptor CreateInterceptor(IProxyRequest request)
{
return request.Context.Kernel.Get<AuthenticationInterceptor>();
}
}
[RequiresAuthentication]
public class OrdersController : IOrdersController
{
// assume you've already been authenticated
}
The interceptor will automatically be created whenever a class that's decorated with RequiresAuthentication is created and the current user credentials will be checked. If they are invalid, the request will be forwarded to the login page, otherwise it will continue as normal. This one interceptor can then be written and tested once whilst being used in many places without duplicating code.
Just as a simple auth and non auth answer that some may find useful.
kernel.Bind<ICustomUser>()
.To<User>()
.When(ctx => HttpContext.Current.User.Identity.IsAuthenticated)
.InRequestScope();
kernel.Bind<ICustomUser>()
.To<Guest>()
.When(ctx => !HttpContext.Current.User.Identity.IsAuthenticated)
.InRequestScope();
Otherwise anything more complex Adam Rodgers awnser is better :)
I am creating an application where I first login with my user account. This user account could be windows or self managed account in my own application database.
Now I want to authorize the logged in user before accessing any business objects of my application. Objects are mapped with database tables so eventually I want to authorize user first, whether to give data back to user or not.
After logging in I store user credentials globally as an object of UserCredential class. But I don't want to pass this credentials to each object when I am creating it.
Is there any way to check/reach the application context (including UserCredential object I stored globally) for each business objects automatically which I am creating further?
I want to achieve this in C#. Code example is much appreciated.
You should take a look at the PrincipalPermissionAttribute class, here is the MSDN documentation:
PrincipalPermissionAttribute class MSDN documentation
The PrincipalPermissionAttribute throws a SecurityException when the Thread.CurrentPrincipal does not match the security assertion.
Examples:
User's name is GDroid:
[PrincipalPermission(SecurityAction.Demand, Name = "GDroid")]
public void YourBusinessMethod()
{
// Do something
}
User belongs to Admin role:
[PrincipalPermission(SecurityAction.Demand, Role = "Admin")]
public void YourBusinessMethod()
{
// Do something
}
User is authenticated:
[PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
public void YourBusinessMethod()
{
// Do something
}
Once authenticatated I use HttpContext.Current.User.Identity.Name; to ensure user is authorized to view a part of my site.
When I access certain parts of my site I need to get the User and get which context (organization they are logged into), url would be something like settings/supercompany/profile. where supercompany is the current context.
For each user I would need to check if they are admin in that company or a general user, if a general user then they cannot see certain things.
public class SettingsApi
{
private readonly string _userId;
private readonly string _contextId;
public SettingsApi(string userId, string contextId)
{
_userId = userId;
_contextId = contextId;
}
}
If I instantiate the class above from a controller (post or get), would caching somehow mess things up? Users role changed and I don't pick it up? Would something like the below work well?
var settings = new SettingsApi(HttpContext.Current.User.Identity.Name, currentContextId);
settings.IsAdmin();
Note: I would have used attributes to authorize but my requirements are I need to pick out the currentContext from the URL plus I need to use the class above elsewhere in my code.
Update
AuthorizeAttribute works well with caching, but the method used to authorize i.e.
protected override bool AuthorizeCore(HttpContextBase httpContext)
Will not hand me back an instance of the class I need...
Update 2 I don't want this class or an instance of this class to be cached in anyway, everytime I ask for a new instance I don't mind fetching one from the DB...
My Question - is the way I am coding ok? Will my user and his permissions NOT be cached?
It is possible, if you're not careful, to let MVC cache the output of the first request by an authenticated user. I use VaryByCustom and the current identity's name.
[OutputCache(VaryByCustom="user")]
public class SomeController : Controller
{
// etc.
}
In my Global.asax.cs I define:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (custom.Equals("user", StringComparison.OrdinalIgnoreCase))
{
return context.User.Identity.IsAuthenticated ? context.User.Identity.Name : string.Empty;
}
return base.GetVaryByCustomString(context, custom);
}
If you are proposing to add instances of the SettingsApi to the cache then it definitely will not work as caching is app wide and so all users will end up sharing the same SettingsApi. Using the OutputCache should be fine (as long as you dont do something like put userid in a hidden field and use [OutputCache(VaryByCustom="user")] or similar).
If you are looking to cache the SettingsApi you should do so through SessionState which is per user/session and wont affect the authentication.
I've never seen this done but I had an idea of doing authorization in a more purely OO way. For each method that requires authorization we associate a delegate. During initialization of the class we wire up the delegates so that they point to the appropriate method (based on the user's rights). For example:
class User
{
private deleteMemberDelegate deleteMember;
public StatusMessage DeleteMember(Member member)
{
if(deleteMember != null) //in practice every delegate will point to some method, even if it's an innocuous one that just reports 'Access Denied'
{
deleteMember(member);
}
}
//other methods defined similarly...
User(string name, string password) //cstor.
{
//wire up delegates based on user's rights.
//Thus we handle authentication and authorization in the same method.
}
}
This way the client code never has to explictly check whether or not a user is in a role, it just calls the method. Of course each method should return a status message so that we know if and why it failed.
Thoughts?
This is basically the null object pattern for authorization. It's an interesting idea if you can figure out a way to design StatusMessage such that the calling code doesn't need special cases. For instance, for certain actions, you'll want to indicate "You can't do that as a guest, but would you like to login or sign up for an account?" So certain StatusMessages might need to redirect to a login/sign up page.
I'm implementing role based security using Microsoft's membership and role provider.
The theoretical problem I'm having is that you implement a specific role on a method such as:
[PrincipalPermissionAttribute(SecurityAction.Demand, Role="Supervisor")]
private void someMethod() {}
What if at some point down the road, I don't want Supervisors to access someMethod() anymore?
Wouldn't I have to change the source code to make that change? Am I missing something?
It seems there has to be some way to abstract the relationship between the supervisors role and the method so I can create a way in the application to change this coupling of role permission to method.
Any insight or direction would be appreciated. Thank you.
If you use the declarative approach, then yes - if you suddenly don't want members of the Supervisor to be able to call your method, you need to change your source code for that.
You can, however, also do all of this in code, programmatically:
private void someMethod()
{
WindowsPrincipal currentUser = (Thread.CurrentPrincipal as WindowsPrincipal);
if (currentUser != null)
{
if (currentUser.IsInRole("Supervisor"))
{
// do something here
}
}
}
You can always get the current Windows principal your Winforms app is running under, and then you can call the IsInRole method to check whether or not a given user is in a given role. Of course, you can also make all of this configurable, e.g. read the required role from a config file, and if you want to allow everyone in, you just simply change the role to be Users or something
PrincipalPermissionAttribute does not tend to be used in many applications I've worked on for the reason you've touched upon; the attribute applies a policy which can only be altered by a code change.
The alternative is to use the PrincipalPermission class directly. All security attributes resolve to similarly named classes and method calls at run time. In the case of your attribute, the following code is executed:
PrincipalPermission permission = new PrincipalPermission(null, "Supervisor");
permission.Demand(); // Throws SecurityException if user is not in the role.
If you use the permission classes directly, you gain more control over how your permissions are formed. You could have a database which you query to get a list of roles and perform a demand for them like this:
private void someMethod()
{
IEnumerable<string> roles = GetRolesForMethod("someMethod");
PrincipalPermission permission = null;
foreach(string role in roles)
{
if(permission == null)
{
permission = new PrincipalPermission(null, role);
}
else
{
permission = permission.Union(
new PrincipalPermission(null, role);
);
}
}
if(permission != null)
{
permission.Demand();
}
}