I have a WCF service binding with netTcp with over 100 methods, I would like to secure all the methods based on a Windows User Group.
I know you can put the attribute [PrincipalPermission(SecurityAction.Demand, Role = "MyWindowsUserGroup")] before each method.
Do I need to do this individually for every single method or is there a way to have every method in the service secured with this same user group by default?
You can add PrincipalPermission at class level as well as method.
// Before:
public class AdministrationService : IAdminService
{
[PrincipalPermission(SecurityAction.Demand, Role = "Domain\Admin Service Admins")]
public bool DisableAdministrator(int userId)
{
}
[PrincipalPermission(SecurityAction.Demand, Role = "Admin Service Admins")]
public bool DeleteAdministrator(int userId)
{
}
}
// After:
[PrincipalPermission(SecurityAction.Demand, Role = "Admin Service Admins")]
public class AdministrationService : IAdminService
{
public bool DisableAdministrator(int userId)
{
}
public bool DeleteAdministrator(int userId)
{
}
}
You can also define multiple instances of it, if you wish to have multiple types of permissions.
[PrincipalPermission(SecurityAction.Demand, Role = "Admin Service Admins")]
[PrincipalPermission(SecurityAction.Demand, Role = "Domain\Domain Admins")]
[PrincipalPermission(SecurityAction.Demand, Role = "Domain\Power Users")]
public class AdministrationService : IAdminService
{
public bool DisableAdministrator(int userId)
{
}
public bool DeleteAdministrator(int userId)
{
}
}
Related
I have ASP.NET Core MVC application using NET 5. Only authenticated users are allowed to access the application. The authorization policy below takes care of it.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
var authorizationPolicy = new AuthorizationPolicyBuilder()
.RequireClaim(ClaimTypes.Email)
.RequireClaim(ClaimTypes.NameIdentifier)
.RequireClaim(ClaimTypes.Name)
.RequireClaim(IdentityClaimTypes.IdToken)
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(authorizationPolicy));
})
}
The controllers are also using AuthorizeRoles attribute to check access based on roles.
public class AuthorizeRolesAttribute : AuthorizeAttribute
{
public AuthorizeRolesAttribute(params string[] roles) : base()
{
if (roles.Length > 0)
{
Roles = string.Join(",", roles);
}
}
}
[AuthorizeRoles("ClientAdmin")]
public class WorkItemClientsController : BaseController
{
private readonly IClientWorkItemService _clientWorkItemService;
public WorkItemClientsController(IClientWorkItemService clientWorkItemService)
{
_clientWorkItemService = clientWorkItemService;
}
[HttpGet]
[Route("workitems/{workItemID}/clients")]
public async Task<ActionResult> Index([FromRoute(Name = "workItemID")] int workItemID)
{
}
}
The application has few actions that need to be further authorized based on the user's data in the database. I have the following
public class WorkItemRequirement : IAuthorizationRequirement
{
}
public class WorkItemAuthorizationHandler : AuthorizationHandler<WorkItemRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, WorkItemRequirement requirement)
{
//check if logged in user can access this route based on workitemid from the route, if true then return context.Succeed(requirement);
}
}
public class WorkItemAuthorizeAttribute : AuthorizeAttribute
{
public WorkItemAuthorizeAttribute()
{
Policy = "WorkItemPolicy"
}
}
I will add WorkItemAuthorizeAttribute to require action methods.
What I am missing here is how WorkItemAuthorizeAttribute will know which handler to invoke. In this case its WorkItemAuthorizationHandler.
What do I need to change/add in AuthorizationPolicyBuilder in startup.cs to make this association?
Pretty much everything you can find in official docs here
basically as you said you need to modify your policy to include your WorkItemRequirement like that:
.Requirements.Add(new WorkItemRequirement());
That will 'glue' Policy in your Attribute with your AuthorizationHandler
I am working on a project where we are using Amazon SimpleDB as a data storage. In this application user can create roles at run time. While creating role, user can give Read/Write/Update permission for specific feature.
The code I have tried;
using System;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class MyAuthorization : ActionFilterAttribute
{
public string Model { get; set; }
public string Action { get; set; }
public override void OnActionExecuting(HttpActionContext filterContext)
{
//My code will go here
base.OnActionExecuting(filterContext);
}
}
In Web API controller I have written as;
// GET api/values
[MyAuthorization(Action = "Edit", Model = "Rack")]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
Now in OnActionExecuting, I want to fetch Action and Model attributes which I have specified over action method in APIController.
How to handle it through code, since role names and rights are not known at design time.
I assume that each feature you will be implementing in a certain controller and each action method designates the type of operation you are performing (ex Read, Write etc).
If my assumption is correct, you may have to first extend the AuthorzeAttribute ASP.NET MVC framework like below.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public string Operation;
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
//Get the User Id from the session
// Get Role associated with the user (probably from database)
// get the permission associated with the role (like Read, write etc)
// Let assume the retrieved operations are in the form of list of strings
List<string> retrievedOperations =RetrieveRoleOperations(userId)
if (!retrievedOperations.Contains(Operation)
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
}
After creating this class, you have to specify the extended authorize filter in required action methods like below.
Class MyFeatureController:Controller
{
[MyCustomAuthorize(Operation="Read")]
public ActionResult MyReadMethod()
{
//
}
}
I hope this will solve your problem.
I know in MVC at the top of a controller you can use the [Authorize()] attribute to restrict access to that entire controller to certain authenticated users and/or roles, but not by IP, but this must be done on a per controller instance. Is there a way to restrict access to an entire MVC Area to an authenticated User/Role or by the request Source IP?
Create a Base Controller in your area:
[AuthorizeArea(AllowIpAddresses = new [] {"1.1.1.1", "1.2.3.4"})]
public class CustomAreaBaseController : Controller
{
public CustomAreaBaseController()
{
// possibly any other common code that you want to run for all controllers in this area
}
}
Have all controllers in your area derive from base controller:
public class HomeController : CustomAreaBaseController
{
// actions for this controller
}
Create custom Authorize Attribute:
public class AuthorizeArea : AuthorizeAttribute
{
public string[] AllowIpAddresses { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool isValid = false;
if (httpContext == null)
throw new ArgumentNullException("httpContext");
// get current ip address
var ipAddress = httpContext.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if (string.IsNullOrEmpty(ipAddress))
ipAddress = httpContext.Request.ServerVariables["remote_host"];
if (AllowIpAddresses.Contains(ipAddress)) isValid = true;
return base.AuthorizeCore(httpContext) && isValid;
}
}
I have an ASP.NET MVC4 site with two types of roles: Administrator and Partner.
Basically all access to the site requires a user to authenticated and have role Administrator.
Therefore I do this in FilterConfig.cs (called from Global.asax):
public class FilterConfig {
public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
filters.Add(new HandleErrorAttribute());
filters.Add(new AuthorizeAttribute { Roles = "Administrator" }); // default deny
}
}
For one controller however I want grant access to users with the Partner role but wihtout the Adminstrator role. I know I could do this by not using the global filter and instead set Authorize attributes on each Controller or Action, but I would like a white list approach instead where all acces by default requires "Administrator" role and then use some form of white listing.
I have tried using a controller where I override OnAuthorization like this:
public class MyController : Controller {
protected override void OnAuthorization(AuthorizationContext filterContext) {
if (User.Identity.IsAuthenticated) {
var attributes = new List<AllowRolesAttribute>();
attributes.AddRange(filterContext.ActionDescriptor.GetCustomAttributes(true).OfType<AllowRolesAttribute>());
attributes.AddRange(filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(true).OfType<AllowRolesAttribute>());
foreach (var authorizationAttribute in attributes) {
foreach (var role in authorizationAttribute.Roles.Split(',')) {
if (User.IsInRole(role))
return; // Skip authorization because user has one of the allowed roles.
}
}
}
base.OnAuthorization(filterContext);
}
}
I then define the controller like this:
[AllowRoles(Roles = "Partner")]
public class HomeController : MyController
{
...
But this does not work. When I access this controller with a user with role "Partner" I can follow the code into the inner return statement, but the user is still redirected to the login page instead of being authorized. What am I missing here?
Not sure if you're using Forms authentication or not. Possibly need something like this?
[AllowRoles(Roles = "Partner")]
public class HomeController : MyController
{
FormsAuthentication.RedirectFromLoginPage( "User", false );
.
.
.
}
I am trying to implement a very simple example of FormsAuthentication. It is not real life but it has thrown up a problem. The AuthenticationService, which is intended to be an Application level singleton, appears to be instantiated twice.
Here is the code:
public class User : IUserIdentity
{
public string UserName { get; set; }
public IEnumerable<string> Claims { get; set; }
}
public interface IAuthenticationService
{
Guid GetIdentifier(string username, string password);
}
public class AuthenticationService : IUserMapper, IAuthenticationService
{
public readonly Guid Identifier = Guid.NewGuid();
private readonly string Username = "admin";
private readonly string Password = "x";
public Guid GetIdentifier(string username, string password)
{
return (username == Username && password == Password) ? Identifier : Guid.Empty;
}
public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context)
{
return (identifier == Identifier) ? new User { UserName = "admin" } : null;
}
}
public class MyBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
base.ConfigureApplicationContainer(container);
container.Register<IAuthenticationService, AuthenticationService>().AsSingleton();
}
}
The code above is being used by my LoginModule as follows. Please note that I am injecting the application-level singleton instance of the AuthenticationService via the module's constructor.
public LoginModule(IAuthenticationService authenticationService)
{
Post["/login"] = _ =>
{
var identifier = authenticationService.GetIdentifier(
(string) Form.Username,
(string) Form.Password);
if (identifier.IsEmpty())
{
return Context.GetRedirect("~/login?error=true");
}
return this.LoginAndRedirect(identifier);
};
}
What should happen is that when the user POSTs their username and password, these are checked by the AuthenticationService via the GetIdentifier(..) method. If the credentials match then the single GUID identifier is returned. This GUID will always be the same because it is created as a readonly field and thus set once when the singleton AuthenticationService is first instantiated at application startup.
However this is not the case. Instead two distinct instances of the AuthenticationService are created, one that is injected into the LoginModule constructor and used to call the GetIdentifier(..) method and another instance which Nancy uses to call the IUserIdentity.GetUserFromIdentifier(..) method.
These two instances have different GUID identifiers and so the GetUserFromIdentifier(..) method always return null.
I have tested a standard singleton service that does not implement IUserMapper and it works as expected, only one instance is created.
So it seems that Nancy is instantiating the IUserMapper singleton twice, once for its own internal use during FormsAuthentication, and once to inject into my LoginModule constructor!
Can you spot my mistake?
Thanks
It's probably because you're using a different interface so you have one singleton for things requesting IUsernameMapper and another for things requesting IAuthenticationService.
You can either:
Register both with an instance of your authentication service
Split out the username mapper and take a dependency on that in your service (so your Application Service has a dependency on the IUsernameMapper - that will be the same one Nancy is using)
Register one of them using a factory that resolves using the other interface (container.Register((c,p) => c.Resolve
My I ask why you're doing any of this though rather than just using the built in forms auth?