diff --git a/brvs-core/build.gradle b/brvs-core/build.gradle index 9dbc437..9c78b8a 100644 --- a/brvs-core/build.gradle +++ b/brvs-core/build.gradle @@ -7,6 +7,9 @@ dependencies { compile('org.glassfish.jersey.containers:jersey-container-grizzly2-http:2.28') compile('org.glassfish.jersey.inject:jersey-hk2:2.28') compile('org.glassfish.jersey.media:jersey-media-json-jackson:2.28') + // Security + compile('org.pac4j:jersey-pac4j:3.0.0') + compile('org.pac4j:pac4j-http:3.6.1') // Logging redirection compile('org.slf4j:jul-to-slf4j:1.8.0-beta2') diff --git a/brvs-core/src/main/java/iroha/validation/Application.java b/brvs-core/src/main/java/iroha/validation/Application.java index f79d3eb..8563501 100644 --- a/brvs-core/src/main/java/iroha/validation/Application.java +++ b/brvs-core/src/main/java/iroha/validation/Application.java @@ -1,5 +1,6 @@ package iroha.validation; +import iroha.validation.security.BrvsAuthenticator; import iroha.validation.service.ValidationService; import iroha.validation.transactions.provider.RegistrationProvider; import iroha.validation.transactions.provider.impl.util.CacheProvider; @@ -11,6 +12,13 @@ import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.ServerProperties; +import org.pac4j.core.authorization.authorizer.IsAuthenticatedAuthorizer; +import org.pac4j.core.config.Config; +import org.pac4j.core.credentials.UsernamePasswordCredentials; +import org.pac4j.http.client.direct.DirectBasicAuthClient; +import org.pac4j.jax.rs.features.JaxRsConfigProvider; +import org.pac4j.jax.rs.features.Pac4JSecurityFeature; +import org.pac4j.jax.rs.grizzly.features.GrizzlyJaxRsContextFactoryProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.bridge.SLF4JBridgeHandler; @@ -51,6 +59,12 @@ protected void configure() { }); resourceConfig.property(ServerProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, 0); + Config securityConfig = getSecurityConfig(context.getBean(UsernamePasswordCredentials.class)); + resourceConfig + .register(new JaxRsConfigProvider(securityConfig)) + .register(new GrizzlyJaxRsContextFactoryProvider()) + .register(new Pac4JSecurityFeature()); + int port = getPort(context); logger.info("Going to establish HTTP server on port " + port); GrizzlyHttpServerFactory @@ -69,4 +83,13 @@ private static int getPort(AbstractApplicationContext context) { } return portBean; } + + private static Config getSecurityConfig(UsernamePasswordCredentials credentials) { + DirectBasicAuthClient basicAuthClient = new DirectBasicAuthClient( + BrvsAuthenticator.getInstance(credentials) + ); + Config config = new Config(basicAuthClient); + config.addAuthorizer("brvs", new IsAuthenticatedAuthorizer()); + return config; + } } diff --git a/brvs-core/src/main/java/iroha/validation/rest/RestService.java b/brvs-core/src/main/java/iroha/validation/rest/RestService.java index f34c6c0..523ea94 100644 --- a/brvs-core/src/main/java/iroha/validation/rest/RestService.java +++ b/brvs-core/src/main/java/iroha/validation/rest/RestService.java @@ -26,9 +26,11 @@ import javax.ws.rs.core.StreamingOutput; import jp.co.soramitsu.iroha.java.IrohaAPI; import jp.co.soramitsu.iroha.java.Utils; +import org.pac4j.jax.rs.annotations.Pac4JSecurity; @Singleton @Path("") +@Pac4JSecurity(authorizers = "isAuthenticated") public class RestService { private final static Printer printer = JsonFormat.printer() diff --git a/brvs-core/src/main/java/iroha/validation/security/BrvsAuthenticator.java b/brvs-core/src/main/java/iroha/validation/security/BrvsAuthenticator.java new file mode 100644 index 0000000..fec7623 --- /dev/null +++ b/brvs-core/src/main/java/iroha/validation/security/BrvsAuthenticator.java @@ -0,0 +1,68 @@ +package iroha.validation.security; + +import com.google.common.base.Strings; +import java.util.Objects; +import org.pac4j.core.context.Pac4jConstants; +import org.pac4j.core.context.WebContext; +import org.pac4j.core.credentials.UsernamePasswordCredentials; +import org.pac4j.core.credentials.authenticator.Authenticator; +import org.pac4j.core.exception.CredentialsException; +import org.pac4j.core.profile.CommonProfile; +import org.pac4j.core.util.CommonHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BrvsAuthenticator implements Authenticator { + + private static final Logger logger = LoggerFactory.getLogger(BrvsAuthenticator.class); + + private final String correctUsername; + + private final String correctPassword; + + private BrvsAuthenticator(String correctUsername, String correctPassword) { + this.correctUsername = correctUsername; + this.correctPassword = correctPassword; + } + + public static BrvsAuthenticator getInstance(String username, String password) { + if (Strings.isNullOrEmpty(username)) { + throw new IllegalArgumentException("Username must not be null nor empty"); + } + if (Strings.isNullOrEmpty(password)) { + throw new IllegalArgumentException("Password must not be null nor empty"); + } + return new BrvsAuthenticator(username, password); + } + + public static BrvsAuthenticator getInstance(UsernamePasswordCredentials credentials) { + Objects.requireNonNull(credentials, "Credentials must not be null"); + return getInstance(credentials.getUsername(), credentials.getPassword()); + } + + @Override + public void validate(UsernamePasswordCredentials credentials, WebContext context) { + if (credentials == null) { + logger.error("No credentials provided to the authenticator."); + throw new CredentialsException("No credential"); + } + String username = credentials.getUsername(); + String password = credentials.getPassword(); + if (CommonHelper.isBlank(username) || CommonHelper.isBlank(password)) { + logger.error("Malformed credentials provided to the authenticator."); + throw new CredentialsException("Username or password cannot be blank"); + } + if (CommonHelper.areNotEquals(username, correctUsername)) { + logger.error("Username : '" + username + "' is unknown."); + throw new CredentialsException("Username : '" + username + "' is unknown."); + } + if (CommonHelper.areNotEquals(password, correctPassword)) { + logger.error("Username : '" + username + "' provided invalid password."); + throw new CredentialsException("Username : '" + username + "' provided invalid password."); + } + final CommonProfile profile = new CommonProfile(); + profile.setId(username); + profile.addAttribute(Pac4jConstants.USERNAME, username); + credentials.setUserProfile(profile); + } +} diff --git a/config/context/application.properties b/config/context/application.properties index ca9e308..761e177 100644 --- a/config/context/application.properties +++ b/config/context/application.properties @@ -6,6 +6,8 @@ brvs.userKeysCount=10 brvs.localhostname=localhost brvs.userDomains=d3,sora brvs.port=8080 +rest.username=brvs +rest.password=brvs quorum.key=user_quorum accounts.holder=client_accounts@notary iroha.host=d3-iroha diff --git a/config/context/spring-context.xml b/config/context/spring-context.xml index 174fda1..46f4caa 100755 --- a/config/context/spring-context.xml +++ b/config/context/spring-context.xml @@ -19,6 +19,12 @@ + + + + + +