/* * Copyright (C) 2015 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package keywhiz; import com.codahale.metrics.MetricRegistry; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.google.inject.Guice; import com.google.inject.Injector; import io.dropwizard.Application; import io.dropwizard.java8.Java8Bundle; import io.dropwizard.jersey.setup.JerseyEnvironment; import io.dropwizard.setup.Bootstrap; import io.dropwizard.setup.Environment; import javax.sql.DataSource; import keywhiz.auth.mutualssl.ClientCertificateFilter; import keywhiz.auth.xsrf.XsrfServletFilter; import keywhiz.commands.AddUserCommand; import keywhiz.commands.DbSeedCommand; import keywhiz.commands.GenerateAesKeyCommand; import keywhiz.commands.MigrateCommand; import keywhiz.commands.PreviewMigrateCommand; import keywhiz.service.filters.CookieRenewingFilter; import keywhiz.service.filters.SecurityHeadersFilter; import keywhiz.service.providers.AuthResolver; import keywhiz.service.providers.AutomationClientAuthFactory; import keywhiz.service.providers.ClientAuthFactory; import keywhiz.service.providers.UserAuthFactory; import keywhiz.service.resources.SecretDeliveryResource; import keywhiz.service.resources.SecretsDeliveryResource; import keywhiz.service.resources.StatusResource; import keywhiz.service.resources.admin.*; import keywhiz.service.resources.automation.AutomationClientResource; import keywhiz.service.resources.automation.AutomationEnrollClientGroupResource; import keywhiz.service.resources.automation.AutomationGroupResource; import keywhiz.service.resources.automation.AutomationSecretAccessResource; import keywhiz.service.resources.automation.AutomationSecretResource; import keywhiz.service.resources.automation.v2.ClientResource; import keywhiz.service.resources.automation.v2.GroupResource; import keywhiz.service.resources.automation.v2.SecretResource; import org.flywaydb.core.Flyway; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Starting point for Keywhiz, an implementation of the Dropwizard Service. */ public class KeywhizService extends Application<KeywhizConfig> { /** * Entry point into application. * @param args passed onto {@link #run(String[])} * @throws Exception for any uncaught application exception */ public static void main(String[] args) throws Exception { new KeywhizService().run(args); } private static final Logger logger = LoggerFactory.getLogger(KeywhizService.class); private Injector injector; @SuppressWarnings("unused") public Injector getInjector() { return injector; } public void setInjector(Injector injector) { this.injector = injector; } @Override public String getName() { return "keywhiz"; } @Override public void initialize(Bootstrap<KeywhizConfig> bootstrap) { customizeObjectMapper(bootstrap.getObjectMapper()); logger.debug("Registering commands"); bootstrap.addCommand(new PreviewMigrateCommand()); bootstrap.addCommand(new MigrateCommand()); bootstrap.addCommand(new DbSeedCommand()); bootstrap.addCommand(new GenerateAesKeyCommand()); bootstrap.addCommand(new AddUserCommand()); logger.debug("Registering bundles"); bootstrap.addBundle(new Java8Bundle()); } @SuppressWarnings("unchecked") @Override public void run(KeywhizConfig config, Environment environment) throws Exception { if (injector == null) { logger.debug("No existing guice injector; creating new one"); injector = Guice.createInjector(new ServiceModule(config, environment)); } JerseyEnvironment jersey = environment.jersey(); logger.debug("Registering resource filters"); jersey.register(injector.getInstance(ClientCertificateFilter.class)); logger.debug("Registering servlet filters"); environment.servlets().addFilter("security-headers-filter", injector.getInstance(SecurityHeadersFilter.class)) .addMappingForUrlPatterns(null, /* Default is for requests */ false /* Can be after other filters */, "/*" /* Every request */); jersey.register(injector.getInstance(CookieRenewingFilter.class)); environment.servlets().addFilter("xsrf-filter", injector.getInstance(XsrfServletFilter.class)) .addMappingForUrlPatterns(null /* Default is for requests */, false /* Can be after other filters */, "/admin/*" /* Path to filter on */); logger.debug("Registering providers"); jersey.register(new AuthResolver.Binder(injector.getInstance(ClientAuthFactory.class), injector.getInstance(AutomationClientAuthFactory.class), injector.getInstance(UserAuthFactory.class))); logger.debug("Registering resources"); jersey.register(injector.getInstance(ClientResource.class)); jersey.register(injector.getInstance(ClientsResource.class)); jersey.register(injector.getInstance(GroupResource.class)); jersey.register(injector.getInstance(GroupsResource.class)); jersey.register(injector.getInstance(MembershipResource.class)); jersey.register(injector.getInstance(SecretsDeliveryResource.class)); jersey.register(injector.getInstance(SecretResource.class)); jersey.register(injector.getInstance(SecretsResource.class)); jersey.register(injector.getInstance(SecretDeliveryResource.class)); jersey.register(injector.getInstance(SessionLoginResource.class)); jersey.register(injector.getInstance(SessionLogoutResource.class)); jersey.register(injector.getInstance(SessionMeResource.class)); jersey.register(injector.getInstance(AutomationClientResource.class)); jersey.register(injector.getInstance(AutomationGroupResource.class)); jersey.register(injector.getInstance(AutomationSecretResource.class)); jersey.register(injector.getInstance(AutomationEnrollClientGroupResource.class)); jersey.register(injector.getInstance(AutomationSecretAccessResource.class)); jersey.register(injector.getInstance(StatusResource.class)); ManualStatusHealthCheck mshc = new ManualStatusHealthCheck(); environment.healthChecks().register("manualStatus", mshc); environment.admin().addServlet("manualStatus", new ManualStatusServlet(mshc)).addMapping("/status/*"); validateDatabase(config); logger.debug("Keywhiz configuration complete"); } private void validateDatabase(KeywhizConfig config) { logger.debug("Validating database state"); DataSource dataSource = config.getDataSourceFactory() .build(new MetricRegistry(), "flyway-validation-datasource"); Flyway flyway = new Flyway(); flyway.setDataSource(dataSource); flyway.setLocations(config.getMigrationsDir()); flyway.validate(); } /** * Customizes ObjectMapper for common settings. * * @param objectMapper to be customized * @return customized input factory */ public static ObjectMapper customizeObjectMapper(ObjectMapper objectMapper) { objectMapper.registerModules(new Jdk8Module()); objectMapper.registerModules(new JavaTimeModule()); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); return objectMapper; } }