I want a way to affect on swagger output documentation. The issue is that user that asks docs can have permissions only to some of methods that described in swagger so I want to exclude specific methods from output. The worst approach I consider is catch swagger.json request by middleware and then just check what methods requested user has access to and exclude necessary paths. But I don't like it very much so may be there is built in feature to do that?
Found an answer. Just need to create custom DocumentFilter that allows to edit output document:
public class RestrictSwaggerOperationsFilter : IDocumentFilter
{
private readonly ILogger<RestrictSwaggerOperationsFilter> _logger;
private readonly IHttpContextAccessor _contextAccessor; // inject service to get HttpContext with user claims
private readonly IServiceScopeFactory _scope; // service for getting database context
public RestrictSwaggerOperationsFilter(IHttpContextAccessor httpContextAccessor, IServiceScopeFactory scope, ILogger<RestrictSwaggerOperationsFilter> logger)
{
_contextAccessor = httpContextAccessor;
_logger = logger;
_scope = scope;
}
public void Apply(OpenApiDocument operation, DocumentFilterContext context)
{
using (var scope = _scope.CreateScope())
{
var dbContext = scope.ServiceProvider.GetService<ApplicationDbContext>();
// do whatever check you need
operation.Paths.Remove("key"); // removes specific path by key that represents path to a method
// DocumentFilterContext contains ActionDescriptor for every API method
}
}
}
And then add this filter to ConfigureServices at Startup.cs:
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
options.DocumentFilter<RestrictSwaggerOperationsFilter>();
});
Works for Swashbuckle.AspNetCore version 5.0.0-rc4. For earlier versions I suppose there will be similar solution.
Related
i have webapi action which is decorated with customauthattribute for authorization. This attribute internally checks with db if current user has viewcustomer permissions. Does anyone know better way of handling it instead of using customattribute. may be intercepting somewhere all request and run authorization checks for user/permisson/resource he is trying to access : eg getcustomer for customer id 10. So if user doesnt have access see customer id 10 he should get 403 status.
[CheckPermission(Process.CustomerManagment,Permissions.View)]
public IHttpActionResult GetCustomer(int customerId)
{
}
You can add global filters in the configuration of your web api.
Practically in your startup class (startup.cs or webapi.config) you can call on the httpconfiguration object the following method
var config = new HttpConfiguration();
config.Filters.Add(new MyAuthFilterAttribute());
In this way it will be global for all your api calls.
You should extend the IAuthenticationFilter interface.
take a look here for documentation
webapi documentation
One option would be to create a filter that you can apply globally. For example, something like this. Yes it's horrible but gives you a start:
public class GlobalAuthoriseAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
var actionName = filterContext.ActionDescriptor.ActionName;
switch (controllerName)
{
case "Home":
//All call to home controller are allowed
return;
case "Admin":
filterContext.Result = new HttpUnauthorizedResult();
return;
}
}
}
Now you can add this to your entire app in the App_Start\FilterConfig.cs file:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new GlobalAuthoriseAttribute());
}
I've been playing around with SimpleInjector and I'm trying to register properly all command handlers.
Here is my code:
CQRS.cs
public interface ICommand {}
public interface ICommandDispatcher
{
void Execute(ICommand command);
}
public class CommandDispatcher : ICommandDispatcher
{
private readonly Container container;
public CommandDispatcher(Container container)
{
this.container = container;
}
public void Execute(ICommand command)
{
var handlerType = typeof(ICommandHandler<>).MakeGenericType(command.GetType());
dynamic handler = container.GetInstance(handlerType);
handler.Handle((dynamic)command);
}
}
public interface ICommandHandler<in TParameter> where TParameter : ICommand
{
void Handle(TParameter command);
}
Handler.cs
public class UserCommandsHandler : ICommandHandler<CreateUser>
{
public void Handle(CreateUser message)
{
var user = new User(message.Email);
/* logic here */
}
}
Command.cs
public class CreateUser : ICommand
{
public readonly string Email;
public CreateUser(string email)
{
Email = email;
}
}
Global.asax.cs
var assemblies = new[] { typeof(ICommandHandler<>).Assembly };
var container = new SimpleInjector.Container();
container.RegisterCollection(typeof(ICommandHandler<>), assemblies);
container.RegisterSingleton<ICommandDispatcher>(new CommandDispatcher(container));
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
HomeController.cs
public class HomeController : Controller
{
private readonly ICommandDispatcher _commandDispatcher;
public HomeController(ICommandDispatcher commandDispatcher)
{
_commandDispatcher = commandDispatcher;
}
public ActionResult Index()
{
var command = new CreateUser("email#example.com");
_commandDispatcher.Execute(command);
return Content("It works");
}
}
at CQRS.cs
dynamic handler = container.GetInstance(handlerType);
I get:
No registration for type ICommandHandler<CreateUser> could be found.
There is, however, a registration for IEnumerable<ICommandHandler<CreateUser>>;
Did you mean to call GetAllInstances<ICommandHandler<CreateUser>>() or depend on IEnumerable<ICommandHandler<CreateUser>>?
The Simple Injector API clearly separates between registrations for collections and one-to-one mappings. In your composition root, you are making the following registration:
container.RegisterCollection(typeof(ICommandHandler<>),
new[] { typeof(ICommandHandler<>).Assembly });
The API Documentation for RegisterCollection states:
Registers a collection of serviceTypes, whose instances will be resolved lazily each time the resolved collection of serviceType is enumerated. The underlying collection is a stream that will return individual instances based on their specific registered lifestyle, for each call to IEnumerator<T>.Current. The order in which the types appear in the collection is the exact same order that the items were registered, i.e the resolved collection is deterministic.
In other words, you are allowing command handlers to be resolved as collections, by requesting IEnumerable<ICommandHandler<T>>.
In your CommandDispatcher however, you request a single ICommandHandler<T> by calling container.GetInstance(handlerType). Since there is no one-to-one mapping for an ICommandHandler<T>, Simple Injector informs you about this by throwing:
No registration for type ICommandHandler<CreateUser> could be found.
There is, however, a registration for IEnumerable<ICommandHandler<CreateUser>>;
Did you mean to call GetAllInstances<ICommandHandler<CreateUser>>() or depend on IEnumerable<ICommandHandler<CreateUser>>?
To fix this, there are two options:
Either you register your handlers using the one-to-one mapping, or
You resolve a collection of handlers within your CommandDispatcher by calling GetAllInstances(Type).
Since there will always be a one-to-one mapping between a command and its handler (meaning: there will be exactly one handler per command), option 1 is the most obvious solution. So change your registration to the following:
// Use 'Register' instead of 'RegisterCollection'.
container.Register(typeof(ICommandHandler<>),
new[] { typeof(ICommandHandler<>).Assembly });
I'm developing a multi tenant application. For now what I want to achieve is obtaining a unique instance of HttpContext per tenant.
Each tenant has its own database.
All tenants share same functionality (There aren't any tenant X specific controllers)
There is a master database for querying all tenants (So before accessing tenant's settings (eg:connectionString) there must be at least one hit to master database).
Tenant is identified from RequestContext RouteData.Values["tenant"]
Here is some simplified code so please do not focus on the architecture:
Route url pattern:{tenant}/{controller}/{action}/{id}
Global.asax.cs:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
// Delegates for BuildSessionFactory and GetSession are declared under Application_Start()
// Registers SessionFactory to be Singletone per Tenant
builder.Register(BuildSessionFactory).As<ISessionFactory>().InstancePerTenant();
builder.Register(GetSession).As<ISession>().InstancePerRequest();
// This is the module responsible for mapping HttpContextBase to HttpContextWrapper and other mvc specific abastractions
builder.RegisterModule(new AutofacWebTypesModule());
var container = builder.Build();
// Build multitenant container
var mtc = new MultitenantContainer(new RouteDataTenantIdentificationStrategy("tenant"), container);
DependencyResolver.SetResolver(new AutofacDependencyResolver(mtc));
}
private ISessionFactory BuildSessionFactory(IComponentContext context)
{
return new NHibernateConfiguration().BuildConfiguration().BuildSessionFactory();
}
private ISession GetSession(IComponentContext context)
{
return context.Resolve<ISessionFactory>().OpenSession();
}
HomeController sample:
public class HomeController : Controller
{
private readonly ISession _session;
private readonly HttpContextBase _context;
public HomeController(ISession session, HttpContextBase context)
{
_session = session;
_context = context;
}
public ActionResult Index()
{
// Setting up HttpContext.Session["testSessionKey"] only for tenant1
if (_context.Request.RequestContext.RouteData.Values["tenant"] as string == "tenant1")
{
_context.Session.Add("testSessionKey","Some value specific to tenant1 only");
}
using (var transaction = _session.BeginTransaction(IsolationLevel.ReadCommitted))
{
var tenants = _session.Query<Tenant>().ToList();
transaction.Commit();
return View(tenants);
}
}
}
The Workflow:
Remark: The code above ignores the true existance of the tenant. It just satisfies the dependencies according to the RouteDataTenantIdentificationStrategy("tenant") class definition (identification by RouteData nothing exceptional).
Now when I'm accessing the url: /tenant1/Home/Index a HttpContext.Session['testSessionKey'] is added.
When I try to acces something like /otherTenant/Home/Index the HttpContext.Session['testSessionKey'] is still there.
Question 1:
How to achieve Unique HttpContext per tenant using Dependency Injection with Autofac?
Question 2:
How to achieve something more than a HttpContext? Lets say a WorkContext which includes the tenant's HttpContext
If something is unclear please ask and I'll provide necessary clarifications. Thank you!
You can do this if you push the customization of the context values to the registration of HttpContextBase. Instead of using the AutofacWebTypesModule, use your own registration:
builder.Register(c =>
{
var httpContext = new HttpContextWrapper(HttpContext.Current);
var strategy = c.Resolve<ITenantIdentificationStrategy>();
// Update the context here
return httpContext;
}).As<HttpContextBase>()
.InstancePerRequest();
That means you should also have your tenant ID strategy registered in the container, but that's pretty easy.
(Sorry for the short snippet; I'm on my phone traveling.)
Our repositories and services are currently being injected into our controllers via a Unity container (using the Web API MVC bootstrapper).
public class AnnouncementsController : BaseApiController
{
protected IAnnouncementRepository AnnouncementRepository{ get; set; }
public AnnouncementsController (IAnnouncementRepository announcementRepository)
: base()
{
this.AnnouncementRepository = announcementRepository;
}
public HttpResponseMessage Post([FromBody]GetAnnouncementsModel model)
{
var announcements = AnnouncementRepository.GetByType(model.AnnouncementType);
// ...
}
}
A new requirement has arisen: All input models (e.g. GetAnnouncementsModel) now need to have an AccessToken.
Why? So that results from repositories are filtered according to data rights. Clients are restriction on what data they can consume.
Bad Solution - Pass in token as a method parameter
One solution is to include an AccessToken parameter to every repository or service call. This is not a good solution. We have to implement this in hundreds of methods. An example of this parameter:
public HttpResponseMessage Post([FromBody]GetAnnouncementsModel model)
{
var announcements = AnnouncementRepository.GetByType(model.AccessToken, model.AnnouncementType);
// ...
}
Better Solution - Inject token during resolution
A better solution would be to provide the AccessToken in the repository constructors and have some base implementation that does the filtering logic implicitly.
But how could I do this with dependency injection? The constructor is resolved and called by the Unity container. How could I inject the property value of an input model into this process?
container.RegisterType<IAnnouncementRepository, AnnouncementRepository>(
new InjectionConstructor(
new InjectionParameter<Guid>(AccessToken)
)
);
You can define a custom interface, call it for example IAccessTokenProvider:
interface IAccessTokenProvider
{
Guid Token { get; }
}
Now you can make an implementation like this:
public class HttpContextAccessTokenProvider
{
public Guid Token
{
get { return (Guid)HttpContext.Current.Items["AccessToken"]; }
}
public static void SetToken(Guid token)
{
HttpContext.Current.Items["AccessToken"] = token;
}
}
Now you should be able to implement a filter to read the token from the request:
public class TokenFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string tokenString = filterContext.HttpContext.Request.QueryString["token"];
ActionExecutingContext.SetToken(Guid.Parse(tokenString));
}
}
You can also read the token from other sources or store it in other containers (sessions, cookies, whatever). You can also directly access it in your controller or repositories.
You have 2 options to use the token in your repository:
Inject IAccessTokenProvider to your repository and get the token directly from the provider.
Inject IAccessTokenProvider to your controller and pass the token
If I want to implement a Dependency Injection for ASP.NET MVC controllers, I can use IControllerFactory interface to create a factory class and then register it in Global.asax.cs, like:
ControllerBuilder.Current.SetControllerFactory(controllerFactory)
(reference: http://www.dotnetcurry.com/ShowArticle.aspx?ID=786)
Now my question is:
How could I set a factory for an IHttpControllerActivator derived class?
Do we have something in ASP.NET MVC 4.0 like:
HttpControllerBuilder.Current.SetApiControllerActivator(httpControllerActivator)
?
Update:
I want to add my current code, it might be helpful to understand the case:
My customized HttpControllerActivator:
public class MyCustomApiActivator : DefaultHttpControllerActivator
//or IHttpControllerActivator ?
{
private readonly Dictionary<string, Func<HttpRequestMessage, IHttpController>> _apiMap;
public MyCustomApiActivator(IMyRepository repository)
{
if (repository == null)
{
throw new ArgumentException("repository");
}
_apiMap = new Dictionary<string, Func<HttpRequestMessage, IHttpController>>();
controllerMap["Home"] = context => new HomeController();
_apiMap["MyCustomApi"] = context => new MyCustomApiController(repository);
}
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
if(_apiMap.ContainsKey(controllerType.Name))
return _apiMap[controllerType.Name](request);
return null;
}
}
My composition root:
public class CompositionRoot
{
private readonly IHttpControllerActivator _apiControllerActivator;
public CompositionRoot()
{
_apiControllerActivator = CompositionRoot.CreateHttpControllerActivator();
}
public IHttpControllerActivator ApiControllerActivator
{
get { return _apiControllerActivator; }
}
private static IHttpControllerActivator CreateHttpControllerActivator()
{
string defaultRepositoryTypeName = ConfigurationManager.AppSettings["DefaultRepositoryTypeName"];
var defaultRepositoryType = Type.GetType(defaultRepositoryTypeName, true);
var defaultRepository = (IMyRepository)Activator.CreateInstance(defaultRepositoryType);
var apiController = new MyCustomApiActivator(defaultRepository);
return apiController;
}
}
Finally this is inside my Global.asax.cs, where I need a trick:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
var root = new CompositionRoot();
//The following line raise this compile-time error:
// cannot convert from 'System.Web.Http.Dispatcher.IHttpControllerActivator'
// to 'System.Web.Mvc.IControllerFactory'
ControllerBuilder.Current.SetControllerFactory(root.ApiControllerActivator);
}
Mark Seemann, author of Dependency Injection in .NET, has a short series on DI with ASP.NET WebAPI.
He places the composition root inside an IHttpControllerActivator
Dependency Injection and Lifetime Management with ASP.NET Web API
Dependency Injection in ASP.NET Web API with Castle Windsor
Maybe that helps.
Update
GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpControllerActivator),
new PoorMansCompositionRoot());
Registers your custom HttpControllerActivator globally.
In ASP.NET Web API HttpControllerDispatcher is responsible for constructing controllers. It works with the DependecyResolver by default but if you want to extend its functionality, you need to override its SendAsync method and register it again.
Example:
public class MyCustomDispatcher : HttpControllerDispatcher {
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken) {
// Do your stuff here
// According to your requirements, either run its default functionality
// or return your own stuff
return base.SendAsync(request, cancellationToken);
}
}
Note:
However, I wasn't able to find an elegant way to replace the default one
globally. You can replace the default one per-route easily by
attaching this custom dispatcher to a route but there seems to be no
way to do this globally. I opened up a discussion on that at the
project site: http://aspnetwebstack.codeplex.com/discussions/400366