Securing exclusively the REST access to a Spring Data Rest Repository - spring-security

I'm using Spring Data Rest to expose a repository. I'm using #PreAuthorize and #PostFilter to restrict the access to the REST end points to exclusively admin users and filter the results.
#PreAuthorize("hasRole('ROLE_ADMIN')")
#PostFilter("hasPermission(filterObject, 'read')
public interface SomeRepository extends CrudRepository<SomeEntity, Long> {
}
At the same time I have another Controller that doesn't require any authentication but is using the repository.
#Controller
public class SomeController {
#Autowired
SomeRepository repository;
#RequestMapping(value = "/test")
public ResponseEntity test () {
// Do something
repository.findAll();
// Do something else
}
}
This doesn't work because the user that send the request to "/test" is not admin so it doesn't have access to the repository.
My question is, it is possible to add security exclusively to the REST interface of the repository and not when the repository is used internally in the application?
Thanks

Please evaluate these possibilities:
Security checks in REST event handlers
Adding custom repository methods for internal use
Using RunAsManager (or temporarily switching SecurityContext to perform a privileged operation)
Securing modifying requests using REST event handlers:
#Service
#RepositoryEventHandler
public class FooService {
/**
* Handles before-* events.
*/
#HandleBeforeCreate
#HandleBeforeSave
#HandleBeforeDelete
#PreAuthorize("hasRole('ADMIN')")
public void onBeforeModify(final Foo entity){
// noop
}
/**
* Handles before-* events.
*/
#HandleBeforeLinkSave
#HandleBeforeLinkDelete
#PreAuthorize("hasRole('ADMIN')")
public void onBeforeModifyLink(final Foo entity, final Object linked){
// noop
}
}
Securing standard CRUD methods while adding non-secure custom methods on repository for internal use:
public interface FooDao extends CrudRepository<Foo, Long> {
#Override
#PreAuthorize("hasRole('ADMIN')")
<S extends Foo> S save(final S entity);
/**
* Saves entity without security checks.
*/
#Transactional
#Modifying
default <S extends Foo> S saveInternal(final S entity) {
return save(entity);
}
}

One solution would be to remove the #PreAuthorize annotation from your repository interface, and in a configuration class, extend WebSecurityConfigAdaptor and override the configure(HttpSecurity security) method. From here you can use AntMatchers to impose access restrictions to the REST endpoints as required. For example:
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/someEntities/**").hasRole('ADMIN')
.anyRequest().permitAll();
}
See http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#jc-httpsecurity for more details.

I ran into the same problem and came up with a workaround that doesn't feel completely right but does its job for the time being.
I basically created a security utils bean which can be used to check if a method was called internally or externally using the Spring Data REST API (remark: my repositories are prefixed /api/, if you have another prefix you need to change the regex accordingly).
#Component("securityUtils")
public class SecurityUtils {
public boolean isRestRequest(){
HttpServletRequest r = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
return Pattern.matches("^/api/", UrlUtils.buildRequestUrl(r));
}
}
To make this work, you need to add the following line to your listeners in the web.xml:
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
And use the method in your expression based access control like so (where the last line in the expression allows you to use the save method from any controller methods that are mapped against URLs which do not start with /api/:
#Override
#PreAuthorize("hasRole('ROLE_ADMINISTRATOR') " +
"or hasPermission(#user, 'WRITE') " +
"or !#securityUtils.isRestRequest()")
<S extends User> S save(#P("user") S user);
Caveats:
You cannot use this when you want to expose custom functionality over the /api route as this is merely a simple regex check against the route
The check has to be explicitly added to each repository or repository method for which you want to omit the authorization check internally (might be an advantage as well)

In my opinion the right solution would be to have two Repositories, one that is called EntityRepository and one SecuredEntityRepository.
Example:
#RestResource(exported = false)
public abstract interface CustomerRepository extends JpaRepository<Customer, Long> {
}
and the secured version:
#RestResource(exported = true)
public abstract interface SecuredCustomerRepository extends CustomerRepository {
#Override
#PreAuthorize("#id == principal.customer.id or hasAuthority('ADMIN_CUSTOMER_ONE')")
public Customer findOne(#Param("id") Long id);
#Override
#Query("SELECT o FROM #{#entityName} o WHERE o.id = ?#{principal.customer.id} or 1 = ?#{ hasAuthority('ADMIN_CUSTOMER_LIST') ? 1 : 0 }")
public Page<Customer> findAll(Pageable pageable);
#Override
#SuppressWarnings("unchecked")
#PreAuthorize("#customer.id == principal.customer.id or hasAuthority('ADMIN_CUSTOMER_SAVE')")
public Customer save(#P("customer") Customer customer);
#Override
#PreAuthorize("hasAuthority('ADMIN_CUSTOMER_DELETE')")
public void delete(#Param("id") Long id);
#Override
#PreAuthorize("hasAuthority('ADMIN_CUSTOMER_DELETE')")
public void delete(Customer customer);
}
This is currently not possible due to an issue with the auto-wiring mechanism in SD REST: https://jira.spring.io/browse/DATAREST-923

Sure. Just change the location of the #PreAuthorize annotation. This annotation can be placed in classes or single methods.
For example
#Controller
public class SomeController {
#Autowired
SomeRepository repository;
#RequestMapping(value = "/test")
#PreAuthorize(....)
public ResponseEntity test () {
// Do something
repository.findAll();
// Do something else
}
}
is perfectly legit (note the annotation on the test() method.

I decorated the repository class with this:
#PreAuthorize("hasRole('admin')")
It locked down everything.
Then whatever I wanted to enable for internal use but not rest, I decorated like this:
#Transactional
#Modifying
#PreAuthorize("hasRole('user')")
#RestResource(exported = false)
default <S extends SomeEntity> S saveInternal(final S entity) {
return save(entity);
}
And whatever I wanted to expose via the Rest interface (handpicked few) I exposed with something like this:
#PreAuthorize("(hasRole('user')) and
(#entity.user.username == principal.name)")
#Override
<S extends SomeEntity> S save(#Param("entity") S entity);
Note that this also validates that you are saving a record you are authorized to save.

I solved this problem by adding my own check
I created my AbstractHttpConfigurer class with global security. I have declared methods that can be public.
public class CommonSpringKeycloakTutorialsSecurityAdapter extends AbstractHttpConfigurer<CommonSpringKeycloakTutorialsSecurityAdapter, HttpSecurity> {
public static String[] PERMIT_ALL_URL = {"/api/user/createUser"};
#Override
public void init(HttpSecurity http) throws Exception {
// any method that adds another configurer
// must be done in the init method
http
// disable csrf because of API mode
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
// manage routes securisation here
.authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll()
// manage routes securisation here
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.antMatchers("/swagger-ui.html*", "/swagger-ui/**", "/v3/api-docs/**").permitAll()
.antMatchers(PERMIT_ALL_URL).permitAll()
.anyRequest().authenticated();
}
Then I created my own check based on global permissions.
#Component("securityUtils")
public class SecurityUtils {
public boolean isPermitRestRequest(){
HttpServletRequest r = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String currentUrl = UrlUtils.buildRequestUrl(r);
for(String url: CommonSpringKeycloakTutorialsSecurityAdapter.PERMIT_ALL_URL) {
if(currentUrl.equals(url)) {
return true;
}
}
return false;
}
}
For native validation to work, include a listener
#WebListener
public class MyRequestContextListener extends RequestContextListener {
}

In my team we evaluated several of the answers in this post and they didn't fit to our scenario.
A variation of Johannes Hiemer answer worked for us. We configured Spring Data REST to only expose annotated repositories:
data.rest:
detection-strategy: annotated
Then we defined 2 repositories without hierarchical relationship.
One of the repos will be exposed by adding the #RepositoryRestResource annotation to it. For this one, we deny access to every method by default so auth will have to be specified on a method level to reduce the chances of exposing methods by mistake. For example, initially we extended CrudRepository and didn't want to expose the deletion operation:
#RepositoryRestResource
#PreAuthorize("denyAll()")
interface SomeRestResourceRepository : Repository<SomeEntity, Long> {
}
The repository to be used for internal calls is defined as a regular Spring Data Repository:
interface SomeRepository : Repository<SomeEntity, Long> {
}
We are using spring-boot-starter-data-rest 2.6.3.

Related

Configure Unity container per-request in OWIN middleware

I'm wanting to configure registrations in a Unity container being used by ASP.NET Web API 2 based on properties of a HTTP request. For example, a request to /api/database1/values should result in a Unity container configuration with an IDbContext configured for database1, while a request to /api/database4/values will get an IDbContext configured for database4.
I've gotten so far as using UnityHierarchicalDependencyResolver as the dependency resolver, so types registered with HierarchicalLifetimeManager last only for the lifetime of the request. This works well for getting types resolved per request. But how to get them registered per request using OWIN middleware is beyond me.
In my middleware, a call to System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IUnityContainer)) gets an instance of IUnityContainer, but it's the same container for all requests, including any registrations from previous requests.
By encapsulating UnityHierarchicalDependencyResolver with my own implementation of IDependencyResolver I can see that IDependencyResolver.BeginScope isn't called until much later in the process. So the problem would seem to be that the child container isn't created until Web API wakes up, long after my middleware calls Next(..).
Is there a way I can get the scope of my dependency resolver to start sooner? Is there some other strategy that I'm missing. In case it makes any difference, I'm hosting in IIS, but favouring the OWIN middleware approach.
Update
This isn't an answer, and it's too big for a comment, but after struggling to solve this with Unity I decided to switch to Autofac and it all just fell into place.
The Autofac OWIN packages (Autofac.Mvc5.Owin, Autofac.Owin, Autofac.WebApi2.Owin) make it dead easy to use Autofac within the OWIN pipeline and ensure appropriate lifetime management in ASP.NET MVC and Web API. This was the missing link.
I couldn't find a way to reconfigure the container per-request, but it did at least make it possible to configure a factory per-request (so yes, #Haukinger and #alltej, you were right to push in that direction.
So I register a factory like:
builder.RegisterType<DataDependencyFactory>().InstancePerRequest();
And register the create method of that factory like:
builder
.Register(c => c.Resolve<DataDependencyFactory>().CreateDataDependency())
.As<IDataDependency>()
.InstancePerRequest();
Registering the factory this way is particularly useful, because downstream dependents don't need to be aware of the factory. I like this because my dependents don't need a factory, they need an instance. The container bends to the needs of my dependents, not the other way around :)
Then, in a piece of OWIN middleware, I resolve the factory, and set a property on it according to the properties of the request. Subsequent resolution of IDataDependency in an MVC or Web API controller, or anything else later in the OWIN pipeline, will get an instance configured according to the property on the factory.
Based on your api URL ("/api/database4/values"), I suggest that you create a filter attribute(e.g. DbIdFilter) so that you can reuse the filter attribute to other controller methods that follow similar url path/segment like this below:
[HttpGet]
[DbIdFilter]
[Route("{databaseId}/values")]
public IHttpActionResult GetValues()
{
return Ok();
}
[HttpGet]
[DbIdFilter]
[Route("{databaseId}/products")]
public IHttpActionResult GetProducts()
{
return Ok();
}
First, create the filter attribute:
public class DbIdFilterAttribute : ActionFilterAttribute
{
private readonly string _routeDataId;
private const string defaultRouteName = "databaseId";
public DbIdFilterAttribute():this(defaultRouteName)
{}
public DbIdFilterAttribute(string routeDataId)
{
_routeDataId = routeDataId;
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
var routeData = actionContext.Request.GetRouteData();
var dbId = routeData.Values[_routeDataId] as string;
//here we create the db instance at the filter level.
DbInstanceFactory.RegisterDbInstance(dbId);
}
}
Next, create an instance factory that will register/resolve the db instance during runtime:
public class DbInstanceFactory : IDbInstanceFactory
{
public static IDbInstance RegisterDbInstance(string databaseId)
{
var factory = UnityConfig.GetConfiguredContainer().Resolve<IDbInstanceFactory>();
return factory.CreateInstance(databaseId);
}
public IDbInstance CreateInstance(string databaseId)
{
var container = UnityConfig.GetConfiguredContainer();
//container.RegisterType<IDbInstance, DbInstance>();
container.RegisterType<IDbInstance, DbInstance>(new InjectionConstructor(databaseId));
var dbInstance = container.Resolve<IDbInstance>();
return dbInstance;
}
public IDbInstance GetInstance()
{
var container = UnityConfig.GetConfiguredContainer();
var dbInstance = container.Resolve<IDbInstance>();
return dbInstance;
}
}
public interface IDbInstanceFactory
{
IDbInstance CreateInstance(string databaseId);
IDbInstance GetInstance();
}
Register this factory class in UnityConfig.cs (or wherever you currently register the types):
container.RegisterType<IDbInstanceFactory, DbInstanceFactory>
(new ContainerControlledLifetimeManager());
It's registered ContainerControlledLifetimeManager since this factory does not have to be a per request.
So just a basic DbInstance class below(for clarity) that takes a parameter in the constructor (this parameter can be your connection string or a named connection):
public class DbInstance : IDbInstance
{
public string DbId { get; }
public DbInstance(string databaseId)
{
DbId = databaseId;
}
}
public interface IDbInstance
{
string DbId { get; }
}
In controller class, you can use it like this:
....
private IDbInstanceFactory _dbFactory;
public MyController(IDbInstanceFactory dbFactory)
{
_dbFactory = dbFactory;
}
// Alternate, if you want to use property injection instead of constructor injection
//[Dependency]
//public IDbInstanceFactory DbFactory { get; set; }
[HttpGet]
[DbIdFilter]
[Route("{databaseId}/test")]
public IHttpActionResult Test()
{
var db = _dbFactory.GetInstance();
return Ok(db.DbId);
}
...

Making business domain objects available to Jersey Servlet Context in embedded Jetty server

Using the following dependencies (Gradle):
org.glassfish.jersey.containers:jersey-container-servlet:2.22.2
org.eclipse.jetty:jetty-servlet:9.3.2.v20150730
I have an embedded Jetty server, with a Jersey servlet container... something like this ...
package mypkg.rest.jersey;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.servlet.ServletContainer;
import se.transmode.tnm.alarm.api.AlarmRetrieval;
import mypkg.rest.RestServer;
import mypkg.rest.jersey.serviceImpl.ModelAdapter;
public class JerseyBasedRestServer implements RestServer {
public static final int INITIALIZE_ON_USE = 0;
private Server server;
private final ServletContextHandler context;
private final ServletHolder servlet;
private final ModelAdapter modelAdapter;
public JerseyBasedRestServer(BusinessObjects businessObjects) {
this.modelAdapter = new ModelAdapter(businessObjects); //I want this instance to somehow be available for my ServletContainer to use.
context = new ServletContextHandler(ServletContextHandler.SESSIONS);
servlet = context.addServlet(ServletContainer.class, "/*");
servlet.setInitOrder(INITIALIZE_ON_USE);
servlet.setInitParameter(ServerProperties.PROVIDER_PACKAGES, "mypackage.jersey.generated.api.service");
servlet.setInitParameter(ServerProperties.MEDIA_TYPE_MAPPINGS, "json : application/json");
context.setContextPath("/");
}
private void startServlet() {
try {
servlet.start();
servlet.initialize();
} catch (Exception e) {
log.error("Failed to initialize servlet. {}", e.getMessage());
}
}
#Override
public void init(int port) {
server = new Server(port);
server.setHandler(context);
try {
server.start();
server.join();
startServlet();
} catch (Exception e) {
log.error("Failed to start jetty server for rest interface");
} finally {
server.destroy();
}
}
The Jersey Container will run server code and model generated using the Swagger code-gen tool
https://github.com/swagger-api/swagger-codegen#getting-started
which delivers the generated model, JacksonJsonProvider, and a RestApi class:
package mypackage.jersey.generated.api.service
Path("/")
public class RestApi {
private final RestApiService delegate = new RestApiServiceImpl(); //Integration point of the generated code
#GET
#Path("/list/")
#Consumes({ "application/json" })
#Produces({ "application/json" })
public Response retrieveAlarmList(#Context SecurityContext securityContext) throws NotFoundException {
return delegate.retrieveAlarmList(securityContext);
}
}
To integrate the generated code we are left to implement RestApiServiceImpl ourselves.
The ModelAdapter's job is to convert our business objects to the generated rest model.
So the question is how do I make the instance of the adapter of our business objects, in this case ModelAdapter, which lies outside the context of the Jersey servlet context, available to the RestApi class, or rather the RestApiServiceImpl?
I kind of understood from reading the past 24 hours that I need to use some sort of Context Dependency Injection either through Jetty, Jersey, or some other library (Weld seems to appear a lot), and have tried various combinations of #Inject, #Context, etc etc, but have come to the conclusion that I have no clue what I am actually doing... I'm not even sure I understand enough about the situation to phrase my question correctly.
More info can be made available on request.
Any help is appreciated.
EDIT: added a link here to https://github.com/englishbobster/JersetAndJetty
using #peeskillets suggestions, but still not working.
First thing you need to make DI work, is an AbstractBinder. This is where you will make your objects available to be injected.
class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(modelAdapter).to(ModelAdapter.class);
}
}
Then you need to register the binder with Jersey. The easiest way is to register in Jersey's ResourceConfig. In your case, you are not using one. You are configuring everything in the "web.xml". For that, you should take a look at this post.
If you want to change your configuration to use a ResourceConfig, which personally I'd rather use, you can do this
package com.some.pkg;
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
packages("mypackage.jersey.generated.api.service");
property(ServerProperties.MEDIA_TYPE_MAPPINGS, "json : application/json");
register(new Binder());
}
}
Then to configure it with Jetty, you can do
servlet.setInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS,
"com.some.pkg.JerseyConfig");
Now you can get rid of those other two init-params, as you are configuring it inside the ResourceConfig.
Another way, without any init-params, is to do
ResourceConfig config = new JerseyConfig();
ServletHolder jerseyServlet = new ServletHolder(ServletContainer(config));
context.addServlet(jerseyServlet, "/*");
See full example of last code snippet, here.
Now you can just inject the ModelAdapter pretty much anywhere within Jersey
In a field
#Inject
private ModelAdapter adapter;
Or in a contructor
#Inject
public RestApi(ModelAdapter adapter) {
this.adapter = adapter;
}
Or method parameter
#GET
public Response get(#Context ModelAdapter adapter) {}

Spring websocket implementation

I am creating a websocket server that interfaces with a web service endpoint on one side and another which receives web socket connection requests from multiple clients. Here are two approaches that I found:
Implement a web socket configurer and web socket handler as such:
Configurer
#Configuration
#EnableWebSocket
public class TestConfig implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(testHandler(), "/testHandler")
.addInterceptors(new HttpSessionHandshakeInterceptor())
.withSockJS();
}
#Bean
public WebSocketHandler testHandler() {
return new TestHandler();
}
Handler
public class TestHandler extends TextWebSocketHandler {
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
//Take request params and check if a current subscription to external webservice exists, if yes then directly add this session to a map cache repository with the subscription id as key
//If it is a new request then add session to a map cache repository and make new subscription to the external webservice
}
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
}
Configure a message broker endpoint to be subscribed to called /subscribe
public class TestWebSocketConfig implement WebSocketMessageBrokerConfigurer {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> arg0) {}
#Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> arg0) {}
#Override
public void configureClientInboundChannel(ChannelRegistration arg0) {
System.out.println("");
}
#Override
public void configureClientOutboundChannel(ChannelRegistration arg0) {
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
}
#Override
public boolean configureMessageConverters(List<MessageConverter> arg0) {
return true;
}
#Override
public void configureWebSocketTransport(WebSocketTransportRegistration arg0) {}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/subscribe").withSockJS();
}
Create controller where websocket clients can communicate with
#Controller
public class SubscriptionController {
#Autowired
private SimpMessagingTemplate template;
#MessageMapping("/subscribe1")
#SendTo("/subscribe")
public void addSubscription(String message) {
System.out.println("hi");
}
Here is my question, am I misunderstanding somewhere where these two methods I speak of meant to be combined together? I was using a tomcat implementation of websocket before which matches method 1 which gives me easy direct control over sessions as I would like to be able to reuse web service subscriptions to avoid duplicate request from distinct clients and also a single requests may map to more than one subscription requests to the external webservice. Yet it seems method 2 would push all data requests to the same "/subscribe" endpoint and all connected clients would be receiving the same data, which is not what I am trying to accomplish. It also seems like the message broker api is limited as it does not allow me access to the subscribed sessions where I can control which sessions the receiving data will be sent to. I realized I had to switch to spring websocket as I needed built in browser compatibility fallback offered by SockJS and automatic heartbeat function offered by Stomp.js.
i think i found my answer, method 1 and 2 can be used side by side but not together. Method 2 is used when i want to implement a message broker that can create multiple channel destinations which many users can subscribe to the same destination. Now the question is how i can check whether i can check the number of subscriptions periodically for each existing destination

Extending ActionDescriptorFilterProvider to allow dependency injection of class level filters

Following up on Authorization Filter Dependency Injection with ASP.New MVC 4 Web Api . Is there a way to use dependency injection on filters that are set globally on all controller classes:
config.Filters.Add(new WebApplicationApiAuthorizeAttribute());
It looks like the GetFilters method in the ActionDescriptorFilterProvider only works on method level filters.
public class UnityWebApiFilterAttributeFilterProvider : ActionDescriptorFilterProvider,
System.Web.Http.Filters.IFilterProvider
{
private readonly IUnityContainer _container;
public UnityWebApiFilterAttributeFilterProvider(IUnityContainer container)
{
_container = container;
}
public IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration,
HttpActionDescriptor actionDescriptor)
{
var filters = base.GetFilters(configuration, actionDescriptor);
this.BuildUpAttributes(filters);
return filters;
}
private void BuildUpAttributes(IEnumerable filterInfo)
{
foreach (FilterInfo filter in filterInfo)
{
object o = _container.BuildUp(filter.GetType(), filter);
}
}
}
If you want these global filters to get injected, you will have to resolve them from the container and add them to the filters collection:
GlobalFilters.Filters.Add(container.Resolve<MyFilter>());
Or do something like:
var filter = WebApplicationApiAuthorizeAttribute();
container.BuildUp(filter.Gettype(), filter);
GlobalFilters.Filters.Add(filter);
But one big warning about using global filters. Global filters are... global. Or in IoC terminology: they are singletons. This means that all its dependencies will effectively become singletons as well, which might cause all sorts of concurrency bugs when they are not expected to live for the duration of the application.
So you should only do this when all the filter's direct and indirect dependencies are singletons, which is great if you can do this, but often isn't the case. So another option is to create a proxy that allows resolving the real instance on the fly:
public sealed class UnityActionFilterProxy<TActionFilter> : IActionFilter
where TActionFilter : IActionFilter
{
private readonly IUnityContainer container;
public UnityActionFilterProxy(IUnityContainer container) {
this.container = container;
}
public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext context,
CancellationToken token, Func<Task<HttpResponseMessage>> continuation) {
return this.container.Resolve<TActionFilter>().ExecuteActionFilterAsync(
context, token, continuation);
}
public bool AllowMultiple { get { return false; } }
}
This proxy can be injected as singleton in the global filters collection as follows:
GlobalFilters.Filters.Add(
container.Resolve<UnityActionFilterProxy<MyFilter>>());
The global filters isn't the only place in Web API where the design is a bit... smelly. Take a look at this related question about DelegatingHandlers.

What is the best way of using NLog with MEF?

I am wondering what is the best way to use NLog with Managed Extensibility Framework (MEF)?
I have an application that support plugins using MEF architecture (Import and Exports etc)
I want to add logging capability to my application.
As a logging component I want to use NLog.
What would you recommend?
1. Create a wrapper for NLog, i.e. additional plugin that configures NLog and exports functions like void Log(string level, string message) that other plugins importing
2. Every plugin should have it is own instance of NLog configured and used. (They all would write to the same file actually).
This is an interesting approach, however, it seems to suffer from the drawback that all loggers that are injected (or the one singleton that is injected) will be the same instance (or will have the same name, the name being the name of the NLogLoggingService class. That means that you cannot very easily control the granularity of logging (i.e. turn logging to "Info" level in one class and "Warn" in another class). Also, if you opt to use the call site formatting tokens, you will always get the call site of the call the the NLog logger rather than the call site in your application code.
Here is an abbreviated version of the logger that was linked:
[Export(Services.Logging.LoggingService, typeof(ILoggingService))]
class NLogLoggingService : ILoggingService
{
Logger log; public NLogLoggingService()
{
log = LogManager.GetCurrentClassLogger();
}
public void Debug(object message)
{
log.Debug(message);
}
public void DebugWithFormat(string format, params object[] args)
{
if (args.Length == 0)
{
log.Debug(format);
}
else
{
Debug(string.Format(format, args));
}
}
public bool IsDebugEnabled
{
get
{
return log.IsDebugEnabled;
}
}
}
In the constructor LogManager.GetCurrentClassLogger() is used to get the NLog logger. GetCurrentClassLogger will return a NLog logger that is "named" based on the "current" type, which, in this case, is NLogLoggingService. So, to configure NLog in the app.config file, you will configure based on the that the logger is named "SoapBox.Core.NLogLoggingService". Commonly, in code that uses NLog (or log4net) directly, each class gets its own uniquely named logger like this:
namespace MyNamespace
{
public class MyClass1
{
private static readonly Logger logger LogManager.GetCurrentClassLogger();
public void DoSomeWork()
{
logger.Info("Logging from inside MyClass1.DoSomeWork");
}
}
public class MyClass2
{
private static readonly Logger logger LogManager.GetCurrentClassLogger();
public void DoSomeWork()
{
logger.Info("Logging from inside MyClass2.DoSomeWork");
}
}
}
Now the logging for MyClass1 and MyClass2 is individually controllable. You can configure different levels for each class, send them to different targets, or turn one or both off altogether. Alternatively, due to the concept of logger hierarchies in both log4net and NLog, you could control the logging in both class simultaneously by configuring a "logger" for the namespace (MyNamespace in this case), or any "ancestor" namespace. If there is not a logger configured for the fully qualified typename, then the logging framework essentially moves up the hierarchy by considering the name a dot delimited string and removing the last chunk and checking to see if that logger is configured. So, in this case, we are requesting loggers for MyNamespace.MyClass1 and MyNamespace.MyClass2. I could configure the app.config file to have MyNamespace log at the "info" and write to a file target (appender in log4net-speak). If I did that, then both loggers that I requested via their fully qualified names would inherit the MyNamespace configuration.
With the suggested way of injecting NLog via MEF, you will only have one logger instance, so you cannot configure each class to log differently. Also, as I mentioned earlier, if you opt to log call site information, you will always get "SoapBox.Core.NLogLoggingService" for the class and "Debug" (or DebugWithFormat, or Info, or InfoWithFormat, etc) for the method.
This seems to be an issue with successfully injecting loggers from log4net and NLog. You can see the question that I asked about this very issue a couple of months ago.
Ultimately I was able to figure out how some dependency injection frameworks can successfully inject log4net and NLog loggers that are specific to the class being created (i.e. if the DI framework is instantiating MyClass, which in turn depends on an ILogger interface, then MyClass will get a logger that is essentially equivalent to what would have happened if MyClass requested the logger itself via the LogManager.GetCurrentClassLogger api). Generally "resolvers" in DI/IoC frameworks are given the current context (containing, among other information, the type of the object currently being created). With that type available, it becomes a simple matter of having a logging framework-specific resolver receive that type and pass it along to the logging framework to create a logger appropriate for that type.
In order to get the most out of NLog's (and log4net's) capabilities you would really like to be able to tell MEF that your class is dependendent on "ILogger", but also that the instance of "ILogger" that gets injected into your class should depend on the Type of your class.
I don't know how easy it will be to achieve that with MEF. Alternatively, you could wrap NLog's static LogManager in a ILogManager and inject that. That would deviate from the normal "inject ILogger" paradigm.
To summarize: If you inject NLog via MEF this way, you will indeed be able to log with NLog, but you will only ever have one named logger (SoapBox.Core.NLogLoggingService). This means that you will not be able control with any degree of granularity - either for levels/on/off or for output (NLog Target/log4net Appender)
I don't have a good answer for what to do as far as injecting NLog via MEF AND keeping the granularity/flexibility that "raw" NLog gives you.
I can say that we have decided to use Common.Logging for .NET to abstract the logging framework but we decided NOT to inject logging. Instead, we will just use a static LogManager (as provided by Common.Logging) to hand out loggers.
I think Option 1 is better.
You can take a look at how the open source framework SoapBox Core imports a reference to an ILoggingService using MEF. It also provides a default implementation of the logging service based on NLog, but you could easily swap it out for log4Net, for example.
For reference:
the ILoggingService interface
the Logging Service that wraps NLog and exports itself using MEF
SoapBox Core is LGPL'd, so you might be able to use (this part) in your application.
I have been fighting with this problem a while now.
Really improtant was the Callsite (FullyQualified Namespace) within the logfiles.
First, i tryed to get the right logger out of the Stacktrace:
[MethodImpl(MethodImplOptions.NoInlining)]
private static NLog.Logger GetLogger()
{
var stackTrace = new StackTrace(false);
StackFrame[] frames = stackTrace.GetFrames();
if (null == frames) throw new ArgumentException("Stack frame array is null.");
StackFrame stackFrame;
switch (frames.Length)
{
case 0:
throw new ArgumentException("Length of stack frames is 0.");
case 1:
case 2:
stackFrame = frames[frames.Length - 1];
break;
default:
stackFrame = stackTrace.GetFrame(2);
break;
}
Type declaringType = stackFrame.GetMethod()
.DeclaringType;
return declaringType == null ? LogManager.GetCurrentClassLogger() : LogManager.GetLogger(declaringType.FullName);
}
But sadly, the Stacktrace with MEF is very long and i cannot clearly identify the correct caller for the Requester of the ILogger.
So, instead of injecting the ILogger Interface via Constructor Injection, i have created a ILogFactory Interface, that can get injected via Constructor Injection and call then the Create Method on the Factory
public interface ILogFactory
{
#region Public Methods and Operators
/// <summary>
/// Creates a logger with the Callsite of the given Type
/// </summary>
/// <example>
/// factory.Create(GetType());
/// </example>
/// <param name="type">The type.</param>
/// <returns></returns>
ILogger Create(Type type);
#endregion
}
And implemented it:
using System;
using System.ComponentModel.Composition;
[Export(typeof(ILogFactory))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class LogFactory : ILogFactory
{
#region Public Methods and Operators
public ILogger Create(Type type)
{
var logger = new Logger().CreateLogger(type);
return logger;
}
#endregion
}
With the ILogger:
public interface ILogger
{
#region Public Properties
bool IsDebugEnabled { get; }
bool IsErrorEnabled { get; }
bool IsFatalEnabled { get; }
bool IsInfoEnabled { get; }
bool IsTraceEnabled { get; }
bool IsWarnEnabled { get; }
#endregion
#region Public Methods and Operators
void Debug(Exception exception);
void Debug(string format, params object[] args);
void Debug(Exception exception, string format, params object[] args);
void Error(Exception exception);
void Error(string format, params object[] args);
void Error(Exception exception, string format, params object[] args);
void Fatal(Exception exception);
void Fatal(string format, params object[] args);
void Fatal(Exception exception, string format, params object[] args);
void Info(Exception exception);
void Info(string format, params object[] args);
void Info(Exception exception, string format, params object[] args);
void Trace(Exception exception);
void Trace(string format, params object[] args);
void Trace(Exception exception, string format, params object[] args);
void Warn(Exception exception);
void Warn(string format, params object[] args);
void Warn(Exception exception, string format, params object[] args);
#endregion
}
and Implementation of:
using System;
using NLog;
using NLog.Config;
/// <summary>
/// The logging service.
/// </summary>
public class Logger : NLog.Logger, ILogger
{
#region Fields
private string _loggerName;
#endregion
#region Public Methods and Operators
/// <summary>
/// The get logging service.
/// </summary>
/// <returns>
/// The <see cref="ILogger" />.
/// </returns>
public ILogger CreateLogger(Type type)
{
if (type == null) throw new ArgumentNullException("type");
_loggerName = type.FullName;
var logger = (ILogger)LogManager.GetLogger(_loggerName, typeof(Logger));
return logger;
}
To use it... just inject the ILogFactory and calle the Create Method in a Mefed Importing Constructor:
[ImportingConstructor]
public MyConstructor(
ILogFactory logFactory)
{
_logger = logFactory.Create(GetType());
}
hope this helps
If you create a new ExportProvider and cast the ImportDefinition being passed in to a ICompositionElement. You can get the type that the logger is being injected into.
Here is the ExportProvider
public class LoggerExportProvider : ExportProvider
{
private readonly ExportDefinition _loggerExportDefinition;
private readonly Func<string, ILogger> _loggerFactory;
/// <summary>
/// Initializes a new instance of the <see cref="LoggerExportProvider"/> class.
/// </summary>
/// <param name="loggerFactory">The logger factory function.</param>
public LoggerExportProvider(Func<string, ILogger> loggerFactory)
{
_loggerFactory = loggerFactory;
_loggerExportDefinition = new ExportDefinition(typeof (ILogger).FullName, new Dictionary<string, object> {{"ExportTypeIdentity", typeof (ILogger).FullName}});
}
protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
{
IList<Export> exports = new List<Export>();
var compositionElement = definition as ICompositionElement;
if (compositionElement == null || compositionElement.Origin == null)
return exports;
var constraint = definition.Constraint.Compile();
if (constraint(_loggerExportDefinition))
exports.Add(new Export(_loggerExportDefinition, () => _loggerFactory(compositionElement.Origin.DisplayName)));
return exports;
}
}
This is setup in such a way that it will work with any logging framework as you need to pass in a function that will return an ILogger (the Ilogger is our own, you'll have to create your own interface or just make it specific to Nlog). The string being passed to the function is the full class name that the type is being injected too. (compositionElement.Origin.DisplayName)
An example of bootstrapping MEF with this would look like this:
public class Example
{
[Import]
public ILogger Logger { get; set;}
public Example()
{
var aggregatecatalogue = new AggregateCatalog();
aggregatecatalogue.Catalogs.Add(new AssemblyCatalog(typeof (ILogger).Assembly));
aggregatecatalogue.Catalogs.Add(new AssemblyCatalog(GetType().Assembly));
var container = new CompositionContainer(aggregatecatalogue, new LoggerExportProvider(s => new MockLogger(s)));
container.ComposeParts(this);
}
}
The code above was copied from a unit test, so I'm just add specific assemblies instead of parsing a directory. The MockLogger is an implementation of the ILogger interface that takes the logging class name (or injecting type) as a parameter to it's constructor.
This doesn't require parsing any stack traces and pulls the information that is otherwise sitting there directly out of MEF.

Resources