I have a controller action and I want to use changeable authorization via web config settings.
public class ProductsController : ApiController
{
[HttpGet, Authorize]
public Product FindProduct(id) {}
}
<appSettings>
<add key="authorize" value="yes"/>
</appSettings>
You can create your own AuthorizeWithConfig attribute that inherits from Authorize attribute:
public class AuthorizeWithConfigAttribute : AuthorizeAttribute
{
private readonly string _configKey;
public AuthorizeWithConfigAttribute(string configKey)
{
_configKey = configKey;
}
protected override bool IsAuthorized(HttpActionContext actionContext)
{
// Will be read from configuration
bool requireAuthorization;
// Skip authorization if
// (1) Found the specified key in app settings
// (2) Could parse app setting value into a boolean
// (3) App setting value is set to FALSE
var skipAuthorization =
ConfigurationManager.AppSettings.ContainsKey(configKey)
&& bool.TryParse(ConfigurationManager.AppSettings[configKey],
out requireAuthorization)
&& !requireAuthorization;
return skipAuthorization ? true : base.IsAuthorized(actionContext);
}
}
Then you can use it for your controller actions:
public class ProductsController : ApiController
{
[HttpGet, AuthorizeWithConfig("App:RequireAuthorization")]
public Product FindProduct(id) {}
}
Assuming that in app settings you have an App:RequireAuthorization setting that takes boolean values:
<appSettings>
<add key="App:RequireAuthorization" value="false"/>
</appSettings>
Related
Here is an example controller to explain the case
[Authorize]
public class AccountController : ControllerBase
{
[AllowAnonymous]
[Authorize(Policy = "SpecificPolicy")]
public string MethodA() {}
public string MethodB() {}
}
MethodA should only be authorized via "SpecificPolicy".
MethodB should be authorized via the Authorized attribute
The issue I'm having is that if I remove the AllowAnonymous attribute then Authorize on the controller takes precedence which I don't want for MethodA.
When I keep AllowAnonymous for MethodA then Authorize(Policy = "SpecificPolicy") is ignored.
When I keep AllowAnonymous for MethodA then Authorize(Policy = "SpecificPolicy") is ignored.
[AllowAnonymous] bypasses all other authorization attributes. When you have it with other authorize attributes at the same time, all other attributes are ignored, even other attributes are the-more-specific method level.
For example:
[AllowAnonymous]
public class DashboardController : Controller
{
[Authorize]
public IActionResult Index()
{
return View();
}
}
/dashboard will be open/public.
The issue I'm having is that if I remove the AllowAnonymous attribute then Authorize on the controller takes precedence which I don't want for MethodA.
When you have multiple authorize attributes, all of them need to be satisfied before you can make the call to the method. In your case, both [Authorize] and [Authorize(Policy = "SpecificPolicy")] must pass before access is granted.
If you don't want [Authorize] to take the precedence, you can only apply it to method B:
public class AccountController : ControllerBase
{
[Authorize(Policy = "SpecificPolicy")]
public string MethodA() {}
[Authorize]
public string MethodB() {}
}
I want to avoid putting specific [Authorize] attributes on actions since that Controller has lots of actions but a single action that has it's own authorize rule.
Then this might be good time for you to separate MethodA into Areas.
For example:
You still have [Authorize] on your AccountController, but just take out the MethodA:
[Authorize]
public class AccountController : ControllerBase
{
public string MethodB() {}
}
Then you create an Area for MethodA:
[Area("specific")]
[Authorize(Policy = "SpecificPolicy")]
public abstract class SpecificControllerBase : ControllerBase
{ }
public class AccountController : SpecificationControllerBase
{
public string MethodA() {}
}
Lastly you need to register the area route in your Startup.cs:
app.UseMvc(routes =>
{
...
routes.MapRoute(
name: "areaRoute",
template: "{area:exists}/{controller=dashboard}/{action=index}/{id?}");
routes.MapRoute(
name: "default",
template: "{controller=home}/{action=index}/{id?}");
});
You could try to implement your own Authorize Attribute with checking the Policy.
Follow Steps below:
AllowAnonymousWithPolicyFilter
public class AllowAnonymousWithPolicyFilter : IAsyncAuthorizationFilter
{
private readonly IAuthorizationService _authorization;
public string Policy { get; private set; }
public AllowAnonymousWithPolicyFilter(string policy, IAuthorizationService authorization)
{
Policy = policy;
_authorization = authorization;
}
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
var authorized = await _authorization.AuthorizeAsync(context.HttpContext.User, Policy);
if (!authorized.Succeeded)
{
context.Result = new ForbidResult();
return;
}
}
}
AllowAnonymousWithPolicyAttribute
public class AllowAnonymousWithPolicyAttribute : TypeFilterAttribute, IAllowAnonymous
{
public AllowAnonymousWithPolicyAttribute(string Policy) : base(typeof(AllowAnonymousWithPolicyFilter))
{
Arguments = new object[] { Policy };
}
}
Use
[Authorize]
public class HomeController : Controller
{
[AllowAnonymousWithPolicy("MyPolicy")]
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
return View();
}
I was using the following code to add roles of the user.
Roles.AddUserToRole(model.Email, "COMPANYVIP");
and then i got this error:
The Role Manager feature has not been enabled
after some research i found out that we have to add the following connection string in web.config
<configuration>
<system.web>
<roleManager enabled="true" />
</system.web>
</configuration>
adding this eliminated my first error but now i get this error:
A network-related or instance-specific error occurred while establishing a connection to SQL Server
what should i do now?
Remove your change in web.config and in Startup.Auth add the following reference to ConfigureAuth:
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
// Add this reference to RoleManager (without changing any other items)
// Make sure it is added below ApplicationDbContext.Create
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
}
Then in your Controller, make sure it includes this in the constructor:
public class YourController : Controller
{
// Add this
private ApplicationRoleManager _roleManager;
// Add roleManager
public YourController(ApplicationRoleManager roleManager)
{
// Add this
RoleManager = roleManager;
}
public ApplicationRoleManager RoleManager {
get {
return _roleManager ?? HttpContext.GetOwinContext().Get<ApplicationRoleManager>();
}
private set {
_roleManager = value;
}
}
}
and also include this in the Controller's Dispose (if you have it):
protected override void Dispose(bool disposing)
{
if (disposing)
{
// include this
if (_roleManager != null)
{
_roleManager.Dispose();
_roleManager = null;
}
}
base.Dispose(disposing);
}
You may also need to add this code to IdentityConfig (in the App_Start folder if you're using the template):
public class ApplicationRoleManager : RoleManager<IdentityRole>
{
public ApplicationRoleManager(IRoleStore<IdentityRole, string> roleStore)
: base(roleStore)
{ }
public static ApplicationRoleManager Create(
IdentityFactoryOptions<ApplicationRoleManager> options,
IOwinContext context)
{
var manager = new ApplicationRoleManager(
new RoleStore<IdentityRole>(context.Get<ApplicationDbContext>()));
return manager;
}
}
You should now be able to use the RoleManager in the Controller.
I am working on the ForgotPassword section of my site. When I test it I have a breakpoint in the function and I can see that this line of code is returning false:
(await UserManager.IsEmailConfirmedAsync(user.Id)))
I have verified that EmailConfirmed field in the AspNetUsers table is set to True. Why would this still be returning false?
Here is the first part of the Account Controller where it initializes the UserManager:
[Authorize]
public class AccountController : Controller
{
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager )
{
UserManager = userManager;
SignInManager = signInManager;
}
public ApplicationSignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set
{
_signInManager = value;
}
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
Found the requested Owin string in my Startup.Auth.cs class:
public partial class Startup
{
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
You are likely pointing to the wrong database. The default templates for an MVC project with Identity will have code like this in the context:
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
This is then used by OwinContext as the method to create your context, and as such means that it will take the connection string from your web.config file called DefaultConnection. So you have 2 choices:
Fix the connection string to point to the correct database.
<add name="DefaultConnection"
connectionString="correct details here"
providerName="System.Data.SqlClient" />
Change the create method to return the context with a specific connection string:
public static ApplicationDbContext Create()
{
return new ApplicationDbContext("NameOfConnectionStringHere");
}
It seems await UserManager.IsEmailConfirmedAsync(user.Id)) always returns false when EmailConfirmed is set to False in AspNetUsers SQL table.
Is there any way to set the Parameter property from web.config?
public class TestHttpHandler : IHttpHandler
{
public bool IsReusable
{
get { return true; }
}
public string Parameter
{ get; set; }
public void ProcessRequest(HttpContext context)
{
context.Response.Write(Parameter);
}
}
If i do the following it just crashes.
<handlers>
<add name="Test" verb="*" path="/Manual/*"
type="Test.TestHttpHandler, Test" Parameter="test1234 "/>
</handlers>
Parameter is not a valid attribute for the handlers/add note. Just adding a property with the same name to your handler class doesn't make it magically work.
You can not pass a parameter directly in the handler definition, but within the code of your handler class you have full access to any other configuration data in web.config; any AppSetting or your own ConfigSections.
I have a custom ValidationAttribute that implements IClientValidatable. But the GetClientValidationRules is never called to actually output the validation rules to the client side.
There is nothing special about the attribute but for some reason it is never called. I've tried registering an adapter in Application_Start() but that also doesnt work.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class CustomAttribute : ValidationAttribute, IClientValidatable
{
public override bool IsValid(object value)
{
return true;
}
#region IClientValidatable Members
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
string errorMessage = FormatErrorMessage(metadata.GetDisplayName());
yield return new ModelClientValidationRule { ErrorMessage = errorMessage, ValidationType = "custom" };
}
#endregion
}
public class CustomAdapter : DataAnnotationsModelValidator<CustomAttribute>
{
public CustomAdapter(ModelMetadata metadata, ControllerContext context, CustomAttribute attribute)
: base(metadata, context, attribute)
{
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
return this.Attribute.GetClientValidationRules(this.Metadata, this.ControllerContext);
}
}
In Application_Start() I have:
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(CustomAttribute), typeof(CustomAdapter));
When I put a breakpoint inside GetClientValidationRules it is never hit.
In order GetClientValidationRules() method to get called you must enable client-side validation support. It can be done in the following ways:
In the web.config (for all pages of application):
<appSettings>
<add key="ClientValidationEnabled" value="true" />
Or on particular view only:
either
#{ Html.EnableClientValidation(); }
or
#(ViewContext.ClientValidationEnabled = true)
Please note it must go before
#using (Html.BeginForm())
statement.
If you are using jquery unobtrusive validation (which seems to be a standard currently), you'll also need to enable it:
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
in web.config or
#Html.EnableUnobtrusiveJavaScript()
for particular views.