I'm having problems with custom membership within MVC 4 I keep getting a context lifetime related error when I do a ajax call to get a partial result from the server(controller), the error is always {"The provider has been closed"} or {"There is already an open DataReader associated with this Command which must be closed first."} the error always lands within the custom RoleProvider.
I will try to explain the current setup im using.
I have inherited from the Membership and RoleProvier and overridden all the methods like so
public class CustomRoleProvider : RoleProvider
{
private IAccountService _accountService;
public CustomRoleProvider()
{
_accountService = new AccountService();
}
public override string[] GetRolesForUser(string username)
{
return _accountService.GetRolesForUser(username);
}
}
The Membership provider is implemented in the same way the IAccountService above is the service layer that deals with all user accounts & roles all the service layer classes implement a base service class called ServiceBase that creates the DB context
public class ServiceBase
{
protected Context Context;
protected ServiceBase() : this("Context") {}
protected ServiceBase(string dbName)
{
IDatabaseInitializer<Context> initializer = new DbInitialiser();
Database.SetInitializer(initializer);
Context = new Context(dbName);
}
}
The Controller that has the ajax to made to it
[Authorize(Roles = "Administrator,Supplier")]
public class AuctionController : Controller
{
private IAuctionService _service;
public AuctionController()
{
_service = new AuctionService();
}
public AuctionController(IAuctionService service)
{
_service = service;
}
[CacheControl(HttpCacheability.NoCache), HttpGet]
public ActionResult RefreshAuctionTimes(int auctionId)
{
return PartialView("_AuctionTimer", BusinessLogic.Map.ConvertAuction(_service.GetAuction (auctionId)));
}
}
The problem only started when I added the [Authorize(Roles = "Administrator,Supplier")] attribute to the controller that handled the ajax call, I know this is the lifetime of the DbContext being for the life of the app and the controllers service layer being destroyed and recreated on every post but I'm not sure of the best way to handle this, I have used this setup before but with DI and Windsor and never had this problem as the IOC was controlling the context.
Would it be best to create the providers its own DB context or is the conflict between the 2 providers and really they need to share the same db context?
Any help would be great thanks
The problem is exactly what you're suspecting. Is due to the fact that you're creating a single instance of the DbContext and therefore you're having connection issues. If you use it with an IOC/DI schema, you're going to fix it. The other option is to manually handle the connections.
An example of how to do this using Ninject as IOC container is here
They need to share the same context in order for the problem to stop.
I would suggest you create your service layer class on each call to GetRolesForUser:
public override string[] GetRolesForUser(string username)
{
return new AccountService().GetRolesForUser(username);
}
Related
I'm looking into asp.net core and the new security policies and claims functionality. Having just looked at it I don't see how it is much better than the existing authorize attribute logic in the past where hard-coded roles or users are decorated on controllers, methods etc. To me the issues has just been moved from hard-coding in attributes to hard-coding policies.
Ideally I would like to perform activity/resource based authorization where everything would be database driven. Each activity or resource would be stored in the database and a permission/role would be assigned to the resource.
While researching the topic I found this fantastic article by Stefan Wloch that pretty much covers exactly what I'm looking to do.
http://www.codeproject.com/Articles/1079552/Custom-Roles-Based-Access-Control-RBAC-in-ASP-NE
So my question is with the new core features how does it prevent us from having to hard-code and recompile when the time comes to change what roles/permissions are allowed to access a controller or method in a controller? I understand how claims can be used to store anything but the policy portion seems susceptible to change, which gets us back to square one. Don't get me wrong, loving asp.net core and all the great changes, just looking for more information on how to handle authorization.
There are at least 2 things that need to be consider in implementing what you want. The first one is how to model the Controller-Action access in database, the second one is to apply that setting in asp.net core Identity.
The first one, there are too many possibilities depend on the application itself, so lets create a Service interface named IActivityAccessService that encapsulate. We use that service via dependency injection so that anything that we need can be injected to it.
As for the second one, it can be achieved by customize AuthorizationHandler in a policy-based authorization. The first step is to setup things in Startup.ConfigureServices :
services.AddAuthorization(options =>
{
options.AddPolicy("ActivityAccess", policy => policy.Requirements.Add( new ActivityAccessRequirement() ));
});
services.AddScoped<IAuthorizationHandler, ActivityAccessHandler>();
//inject the service also
services.AddScoped<IActivityAccessService, ActivityAccessService>();
//code below will be explained later
services.AddHttpContextAccessor();
next we create the ActivityAccessHandler:
public class ActivityAccessHandler : AuthorizationHandler<ActivityAccessRequirement>
{
readonly IActivityAccessService _ActivityAccessService;
public ActivityAccessHandler (IActivityAccessService r)
{
_ActivityAccessService = r;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext authHandlerContext, ActivityAccessRequirement requirement)
{
if (context.Resource is AuthorizationFilterContext filterContext)
{
var area = (filterContext.RouteData.Values["area"] as string)?.ToLower();
var controller = (filterContext.RouteData.Values["controller"] as string)?.ToLower();
var action = (filterContext.RouteData.Values["action"] as string)?.ToLower();
var id = (filterContext.RouteData.Values["id"] as string)?.ToLower();
if (_ActivityAccessService.IsAuthorize(area, controller, action, id))
{
context.Succeed(requirement);
}
}
}
}
public class ActivityAccessRequirement : IAuthorizationRequirement
{
//since we handle the authorization in our service, we can leave this empty
}
Since we can use dependency injection in AuthorizationHandler, it is here that we inject the IActivityAccessService.
Now that we have access to what resource is being requested, we need to know who is requesting it. This can be done by injecting IHttpContextAccessor. Thus services.AddHttpContextAccessor() is added in code above, it is for this reason.
And for the IActivityAccessService, you could do something like:
public class ActivityAccessService : IActivityAccessService
{
readonly AppDbContext _context;
readonly IConfiguration _config;
readonly IHttpContextAccessor _accessor;
readonly UserManager<AppUser> _userManager;
public class ActivityAccessService(AppDbContext d, IConfiguration c, IHttpContextAccessor a, UserManager<AppUser> u)
{
_context = d;
_config = c;
_accessor = a;
_userManager = u;
}
public bool IsAuthorize(string area, string controller, string action, string id)
{
//get the user object from the ClaimPrincipals
var appUser = await _userManager.GetUserAsync(_accessor.HttpContext.User);
//get user roles if necessary
var userRoles = await _userManager.GetRolesAsync(appUser);
// all of needed data are available now, do the logic of authorization
return result;
}
}
Please note that the code in IsAuthorize body above is an example. While it will works, people might say it's not a good practice. But since IActivityAccessService is just a common simple service class, we can inject anything that wee need to it and modify the IsAuthorize method signature in any way that we want to. For example, we can just pass the filterContext.RouteData instead.
As for how to apply this to a controller or action:
[Authorize(Policy = "ActivityAccess")]
public ActionResult<IActionResult> GetResource(int resourceId)
{
return Resource;
}
hope this helps
I set up my first mvc project with ninject, and im not sure if i understand this fully. I have the following simple setup.
I am using entity framework 6 as my orm.
Customer repository
public class CustomerRepository : ICustomerRepository
{
private readonly ApplicationDbContext db;
public CustomerRepository(ApplicationDbContext db)
{
this.db = db;
}
public IEnumerable<Customer> GetAll()
{
return this.db.Customers.ToList();
}
}
ICustomerRepository
public interface ICustomerRepository
{
IEnumerable<Customer> GetAll();
}
Ninject
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ICustomerRepository>().To<CustomerRepository>().InRequestScope();
kernel.Bind<ICustomerDetailsRepository>().To<CustomerDetailsRepository>().InRequestScope();
kernel.Bind<ApplicationDbContext>().To<ApplicationDbContext>().InRequestScope();
}
Controller
public HomeController(ICustomerRepository customerRepository, ICustomerDetailsRepository customerDetailsRepository)
{
this.customerRepository = customerRepository;
this.customerDetailsRepository = customerDetailsRepository;
}
As you can see, i am calling both repositories from the same controller. Both repositories are setup exactly the same way.
Will both my repositories use the same dbcontext when requested, and will it automaticly be disposed afterwards??
This is not a real life setup. I made it really basic to try and understand how ninject work.
The fact that you configured your binding to be InRequestScope means that the requested object will be created the first time it's resolved after a new request starts, and for every subsequent resolutions of the same object within the same request, you will get the same instance.
Keep in mind that the lifetime of the request is determined by the lifetime of the HttpContext.Current object.
Just for reference:
As you can see here:
InThreadScope matches the lifetime of your object to the lifetime of System.Threading.Thread.CurrentThread
InSingletonScope matches the lifetime of your object to the lifetime of Ninject's Kernel
InTransientScope matches the lifetime of your object to the lifetime of null
Update
Regarding your comment about people implementing Dispose():
Even if you don't dispose your objects manually, when the dependency injection container disposes your object, it calls the dispose method if it implements IDisposable
I have the following in my MVC Application:
namespace WebUx.Areas.User.Controllers
{
[Authorize]
[InitializeSimpleMembership]
public class AccountController : Controller
{
Plus:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class InitializeSimpleMembershipAttribute : ActionFilterAttribute
{
private static SimpleMembershipInitializer _initializer;
private static object _initializerLock = new object();
private static bool _isInitialized;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Ensure ASP.NET Simple Membership is initialized only once per app start
LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
}
private class SimpleMembershipInitializer
{
public SimpleMembershipInitializer()
{
System.Diagnostics.Debug.Write("Set Initializer\n");
Database.SetInitializer<UsersContext>(null);
try
{
using (var context = new UsersContext())
{
if (!context.Database.Exists())
{
// Create the SimpleMembership database without Entity Framework migration schema
((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
}
}
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
}
I understand that when there's a call to the account controller then this will set the DB context but once this is set will it stay set for my application. What about later on for other users who connect. Will the DB context always be available?
The reason I am asking this is that I have other information that I want to store in a table and access with Web API. Should I code in something similar for these controllers so that each time I check that there's a DB context available or could I just use this?
The connection is tightly coupled to the DbContext. As a result, the connection will only be open when your class which inherits DbContext, UsersContext in your case, retains its scope.
In your example, UsersContext is scoped to the using block.
using (var context = new UsersContext())
{
//some actions
}
Therefore, once "some actions" are finished, the connection will close and any attempt to access lazy loading will throw an exception stating the connection is no longer available. Every time you need to access your database, you should start a new connection in my opinion. What you want to make sure is that you only make one actual trip to the database. Make sure that your query is optimized so that you do not make multiple trips to the database instead of just doing it all at once as that will affect your performance.
Edit
As a side note, the using block breaks down into this:
try{
var context = new UsersContext();
//some actions
}finally{
context.Dispose();
}
I'm just getting started with ServiceStack and, as a test case, I am looking to rework an existing service which is built using standard ASP.Net handlers. I've managed to get it all working as I want it but have certain aspects which make use of the ASP.Net Session object.
I've tried adding IRequiresSessionState into the service interface:
public class SessionTestService : RestServiceBase<SessionTest>, IRequiresSessionState {
public override object OnPost(SessionTest request) {
// Here I want to use System.Web.HttpContext.Current.Session
}
}
The trouble is I can't seem to get it to work as the Session object is always null.
I've done a lot of Googling and have puzzled over https://github.com/mythz/ServiceStack/blob/master/tests/ServiceStack.WebHost.IntegrationTests/Services/Secure.cs and similar but I can't find any example code which does this (which surprises me). Can anyone explain why the above doesn't work and advise what I need to do to get it working?
Note: Ultimately I'll probably look to replace this with Redis or will try to remove any serverside session requirement, but I figured that I'd use the ASP.Net implementation for the time being, to get things working and to avoid reworking it more than is necessary at this point.
Using ServiceStack ISession
ServiceStack has a new ISession interface backed by ICacheClient that lets you share same ISession between MVC Controllers, ASP.NET base pages and ServiceStack's Web Services which share the same Cookie Id allowing you to freely share data between these web frameworks.
Note: ISession is a clean implementation that completely by-passes the existing ASP.NET session with ServiceStack's own components as described in ServiceStack's MVC PowerPack and explained in detail in the Sessions wiki page.
To easily make use of ServiceStack's Session (Cache & JSON Serializer) have your Controllers inherit from ServiceStackController (in MVC) or PageBase (in ASP.NET)
There is also new Authentication / Validation functionality added in ServiceStack which you can read about on the wiki:
Authentication and authorization
Validation
Using ASP.NET Session
Essentially ServiceStack is just a set of lightweight IHttpHandler's running on either an ASP.NET or HttpListener host. If hosted in IIS/ASP.NET (most common) it works like a normal ASP.NET request.
Nothing in ServiceStack accesses or affects the configured Caching and Session providers in the underlying ASP.NET application. If you want to enable it you would need to configure it as per normal in ASP.NET (i.e. outside of ServiceStack) see:
http://msdn.microsoft.com/en-us/library/ms178581.aspx
Once configured you can access the ASP.NET session inside a ServiceStack web service via the singleton:
HttpContext.Current.Session
Or alternatively via the underlying ASP.NET HttpRequest with:
var req = (HttpRequest)base.RequestContext.Get<IHttpRequest>().OriginalRequest;
var session = req.RequestContext.HttpContext.Session;
Although because of the mandatory reliance on XML config and degraded performance by default, I prefer to shun the use of ASP.NET's Session, instead opting to use the cleaner Cache Clients included with ServiceStack.
Basically the way Sessions work (ASP.NET included) is a cookie containing a unique id is added to the Response uniquely identifying the browser session. This id points to a matching Dictionary/Collection on the server which represents the browsers' Session.
The IRequiresSession interface you link to doesn't do anything by default, it simply is a way to signal to either a Custom Request Filter or base web service that this request needs to be authenticated (i.e. two places where you should put validation/authentication logic in ServiceStack).
Here's a Basic Auth implementation that looks to see if a web service is Secure and if so make sure they have authenticated.
Here's another authentication implementation that instead validates all services marked with an [Authenticate] attribute, and how to enable Authentication for your service by adding the Attribute on your Request DTO.
New Authentication Model in ServiceStack
The above implementation is apart of the multi-auth provider model included in the next version of ServiceStack. Here's the reference example showing how to register and configure the new Auth model in your application.
Authentication Strategies
The new Auth model is entirely an opt-in convenience as you can simply not use it and implement similar behaviour yourself using Request Filters or in base classes (by overriding OnBeforeExecute). In fact the new Auth services are not actually built-into ServiceStack per-se. The entire implementation lives in the optional ServiceStack.ServiceInterfaces project and implemented using Custom Request Filters.
Here are different Authentication strategies I've used over the years:
Mark services that need authentication with an [Attribute]. Likely the most idiomatic C# way, ideal when the session-id is passed via a Cookie.
Especially outside of a Web Context, sometimes using a more explicit IRequiresAuthentication interface is better as it provides strong-typed access to the User and SessionId required for Authentication.
You can just have a 1-liner to authenticate on each service that needs it - on an adhoc basis. A suitable approach when you have very few services requiring authentication.
That's a great and comprehensive answer by #mythz. However, when trying to access the ASP.NET session by HttpContext.Current.Session within a ServiceStack web service, it always returns null for me. That's because none of the HttpHandlers within ServiceStack are adorned with the IRequiresSessionState interface, so the .NET Framework does not provide us with the session object.
To get around this, I've implemented two new classes, both of which use the decorator pattern to provide us with what we need.
Firstly, a new IHttpHandler which requires session state. It wraps the IHttpHandler provided by ServiceStack and passes calls through to it...
public class SessionHandlerDecorator : IHttpHandler, IRequiresSessionState {
private IHttpHandler Handler { get; set; }
internal SessionHandlerDecorator(IHttpHandler handler) {
this.Handler = handler;
}
public bool IsReusable {
get { return Handler.IsReusable; }
}
public void ProcessRequest(HttpContext context) {
Handler.ProcessRequest(context);
}
}
Next, a new IHttpHandlerFactory which delegates the responsibility for generating the IHttpHandler to ServiceStack, before wrapping the returned handler in our new SessionHandlerDecorator...
public class SessionHttpHandlerFactory : IHttpHandlerFactory {
private readonly static ServiceStackHttpHandlerFactory factory = new ServiceStackHttpHandlerFactory();
public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated) {
var handler = factory.GetHandler(context, requestType, url, pathTranslated);
return handler == null ? null : new SessionHandlerDecorator(handler);
}
public void ReleaseHandler(IHttpHandler handler) {
factory.ReleaseHandler(handler);
}
}
Then, it's just a matter of changing the type attributes in the handlers in Web.config to SessionHttpHandlerFactory instead of ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack, and your web services should now have the ASP.NET session avaialble to them.
Despite the above, I fully endorse the new ISession implementation provided by ServiceStack. However, in some cases, on a mature product, it just seems too big a job to replace all uses of the ASP.NET session with the new implementation, hence this workaround!
Thanks #Richard for your answer above. I am running a new version of service stack and they have removed the ServiceStackHttpFactory with HttpFactory. Instead of having
private readonly static ServiceStackHttpHandlerFactory factory = new ServiceStackHttpHandlerFactory();
You need to have
private static readonly HttpHandlerFactory Factory = new HttpHandlerFactory();
Here is updated code for this service
using ServiceStack;
using System.Web;
using System.Web.SessionState;
namespace MaryKay.eCommerce.Mappers.AMR.Host
{
public class SessionHttpHandlerFactory : IHttpHandlerFactory
{
private static readonly HttpHandlerFactory Factory = new HttpHandlerFactory();
public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
{
var handler = Factory.GetHandler(context, requestType, url, pathTranslated);
return handler == null ? null : new SessionHandlerDecorator(handler);
}
public void ReleaseHandler(IHttpHandler handler)
{
Factory.ReleaseHandler(handler);
}
}
public class SessionHandlerDecorator : IHttpHandler, IRequiresSessionState
{
private IHttpHandler Handler { get; set; }
internal SessionHandlerDecorator(IHttpHandler handler)
{
Handler = handler;
}
public bool IsReusable
{
get { return Handler.IsReusable; }
}
public void ProcessRequest(HttpContext context)
{
Handler.ProcessRequest(context);
}
}
}
As of ServiceStack 4.5+ the HttpHandler can also support Async. Like so:
namespace FboOne.Services.Host
{
public class SessionHttpHandlerFactory : IHttpHandlerFactory
{
private static readonly HttpHandlerFactory Factory = new HttpHandlerFactory();
public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
{
var handler = Factory.GetHandler(context, requestType, url, pathTranslated);
return handler == null ? null : new SessionHandlerDecorator((IHttpAsyncHandler)handler);
}
public void ReleaseHandler(IHttpHandler handler)
{
Factory.ReleaseHandler(handler);
}
}
public class SessionHandlerDecorator : IHttpAsyncHandler, IRequiresSessionState
{
private IHttpAsyncHandler Handler { get; set; }
internal SessionHandlerDecorator(IHttpAsyncHandler handler)
{
Handler = handler;
}
public bool IsReusable
{
get { return Handler.IsReusable; }
}
public void ProcessRequest(HttpContext context)
{
Handler.ProcessRequest(context);
}
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
return Handler.BeginProcessRequest(context, cb, extraData);
}
public void EndProcessRequest(IAsyncResult result)
{
Handler.EndProcessRequest(result);
}
}
}
HI,
I am implementing a custom role provider in my nhibernate application
I have a repository that I call whenever I want to access the nhibernate session.
So when my role provider initializes itself
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) {
base.Initialize(name, config);
Repository = new Repository();
}
Then I override
public override string[] GetRolesForUser(string username) {
var users = Repository.QueryAll<Users>();
//I then filter and so on
}
But when this function is called I always get an error that the NHibernate session is closed.
I debugged the nhibernate source code and it turns out that the session here has a different guid that the session in my controllers(I am using ASP.NET MVC also).
And this particular session is closed by the time I get here.
I don't know who closes it. I know it is started when the application starts and only then.
Does anyone have any idea what I am doing wrong?
I want to still use Nhibernate in this provider but not get the error any more.
Thank you
I had what appears to be the exact same problem. Don't forget that Role and Membership providers are only instantiated once and exist for the lifetime of the application. If you're using a Session per Web request pattern, the ISession will be closed after the first request and then any reference to an ISession internal to the provider will likely be null for subsequent requests.
You can get around this by injecting a reference to the ISessionFactory and calling GetCurrentSession, instead of directly holding a reference to an ISession.
This is how I evetually fixed it.
in my repository class I had this:
public Repository()
{
this.Session = SessionManager.GetCurrentSession();
}
I deleted the constructor entirely
I put in this instead:
private ISession _session;
protected ISession Session
{
get
{
if (_session == null)
{
_session = SessionManager.GetCurrentSession();
}
return _session;
}
}