diff --git a/pom.xml b/pom.xml index ba5449b..fc2daba 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,14 @@ dropwizard-hibernate ${dropwizard.version} - + + + io.dropwizard + dropwizard-auth + ${dropwizard.version} + + + diff --git a/src/main/java/someshbose/github/io/HelloWorldApplication.java b/src/main/java/someshbose/github/io/HelloWorldApplication.java index 4ecb3fc..6986144 100644 --- a/src/main/java/someshbose/github/io/HelloWorldApplication.java +++ b/src/main/java/someshbose/github/io/HelloWorldApplication.java @@ -3,9 +3,13 @@ import com.google.inject.Guice; import com.google.inject.Injector; import io.dropwizard.Application; +import io.dropwizard.auth.AuthDynamicFeature; +import io.dropwizard.auth.AuthValueFactoryProvider; +import io.dropwizard.auth.basic.BasicCredentialAuthFilter; import io.dropwizard.setup.Bootstrap; import io.dropwizard.setup.Environment; import lombok.extern.slf4j.Slf4j; +import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature; import someshbose.github.io.app.config.HelloWorldApplicationModule; import someshbose.github.io.app.config.HelloWorldApplicationConfiguration; import someshbose.github.io.app.controller.HelloWorldApplicationResource; @@ -14,7 +18,10 @@ import io.dropwizard.migrations.MigrationsBundle; import io.dropwizard.hibernate.HibernateBundle; import someshbose.github.io.app.controller.PersonResource; -import someshbose.github.io.model.Person; +import someshbose.github.io.domain.model.Person; +import someshbose.github.io.domain.model.User; +import someshbose.github.io.infra.auth.AppAuthorizer; +import someshbose.github.io.infra.auth.AppBasicAuthenticator; @Slf4j public class HelloWorldApplication extends Application { @@ -61,7 +68,7 @@ public void run(HelloWorldApplicationConfiguration configuration, Environment en Injector injector = Guice.createInjector(new HelloWorldApplicationModule(configuration,hibernate)); registerResources(injector,environment,configuration); registerHealthCheck(environment,configuration); - + registerAuthentication(environment); } private void registerResources(Injector injector, Environment environment, HelloWorldApplicationConfiguration configuration){ @@ -72,5 +79,16 @@ private void registerResources(Injector injector, Environment environment, Hello private void registerHealthCheck(Environment environment, HelloWorldApplicationConfiguration configuration){ environment.healthChecks().register("HelloWorldApplicationHealthCheck", new HelloWorldApplicationHealthCheck(configuration.getTemplate())); } + + /****** Dropwizard security - custom classes ***********/ + private void registerAuthentication(Environment environment){ + environment.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder() + .setAuthenticator(new AppBasicAuthenticator()) + .setAuthorizer(new AppAuthorizer()) + .setRealm("BASIC-AUTH-REALM") + .buildAuthFilter())); + environment.jersey().register(RolesAllowedDynamicFeature.class); + environment.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class)); + } } diff --git a/src/main/java/someshbose/github/io/app/controller/HelloWorldApplicationResource.java b/src/main/java/someshbose/github/io/app/controller/HelloWorldApplicationResource.java index 6ca381f..898a974 100644 --- a/src/main/java/someshbose/github/io/app/controller/HelloWorldApplicationResource.java +++ b/src/main/java/someshbose/github/io/app/controller/HelloWorldApplicationResource.java @@ -2,6 +2,7 @@ import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; +import javax.annotation.security.RolesAllowed; import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; @@ -9,7 +10,9 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import com.codahale.metrics.annotation.Timed; -import someshbose.github.io.model.Saying; +import io.dropwizard.auth.Auth; +import someshbose.github.io.domain.model.Saying; +import someshbose.github.io.domain.model.User; @Path("/hello-world") @Produces(MediaType.APPLICATION_JSON) @@ -27,8 +30,9 @@ public HelloWorldApplicationResource(String template, String defaultName) { } @GET + @RolesAllowed({ "ADMIN"}) @Timed - public Saying sayHello(@QueryParam("name") Optional name) { + public Saying sayHello(@QueryParam("name") Optional name, @Auth User user) { final String value = String.format(template, name.orElse(defaultName)); return new Saying(counter.incrementAndGet(), value); } diff --git a/src/main/java/someshbose/github/io/app/controller/PersonResource.java b/src/main/java/someshbose/github/io/app/controller/PersonResource.java index cc69895..cadfc03 100644 --- a/src/main/java/someshbose/github/io/app/controller/PersonResource.java +++ b/src/main/java/someshbose/github/io/app/controller/PersonResource.java @@ -13,8 +13,8 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import io.dropwizard.hibernate.UnitOfWork; -import someshbose.github.io.app.dao.PersonDao; -import someshbose.github.io.model.Person; +import someshbose.github.io.domain.dao.PersonDao; +import someshbose.github.io.domain.model.Person; @Path("/person") @Consumes({MediaType.APPLICATION_JSON}) diff --git a/src/main/java/someshbose/github/io/app/dao/PersonDao.java b/src/main/java/someshbose/github/io/domain/dao/PersonDao.java similarity index 89% rename from src/main/java/someshbose/github/io/app/dao/PersonDao.java rename to src/main/java/someshbose/github/io/domain/dao/PersonDao.java index eb18c1b..fee9358 100644 --- a/src/main/java/someshbose/github/io/app/dao/PersonDao.java +++ b/src/main/java/someshbose/github/io/domain/dao/PersonDao.java @@ -1,10 +1,10 @@ -package someshbose.github.io.app.dao; +package someshbose.github.io.domain.dao; import java.util.List; import org.hibernate.SessionFactory; import io.dropwizard.hibernate.AbstractDAO; import org.hibernate.query.Query; -import someshbose.github.io.model.Person; +import someshbose.github.io.domain.model.Person; import javax.inject.Inject; diff --git a/src/main/java/someshbose/github/io/model/Person.java b/src/main/java/someshbose/github/io/domain/model/Person.java similarity index 97% rename from src/main/java/someshbose/github/io/model/Person.java rename to src/main/java/someshbose/github/io/domain/model/Person.java index 911db00..c80c430 100644 --- a/src/main/java/someshbose/github/io/model/Person.java +++ b/src/main/java/someshbose/github/io/domain/model/Person.java @@ -1,4 +1,4 @@ -package someshbose.github.io.model; +package someshbose.github.io.domain.model; import javax.persistence.*; import javax.validation.constraints.NotNull; diff --git a/src/main/java/someshbose/github/io/model/Saying.java b/src/main/java/someshbose/github/io/domain/model/Saying.java similarity index 84% rename from src/main/java/someshbose/github/io/model/Saying.java rename to src/main/java/someshbose/github/io/domain/model/Saying.java index 6857673..4b4978f 100644 --- a/src/main/java/someshbose/github/io/model/Saying.java +++ b/src/main/java/someshbose/github/io/domain/model/Saying.java @@ -1,4 +1,4 @@ -package someshbose.github.io.model; +package someshbose.github.io.domain.model; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/src/main/java/someshbose/github/io/domain/model/User.java b/src/main/java/someshbose/github/io/domain/model/User.java new file mode 100644 index 0000000..7352ae8 --- /dev/null +++ b/src/main/java/someshbose/github/io/domain/model/User.java @@ -0,0 +1,32 @@ +package someshbose.github.io.domain.model; + +import java.security.Principal; +import java.util.Set; + +public class User implements Principal { + private final String name; + + private final Set roles; + + public User(String name) { + this.name = name; + this.roles = null; + } + + public User(String name, Set roles) { + this.name = name; + this.roles = roles; + } + + public String getName() { + return name; + } + + public int getId() { + return (int) (Math.random() * 100); + } + + public Set getRoles() { + return roles; + } +} diff --git a/src/main/java/someshbose/github/io/infra/auth/AppAuthorizer.java b/src/main/java/someshbose/github/io/infra/auth/AppAuthorizer.java new file mode 100644 index 0000000..693cb1d --- /dev/null +++ b/src/main/java/someshbose/github/io/infra/auth/AppAuthorizer.java @@ -0,0 +1,12 @@ +package someshbose.github.io.infra.auth; + +import io.dropwizard.auth.Authorizer; +import someshbose.github.io.domain.model.User; + +public class AppAuthorizer implements Authorizer +{ + @Override + public boolean authorize(User user, String role) { + return user.getRoles() != null && user.getRoles().contains(role); + } +} diff --git a/src/main/java/someshbose/github/io/infra/auth/AppBasicAuthenticator.java b/src/main/java/someshbose/github/io/infra/auth/AppBasicAuthenticator.java new file mode 100644 index 0000000..0094712 --- /dev/null +++ b/src/main/java/someshbose/github/io/infra/auth/AppBasicAuthenticator.java @@ -0,0 +1,31 @@ +package someshbose.github.io.infra.auth; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import io.dropwizard.auth.AuthenticationException; +import io.dropwizard.auth.Authenticator; +import io.dropwizard.auth.basic.BasicCredentials; +import someshbose.github.io.domain.model.User; + +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +public class AppBasicAuthenticator implements Authenticator +{ + private static final Map> VALID_USERS = ImmutableMap.of( + "guest", ImmutableSet.of(), + "user", ImmutableSet.of("USER"), + "admin", ImmutableSet.of("ADMIN", "USER") + ); + + @Override + public Optional authenticate(BasicCredentials credentials) throws AuthenticationException + { + if (VALID_USERS.containsKey(credentials.getUsername()) && "password".equals(credentials.getPassword())) + { + return Optional.of(new User(credentials.getUsername(), VALID_USERS.get(credentials.getUsername()))); + } + return Optional.empty(); + } +}