Accessing Dropwizard config from Initialize()? - dropwizard

I am trying to create a scheduled job for Dropwizard that rans every minute and queries a database for values.
For that to work I need to register some DAOs and a service in the initialize() stage, like so:
#Override
public void initialize(final Bootstrap<KMDServiceConfiguration> bootstrap) {
//JDBI
final DBIFactory factory = new DBIFactory();
final DateTimeArgumentFactory dateTimeFactory = new DateTimeArgumentFactory();
final DBI jdbi = factory
.build(environment, config.getDataSourceFactory(), "kmd");
jdbi.registerArgumentFactory(dateTimeFactory);
//DAOs
final UserDao userDao = jdbi.onDemand(UserDao.class);
final NotificationDao notificationDao = jdbi.onDemand(NotificationDao.class);
//Domain services
final UserService userService = new UserServiceImpl(userDao);
final NotificationService notificationService = new NotificationServiceImpl(notificationDao);
final MessageService messageService = new MessageServiceImpl(userService, notificationService);
final Job worker= new MessageWorker(messageService);
bootstrap.addBundle(new JobsBundle(worker));
JDBI requires the Dropwizard environment and the configuration object.
Is it possible to access these at this stage so that my dependencies can be properly setup?

You should be able to initialize the job from the "run" method in the DW Application class, which has access to the configuration and environment attributes. I have put together a simple project to show this but this is how the run method looks like:
#Override
public void run(SOQ49638642Configuration configuration, Environment environment) throws Exception {
final DBI dbi = (new DBIFactory()).build(environment, configuration.getDataSourceFactory(), "kmd");
final SOQ49638642Job soq49638642Job = new SOQ49638642Job(new SOQ49638642Service(dbi.onDemand(SOQ49638642Dao.class)));
final JobsBundle jobsBundle = new JobsBundle(soq49638642Job);
jobsBundle.run(configuration, environment);
}
As far as I know, it is not possible to get those objects in the "initialize" phase. You can also have a look at Guice integrations for DW like dropwizard-guicey which provide nice mechanisms for DI of Bundles and Modules in DW.

Related

Single resource server with multiple authorisation servers, one for each tenant

I am working on a Spring Boot application, which is basically a resource server. As of now, my application has one tenant, which gets authenticated with an authorization server, external to my application.
In order to achieve the same, as of now, I have made the following changes in my application:
config changes are as following:
spring.security.oauth2.client.registration.tenant1.client-id=abcd
spring.security.oauth2.client.registration.tenant1.client-authentication-method=basic
spring.security.oauth2.client.registration.tenant1.authorization-grant-type=authorization_code
myapp.oauth2.path=https://external.authorization.server/services/oauth2/
spring.security.oauth2.client.provider.tenant1.token-uri=${myapp.oauth2.path}token
spring.security.oauth2.client.provider.tenant1.authorization-uri=${myapp.oauth2.path}authorize
spring.security.oauth2.client.provider.tenant1.user-info-uri=${myapp.oauth2.path}userinfo
spring.security.oauth2.client.provider.tenant1.user-name-attribute=name
As of now, I am fetching client secrets from Vault, so I had to define the OAuth2 configuration as follows:
#EnableConfigurationProperties(OAuth2ClientProperties.class)
#Conditional(ClientsConfiguredCondition.class)
#Configuration
public class OAuth2Configuration {
static final String OAUTH2_CLIENT_SECRET_KEY = "oauth2_client_secret";
private static final Logger log = LoggerFactory.getLogger(OAuth2Configuration.class);
private static final String OAUTH2_REGISTRATION_MISSING =
"oAuth2 registration properties are missing";
private final ApplicationSecretProvider applicationSecretProvider;
private final Map<String, ClientAuthenticationMethod> clientAuthenticationMethodMap =
new HashMap<>();
private final String authenticationMethod;
public OAuth2Configuration(
#Value("${spring.security.oauth2.client.registration.tenant1.client-authentication-method}")
final String authenticationMethod,
final ApplicationSecretProvider applicationSecretProvider) {
this.authenticationMethod = authenticationMethod;
this.applicationSecretProvider = applicationSecretProvider;
this.clientAuthenticationMethodMap
.put(ClientAuthenticationMethod.POST.getValue(), ClientAuthenticationMethod.POST);
this.clientAuthenticationMethodMap
.put(ClientAuthenticationMethod.BASIC.getValue(), ClientAuthenticationMethod.BASIC);
this.clientAuthenticationMethodMap
.put(ClientAuthenticationMethod.NONE.getValue(), ClientAuthenticationMethod.NONE);
}
#Bean
public InMemoryClientRegistrationRepository getClientRegistrationRepository(
OAuth2ClientProperties properties) {
List<ClientRegistration> registrations = new ArrayList<>(
OAuth2ClientPropertiesRegistrationAdapter.getClientRegistrations(properties).values());
//We will have only one client registered for oAuth
if (CollectionUtils.isEmpty(registrations)) {
log.error(OAUTH2_REGISTRATION_MISSING);
throw new IllegalStateException(OAUTH2_REGISTRATION_MISSING);
}
ClientRegistration registration = registrations.get(0);
ClientRegistration.Builder builder = ClientRegistration.withClientRegistration(registration);
ClientAuthenticationMethod clientAuthenticationMethod =
getClientAuthenticationMethod(authenticationMethod);
ClientRegistration completeRegistration = builder
.clientSecret(applicationSecretProvider.getSecretForKey(OAUTH2_CLIENT_SECRET_KEY))
.clientAuthenticationMethod(clientAuthenticationMethod)
.build();
return new InMemoryClientRegistrationRepository(completeRegistration);
}
protected ClientAuthenticationMethod getClientAuthenticationMethod(String grantType) {
ClientAuthenticationMethod retValue = clientAuthenticationMethodMap.get(grantType);
if (retValue == null) {
return ClientAuthenticationMethod.NONE;
}
return retValue;
}
}
Then I extended DefaultOAuth2UserService in order to save user details in my application as follows:
#Component
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private UserRepository userRepository;
private AuthorityRepository authRepository;
#Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Autowired
public void setAuthorityRepository(AuthorityRepository
authorityRepository) {
this.authorityRepository = authorityRepository;
}
#Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) {
DefaultOAuth2User oAuth2User = (DefaultOAuth2User) super.loadUser(userRequest);
Collection<GrantedAuthority> authorities = new HashSet<>(oAuth2User.getAuthorities());
Map<String, Object> attributes = oAuth2User.getAttributes();
...
return new DefaultOAuth2User(authorities, oAuth2User.getAttributes(), userNameAttributeName);
}
}
Security configuration is as follows:
#EnableWebSecurity
#Import(SecurityProblemSupport.class)
#ConditionalOnProperty(
value = "myapp.authentication.type",
havingValue = "oauth",
matchIfMissing = true
)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final CustomOAuth2UserService customoAuth2UserService;
public SecurityConfiguration(CustomOAuth2UserService customoAuth2UserService) {
this.customoAuth2UserService = customoAuth2UserService;
}
public void configure(HttpSecurity http) throws Exception {
http
.csrf()
.authorizeRequests()
.antMatchers("/login**").permitAll()
.antMatchers("/manage/**").permitAll()
.antMatchers("/api/auth-info").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/info").permitAll()
.antMatchers("/management/prometheus").permitAll()
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
.anyRequest().authenticated()
//.and().oauth2ResourceServer().jwt()
.and()
//.and()
.oauth2Login()
.redirectionEndpoint()
.baseUri("/oauth2**")
.and()
.failureUrl("/api/redirectToHome")
.userInfoEndpoint().userService(customoAuth2UserService);
http.cors().disable();
}
}
Now, I would like to onboard multiple tenants using OAuth2 as well. Say I want to onboard another tenant tenant2. In order to achieve this, I think, I need to do the following changes in the existing code base as follows:
adding config entries in the properties file as above:
spring.security.oauth2.client.registration.tenant2.client-id=efgh
spring.security.oauth2.client.registration.tenant2.client-authentication-method=basic
spring.security.oauth2.client.registration.tenant2.authorization-grant-type=authorization_code
spring.security.oauth2.client.provider.tenant2.token-uri=${myapp.oauth2.path}token
spring.security.oauth2.client.provider.tenant2.authorization-uri=${myapp.oauth2.path}authorize
spring.security.oauth2.client.provider.tenant2.user-info-uri=${myapp.oauth2.path}userinfo
spring.security.oauth2.client.provider.tenant2.user-name-attribute=name
I need to do changes in the security configuration class:
SecurityConfiguration and OAuth2 configuration class OAuth2Configuration as well. But I am not able to understand what should I add there in order to make my applications work seamlessly for multiple tenants.
In this context, I found this related post: Dynamically register OIDC client with Spring Security OAuth in a multi-tenant stup, but could not get any concrete idea regarding what changes should I do in the existing code base to make my application work in multi-tenancy set up.
Could anyone please help here?
I think there's a bit of confusion that it might help to clear up.
First, it seems that you are not actually building a resource server, as a resource server would require an access token for authentication. Using .oauth2Login() is for either OAuth 2.0 or OpenID Connect 1.0 login, which is a regular application in most respects except how you log in. You still have a browser session after login is successful, which you would not have in a resource server.
Second, configuring a static number of client registrations isn't really quite the same as building a multi-tenant application. Perhaps you're building up to that later, by demonstrating two clients. When configuring two clients using static configuration properties, nothing is really different from a single configuration, other than that there are two possible registrationIds.
Start by building a simple hello world application, such as the OAuth 2.0 Login Sample. If you add a second client registration to your properties, you'll notice that the auto-generated login page (/login) simply shows two links, one for each client. See docs for more on this.
The default URI for initiating the authorization_code flow is /oauth2/authorization/{registrationId}, which means navigating to /oauth2/authorization/abcd launches the first client's login flow. Navigating to /oauth2/authorization/efgh launches the second client's login flow. There's not really anything else needed to support multiple login clients other than understanding how to initiate login.
If you wish to support a fully multi-tenant login configuration, you would then provide a custom ClientRegistrationRepository, which you have done. The only difference is that you should no longer seek to configure clients through the Spring Boot properties, as that seems to be the point that is confusing in your example. If you want to use properties for some of the configuration, create your own configuration properties for your custom repository implementation. Typically at that point, all of this configuration would come from a database.
I would start with that progression (hello world, two statically configured clients, custom ClientRegistrationRepository) then proceed to add other custom components. It will help illustrate the differences at each point.

How-to configure IConfigurationRoot and Microsoft.Azure.WebJobs.ExecutionContext for dependency injection?

I have an Azure Functions app. I would like my code to be able to access settings configured for the Azure Functions app. I am using Microsoft.Extensions.DependencyInjection for dependency injection.
To access configuration data, I am using this code:
[FunctionName("MyEndpoint")]
public static Task<IActionResult> MyFuncAsync(ExecutionContext context)
{
IConfigurationRoot config = new ConfigurationBuilder()
.SetBasePath(context.FunctionAppDirectory)
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
}
This works great in the function itself. However, I would like to be able to injection IConfigurationRoot into my classes. I can't figure out how set up the dependency injection container so that it will create the IConfigurationRoot class using the execution context injected into the Azure function method. I'd like to be able to create a class like this:
public class MyWorkerClass(IConfigurationRoot config)
{
...
}

Configure OAuthAuthorizationServerProvider via Autofac 4.0

I must integrate Oauth with Autofac. But something goes wrong. I think I understand why, but I don't know how to solve it. I let you see my code.
My Autofac Config
{
builder.RegisterType<CustomAuthorizationServerProvider>()
.PropertiesAutowired()
.SingleInstance();
builder.RegisterType<MyBusinessObj>()
.As<IMyBusinessObj>()
.InstancePerRequest()
.PropertiesAutowired();
//IMySessionObj is a prop inside MyBusinessObj
builder.RegisterType<MySessionObj>()
.As<IMySessionObj>()
.InstancePerRequest()
.PropertiesAutowired();
//IMyUnitOfWorkObjis a prop inside MyBusinessObj
builder.RegisterType<MyUnitOfWorkObj>()
.As<IMyUnitOfWorkObj>()
.InstancePerRequest();
...
}
Startup.cs
I have the classic configuration plus the resolution of my authorizationServerProvider.. As you can see, I resolve it in the container... because it is a singleton.
app.UseAutofacMiddleware(_container);
app.UseAutofacWebApi(config);
var oauthServerOptions = new OAuthAuthorizationServerOptions
{
...,
Provider = _container.Resolve<CustomAuthorizationServerProvider>()
};
app.UseOAuthAuthorizationServer(oauthServerOptions);
app.UseWebApi(config);
CustomAuthorizationServerProvider.cs
This is how I have implemented my CustomAuthorizationServerProvider.
public class CustomAuthorizationServerProvider: OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
var autofacLifetimeScope = OwinContextExtensions.GetAutofacLifetimeScope(context.OwinContext);
var myBusinessObj = autofacLifetimeScope.Resolve<IMyBusinessObj>();
var xxx = myBusinessObj.DoWork();
...
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var myBusinessObj = autofacLifetimeScope.Resolve<IMyBusinessObj>();
var xxx = myBusinessObj.DoWork();
...
context.Validated(ticket);
}
}
Here I solve my IMyBusinessObj in a lifetimescope, not in the container. This object is responsible (indirectly) to connect to db, access the session, access the cache and so on... so it cannot be a singleton.
I need it would have a lifetime per request.
So here the problems.. Two problems there are in my configuration.
I have a InstancePerRequest object inside a SingleInstance object. I cannot do that. Troubleshooting Per-Request Dependencies
I effectively cannot have a InstancePerRequest object when I configure oauth in the startup... because in that context does not exist a request yet.
So.. I have understood which are my problems.
Any idea or tips?
Thank you
I was having a very similar issue if not the same. I was able to resolve it by doing the following:
1. Do not resolve IOAuthAuthorizationServerProvider directly from the container. Instead, use IDependencyResolver. In your example:
var oauthServerOptions = new OAuthAuthorizationServerOptions
{
...,
Provider = (IOAuthAuthorizationServerProvider)resolver.GetService(typeof(IOAuthAuthorizationServerProvider)),
};
This requires that you have correctly configured HttpConfiguration.DependencyResolver:
HttpConfiguration config = new HttpConfiguration();
... // Configure unrelated
var builder = new ContainerBuilder();
... // set Autofac registrations
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
// THIS is where you configure OAuthAuthorizationServerOptions, using IDependencyResolver to resolve IOAuthorizationServerProvider instead of IContainer
ConfigureAuth(app, config.DependencyResolver);
2. I was using dynamic assembly scanning to configure certain Autofac module registrations. I inadvertently was scanning for classes ending with "Provider" and registering them as RequestPerInstance(). This was a problem for me, as it was re-registering my IOAuthAuthorizationServerProvider as a per-request instance, which is unable to be resolved during Startup() code.
Problem code which I replaced with individual registrations:
builder.RegisterAssemblyTypes(executingAssembly)
.Where(t => t.Name.EndsWith("Provider"))
.AsImplementedInterfaces()
.InstancePerRequest();
3. Check properties of MySessionObj, and see what dependencies Autofac is resolving (if any). Make sure that you are explicitly registering dependencies as InstancePerRequest().
By using OwinContextExtensions.GetAutofacLifetimeScope(context.OwinContext);, you are resolving dependencies to the "AutoFacWebRequest" lifetime scope, and not the root scope. As a result, my understanding is dependencies resolved to this lifetime scope should be disposed after the request has finished and shouldn't cause future problems with your application.
4. I am not sure what properties are being autowired for your CustomAuthorizationServerProvider object, but assuming you did not post a complete code sample for this class, I would try removing PropertiesAutowired() from it's Autofac registration and inside the object continue to use OwinContextExtensions.GetAutofacLifetimeScope(context.OwinContext); to resolve these other properties if possible.
Could you provide specific detail about what the error or error message you are observing is?

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.

How to force an embedded Grizzly web application context to start

I use an embedded Grizzly web server to host RESTful web-services with Jersey. This is all working correctly.
My question is how to force the web application context to eagerly initialise when I start the server rather than waiting for the first incoming client request. This is a minor problem, but one I would like to solve.
I start the embedded server like this:
public final class TestApplication {
public TestApplication() throws Exception {
HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(
"http://0.0.0.0:8888",
new ResourceConfig()
.registerInstances(
new TestBinder(),
)
.registerClasses(
JacksonJsonProvider.class,
)
.packages(
AbstractResource.class.getPackage().getName()
),
true
);
}
}
The "TestBinder" configures the dependency injection that I need and that class looks like this:
final class TestBinder extends AbstractBinder {
#Override
protected void configure() {
bind(CatalogManager.class).to(CatalogManager.class).in(Singleton.class);
}
}
Finally, "CatalogManager" is a singleton that is used to pre-load and cache all of the static data that my application exposes via the RESTful web-services.
The essence of "CatalogManager" is this:
public final class CatalogManager {
#PostConstruct
public void initialise() {
// Load and cache a large application data-set here...
}
}
So the problem is that the dependency injection and consequently the #PostConstruct method do not run when the server starts up, instead it waits until the first application request and the first user of my application gets a long delay.

Resources