Can I use Method Security on Spring Rest Repository methods? - spring-security

Let CountryRepo be a Spring RepositoryRestResource
#RepositoryRestResource
public interface CountryRepo implements CrudRepository<Country, Long> { }
for a simple Country domain with country code and common name.
Can I override the methods and introduce method level security to allow only users with ADMIN role to send e.g. POST or DELETE requests?
#RepositoryRestResource
public interface CountryRepo extends CrudRepository<Country, Long> {
#Override
public Iterable<Country> findAll(); // <-- Globally allowed
#Override
#PreAuthorize("hasRole('ADMIN')")
public <S extends Country> S save(S entity); // <-- Only admins can create countries.
// ... Remaining methods from CRUD repository interface.
}
I'm trying to build a simple example to see if it works, but I think I'm having problem elsewhere, so interested in hearing if this at all is possible.

Related

Resolve all already created service instances from .NET service provider

I would like to request all created instances from a transient service via the IServiceProvdier. My problem is that requesting them seems to create additional instances instead of retrieving only the already existing instances.
I have a service interface and implementation
public interface ISomeService {}
public class SomeService : ISomeService
{
public SomeService()
{
}
}
It is registered transient
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ISomeService, SomeService>();
}
Another service where I try to get all already created services
public class AnotherService
{
// calls the constructor of SomeService
//public AnotherService(IEnumerable<ISomeService> instances) {}
public AnotherService(IServiceProvider serviceProvider)
{
// calls the constructor of SomeService
//IEnumerable<ISomeService> instances = serviceProvider.GetServices<ISomeService>();
// calls the constructor of SomeService
//IEnumerable<ISomeService> instances = serviceProvider.GetRequiredServices<ISomeService>();
}
}
I don't know why the constructor of SomeService is called but it definitly seems to do it due to the calls of Get...
Anyone managed to get the list of instances without creating one?
By definition a transient service will always be created each time you request the service provider or the DI system to resolve it.
If you want to reuse the same instances you can register services with a different lifetime
services.AddSingleton<ISomeService, SomeService>();
or
services.AddScoped<ISomeService, SomeService>();
if you register the dependency as a singleton than there will be a single instance of there service for the entire application lifetime.
if you register the dependency with the scoped lifetime than a new instance will be created for each scope. In Asp.Net a scope consists of a request.
If you want your dependency to be transient and want to have track of all the instances that have been created you can do a little trick using static references:
public static class SomeServiceReferences {
public static readonly IList<ISomeService> References { get; } = new List<ISomeService>();
}
public class SomeService: ISomeService {
public SomeService() {
SsomeServiceReferences.References.Add(this);
}
}
but I don't reccomend this approach cause holding the reference of those dependencies might cause performance problems and if you need to do such a thing there might be some problems with the design of your application.

How can I configure multiple authentication managers depending on the incoming URL for an authorization server?

With Spring Security <5.2
In a legacy project, for a password grant scenario, I need to configure an authorization server.
Currently it is done extending AuthorizationServerConfigurerAdapter, and the authorization endpoints are configured overriding the configure(AuthorizationEndpointsServerConfigurer) method.
My problem is that this configurer takes one AuthenticationManager for the password grant, when I would need something like an AuthenticationManagerResolver (but I can't upgrade to 5.2) to be able to apply a different authentication depending on the incoming URL (an authentication manager for admin URLs, e.g. "/admin/**", and another one for non-admin).
How can I do that? I can change the approach, but again I can't upgrade.
You can try implementing your own DelegatingAuthenticationManager, inject the list of your AuthenticationManagers in it, and put your logic in authenticate method. E.g:
#Component("delegatingAM")
public class DelegatingAuthenticationManager implements AuthenticationManager {
private final List<AuthenticationManager> ams;
#Autowire
public DelegatingAuthenticationManager(#Qualifier("x") AuthenticationManager amX, #Qualifier("y") AuthenticationManager amY) {
this.ams = List.of(amX, amY); // Arrays.asList(amX, amY);
// you can inject variables for your conditions here
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (...) this.ams.get(0).authenticate(authentication);
if (...) this.ams.get(0).authenticate(authentication);
// Or you can loop over the list like AuthenticationManager is implemented with AuthenticatioProvider
}
}
Then inject it to AuthorizationServerConfigurerAdapter
#Configuration
#EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("delegatingAM")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.authenticationManager(this.authenticationManager)
}
...
}
Hope it will help, for the worse case, you could start to think about using many AuthorizationServerSecurityFilterChains, one AuthenticationManager for each. And based on the URL, direct the request to the right SecurityFilterChain.

DI parameters to class library without controller

So I'm not sure if I'm just missing something, but basically every example of DI I see with asp.net core shows passing of parameters from the appSettings.json file through a constructor in the controller and then to anything else.
Can I bypass the Controller and just inject directly a Class Library?
For an example of what I'm trying to do, let's say I have appSettings.json with
"EmailSettings":{"smtpServer":"mail.example.com", "port":123, "sendErrorsTo":"errors#example.com"}
Then a Class Library for EmailServices
EmailSettings.cs
public class EmailSettings{
public string smtpServer {get;set;}
public int port {get;set;}
public string sendErrorsTo {get;set;}
}
IEmailService.cs
public interface IEmailService
{
void SendErrorEmail(string method, Exception ex);
}
and EmailService.cs
public class EmailService :IEmailService
{
private readonly EmailSettings _emailSettings;
public EmailService(EmailSettings emailSettings)
{
_emailSettings = emailSettings;
}
public void SendErrorEmail(string method, Exception ex)
{
....
}
}
Startup.cs in the main asp.net core application
public void ConfigureServices(IServiceCollection services)
{
...
services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));
services.AddScoped<IEmailService, EmailService>(p => {
return new EmailService(p.GetService<EmailSettings>());
});
...
}
Without loading the EmailServices or the appsetting.json parameters through the controller and then into the BusinessLayer class library, I want to be able to call the SendErrorEmail from BusinessLayer (or any other place).
DoWork.cs
public MakeItWork()
{
try
{...}
catch (exception ex)
{
IEmailService.SendErrorEmail("BAL - MakeItWork",ex)
}
}
But it just fails with a null exception. The DI in the startup doesn't create the EmailService in place of the IEmailService, and I'm going to guess the parameters are not there either.
Thanks for any help you can give.
----EDIT----
I ended up just switching to using AutoFac for DI. It's able to accomplish what I was looking for. Accepted the answer below to give Phantom the points for trying to assist.
A couple of things:
In your MakeItWork() method, you have code that "calls" a method using the interface name - not even sure how that will compile. You need to use an object of a class that implements that interface to actually make method calls at runtime. For example, in your DoWork class, you could have a constructor requesting for an instance of a class that implements the IEmailService interface and store it for future use in other methods.
Second, in the Services collection, you are adding a "Scoped" dependency (in the ConfigureServices method). A "scoped" dependency is only created upon a (http)Request, typically via calls to controllers. From your code and your explanation, it looks like you are wanting to add a Singleton object for your IEmailService interface. So, instead of adding a Scoped dependency use AddSingleton - as you have done, you can also create the specific object in the call to AddSingleton - that means this object will be provided every time you request it (through class constructors, for example). If you are using it as a singleton, you should also make sure that it is thread safe. Alternatively, you can also add the dependency using AddTransient - if you use this, a new object will be created every time you request it.
Update:
Sample Code
Modify your ConfigureServices to make the EmailService as Transient (this means a new object every time this service is requested):
public void ConfigureServices(IServiceCollection services)
{
...
services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));
services.AddTransient<IEmailService, EmailService>();
...
}
Your "DoWork" class should request the EMail Service in the constructor:
public class DoWork()
{
private IEmailService _emailService;
//Dependency should be injected here
public DoWork(IEmailService emailService)
{
_emailService = emailService;
}
public MakeItWork()
{
try
{...}
catch (exception ex)
{
//Use the saved email service object to do your work
_emailService.SendErrorEmail("BAL - MakeItWork", ex)
}
}
}
It doesn't end here. The question remains as to how you are going to create an Object of the DoWork class. For this, one idea is to create an interface for the DoWork class itself and then setup the container for that interface as well. Then wherever you would want to use the DoWork implementation you can "request" the interface for DoWork. Or use the container directly to create an instance.

Dropwizard and Guice: injecting Environment

I am currently building a Dropwizard + Guice + Jersey-based application where the database access is being handled by JDBI for the time being.
What I am trying to achieve is to have your typical enterprise architecture, where Resources access Service classes accessing a DAO class that in turn accesses the database. It would be nice to get all this wired up in a proper DI way, although I guess I can build my object graph in the run() method of the application if all else fails.
So, I'm running into this problem that has been mentioned here before: Getting a DBIFactory requires both the Environment and the Configuration, which somehow need to be available at the time when Guice does its injection magic and not at run()-time.
Being a Dropwizard and Guice noob, what I've managed to put together so far is that I need a Provider for my DAO objects, something to the tune of
public class UserDAOProvider implements Provider<UserDAO> {
#Inject
Environment environment;
#Inject
Configuration configuration;
#Override
public UserDAO get() {
final DBIFactory factory = new DBIFactory();
final (MyConfiguration) config = (MyConfiguration) configuration;
DBI jdbi = null;
try {
jdbi = factory.build(environment, config.getDataSourceFactory(),
"mysql");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return jdbi.onDemand(UserDAO.class);
}
}
Registering this as a singleton provider should let me then inject the UserDAO into my Services.
Now, how do we actually get the environment injected into the Provider? Currently I am stuck at Guice complaining about not finding a suitable constructor for the Environment, so it is trying to instantiate it and not grab it from Dropwizard itself.
It seems like this is doable; there is the dropwizard-guice package whose DropWizardEnvironmentModule is, I think, what I need. But I feel like I'm just missing some piece of the puzzle here for an understanding of how to put things together. I've not managed to find a complete working example so far...
I had the same issue as OP but using Hibernate rather than JDBI. My simple solution is applicable to JDBI, though - just switch DBIFactory for SessionFactory.
First add an injection provider for a singleton SessionFactory in your Guice module:
public class MyModule extends AbstractModule {
private SessionFactory sessionFactory;
#Override
protected void configure() {
}
#Provides
SessionFactory providesSessionFactory() {
if (sessionFactory == null) {
throw new ProvisionException("The Hibernate session factory has not yet been set. This is likely caused by forgetting to call setSessionFactory during Application.run()");
}
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
}
You need to set the singleton SessionFactory from your application's run() method. In your case, using JDBI, this is where you would create and configure your DBIFactory before handing it over to the Guice module:
public void run(MyConfiguration configuration, Environment environment) {
myModule.setSessionFactory(hibernateBundle.getSessionFactory());
...
}
Now SessionFactory can be injected wherever it is needed. I now use implicit binding for my DAO classes by just annotating the constructor with #Inject and injecting the SessionFactory singleton. I don't explicitly create providers for DAO classes:
#Singleton
public class WidgetDAO extends AbstractDAO<App> {
#Inject
public WidgetDAO(SessionFactory factory) {
super(factory);
}
public Optional<Widget> findById(Long id) {
return Optional.fromNullable(get(id));
}
...
}
Now I can inject my DAO singleton instances into resources:
#Path("/widgets")
#Produces(MediaType.APPLICATION_JSON)
public class WidgetsResource {
private final WidgetDAO widgetDAO;
#Inject
public WidgetsResource(WidgetDAO widgetDAO) {
this.widgetDAO = widgetDAO;
}
...
}
Note that this approach follows the Guice recommendation of injecting direct dependencies only. Don't try to inject Envrionment and Configuration just so that you can create a DBI factory - inject the prebuilt DBI factory itself.
This is how I use Guice with Dropwizard. Inside your run() method add the line
Guice.createInjector(new ConsoleModule());
You cannot inject Environ
Create the class ConsoleModule
public class ConsoleModule extends AbstractModule {
//configuration and env variable declaration
public ConsoleModule(ConsoleConfiguration consoleConfig, Environment env)
{
this.consoleConfig = consoleConfig;
this.env= env;
}
protected void configure()
{
//You should not inject Configuration and Environment in your provider since you are mixing
//dropwizard framework stuff with Guice.Neverthless you will have to bind them in the below order
bind(Configuration.class).toInstance(consoleConfig.class);
bind(Environment.class).toInstance(env.class);
bind(UserDAO.class).toProvider(UserDAOProvider.class).in(Singleton.class);
}
}
We have the same configuration (dw-jdbi-guice) and also an abstract 'base' Application class which complicates things even more.
Since a lot of things happen during run method, and many things depend on the configuration objects we ended up creating the injector in the run method. But since we need objects from bootsrap also (e.g. ObjectMapper), we ended up having a List<Module> field in the Application class. Not the prettiest solution but can handle variety of scenarios.

Lookup LDAP entries from inside LdapAuthoritiesPopulator

I'm using Spring Security and spring-security-ldap for authentication in a Spring Boot application.
I want to implement an LdapAuthoritiesPopulator that looks up some entries from the LDAP server to decide the roles of the user. These entries are not under the user, so the userData object provided to getGrantedAuthorities method is not enough. (I have no control over the LDAP server so I cannot add the roles to user entries).
I thought about injecting the ContextSource that Spring Security creates when calling auth.ldapAuthentication().contextSource() (see (1) in the code below) into the LdapAuthoritiesPopulator. But that doesn't work because the LdapAuthoritiesPopulator is created before the WebSecurityConfigurerAdapter, and so the ContextSource does not exist yet.
My WebSecurityConfigurerAdapter contains the following method:
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication()
.contextSource() // (1)
.url("ldap://example.org/o=organization")
.and()
.ldapAuthoritiesPopulator(ldapAuthoritiesPopulator)
.userSearchFilter("uid={0}");
}
ldapAuthoritiesPopulator is autowired, the class is currently a dummy implementation that simply adds ROLE_USER:
#Component
public class CustomLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {
#Override
public Collection<? extends GrantedAuthority> getGrantedAuthorities(
DirContextOperations userData, String username) {
// I want to look up some entries in LDAP here, so I need a ContextSource
// but I can't inject it because it does not exist when this bean is created
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("USER"));
return authorities;
}
}
You don't need the roles populated till after the application is running (I suppose). So one thing that would work would be to inject the ApplicationContext instead of the ContextSource and look up the latter lazily. If that works there might be a truck with #Lazy.

Resources