I am trying to use the Authorize class in my ASP MVC3 app. Unfortunately due to business rules I need to pull the Roles from our web.config, however this is throwing the following exception:
An attribute must be a constant expression, typeof or array creation expression of an attribute parameter type
Here is the code I'm referencing.
[Authorize(Roles = ConfigurationManager.AppSettings.Get("user"))]
public class AdminController : Controller
{
Here is the user section of my web.config
<add key="user" value="SA\\Application.MortalityConcentrationRA.Dev.Users" />
Try creating a custom authorize attribute like this:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
public MyAuthorizeAttribute()
{
this.Roles = ConfigurationManager.AppSettings["user"];
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return base.AuthorizeCore(httpContext);
}
}
And using it in your controller like this:
[MyAuthorize]
public class HomeController : Controller
{
//code here
}
I'm afraid you'll need a custom authorize attribute. I looked around a bit and didn't find any other possible solution. The current attribute is requiring a constant, and I just do not know of a way to have your config.AppSettings[] ever be a constant value (it's pretty much by definition not a constant).
Have a look at this SO post that explains pretty much exactly what you need to do. You question is almost a duplicate of this one (which is good for you, there's already an answer).
Related
I recently made changes to my MVC3 Application in attempt to properly dispose of the DbContext objects [1]. This worked great in development, but once the application was pushed to my production server, I started intermittently getting some funny exceptions which would persist until the AppPool was recycled. The exceptions can be traced back to code in my custom AuthorizeAttribute and look like:
System.InvalidOperationException: The 'Username' property on 'User' could not be set to a 'Int32' value. You must set this property to a non-null value of type 'String'.
System.InvalidOperationException: The 'Code' property on 'Right' could not be set to a 'String' value. You must set this property to a non-null value of type 'Int32'.
(Database schema looks like this: Users: [Guid, String, ...], Rights: [Guid, Int32, ...])
It is as if some "wires are getting crossed", and the application is mixing up results from the database: trying to materialize the Right result as a User and vise versa.
To manage the disposal of DbContext, I put code in to store this at a per-controller level. When the controller is disposed, I dispose the DbContext as well. I know it's hacky, but the AuthorizeAttribute uses the same context via filterContext.Controller.
Is there something wrong with handling the object lifecycle of DbContext in this manor? Are there any logical explanations as to why I am getting the crisscross exceptions above?
[1] Although I understand that it is not necessary to dispose of DbContext objects, I recently came across a number of sources stating that it was best practice regardless.
Edit (per #MikeSW's comment)
A property of the AuthorizeAttribute representing the DbContext is being set in the OnAuthorization method, when the AuthorizationContext is in scope. This property is then later used in the AuthorizeCore method.
Do you actually need to dispose the context?
According to this post by Jon Gallant who has been in touch with the Microsoft ADO.NET Entity Framework team:
Do I always have to call Dispose() on my DbContext objects? Nope
Before I talked with the devs on the EF team my answer was always a resounding “of course!”. But it’s not true with DbContext. You don’t need to be religious about calling Dispose on your DbContext objects. Even though it does implement IDisposable, it only implements it so you can call Dispose as a safeguard in some special cases. By default DbContext automatically manages the connection for you.
First i recommend that you get "really" familiar with
ASP.NET Application Life Cycle Overview for IIS 7.0 as it's fundamental to good MVC application design.
Now to try and "mimic" your code base
Let's say you have a similar custom MembershipProvider as described here https://stackoverflow.com/a/10067020/1241400
then you would only need a custom Authorize attribute
public sealed class AuthorizeByRoles : AuthorizeAttribute
{
public AuthorizeByRoles(params UserRoles[] userRoles)
{
this.Roles = AuthorizationHelper.GetRolesForEnums(userRoles);
}
}
public static class AuthorizationHelper
{
public static string GetRolesForEnums(params UserRoles[] userRoles)
{
List<string> roles = new List<string>();
foreach (UserRoles userRole in userRoles)
{
roles.Add(GetEnumName(userRole));
}
return string.Join(",", roles);
}
private static string GetEnumName(UserRoles userRole)
{
return Enum.GetName(userRole.GetType(), userRole);
}
}
which you can use on any controller or specific action
[AuthorizeByRoles(UserRoles.Admin, UserRoles.Developer)]
public class MySecureController : Controller
{
//your code here
}
If you want you can also subscribe to the PostAuthorizeRequest event and discard the results based on some criteria.
protected void Application_PostAuthorizeRequest(Object sender, EventArgs e)
{
//do what you need here
}
As for the DbContext, i have never run into your situation and yes per request is the right approach so you can dispose it in the controller or in your repository.
Of course it's recommended that you use filters and then add [AllowAnonymous] attribute to your actions.
I want to manager the Attribute easier, so I'm finding the way control all Attribute.
Currently, each function in controller I registration one or many Attribute.
Ex:
[MaintenanceModeDenyAttribute]
public ActionResult SaveGenericObject(GenericSaveObject saveObject) { }
[MaintenanceModeDenyAttribute("New student")]
public ActionResult ReturnGraduates() { }
How do manager all Attribute in a class, they can be applied for many functions of controller?
When app start, Attribute class can registration.
Thank you.
You can apply attribute at controller level if its usage is permitted or you can also add it into Global Filters collection in global.asax.cs for whole application as below.
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new MaintenanceModeDenyAttribute());
}
Does WebAPI can handle ValidationAttribute on action parameter?
For instance:
public class SampleController : ApiController
{
public string Get([RegularExpression("sampleExpression")]string id)
{
return "result";
}
}
In this sample WebAPI doesn't invoke any methods of RegularExpressionAttribute (or any other data annotation attribute) to validate input parameter. But in case if we passing an object as parameter, for instance a class then WebAPI can validate properties.
Is it a bug? Or I'm doing something wrong?
UPD: It's an open issue:
http://aspnetwebstack.codeplex.com/workitem/24?PendingVoteId=24
Does anyone can suggest a workaround?
This is a genuine question, I'm curious why not just do something like :
public class SampleController : ApiController
{
public string Get(string id)
{
RegularExpressionHelper.ValidateWith("sampleExpression",id);
return "result";
}
}
To me this seems to be equivalently concise. It is obvious when the validation occurs. I can safely assume that if the validation fails then an exception will be thrown. I can easily add additional validation and be confident of the order in which they occur.
Regarding the attribute method, I don't know if the validation is used as part of the route matching, I have no idea what happens if it fails. I don't know what filters run before or after the validation. I'm sure if I knew MVC better I would know the answer to these questions, but I don't see the advantage of using an attribute that makes my code's behaviour dependent on some framework controlled infrastructure.
Am I missing some significant benefit?
I had the same doubt. My workaround consists in creating a class just for encapsulating the parameter, so I can decorate it with the validation attribute I want. I could use the workaround proposed by Darrel in his answer, but I have a filter that checks if ModelState.IsValid before entering the action, so I need to validate before the action gets executed.
[ModelBinder]
public class Item
{
[RegularExpression("sampleExpression")]
public string Id { get; set; }
}
The class must be annotated with [ModelBinder], otherwise the parameter binding mechanism will try to extract the id field from the body of the request. Read this article for more info.
Also, note that Id is now in PascalCase instead of camelCase. Read this article to understand how the conversion is made.
The action signature is:
public string Get(Item item)
When a view model is created you can populate the options (e.g. used in a dropdown list) into a setter property of the view model.
The problem is that when that view model is later passed as a parameter (by the framework!) into an action method, those property values has not become automagically
repopulated, so if you need to redisplay the form because of validation errors, you need to repopulate those options again.
One potential solution, which I am asking for specifically in this question, is how to make the MVC framework instantiate the view model with constructor injection, which would provide the view model constructor with an implementation of some kind of data access object (e.g. a repository) that can be used for retrieving the options when they are requested by the view (e.g. in the helper method "DropDownListFor") ?
I think the solution might have something to do with implementations of IModelBinderProvider or IModelBinder but after having experimented with these things from example code snippets here and there on the net, I am still looking for a completely working example, with downloadable executable code without any missing piece of how putting all things together.
If you are looking for some alternative discussion about how to populate a select list, e.g. with "Dependecy Lookup" instead of "Dependecy Injection" you may want to check out the following discussion:
Best way to populate SelectList for ViewModel on GET/POST
Best way to populate SelectList for ViewModel on GET/POST
Some days ago I wrote the following follow-up-question in that thread about the "Dependecy Injection" I am now looking for in this thread:
https://stackoverflow.com/a/8674525/310457
(which provides a code example about the problem I am looking for a solution of)
But instead of hoping that someone will find that old thread with a less specific title, I have created this new question with a more specific subject about what I am looking for.
And I will also provide a link from that thread into this new question for anyone that want to follow-up regarding this specific solution I am looking for.
I'm assuming you want to have your ViewModels automatically injected with something via their Constructor - for example some kind of configuration object that the View will use to determine what to show. I'm also assuming that this approach is causing a "No parameterless constructor defined for this object" error when MVC tries to automatically create and bind a model instance, from the arguments of your Controller Action. Let's also then assume that we will use a DI framework to inject the SiteConfig object into our Controllers automatically at runtime.
This means that the only problem we have to solve is how to get the injected object from our Controller into its Actions' ViewModels when they are automatically bound.
So let's define a base model for others to inherit from.
BaseViewModel
public class BaseViewModel
{
public ISiteConfig SiteConfig { get; set; }
public BaseViewModel(ISiteConfig siteConfig)
{
this.SiteConfig = siteConfig;
}
}
And now let's create a model that inherits from it.
IndexViewModel
public class IndexViewModel : BaseViewModel
{
public string SomeIndexProperty { get; set; }
public IndexViewModel (ISiteConfig siteConfig) : base(siteConfig) {}
}
And now let's define a Base Controller that our Controllers will inherit from.
BaseController
public abstract class BaseController : Controller
{
protected BaseController(ISiteConfig siteConfig)
{
_siteConfig = siteConfig;
}
private readonly ISiteConfig _siteConfig;
public ISiteConfig SiteConfig
{
get
{
return _siteConfig;
}
}
}
Now we define our actual controller.
HomeController
public HomeController: BaseController
{
public HomeController(ISiteConfig siteConfig): base(siteConfig) {}
}
Assuming we're using Ninject for DI, Ninject would be configured to automatically create the Controller and pass a concrete ISiteConfig object into its Constructor at runtime.
Now we add our Action to the Controller.
Index Action
public ActionResult Index(IndexViewModel model)
{
return View(model);
}
And so this is the point where without doing anything else, MVC will explode with a "Parameterless Constructor" error if you try to call the Index Action, because MVC can't find a ViewModel constructor that takes no arguments.
And so, the answer. We need to override the default ModelBinder.
BaseViewModelBinder
public class BaseViewModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
if (modelType == typeof(BaseViewModel) || modelType.IsSubclassOf(typeof(BaseViewModel)))
{
var baseControl = controllerContext.Controller as BaseController;
if (baseControl == null)
{
throw new Exception("The Controller must derive from BaseController");
}
var instance = Activator.CreateInstance(modelType, baseControl.SiteConfig);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, modelType);
return instance;
}
else
{
return base.CreateModel(controllerContext, bindingContext, modelType);
}
}
}
And we need to set this as the default model binder in global.asax.cs :
protected void Application_Start()
{
...
ModelBinders.Binders.DefaultBinder = new BaseViewModelBinder();
}
That's all. As you can see, when you view the Index Action now, MVC will use our custom model binder. It will realise that the IndexViewModel derives from BaseViewModel, and so will attempt to spin up an IndexViewModel instance using the ISiteConfig it can find in the Action's Controller (because the Controller derives from BaseController).
What's a good way to validate a model when information external to the model is required in order for the validation to take place? For example, consider the following model:
public class Rating {
public string Comment { get; set; }
public int RatingLevel { get; set; }
}
The system administrator can then set the RatingLevels for which a comment is required. These settings are available through a settings service.
So, in order to fully validate the model I need information external to it, in this case the settings service.
I've considered the following so far:
Inject the service into the model. The DefaultModelBinder uses System.Activator to create the object so it doesn't go through the normal dependency resolver and I can't inject the service into the model without creating a new model binder (besides which, that doesn't feel like the correct way to go about it).
Inject the service into an annotation. I'm not yet sure this is possible but will investigate further soon. It still feels clumsy.
Use a custom model binder. Apparently I can implement OnPropertyValidating to do custom property validation. This seems the most preferable so far though I'm not yet sure how to do it.
Which method, above or not, is best suited to this type of validation problem?
Option 1 doesn't fit. The only way it would work would be to pull in the dependency via the service locator anti-pattern.
Option 2 doesn't work. Although I couldn't see how this was possible because of the C# attribute requirements, it is possible. See the following for references:
Resolving IoC Container Services for Validation Attributes in ASP.NET MVC
NInjectDataAnnotationsModelValidatorProvider
Option 3: I didn't know about this earlier, but what appears to be a very powerful way to write validators is to use the ModelValidator class and a corresponding ModelValidatorProvider.
First, you create your custom ModelValidatorProvider:
public class CustomModelValidatorProvider : ModelValidatorProvider
{
public CustomModelValidatorProvider(/* Your dependencies */) {}
public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
{
if (metadata.ModelType == typeof(YourModel))
{
yield return new YourModelValidator(...);
}
}
}
ASP.NET MVC's IDependencyResolver will attempt to resolve the above provider, so as long as it's registered with your IoC container you won't need to do anything else. And then the ModelValidator:
public class EntryRatingViewModelValidatorMvcAdapter : ModelValidator
{
public EntryRatingViewModelValidatorMvcAdapter(
ModelMetadata argMetadata,
ControllerContext argContext)
: base(argMetadata, argContext)
{
_validator = validator;
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
if (/* error condition */)
{
yield return new ModelValidationResult
{
MemberName = "Model.Member",
Message = "Rating is required."
};
}
}
}
As the provider is retrieved through the IDependencyResolver and the provider has full control over the returned ModelValidators I was easily able to inject the dependencies and perform necessary validation.
You could try fluent validation. It supports asp.net mvc and DI so you can inject external services into your validators.
Assuming that you want both client and server-side validation of the model based upon the values returned from the service, I would opt for 2., Inject the service into an annotation.
I give some sample code in my response to this question about adding validators to a model. The only additional step in your case is that you will need to inject your service into your class inheriting from DataAnnotationsModelValidatorProvider.
What about just simply using IValidateableObject and in that method determine if validation is appropriate or not and setting the errors there?
How do I use IValidatableObject?