Json.Net Deserialization - Web API and Nullable Date - asp.net

Say you've got a model that looks like
public class UserModel
{
public string UserName { get; set; }
public DateTime? DateOfBirth { get; set; }
}
The DateOfBirth field isn't required, but could be specified. You have a Web API POST endpoint that looks like
[Route("")]
[AcceptVerbs("POST")]
public async Task<IHttpActionResult> Create(UserModel user)
{
}
And we've set the JSON serializer in start up like so,
public static void Register(HttpConfiguration config)
{
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
var settings = jsonFormatter.SerializerSettings;
settings.Converters.Add(new IsoDateTimeConverter());
settings.Error += (sender, args) => Console.WriteLine("This event is fired ok");
}
If we send some JSON to the endpoint that looks like this
{
"userName": "User1",
"dateOfBirth": "jhdgjhjfg"
}
...the error event is fired in the Serializer settings and the endpoint is called. At this point, the DateOfBirth field is null and I don't have any context that a deserialization error has occurred
Reading the JSON.Net documentation, because Handled == false in the Error event arguments of the Settings object, an exception should be raised into the application code - this doesn't happen? Is there a setting I haven't configured correctly for this?
How can I get context within the action so that I know a value was specified for a field and couldn't be deserialized? Even global behaviour would be fine, as long as I know this has happened and can return a 400.
UPDATE:
We can use a filter to check the Model state, then check the Model State errors for exceptions of type JsonReaderException. This lets you return a 400 with a list of violating fields
public class CheckJsonExceptionModelStateAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ModelState.IsValid)
{
return;
}
var fieldsInError = new List<string>();
foreach (var jsonException in
actionContext.ModelState.Keys.SelectMany(key => actionContext.ModelState[key].Errors)
.Select(error => error.Exception).OfType<JsonReaderException>())
{
Trace.TraceError(jsonException.Message);
fieldsInError.Add(jsonException.Path);
}
var apiError = new { ErrorMessages.BadRequestModel.Message, FieldsInError = new List<string>() };
foreach (var fieldError in fieldsInError)
{
apiError.FieldsInError.Add(fieldError);
}
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, apiError);
}
}

You have multiple options. But first, you are getting no exception because the WebApi handles this exception. Bad news.
Good news, you can handle it in at least two ways; use the ModelState.IsValid property - in your case it will be false. You can access them in your post-method. When you remove the invalid dateOfBirth it is true ;-)
Or you can use an ActionFilterAttribute to put it on your methods for re-use purposes.
For example:
public async Task<IHttpActionResult> Create(UserModel user) {
if (!ModelState.IsValid) {
// ModelState.Keys // Get all error-keys
}
}

Related

Action filter : how to call service layer and async method

I have a controller with many action method. The requirement for me is to check a value of a field from database and if the field value is "true" all the action methods can execute otherwise these action methods should not execute.
The method is in service layer
public class CustomAttributeFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var myFlag = await _adminDB.GetFlagSettingsAsync();
// how do i call async method from OnActionExecuting filter
if (!myFlag)
{
//Create your result
filterContext.Result = new EmptyResult();
}
else
{
base.OnActionExecuting(filterContext);
}
}
}
Interface implementaion
public interface IAdminDB
{
Task<MySettings> GetMySettingsAsync();
}
public class AdminDB : IAdminDB
{
public async Task<MySettings> GetMySettingsAsync()
{
var dbName = _appSettings.AdminDbName;
var blobName = _appSettings.AdminBlobName;
return await _dbStorage.GetBlobAsync<MySettings>(blobName, dbName);
}
}
public class MySettings
{
public bool MyFlag { get; set; }
}
I get an error message "no suitable method found to override". How do i clear this error and how to inject service properly . Above is what i have tried, the call to async getting failed here.
I don't see where the _adminDB dependency comes from in your code, but I'm guessing that is causing the problem.
If you want to use async filters you have to implement the IAsyncActionFilter interface.
You can retrieve services from the executing context's DI container and use async methods the following way:
public class CustomAttributeFilter : ActionFilterAttribute
{
public override async Task OnActionExecutionAsync(
ActionExecutingContext context, ActionExecutionDelegate next)
{
var adminDb = filterContext.HttpContext.RequestServices.GetService<AdminDb>();
var myFlag = await adminDb.GetFlagSettingsAsync();
//..
await next();
}
}
Depending on your your needs, you can place your custom logic after the next() call as well.
See the documentation for more information.

Is it possile to return .NET object from controller to middleware

I was working on one of the requirements, where I need to modify result data in middleware (not any MVC Filters due to some other services injected through middleware).
In middleware I was getting data in json format and then deserializing that data then updating that data and finally serializing to JSON and sending it back as a response.
I don't want to serialize data in MVC pipeline so I tried to remove output formator but that didn't work for me and throwing error.
services.AddControllers(options =>
{
options.OutputFormatters.Clear();
});
Is there any solution to get the .Net object in the pipeline and modify that object (as we do in MVC filter) and then serialize at last?
I am not sure whether it fits your requirements but you can use HttpContext to store some data in the scope of the request. There is a 'Items' key-value collection.
Beside the other suggestion to use Items of HttpContext, I want to note that you can inject services into Action Filters:
public class ResultFilter : IActionFilter
{
// Inject anything you want
IHostEnvironment env;
public ResultFilter(IHostEnvironment env)
{
this.env = env;
}
public void OnActionExecuted(ActionExecutedContext context)
{
if (context.Result is OkObjectResult result)
{
result.Value = JsonSerializer.Serialize(new
{
Value = result.Value,
Environment = this.env.EnvironmentName,
});
}
}
public void OnActionExecuting(ActionExecutingContext context) { }
}
Register to DI Builder:
services.AddScoped<ResultFilter>();
Apply to action/controller:
[HttpGet, Route("/test"), ServiceFilter(typeof(ResultFilter))]
public IActionResult ReturnOk()
{
return this.Ok(new
{
Value = 1,
});
}
Testing by accessing the URL:
{"Value":{"Value":1},"Environment":"Development"}
Another alternative is to use DI service with Scoped lifetime.
Scoped objects are the same for a given request but differ across each new request.
Service:
public interface IMyRequestDataService
{
object? MyData { get; set; }
}
public class MyRequestDataService : IMyRequestDataService
{
public object? MyData { get; set; }
}
Register to DI:
services.AddScoped<IMyRequestDataService, MyRequestDataService>();
Set data in Controller:
readonly IMyRequestDataService dataService;
public TestController(IMyRequestDataService dataService)
{
this.dataService = dataService;
}
[HttpGet, Route("/test-scoped")]
public IActionResult ReturnObj()
{
this.dataService.MyData = new
{
Value = 1,
};
return this.Ok();
}
Your middleware that consumes it:
class CustomMiddleware
{
readonly RequestDelegate next;
public CustomMiddleware(RequestDelegate next)
{
this.next = next;
}
// Add DI Services here
public async Task InvokeAsync(HttpContext httpContext, IMyRequestDataService dataService, IHostEnvironment env)
{
await this.next(httpContext);
// Data should be here
if (dataService.MyData != null)
{
// Do something with it
await httpContext.Response.WriteAsJsonAsync(new
{
Data = dataService.MyData,
Env = env.EnvironmentName,
});
}
}
}
// Register it:
app.UseMiddleware<CustomMiddleware>();
// Make sure it's before the Controller middleware since we wrap it around the next()
// ...
app.MapControllers();
Test with the URL:
{"data":{"value":1},"env":"Development"}
You can store data in HTTP context items.
In controller action:
Request.HttpContext.Items.Add("SomeKey", data);
In middleware:
object data = httpContext.Items["SomeKey"];

Get data from external API with a asp.net core API

I'm learning to create APIs using ASP.NET core, in this I came up with a problem, I'm trying to execute a request for an external API using my API, but I do not know how to execute the request and return the JSON of the request, any help?
The flow of the application looks something like this:
SPA -> AspNet Core WEB API -> External API
What I've done so far:
[Route("api/[Controller]")]
public class RankingsController : Controller
{
private readonly IRankingRepository _rankingRepository;
public RankingsController(IRankingRepository rankingRepo)
{
_rankingRepository = rankingRepo;
}
[HttpGet("{id}", Name = "GetRanking")]
public IActionResult GetById(long id)
//Here is where I want to make the requisition
}
}
I need to make the request for this API:
http://api.football-data.org/v1/competitions/{id}/leagueTable
In the ID location, I need to pass a parameter that comes from the request made in my API;
Any help for this problem?
Sorry for not being such a complex question.
Thanks!!
You could use an HttpClient instance to achieve what you want. However, I always find easier to use RestSharp though.
That, of course will depend on your constraints but assuming you have none for this case, you could use RestSharp to make the call to the external API:
Install it:
Install-Package RestSharp
Usage:
using RestSharp;
[HttpGet("{id}", Name = "GetRanking")]
public async Task<IActionResult> GetByIdAync(long id)
{
var client = new RestClient($"http://api.football-data.org/v1/competitions/{id}/leagueTable");
var request = new RestRequest(Method.GET);
IRestResponse response = await client.ExecuteAsync(request);
//TODO: transform the response here to suit your needs
return Ok(data);
}
To consume the rest response from RestSharp you must use the response.Content property.
You could, for example, deserialize it to Json, manipulate it to fit your needs and return the required data to your API caller.
Example:
Let's say I'd like to get the rankings for the Premier League 2017/18 (Id = 445):
I'll get a lot of help below from the legendary Newtonsoft.Json package and a little of jpath syntax but I'll assume you've already used both :)
Create a couple models to hold the values to be returned to the API caller:
public class LeagueTableModel
{
public string LeagueCaption { get; set; }
public IEnumerable<StandingModel> Standings { get; set; }
}
public class StandingModel
{
public string TeamName { get; set; }
public int Position { get; set; }
}
Implement the below method in the service class, injected to your controller through DI/IoC to avoid coupling and increase testability (as we all know we should do right?). I'm assuming this class is RankingRepository in your sample:
public RankingRepository: IRankingRepository
{
public Task<LeagueTableModel> GetRankingsAsync(long id)
{
var client = new RestClient($"http://api.football-data.org/v1/competitions/{id}/leagueTable");
var request = new RestRequest(Method.GET);
IRestResponse response = await client.ExecuteAsync(request);
if (response.IsSuccessful)
{
var content = JsonConvert.DeserializeObject<JToken>(response.Content);
//Get the league caption
var leagueCaption = content["leagueCaption"].Value<string>();
//Get the standings for the league.
var rankings = content.SelectTokens("standing[*]")
.Select(team => new StandingModel
{
TeamName = (string)team["teamName"],
Position = (int)team["position"]
})
.ToList();
//return the model to my caller.
return new LeagueTableModel
{
LeagueCaption = leagueCaption,
Standings = rankings
};
}
//TODO: log error, throw exception or do other stuffs for failed requests here.
Console.WriteLine(response.Content);
return null;
}
}
Use it from the controller:
[Route("api/[Controller]")]
public class RankingsController : Controller
{
private readonly IRankingRepository _rankingRepository;
public RankingsController(IRankingRepository rankingRepo)
{
_rankingRepository = rankingRepo;
}
[HttpGet("{id}", Name = "GetRanking")]
public Task<IActionResult> GetByIdAsync(long id)
//Here is where I want to make the requisition
var model = await _rankingRepository.GetRankingsAsync(id);
//Validate if null
if (model == null)
return NotFound(); //or any other error code accordingly. Bad request is a strong candidate also.
return Ok(model);
}
}
Hope this helps!

IConfigureOptions<T> is not creating scoped options

Typically Options are singleton. However i am building options from the database, and one of the Options property is password which keep changing every month. So i wanted to create Scoped instance of Options. I am using IConfigureOptions<T> like below to build Options from the database
public class MyOptions
{
public string UserID {get;set;}
public string Password {get;set;
}
public class ConfigureMyOptions : IConfigureOptions<MyOptions>
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public ConfigureMyOptions(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public void Configure(MyOptions options)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var provider = scope.ServiceProvider;
using (var dbContext = provider.GetRequiredService<MyDBContext>())
{
options.Configuration = dbContext.MyOptions
.SingleOrDefault()
.Select(x => new MyOptions()
{
UserID = x.UserID,
Password = x.Password
});
}
}
}
}
Use it in controller
public class HomeController : BaseController
{
private readonly MyOptions _options;
public HomeController(IOptions<MyOptions> option)
{
_options = option.Value;
}
[HttpGet]
[Route("home/getvalue")]
public string GetValue()
{
// do something with _options here
return "Success";
}
}
I want to create an instance of MyOptions for every new request so register it as Scoped in startup.cs
services.AddScoped<IConfigureOptions<MyOptions>, ConfigureMyOptions>();
However, when i put debugger inside ConfigureMyOptions's Configure method it only gets hit once for the first request. For next request onward the container returns the same instance (like singleton).
How do i set the scope here so MyOptions will get created for each request?
Use IOptionsSnapshot instead of IOptions in your controller and it will recreate options per request.
Why doesn't work with IOptions:
.AddOptions extension method of Configuration API registers the OptionsManager instance as a singlethon for IOptions<>
services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(OptionsManager<>)));
services.TryAdd(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot<>), typeof(OptionsManager<>)));
and OptionsManager class uses caching internally:
public virtual TOptions Get(string name)
{
name = name ?? Options.DefaultName;
// Store the options in our instance cache
return _cache.GetOrAdd(name, () => _factory.Create(name));
}
The following issue on github helped to find above: OptionsSnapshot should always be recreated per request

ASP.NET MVC - Alternative to Role Provider?

I'm trying to avoid the use of the Role Provider and Membership Provider since its way too clumsy in my opinion, and therefore I'm trying to making my own "version" which is less clumsy and more manageable/flexible. Now is my question.. is there an alternative to the Role Provider which is decent? (I know that I can do custom Role provier, membership provider etc.)
By more manageable/flexible I mean that I'm limited to use the Roles static class and not implement directly into my service layer which interact with the database context, instead I'm bound to use the Roles static class which has its own database context etc, also the table names is awful..
Thanks in advance.
I'm in the same boat as you - I've always hated the RoleProviders. Yeah, they're great if you want to get things up and running for a small website, but they're not very realistic. The major downside I've always found is that they tie you directly to ASP.NET.
The way I went for a recent project was defining a couple of interfaces that are part of the service layer (NOTE: I simplified these quite a bit - but you could easily add to them):
public interface IAuthenticationService
{
bool Login(string username, string password);
void Logout(User user);
}
public interface IAuthorizationService
{
bool Authorize(User user, Roles requiredRoles);
}
Then your users could have a Roles enum:
public enum Roles
{
Accounting = 1,
Scheduling = 2,
Prescriptions = 4
// What ever else you need to define here.
// Notice all powers of 2 so we can OR them to combine role permissions.
}
public class User
{
bool IsAdministrator { get; set; }
Roles Permissions { get; set; }
}
For your IAuthenticationService, you could have a base implementation that does standard password checking and then you could have a FormsAuthenticationService that does a little bit more such as setting the cookie etc. For your AuthorizationService, you'd need something like this:
public class AuthorizationService : IAuthorizationService
{
public bool Authorize(User userSession, Roles requiredRoles)
{
if (userSession.IsAdministrator)
{
return true;
}
else
{
// Check if the roles enum has the specific role bit set.
return (requiredRoles & user.Roles) == requiredRoles;
}
}
}
On top of these base services, you could easily add services to reset passwords etc.
Since you're using MVC, you could do authorization at the action level using an ActionFilter:
public class RequirePermissionFilter : IAuthorizationFilter
{
private readonly IAuthorizationService authorizationService;
private readonly Roles permissions;
public RequirePermissionFilter(IAuthorizationService authorizationService, Roles requiredRoles)
{
this.authorizationService = authorizationService;
this.permissions = requiredRoles;
this.isAdministrator = isAdministrator;
}
private IAuthorizationService CreateAuthorizationService(HttpContextBase httpContext)
{
return this.authorizationService ?? new FormsAuthorizationService(httpContext);
}
public void OnAuthorization(AuthorizationContext filterContext)
{
var authSvc = this.CreateAuthorizationService(filterContext.HttpContext);
// Get the current user... you could store in session or the HttpContext if you want too. It would be set inside the FormsAuthenticationService.
var userSession = (User)filterContext.HttpContext.Session["CurrentUser"];
var success = authSvc.Authorize(userSession, this.permissions);
if (success)
{
// Since authorization is performed at the action level, the authorization code runs
// after the output caching module. In the worst case this could allow an authorized user
// to cause the page to be cached, then an unauthorized user would later be served the
// cached page. We work around this by telling proxies not to cache the sensitive page,
// then we hook our custom authorization code into the caching mechanism so that we have
// the final say on whether or not a page should be served from the cache.
var cache = filterContext.HttpContext.Response.Cache;
cache.SetProxyMaxAge(new TimeSpan(0));
cache.AddValidationCallback((HttpContext context, object data, ref HttpValidationStatus validationStatus) =>
{
validationStatus = this.OnCacheAuthorization(new HttpContextWrapper(context));
}, null);
}
else
{
this.HandleUnauthorizedRequest(filterContext);
}
}
private void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
// Ajax requests will return status code 500 because we don't want to return the result of the
// redirect to the login page.
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.Result = new HttpStatusCodeResult(500);
}
else
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
public HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext)
{
var authSvc = this.CreateAuthorizationService(httpContext);
var userSession = (User)httpContext.Session["CurrentUser"];
var success = authSvc.Authorize(userSession, this.permissions);
if (success)
{
return HttpValidationStatus.Valid;
}
else
{
return HttpValidationStatus.IgnoreThisRequest;
}
}
}
Which you can then decorate on your controller actions:
[RequirePermission(Roles.Accounting)]
public ViewResult Index()
{
// ...
}
The advantage of this approach is you can also use dependency injection and an IoC container to wire things up. Also, you can use it across multiple applications (not just your ASP.NET one). You would use your ORM to define the appropriate schema.
If you need more details around the FormsAuthorization/Authentication services or where to go from here, let me know.
EDIT: To add "security trimming", you could do it with an HtmlHelper. This probably needs a little more... but you get the idea.
public static bool SecurityTrim<TModel>(this HtmlHelper<TModel> source, Roles requiredRoles)
{
var authorizationService = new FormsAuthorizationService();
var user = (User)HttpContext.Current.Session["CurrentUser"];
return authorizationService.Authorize(user, requiredRoles);
}
And then inside your view (using Razor syntax here):
#if(Html.SecurityTrim(Roles.Accounting))
{
<span>Only for accounting</span>
}
EDIT: The UserSession would look something like this:
public class UserSession
{
public int UserId { get; set; }
public string UserName { get; set; }
public bool IsAdministrator { get; set; }
public Roles GetRoles()
{
// make the call to the database or whatever here.
// or just turn this into a property.
}
}
This way, we don't expose the password hash and all other details inside the session of the current user since they're really not needed for the user's session lifetime.
I have implemented a role provider based on #TheCloudlessSky post here. There are few things that I thought I can add and share what I have done.
First if you want to use the RequirepPermission class for your action filters as an attribute you need to implement ActionFilterAttribute class for RequirepPermission class.
Interface classes IAuthenticationService and IAuthorizationService
public interface IAuthenticationService
{
void SignIn(string userName, bool createPersistentCookie);
void SignOut();
}
public interface IAuthorizationService
{
bool Authorize(UserSession user, string[] requiredRoles);
}
FormsAuthenticationService class
/// <summary>
/// This class is for Form Authentication
/// </summary>
public class FormsAuthenticationService : IAuthenticationService
{
public void SignIn(string userName, bool createPersistentCookie)
{
if (String.IsNullOrEmpty(userName)) throw new ArgumentException(#"Value cannot be null or empty.", "userName");
FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
}
public void SignOut()
{
FormsAuthentication.SignOut();
}
}
UserSession calss
public class UserSession
{
public string UserName { get; set; }
public IEnumerable<string> UserRoles { get; set; }
}
Another point is FormsAuthorizationServiceclass and how we can assign a user to the httpContext.Session["CurrentUser"]. My Approach in this situation is to create a new instance of userSession class and directly assign the user from httpContext.User.Identity.Name to the userSession variable as you can see in FormsAuthorizationService class.
[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method, Inherited = false)]
public class RequirePermissionAttribute : ActionFilterAttribute, IAuthorizationFilter
{
#region Fields
private readonly IAuthorizationService _authorizationService;
private readonly string[] _permissions;
#endregion
#region Constructors
public RequirePermissionAttribute(string requiredRoles)
{
_permissions = requiredRoles.Trim().Split(',').ToArray();
_authorizationService = null;
}
#endregion
#region Methods
private IAuthorizationService CreateAuthorizationService(HttpContextBase httpContext)
{
return _authorizationService ?? new FormsAuthorizationService(httpContext);
}
public void OnAuthorization(AuthorizationContext filterContext)
{
var authSvc = CreateAuthorizationService(filterContext.HttpContext);
// Get the current user... you could store in session or the HttpContext if you want too. It would be set inside the FormsAuthenticationService.
if (filterContext.HttpContext.Session == null) return;
if (filterContext.HttpContext.Request == null) return;
var success = false;
if (filterContext.HttpContext.Session["__Roles"] != null)
{
var rolesSession = filterContext.HttpContext.Session["__Roles"];
var roles = rolesSession.ToString().Trim().Split(',').ToList();
var userSession = new UserSession
{
UserName = filterContext.HttpContext.User.Identity.Name,
UserRoles = roles
};
success = authSvc.Authorize(userSession, _permissions);
}
if (success)
{
// Since authorization is performed at the action level, the authorization code runs
// after the output caching module. In the worst case this could allow an authorized user
// to cause the page to be cached, then an unauthorized user would later be served the
// cached page. We work around this by telling proxies not to cache the sensitive page,
// then we hook our custom authorization code into the caching mechanism so that we have
// the final say on whether or not a page should be served from the cache.
var cache = filterContext.HttpContext.Response.Cache;
cache.SetProxyMaxAge(new TimeSpan(0));
cache.AddValidationCallback((HttpContext context, object data, ref HttpValidationStatus validationStatus) =>
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}, null);
}
else
{
HandleUnauthorizedRequest(filterContext);
}
}
private static void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
// Ajax requests will return status code 500 because we don't want to return the result of the
// redirect to the login page.
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.Result = new HttpStatusCodeResult(500);
}
else
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
private HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext)
{
var authSvc = CreateAuthorizationService(httpContext);
if (httpContext.Session != null)
{
var success = false;
if (httpContext.Session["__Roles"] != null)
{
var rolesSession = httpContext.Session["__Roles"];
var roles = rolesSession.ToString().Trim().Split(',').ToList();
var userSession = new UserSession
{
UserName = httpContext.User.Identity.Name,
UserRoles = roles
};
success = authSvc.Authorize(userSession, _permissions);
}
return success ? HttpValidationStatus.Valid : HttpValidationStatus.IgnoreThisRequest;
}
return 0;
}
#endregion
}
internal class FormsAuthorizationService : IAuthorizationService
{
private readonly HttpContextBase _httpContext;
public FormsAuthorizationService(HttpContextBase httpContext)
{
_httpContext = httpContext;
}
public bool Authorize(UserSession userSession, string[] requiredRoles)
{
return userSession.UserRoles.Any(role => requiredRoles.Any(item => item == role));
}
}
then in your controller after the user is authenticated you can get roles from the database and assign it to the roles session:
var roles = Repository.GetRolesByUserId(Id);
if (ControllerContext.HttpContext.Session != null)
ControllerContext.HttpContext.Session.Add("__Roles",roles);
FormsService.SignIn(collection.Name, true);
After the user is logged out of the system you can clear the session
FormsService.SignOut();
Session.Abandon();
return RedirectToAction("Index", "Account");
The caveat in this model is that, when the user is signed into the system, if a role is assigned to the user, authorization doesn't work unless he logs out and logs back in the system.
Another thing is that there is no need to have a separate class for roles, since we can get roles directly from database and set it into roles session in a controller.
After you are done with implementing all these codes one last step is to bind this attribute to your methods in your controller:
[RequirePermission("Admin,DM")]
public ActionResult Create()
{
return View();
}
If you use Castle Windsor Dependency Injection you can inject lists of RoleProviders that can be used to ascertain user rights from any source you choose to implement.
http://ivida.co.uk/2011/05/18/mvc-getting-user-roles-from-multiple-sources-register-and-resolve-arrays-of-dependencis-using-the-fluent-api/
You don't need to use a static class for roles. For instance, the SqlRoleProvider allows you to define the roles in a database.
Of course, if you want to retrieve roles from your own service layer, it's not that hard to create your own role provider - there really aren't that many methods to implement.
You can implement your own membership and role providers by overriding the appropriate interfaces.
If you want to start from scratch, typically these types of things are implemented as a custom http module which stores the users credentials either in the httpcontext or the session. Either way you'll probably want to set a cookie with some sort of authentication token.

Resources