diff --git a/pom.xml b/pom.xml index 931932d99b791fcc5d6fdd0c41750a512b2b2d04..7a41f5d74bc1599557bcc375794b4a8ce12f2456 100644 --- a/pom.xml +++ b/pom.xml @@ -165,6 +165,11 @@ 3.4.0 + + org.springframework.boot + spring-boot-configuration-processor + true + diff --git a/src/main/java/biz/nynja/account/StartupScriptsListener.java b/src/main/java/biz/nynja/account/StartupScriptsListener.java index 4dcf578dae24fd8e7e8a238d8038603141dfc154..ca66b8792a2d8424d27993222867d2dd03e11b77 100644 --- a/src/main/java/biz/nynja/account/StartupScriptsListener.java +++ b/src/main/java/biz/nynja/account/StartupScriptsListener.java @@ -9,6 +9,7 @@ import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; @@ -16,11 +17,9 @@ import org.springframework.stereotype.Component; import com.datastax.driver.core.Session; import com.datastax.driver.core.exceptions.InvalidQueryException; -import biz.nynja.account.configuration.CassandraConfig; import biz.nynja.account.repositories.AccountRepositoryAdditional; - /** - * This acts as {@link CassandraConfig} startupScripts executor but activated after the spring has setup the needed + * This acts as {@link CassandraAccountConfig} startupScripts executor but activated after the spring has setup the needed * tables though JPA * * @author dragomir.todorov @@ -33,6 +32,7 @@ public class StartupScriptsListener { private String keyspace; @Autowired + @Qualifier("accountSession") private Session session; @Autowired diff --git a/src/main/java/biz/nynja/account/accesspoints/AccessPoint.java b/src/main/java/biz/nynja/account/accesspoints/AccessPoint.java new file mode 100644 index 0000000000000000000000000000000000000000..978d6e18398521800bd5a706400663827dc62a9b --- /dev/null +++ b/src/main/java/biz/nynja/account/accesspoints/AccessPoint.java @@ -0,0 +1,126 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.accesspoints; + + +import java.io.Serializable; +import java.util.UUID; + +import org.springframework.data.cassandra.core.cql.PrimaryKeyType; +import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn; +import org.springframework.data.cassandra.core.mapping.Table; + +@Table +public class AccessPoint implements Serializable { + + @PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) + private UUID accountId; + @PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.CLUSTERED) + private String accessToken; + private String deviceId; + private Long creationTimestamp; + private Long expirationTimestamp; + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public UUID getAccountId() { + return accountId; + } + + public void setAccountId(UUID accountId) { + this.accountId = accountId; + } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public Long getCreationTimestamp() { + return creationTimestamp; + } + + public void setCreationTimestamp(Long creationTimestamp) { + this.creationTimestamp = creationTimestamp; + } + + public Long getExpirationTimestamp() { + return expirationTimestamp; + } + + public void setExpirationTimestamp(Long expirationTimestamp) { + this.expirationTimestamp = expirationTimestamp; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((deviceId == null) ? 0 : deviceId.hashCode()); + result = prime * result + ((accountId == null) ? 0 : accountId.hashCode()); + result = prime * result + ((accessToken == null) ? 0 : accessToken.hashCode()); + result = prime * result + ((creationTimestamp == null) ? 0 : creationTimestamp.hashCode()); + result = prime * result + ((expirationTimestamp == null) ? 0 : expirationTimestamp.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AccessPoint other = (AccessPoint) obj; + if (deviceId == null) { + if (other.deviceId != null) + return false; + } else if (!deviceId.equals(other.deviceId)) + return false; + if (accountId == null) { + if (other.accountId != null) + return false; + } else if (!accountId.equals(other.accountId)) + return false; + if (accessToken == null) { + if (other.accessToken != null) + return false; + } else if (!accessToken.equals(other.accessToken)) + return false; + if (creationTimestamp == null) { + if (other.creationTimestamp != null) + return false; + } else if (!creationTimestamp.equals(other.creationTimestamp)) + return false; + if (expirationTimestamp == null) { + if (other.expirationTimestamp != null) + return false; + } else if (!expirationTimestamp.equals(other.expirationTimestamp)) + return false; + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("AccessPoint [accountId=").append(accountId) + .append(", deviceId=").append(deviceId) + .append(", accessToken=").append(accessToken) + .append(", creationTimestamp=").append(creationTimestamp) + .append(", expirationTimestamp=").append(expirationTimestamp) + .append("]"); + return builder.toString(); + } + +} diff --git a/src/main/java/biz/nynja/account/accesspoints/AccessPointRepository.java b/src/main/java/biz/nynja/account/accesspoints/AccessPointRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..6888c6df5cea70596cc543c8bdda533c5ef1b7bc --- /dev/null +++ b/src/main/java/biz/nynja/account/accesspoints/AccessPointRepository.java @@ -0,0 +1,17 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.accesspoints; + +import java.util.UUID; + +import org.springframework.data.cassandra.repository.CassandraRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface AccessPointRepository + extends CassandraRepository { + + AccessPoint findByAccountIdAndAccessToken(UUID accountId, String accessToken); + +} diff --git a/src/main/java/biz/nynja/account/accesspoints/AccessPointService.java b/src/main/java/biz/nynja/account/accesspoints/AccessPointService.java new file mode 100644 index 0000000000000000000000000000000000000000..ca24bd22e1bb812ec898871c76dd9cdd1d7793ae --- /dev/null +++ b/src/main/java/biz/nynja/account/accesspoints/AccessPointService.java @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.accesspoints; + +import java.util.Date; +import java.util.Optional; +import java.util.UUID; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +@Service +public class AccessPointService { + private final Logger logger = LoggerFactory.getLogger(AccessPointService.class); + private AccessPointRepository accessPointRepository; + + public AccessPointService(AccessPointRepository accessPointRepository) { + this.accessPointRepository = accessPointRepository; + } + + public Optional getAccessPoint(UUID accountId, String accessToken) { + AccessPoint accessPoint = accessPointRepository.findByAccountIdAndAccessToken(accountId, accessToken); + if (accessPoint != null) { + return Optional.of(accessPoint); + } + return Optional.empty(); + } + + public AccessPoint buildAccessPoint(String deviceId, String accessToken, String accountId, long expiresIn) { + AccessPoint accessPoint = new AccessPoint(); + accessPoint.setDeviceId(deviceId); + accessPoint.setAccountId(UUID.fromString(accountId)); + accessPoint.setAccessToken(accessToken); + accessPoint.setCreationTimestamp(new Date().getTime()); + accessPoint.setExpirationTimestamp(new Date().getTime() + expiresIn); + return accessPoint; + } + +} diff --git a/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java b/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java index 91b9b32575dc5804591380f851ee8366510e02e4..20be301ffa8a81bd42aaea6160e46f4d00a1885e 100644 --- a/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java +++ b/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java @@ -18,20 +18,20 @@ import com.datastax.driver.core.UDTValue; import com.datastax.driver.core.UserType; import biz.nynja.account.codecs.AuthenticationProviderCodec; -import biz.nynja.account.configuration.CassandraConfig; +import biz.nynja.account.configuration.CassandraAccountConfig; import biz.nynja.account.models.AuthenticationProvider; @Configuration public class PreparedStatementsCache { - private final CassandraConfig cassandraConfig; + private final CassandraAccountConfig cassandraConfig; private final Session session; private static Map statementsCache; @Autowired - public PreparedStatementsCache(Session session, CassandraConfig cassandraConfig) { + public PreparedStatementsCache(Session session, CassandraAccountConfig cassandraConfig) { this.session = session; this.cassandraConfig = cassandraConfig; } diff --git a/src/main/java/biz/nynja/account/configuration/CassandraAccessPointConfig.java b/src/main/java/biz/nynja/account/configuration/CassandraAccessPointConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..1b5472a3d35cd828b6d560900e30f381ce2f7a43 --- /dev/null +++ b/src/main/java/biz/nynja/account/configuration/CassandraAccessPointConfig.java @@ -0,0 +1,92 @@ +package biz.nynja.account.configuration; + +import java.util.Arrays; +import java.util.List; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.data.cassandra.config.CassandraClusterFactoryBean; +import org.springframework.data.cassandra.config.CassandraSessionFactoryBean; +import org.springframework.data.cassandra.core.CassandraAdminOperations; +import org.springframework.data.cassandra.core.CassandraAdminTemplate; +import org.springframework.data.cassandra.core.cql.keyspace.CreateKeyspaceSpecification; +import org.springframework.data.cassandra.core.mapping.CassandraMappingContext; +import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories; + +import biz.nynja.account.StartupScriptsListener; + +@Configuration +@ConfigurationProperties("spring.data.cassandra.accesspoint") +@EnableCassandraRepositories( + basePackages = { "biz.nynja.account.accesspoints" }, + cassandraTemplateRef = "CassandraAccessPointTemplate") +@ConditionalOnMissingClass("org.springframework.test.context.junit4.SpringRunner") +public class CassandraAccessPointConfig extends CassandraBaseConfig { + + @Value("${spring.data.cassandra.accesspoint.keyspace-name}") + protected String keyspaceName; + + @Override + protected String getKeyspaceName() { + return keyspaceName; + } + + public void setKeyspaceName(String keyspaceName) { + this.keyspaceName = keyspaceName; + } + + @Bean + public CassandraMappingContext mappingContext() { + return new CassandraMappingContext(); + } + + @Override + public String[] getEntityBasePackages() { + return new String[] { "biz.nynja.account.accesspoints" }; + } + + @Override + @Primary + @Bean(name = "CassandraAccessPointTemplate") + public CassandraAdminOperations cassandraTemplate() throws Exception { + return new CassandraAdminTemplate(session().getObject(), cassandraConverter()); + } + + @Override + @Bean(name = "accessPointSession") + public CassandraSessionFactoryBean session() { + + CassandraSessionFactoryBean session = new CassandraSessionFactoryBean(); + + session.setCluster(cluster().getObject()); + session.setConverter(cassandraConverter()); + session.setKeyspaceName(getKeyspaceName()); + session.setSchemaAction(getSchemaAction()); + session.setStartupScripts(getStartupScripts()); + session.setShutdownScripts(getShutdownScripts()); + + return session; + } + + @Override + protected List getKeyspaceCreations() { + CreateKeyspaceSpecification specification = CreateKeyspaceSpecification.createKeyspace(getKeyspaceName()) + .ifNotExists().withSimpleReplication(); + return Arrays.asList(specification); + } + + @Bean + public CassandraClusterFactoryBean cluster() { + CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean(); + cluster.setContactPoints(getContactPoints()); + cluster.setPort(getPort()); + cluster.setKeyspaceCreations(getKeyspaceCreations()); + + return cluster; + } + +} \ No newline at end of file diff --git a/src/main/java/biz/nynja/account/configuration/CassandraAccountConfig.java b/src/main/java/biz/nynja/account/configuration/CassandraAccountConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..311fa8d4d258eddd6b69566d0083cf960f2adf46 --- /dev/null +++ b/src/main/java/biz/nynja/account/configuration/CassandraAccountConfig.java @@ -0,0 +1,103 @@ +package biz.nynja.account.configuration; + + +import java.util.Arrays; +import java.util.List; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.cassandra.config.CassandraClusterFactoryBean; +import org.springframework.data.cassandra.config.CassandraSessionFactoryBean; +import org.springframework.data.cassandra.core.CassandraAdminOperations; +import org.springframework.data.cassandra.core.CassandraAdminTemplate; +import org.springframework.data.cassandra.core.cql.keyspace.CreateKeyspaceSpecification; +import org.springframework.data.cassandra.core.mapping.CassandraMappingContext; +import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories; + +import biz.nynja.account.StartupScriptsListener; + +@Configuration +@ConfigurationProperties("spring.data.cassandra.account") +@EnableCassandraRepositories( + basePackages = { "biz.nynja.account.models", "biz.nynja.account.repositories"}, + cassandraTemplateRef = "CassandraAccountTemplate") +@ConditionalOnMissingClass("org.springframework.test.context.junit4.SpringRunner") +public class CassandraAccountConfig extends CassandraBaseConfig { + + @Value("${spring.data.cassandra.account.keyspace-name}") + protected String keyspaceName; + + @Override + protected String getKeyspaceName() { + return keyspaceName; + } + + public void setKeyspaceName(String keyspaceName) { + this.keyspaceName = keyspaceName; + } + + @Bean + public CassandraMappingContext mappingContext() { + return new CassandraMappingContext(); + } + + @Override + public String[] getEntityBasePackages() { + return new String[] { "biz.nynja.account.models", "biz.nynja.account.repositories" }; + } + + @Override + @Bean(name = "CassandraAccountTemplate") + public CassandraAdminOperations cassandraTemplate( + @Qualifier("accountSession") final CassandraSessionFactoryBean session) throws Exception { + return new CassandraAdminTemplate(session().getObject(), cassandraConverter()); + } + + @Override + @Bean(name = "accountSession") + public CassandraSessionFactoryBean session() { + + CassandraSessionFactoryBean session = new CassandraSessionFactoryBean(); + + session.setCluster(cluster().getObject()); + session.setConverter(cassandraConverter()); + session.setKeyspaceName(getKeyspaceName()); + session.setSchemaAction(getSchemaAction()); + session.setStartupScripts(getStartupScripts()); + session.setShutdownScripts(getShutdownScripts()); + + return session; + } + + @Override + protected List getKeyspaceCreations() { + CreateKeyspaceSpecification specification = CreateKeyspaceSpecification.createKeyspace(getKeyspaceName()) + .ifNotExists().withSimpleReplication(); + return Arrays.asList(specification); + } + + @Bean + public CassandraClusterFactoryBean cluster() { + CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean(); + cluster.setContactPoints(getContactPoints()); +// cluster.setContactPoints(InetAddress.getLocalHost().getHostName()); + cluster.setPort(getPort()); + cluster.setKeyspaceCreations(getKeyspaceCreations()); + + return cluster; + } + + /** + * See {@link StartupScriptsListener} for scripts + * that require JPA annotated tables + */ + @Override + protected List getStartupScripts() { + return super.getStartupScripts(); + } + +} \ No newline at end of file diff --git a/src/main/java/biz/nynja/account/configuration/CassandraConfig.java b/src/main/java/biz/nynja/account/configuration/CassandraBaseConfig.java similarity index 52% rename from src/main/java/biz/nynja/account/configuration/CassandraConfig.java rename to src/main/java/biz/nynja/account/configuration/CassandraBaseConfig.java index 739d5063f39b8523254bf50a494cdee7e90b7e96..873dae4af0abd918a9f1fe9b874d68bbb161bf56 100644 --- a/src/main/java/biz/nynja/account/configuration/CassandraConfig.java +++ b/src/main/java/biz/nynja/account/configuration/CassandraBaseConfig.java @@ -1,77 +1,66 @@ -/** - * Copyright (C) 2018 Nynja Inc. All rights reserved. - */ -package biz.nynja.account.configuration; - -import java.util.Arrays; -import java.util.List; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.cassandra.config.AbstractCassandraConfiguration; -import org.springframework.data.cassandra.config.SchemaAction; -import org.springframework.data.cassandra.core.cql.keyspace.CreateKeyspaceSpecification; -import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories; - -import biz.nynja.account.StartupScriptsListener; - -@Configuration -@EnableCassandraRepositories -@ConditionalOnMissingClass("org.springframework.test.context.junit4.SpringRunner") -public class CassandraConfig extends AbstractCassandraConfiguration { - - @Value("${spring.data.cassandra.keyspace-name}") - private String keyspace; - - @Override - protected String getKeyspaceName() { - return keyspace; - } - - @Value("${spring.data.cassandra.contact-points}") - private String contactPoints; - - @Override - protected String getContactPoints() { - return contactPoints; - } - - @Value("${spring.data.cassandra.port}") - private int port; - - @Override - protected int getPort() { - return port; - } - - @Override - public SchemaAction getSchemaAction() { - return SchemaAction.CREATE_IF_NOT_EXISTS; - } - - @Override - protected List getKeyspaceCreations() { - CreateKeyspaceSpecification specification = CreateKeyspaceSpecification.createKeyspace(getKeyspaceName()) - .ifNotExists().withSimpleReplication(); - return Arrays.asList(specification); - } - - @Override - public String[] getEntityBasePackages() { - return new String[] { "biz.nynja.account.models" }; - } - - /** - * See {@link StartupScriptsListener} for scripts - * that require JPA annotated tables - */ - @Override - protected List getStartupScripts() { - return super.getStartupScripts(); - } - - public String getConfiguredKeyspaceName() { - return getKeyspaceName(); - } -} +package biz.nynja.account.configuration; + + + +import java.util.List; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.cassandra.config.AbstractCassandraConfiguration; +import org.springframework.data.cassandra.config.CassandraClusterFactoryBean; +import org.springframework.data.cassandra.config.CassandraSessionFactoryBean; +import org.springframework.data.cassandra.config.SchemaAction; +import org.springframework.data.cassandra.core.CassandraAdminOperations; +import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories; + +@Configuration +@EnableCassandraRepositories +@ConditionalOnMissingClass("org.springframework.test.context.junit4.SpringRunner") +public abstract class CassandraBaseConfig extends AbstractCassandraConfiguration { + + @Value("${spring.data.cassandra.contact-points}") + protected String contactPoints; + + @Value("${spring.data.cassandra.port}") + protected int port; + + @Override + protected String getContactPoints() { + return contactPoints; + } + + public void setContactPoints(String contactPoints) { + this.contactPoints = contactPoints; + } + + @Override + protected int getPort() { + return port; + } + + protected int setPort() { + return port; + } + + @Override + public SchemaAction getSchemaAction() { + return SchemaAction.CREATE_IF_NOT_EXISTS; + } + + @Override + protected List getStartupScripts() { + return super.getStartupScripts(); + } + + public String getConfiguredKeyspaceName() { + return getKeyspaceName(); + } + + public CassandraAdminOperations cassandraTemplate(CassandraSessionFactoryBean session) throws Exception { + return null; + } + + +} diff --git a/src/main/java/biz/nynja/account/permissions/PermissionsInterceptor.java b/src/main/java/biz/nynja/account/permissions/PermissionsInterceptor.java index 1fc1d1362e52e8e49827a30e84d78302742cabff..7a37de8e11b6aedf94ee692b6c545e67f03f8170 100644 --- a/src/main/java/biz/nynja/account/permissions/PermissionsInterceptor.java +++ b/src/main/java/biz/nynja/account/permissions/PermissionsInterceptor.java @@ -7,27 +7,36 @@ import static io.grpc.Metadata.ASCII_STRING_MARSHALLER; import java.lang.reflect.Method; import java.util.Base64; +import java.util.Optional; +import java.util.UUID; import org.lognet.springboot.grpc.GRpcGlobalInterceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import com.auth0.jwt.JWT; import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT; import biz.nynja.account.services.AccountServiceImpl; +import biz.nynja.account.accesspoints.AccessPointService; +import biz.nynja.account.accesspoints.AccessPoint; import io.grpc.Context; +import io.grpc.Contexts; import io.grpc.Metadata; import io.grpc.ServerCall; +import io.grpc.ServerCall.Listener; import io.grpc.ServerCallHandler; import io.grpc.ServerInterceptor; +import io.grpc.Status; /** - * @author Stoyan.Tzenkov - account-service ServerInterceptor. Validates roles for granting permissions to - * account-service endpoints(rpcs). - * General rules: + * @author Stoyan.Tzenkov - account-service ServerInterceptor. + * Validates roles for granting permissions to account-service endpoints(rpcs). + * General rules: * - if access token is not present - PERMISSION DENIED; + * - if no accesspoint found for the requesting account ID and access token - PERMISSION DENIED; * - if no roles found in the access token - PERMISSION DENIED; * - if rpc not found in the account-service class - PERMISSION DENIED; * - if no Permitted annotation found for the rpc method - PERMISSION DENIED; @@ -49,48 +58,59 @@ public class PermissionsInterceptor implements ServerInterceptor { private static final ServerCall.Listener NOOP_LISTENER = new ServerCall.Listener() { }; + @Autowired + private AccessPointService accessPointService; + @Override public ServerCall.Listener interceptCall(ServerCall call, Metadata headers, ServerCallHandler next) { // WARNING: THe line bellow is to be removed and code following uncommented // when Istio starts sending an access token with each and every request - return next.startCall(call, headers); + return next.startCall(call, headers); /* * Expected metadata is "Authorization" : "Bearer --accessTokenValue--" so we can skip validation as istio won't * allow this request through */ - // String accessToken = (headers.get(ACCESS_TOKEN_METADATA).split(" "))[1]; - // String rpc = getRpcName(call); - // - // boolean permitted = false; - // Context ctx = null; - // String[] requestingRoles = null; - // - // if (accessToken != null) { - // ctx = Context.current().withValue(ACCESS_TOKEN_CTX, accessToken); - // requestingRoles = getRolesFromAccessToken(accessToken, rpc); - // - // if (requestingRoles != null) { - // Method method = getMethod(rpc); - // - // if (method != null) { - // Permitted[] permittedRoles = method.getAnnotationsByType(Permitted.class); - // permitted = checkPermissions(requestingRoles, permittedRoles); - // } - // } - // } - // - // if (permitted) { - // logger.info("Permission granted to rpc {}.", rpc); - // return Contexts.interceptCall(ctx, call, headers, next); - // } else { - // logger.error("Permission denied for rpc {}, roles {}.", rpc, requestingRoles); - // call.close(Status.PERMISSION_DENIED.withDescription("An unauthorized call was made to " + rpc + "."), - // headers); - // return NOOP_LISTENER; - // } +// String accessToken = (headers.get(ACCESS_TOKEN_METADATA).split(" "))[1]; +// String rpc = getRpcName(call); +// +// boolean permitted = false; +// Context ctx = null; +// String[] requestingRoles = null; +// +// if (accessToken == null && accessToken.isEmpty()) { +// permissionDenied(call, headers, "Permission denied for rpc {}. Access token not in headers", rpc ); +// } +// ctx = Context.current().withValue(ACCESS_TOKEN_CTX, accessToken); +// DecodedJWT decodedToken = JWT.decode(accessToken); +// +// if (!accessPointAvailable(accessToken, decodedToken, rpc)) { +// permissionDenied(call, headers, "Permission denied for rpc {}. No access point available for this account and access token.", rpc ); +// } +// +// requestingRoles = getRolesFromAccessToken(decodedToken); +// if (requestingRoles == null) { +// permissionDenied(call, headers, "Permission denied for rpc {}. No roles found for requesting account in access token.", rpc ); +// } +// +// Method method = getMethod(rpc); +// if (method == null) { +// permissionDenied(call, headers, "Permission denied for rpc {}. Could not identify the method implementing this rpc.", rpc ); +// } +// +// Permitted[] permittedRoles = method.getAnnotationsByType(Permitted.class); +// permitted = checkPermissions(requestingRoles, permittedRoles); +// if (permitted) { +// logger.info("Permission granted to rpc {}.", rpc); +// return Contexts.interceptCall(ctx, call, headers, next); +// } else { +// logger.error("Permission denied for rpc {}, roles {}.", rpc, requestingRoles); +// call.close(Status.PERMISSION_DENIED.withDescription("An unauthorized call was made to " + rpc + "."), +// headers); +// return NOOP_LISTENER; +// } } private String getRpcName(ServerCall call) { @@ -99,19 +119,22 @@ public class PermissionsInterceptor implements ServerInterceptor { return callName.substring(callName.lastIndexOf('/') + 1); } - private String[] getRolesFromAccessToken(String accessToken, String rpc) { + private boolean accessPointAvailable(String accessToken, DecodedJWT decodedToken, String rpc) { + + String accountId = new String(Base64.getDecoder().decode(decodedToken.getSubject())); + logger.info("Verifying permissions for rpc {} for user with account id {}.", rpc, accountId); + + Optional accessPoint = accessPointService.getAccessPoint(UUID.fromString(accountId), accessToken); + return accessPoint.isPresent(); + } + + private String[] getRolesFromAccessToken(DecodedJWT decodedToken) { // Get roles from access token String[] requestingRoles = null; - if (accessToken != null && !accessToken.isEmpty()) { - DecodedJWT decodedToken = JWT.decode(accessToken); - String requestingAccountId = new String(Base64.getDecoder().decode(decodedToken.getSubject())); - logger.info("Verifying permissions for rpc {} for user with account id {}.", rpc, requestingAccountId); - - Claim claim = decodedToken.getClaim("roles"); - if (claim != null) { - requestingRoles = claim.asArray(String.class); - } + Claim claim = decodedToken.getClaim("roles"); + if (claim != null) { + requestingRoles = claim.asArray(String.class); } return requestingRoles; } @@ -135,7 +158,7 @@ public class PermissionsInterceptor implements ServerInterceptor { return true; } for (String role : requestingRoles) { - if (role.equals(permitted.role()) || role.equals(RoleConstants.ADMIN) + if (role.equals(permitted.role()) || role.equals(RoleConstants.ACCOUNT_ADMIN) || role.equals(RoleConstants.AUTH_SERVICE)) { return true; } @@ -144,4 +167,11 @@ public class PermissionsInterceptor implements ServerInterceptor { return false; } + private ServerCall.Listener permissionDenied(ServerCall call, Metadata headers, String message, String rpc ) { + logger.error(message, rpc); + call.close(Status.PERMISSION_DENIED.withDescription("An unauthorized call was made to " + rpc + "."), + headers); + return NOOP_LISTENER; + } + } diff --git a/src/main/java/biz/nynja/account/permissions/PermissionsValidator.java b/src/main/java/biz/nynja/account/permissions/PermissionsValidator.java index 10144e4859bd42fd0836047ff2ea6b86dac942ea..c213cab71771f05f1aac833e789a35445d75a1a2 100644 --- a/src/main/java/biz/nynja/account/permissions/PermissionsValidator.java +++ b/src/main/java/biz/nynja/account/permissions/PermissionsValidator.java @@ -68,7 +68,7 @@ public class PermissionsValidator { private static boolean isAuthorized(String[] requestingRoles) { for (String role : requestingRoles) { switch (role) { - case RoleConstants.ADMIN: + case RoleConstants.ACCOUNT_ADMIN: case RoleConstants.AUTH_SERVICE: return true; } diff --git a/src/main/java/biz/nynja/account/permissions/RoleConstants.java b/src/main/java/biz/nynja/account/permissions/RoleConstants.java index 4a9b8d4afb5540b2c2bda5cbfa5f785f30a0ac0b..ff0185c051a1b68291a246e6b40a35ebaf6b8960 100644 --- a/src/main/java/biz/nynja/account/permissions/RoleConstants.java +++ b/src/main/java/biz/nynja/account/permissions/RoleConstants.java @@ -8,7 +8,7 @@ package biz.nynja.account.permissions; */ public class RoleConstants { - public static final String ADMIN = "ADMIN"; + public static final String ACCOUNT_ADMIN = "ACCOUNT_ADMIN"; public static final String USER = "USER"; public static final String ANY = "ANY"; public static final String AUTH_SERVICE = "AUTH_SERVICE"; diff --git a/src/main/java/biz/nynja/account/services/AccountServiceImpl.java b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java index 636d301a4192f30890e5d201a2867ab422540a0f..ae63d023c7b8016a3c5cd227aacd77527cceb196 100644 --- a/src/main/java/biz/nynja/account/services/AccountServiceImpl.java +++ b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java @@ -11,6 +11,7 @@ import static biz.nynja.account.validation.Validators.util; import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.stream.Collectors; import org.apache.commons.lang3.tuple.ImmutablePair; import org.lognet.springboot.grpc.GRpcService; @@ -105,7 +106,8 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas AccountByQrCodeRepository accountByQrCodeRepository, AccountByUsernameRepository accountByUsernameRepository, AccountProvider accountProvider, AccountByProfileIdRepository accountByProfileIdRepository, PhoneNumberNormalizer phoneNumberNormalizer, - AccountCreator accountCreator, ProfileProvider profileProvider, PermissionsValidator permissionsValidator) { + AccountCreator accountCreator, ProfileProvider profileProvider, + PermissionsValidator permissionsValidator) { this.accountRepositoryAdditional = accountRepositoryAdditional; this.profileRepository = profileRepository; this.profileByAutheticationProviderRepository = profileByAutheticationProviderRepository; @@ -121,7 +123,7 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas @Override @PerformPermissionCheck - @Permitted(role = RoleConstants.ADMIN) + @Permitted(role = RoleConstants.ACCOUNT_ADMIN) @Permitted(role = RoleConstants.AUTH_SERVICE) @Permitted(role = RoleConstants.USER) public void getAccountByCreationProvider(AuthenticationProviderRequest request, @@ -240,7 +242,7 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas @Override @PerformPermissionCheck - @Permitted(role = RoleConstants.ADMIN) + @Permitted(role = RoleConstants.ACCOUNT_ADMIN) @Permitted(role = RoleConstants.USER) public void getAccountByUsername(GetByUsernameRequest request, StreamObserver responseObserver) { logger.info("Getting account by username: {}", request.getUsername()); @@ -325,7 +327,7 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas @Override @PerformPermissionCheck - @Permitted(role = RoleConstants.ADMIN) + @Permitted(role = RoleConstants.ACCOUNT_ADMIN) @Permitted(role = RoleConstants.USER) public void getAccountByQrCode(GetByQrCodeRequest request, StreamObserver responseObserver) { logger.info("Search account by QR code: {}", request.getQrCode()); @@ -383,7 +385,7 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas } @Override - @Permitted(role = RoleConstants.ADMIN) + @Permitted(role = RoleConstants.ACCOUNT_ADMIN) public void getAllAccountsByProfileId(AccountsByProfileIdRequest request, StreamObserver responseObserver) { logger.info("Getting accounts by profile id: {}", request.getProfileId()); @@ -413,7 +415,7 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas @Override @PerformPermissionCheck - @Permitted(role = RoleConstants.ADMIN) + @Permitted(role = RoleConstants.ACCOUNT_ADMIN) @Permitted(role = RoleConstants.AUTH_SERVICE) @Permitted(role = RoleConstants.USER) public void getAccountByAccountId(AccountByAccountIdRequest request, @@ -449,7 +451,7 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas } @Override - @Permitted(role = RoleConstants.ADMIN) + @Permitted(role = RoleConstants.ACCOUNT_ADMIN) @Permitted(role = RoleConstants.AUTH_SERVICE) @Permitted(role = RoleConstants.USER) public void createPendingAccount(CreatePendingAccountRequest request, @@ -479,7 +481,7 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas @Override @PerformPermissionCheck - @Permitted(role = RoleConstants.ADMIN) + @Permitted(role = RoleConstants.ACCOUNT_ADMIN) @Permitted(role = RoleConstants.USER) public void updateAccount(UpdateAccountRequest request, StreamObserver responseObserver) { logger.info("Updating account..."); @@ -526,7 +528,7 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas @Override @PerformPermissionCheck - @Permitted(role = RoleConstants.ADMIN) + @Permitted(role = RoleConstants.ACCOUNT_ADMIN) @Permitted(role = RoleConstants.USER) public void deleteAccount(DeleteAccountRequest request, StreamObserver responseObserver) { logger.debug("Deleting account...: {}", request); @@ -563,7 +565,7 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas @Override @PerformPermissionCheck - @Permitted(role = RoleConstants.ADMIN) + @Permitted(role = RoleConstants.ACCOUNT_ADMIN) @Permitted(role = RoleConstants.USER) public void deleteProfile(DeleteProfileRequest request, StreamObserver responseObserver) { logger.debug("Deleting profile: {}", request); @@ -593,7 +595,7 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas @Override @PerformPermissionCheck - @Permitted(role = RoleConstants.ADMIN) + @Permitted(role = RoleConstants.ACCOUNT_ADMIN) @Permitted(role = RoleConstants.AUTH_SERVICE) @Permitted(role = RoleConstants.USER) public void addAuthenticationProviderToProfile(AddAuthenticationProviderRequest request, @@ -674,7 +676,7 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas @Override @PerformPermissionCheck - @Permitted(role = RoleConstants.ADMIN) + @Permitted(role = RoleConstants.ACCOUNT_ADMIN) @Permitted(role = RoleConstants.USER) public void addContactInfoToAccount(AddContactInfoRequest request, StreamObserver responseObserver) { @@ -727,7 +729,7 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas @Override @PerformPermissionCheck - @Permitted(role = RoleConstants.ADMIN) + @Permitted(role = RoleConstants.ACCOUNT_ADMIN) @Permitted(role = RoleConstants.USER) public void deleteContactInfoFromAccount(DeleteContactInfoRequest request, StreamObserver responseObserver) { @@ -770,7 +772,7 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas @Override @PerformPermissionCheck - @Permitted(role = RoleConstants.ADMIN) + @Permitted(role = RoleConstants.ACCOUNT_ADMIN) @Permitted(role = RoleConstants.USER) public void deleteAuthenticationProviderFromProfile(DeleteAuthenticationProviderRequest request, StreamObserver responseObserver) { @@ -877,7 +879,7 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas @Override @PerformPermissionCheck - @Permitted(role = RoleConstants.ADMIN) + @Permitted(role = RoleConstants.ACCOUNT_ADMIN) @Permitted(role = RoleConstants.USER) public void getProfileByProfileId(ProfileByProfileIdRequest request, StreamObserver responseObserver) { @@ -918,7 +920,7 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas @Override @PerformPermissionCheck - @Permitted(role = RoleConstants.ADMIN) + @Permitted(role = RoleConstants.ACCOUNT_ADMIN) @Permitted(role = RoleConstants.USER) public void editContactInfoForAccount(EditContactInfoRequest request, StreamObserver responseObserver) { @@ -998,7 +1000,7 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas @Override @PerformPermissionCheck - @Permitted(role = RoleConstants.ADMIN) + @Permitted(role = RoleConstants.ACCOUNT_ADMIN) @Permitted(role = RoleConstants.AUTH_SERVICE) @Permitted(role = RoleConstants.USER) public void updateAuthenticationProviderForProfile(UpdateAuthenticationProviderRequest request, @@ -1067,38 +1069,9 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas return; } - private static void logAndBuildGrpcAccountResponse(StreamObserver responseObserver, - AccountResponse.Builder newBuilder, String logMessage, String logValue, Cause cause) { - - logger.error(logMessage, logValue); - responseObserver.onNext(newBuilder.setError(ErrorResponse.newBuilder().setCause(cause)).build()); - responseObserver.onCompleted(); - } - - private static void logAndBuildGrpcAccountsResponse(StreamObserver responseObserver, - AccountsResponse.Builder newBuilder, String logMessage, String logValue, Cause cause) { - logger.error(logMessage, logValue); - responseObserver.onNext(newBuilder.setError(ErrorResponse.newBuilder().setCause(cause)).build()); - responseObserver.onCompleted(); - } - - private static void logAndBuildGrpcProfileResponse(StreamObserver responseObserver, - ProfileResponse.Builder newBuilder, String logMessage, String logValue, Cause cause) { - logger.error(logMessage, logValue); - responseObserver.onNext(newBuilder.setError(ErrorResponse.newBuilder().setCause(cause)).build()); - responseObserver.onCompleted(); - } - - private static void logAndBuildGrpcStatusResponse(StreamObserver responseObserver, - StatusResponse.Builder newBuilder, String logMessage, String logValue, Cause cause) { - logger.error(logMessage, logValue); - responseObserver.onNext(newBuilder.setError(ErrorResponse.newBuilder().setCause(cause)).build()); - responseObserver.onCompleted(); - } - @Override @PerformPermissionCheck - @Permitted(role = RoleConstants.ADMIN) + @Permitted(role = RoleConstants.ACCOUNT_ADMIN) @Permitted(role = RoleConstants.USER) @Permitted(role = RoleConstants.AUTH_SERVICE) public void getAccountByLoginOption(AuthenticationProviderRequest request, @@ -1182,4 +1155,33 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas responseObserver.onCompleted(); } + private static void logAndBuildGrpcAccountResponse(StreamObserver responseObserver, + AccountResponse.Builder newBuilder, String logMessage, String logValue, Cause cause) { + + logger.error(logMessage, logValue); + responseObserver.onNext(newBuilder.setError(ErrorResponse.newBuilder().setCause(cause)).build()); + responseObserver.onCompleted(); + } + + private static void logAndBuildGrpcAccountsResponse(StreamObserver responseObserver, + AccountsResponse.Builder newBuilder, String logMessage, String logValue, Cause cause) { + logger.error(logMessage, logValue); + responseObserver.onNext(newBuilder.setError(ErrorResponse.newBuilder().setCause(cause)).build()); + responseObserver.onCompleted(); + } + + private static void logAndBuildGrpcProfileResponse(StreamObserver responseObserver, + ProfileResponse.Builder newBuilder, String logMessage, String logValue, Cause cause) { + logger.error(logMessage, logValue); + responseObserver.onNext(newBuilder.setError(ErrorResponse.newBuilder().setCause(cause)).build()); + responseObserver.onCompleted(); + } + + private static void logAndBuildGrpcStatusResponse(StreamObserver responseObserver, + StatusResponse.Builder newBuilder, String logMessage, String logValue, Cause cause) { + logger.error(logMessage, logValue); + responseObserver.onNext(newBuilder.setError(ErrorResponse.newBuilder().setCause(cause)).build()); + responseObserver.onCompleted(); + } + } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 5a241bc80df56519b99c9e210327f745986fec9f..5de0eb4131e96a65d07733a20701b83b0977a70c 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -10,9 +10,12 @@ grpc: spring: data: cassandra: - keyspace-name: account contact-points: localhost port: 9042 + account: + keyspace-name: account + accesspoint: + keyspace-name: accesspoint # To enable colors in Eclipse: # spring.output.ansi.enabled=ALWAYS and in eclipse # Help -> Install New Software... and click "Add..." to add the following URL: diff --git a/src/test/java/biz/nynja/account/configurations/CassandraTestsConfig.java b/src/test/java/biz/nynja/account/configurations/CassandraTestsConfig.java index f57d3f5524930b8cec693735cb13e539ac4b6f24..9a599b08b393effe79fb9019986c8a7b16329e8a 100644 --- a/src/test/java/biz/nynja/account/configurations/CassandraTestsConfig.java +++ b/src/test/java/biz/nynja/account/configurations/CassandraTestsConfig.java @@ -1,5 +1,6 @@ package biz.nynja.account.configurations; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Bean; @@ -17,6 +18,7 @@ import com.datastax.driver.core.Session; public class CassandraTestsConfig { @MockBean + @Qualifier("accountSession") private Session session; @Bean diff --git a/src/test/java/biz/nynja/account/services/AccountServiceTests.java b/src/test/java/biz/nynja/account/services/AccountServiceTests.java index 8c06861cf7a0981bbbe1869d842819e4af7ddc45..fce0d7d6bd0498b5c945278f655b56c5556639ea 100644 --- a/src/test/java/biz/nynja/account/services/AccountServiceTests.java +++ b/src/test/java/biz/nynja/account/services/AccountServiceTests.java @@ -3,15 +3,20 @@ */ package biz.nynja.account.services; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.verify; -import java.util.*; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; -import org.apache.commons.lang3.tuple.ImmutablePair; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -25,21 +30,78 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; +import biz.nynja.account.accesspoints.AccessPoint; +import biz.nynja.account.accesspoints.AccessPointService; import biz.nynja.account.components.AccountServiceHelper; import biz.nynja.account.components.PreparedStatementsCache; import biz.nynja.account.configurations.CassandraTestsConfig; -import biz.nynja.account.grpc.*; +import biz.nynja.account.grpc.AccountByAccountIdRequest; +import biz.nynja.account.grpc.AccountDetails; +import biz.nynja.account.grpc.AccountResponse; +import biz.nynja.account.grpc.AccountServiceGrpc; +import biz.nynja.account.grpc.AccountsByProfileIdRequest; +import biz.nynja.account.grpc.AccountsList; +import biz.nynja.account.grpc.AccountsResponse; +import biz.nynja.account.grpc.AddAuthenticationProviderRequest; +import biz.nynja.account.grpc.AddContactInfoRequest; +import biz.nynja.account.grpc.AuthProviderDetails; +import biz.nynja.account.grpc.AuthenticationProviderRequest; +import biz.nynja.account.grpc.AuthenticationType; +import biz.nynja.account.grpc.CompletePendingAccountCreationRequest; +import biz.nynja.account.grpc.ContactDetails; +import biz.nynja.account.grpc.ContactType; +import biz.nynja.account.grpc.CreatePendingAccountRequest; +import biz.nynja.account.grpc.CreatePendingAccountResponse; import biz.nynja.account.grpc.Date; +import biz.nynja.account.grpc.DeleteAccountRequest; +import biz.nynja.account.grpc.DeleteAuthenticationProviderRequest; +import biz.nynja.account.grpc.DeleteContactInfoRequest; +import biz.nynja.account.grpc.DeleteProfileRequest; +import biz.nynja.account.grpc.EditContactInfoRequest; import biz.nynja.account.grpc.ErrorResponse.Cause; -import biz.nynja.account.models.*; +import biz.nynja.account.grpc.GetByEmailRequest; +import biz.nynja.account.grpc.GetByPhoneNumberRequest; +import biz.nynja.account.grpc.GetByQrCodeRequest; +import biz.nynja.account.grpc.GetByUsernameRequest; +import biz.nynja.account.grpc.ProfileByProfileIdRequest; +import biz.nynja.account.grpc.ProfileResponse; +import biz.nynja.account.grpc.Role; +import biz.nynja.account.grpc.SearchResponse; +import biz.nynja.account.grpc.StatusResponse; +import biz.nynja.account.grpc.UpdateAccountRequest; +import biz.nynja.account.grpc.UpdateAuthenticationProviderRequest; import biz.nynja.account.models.Account; -import biz.nynja.account.repositories.*; +import biz.nynja.account.models.AccountByAuthenticationProvider; +import biz.nynja.account.models.AccountByProfileId; +import biz.nynja.account.models.AccountByQrCode; +import biz.nynja.account.models.AccountByUsername; +import biz.nynja.account.models.AuthenticationProvider; +import biz.nynja.account.models.ContactInfo; +import biz.nynja.account.models.PendingAccount; +import biz.nynja.account.models.PendingAccountByAuthenticationProvider; +import biz.nynja.account.models.Profile; +import biz.nynja.account.models.ProfileByAuthenticationProvider; +import biz.nynja.account.permissions.PermissionsInterceptor; +import biz.nynja.account.repositories.AccountByAuthenticationProviderRepository; +import biz.nynja.account.repositories.AccountByProfileIdRepository; +import biz.nynja.account.repositories.AccountByQrCodeRepository; +import biz.nynja.account.repositories.AccountByUsernameRepository; +import biz.nynja.account.repositories.AccountRepository; +import biz.nynja.account.repositories.AccountRepositoryAdditional; +import biz.nynja.account.repositories.PendingAccountRepository; +import biz.nynja.account.repositories.ProfileByAuthenticationProviderRepository; +import biz.nynja.account.repositories.ProfileRepository; import biz.nynja.account.services.decomposition.AccountProvider; import biz.nynja.account.services.decomposition.IncorrectAccountCountException; import biz.nynja.account.services.decomposition.ProfileProvider; import biz.nynja.account.utils.GrpcServerTestBase; import biz.nynja.account.utils.Util; +import io.grpc.Context; +import io.grpc.Contexts; import io.grpc.Metadata; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; import io.grpc.stub.MetadataUtils; /** @@ -174,23 +236,31 @@ public class AccountServiceTests extends GrpcServerTestBase { @MockBean private ProfileProvider profileProvider; + @MockBean + private AccessPointService accessPointService; + private AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub; private AccountServiceGrpc.AccountServiceBlockingStub searchServiceBlockingStub; @Before public void setupTest() { - String adminAccessToken = "eyJraWQiOiIyMDE4MDYwOCIsInR5cCI6IkpXVCIsImFsZyI6IkVTMjU2In0.eyJzdWIiOiJZemt4T0dOalpqWXRPRFl3WkMwME1EVmtMV0l5WVdFdE9USmhZV0pqTURKa1ltUXgiLCJhdWQiOiJibmx1YW1FPTpueW5qYTpueW5qYSIsInNjb3BlIjoiYWNjZXNzIiwicm9sZXMiOlsiQURNSU4iLCJVU0VSIl0sImlzcyI6Imh0dHBzOi8vYXV0aC5ueW5qYS5iaXovIiwiZXhwIjoxNTQzMzEwMTI4LCJpYXQiOjE1NDMzMDY1Mjh9.IdfUiOx_cPdp3lUIVbTKx51L9BrTigzWh3-yUc9-sLNMonO1GFaM2lJrpus4-Wi5G7D1lsX5PLWxt85pRJ0AnQ"; + AccessPoint accessPoint = new AccessPoint(); + String accountId = "0994566e-ac7b-45b2-b6ef-36440e44a15a"; + String adminAccessToken = "eyJraWQiOiIyMDE4MTEzMCIsInR5cCI6IkpXVCIsImFsZyI6IkVTMjU2In0.eyJzdWIiOiJNRGs1TkRVMk5tVXRZV00zWWkwME5XSXlMV0kyWldZdE16WTBOREJsTkRSaE1UVmgiLCJhdWQiOiJibmx1YW1FPTpueW5qYTpueW5qYSIsInNjb3BlIjoiYWNjZXNzIiwicm9sZXMiOlsiQURNSU4iLCJVU0VSIl0sImlzcyI6Imh0dHBzOi8vYXV0aC5ueW5qYS5iaXovIiwiZXhwIjoxNTQ1MTQzNTY0LCJpYXQiOjE1NDUxMzk5NjR9.w9LZEKB8GLRHDRpa80Lp2EFlGOjmJA4mWP6__Fk0JDu8HlJPk5GyXAm7081s6BqH99ixsKXsGGea6CuT0NHKqQ"; - accountServiceBlockingStub = AccountServiceGrpc.newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); - searchServiceBlockingStub = AccountServiceGrpc.newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + searchServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); Metadata headers = new Metadata(); - Metadata.Key key = Metadata.Key.of("accessToken", Metadata.ASCII_STRING_MARSHALLER); - headers.put(key, adminAccessToken); + Metadata.Key key = Metadata.Key.of("Authorization", Metadata.ASCII_STRING_MARSHALLER); + headers.put(key, "Bearer " + adminAccessToken); accountServiceBlockingStub = MetadataUtils.attachHeaders(accountServiceBlockingStub, headers); searchServiceBlockingStub = MetadataUtils.attachHeaders(searchServiceBlockingStub, headers); - + given(accessPointService.getAccessPoint(UUID.fromString(accountId), adminAccessToken)) + .willReturn(Optional.of(accessPoint)); } @Test @@ -204,7 +274,7 @@ public class AccountServiceTests extends GrpcServerTestBase { Optional accountResponse = Optional.of(response); given(accountProvider.getAccountByAccountId(request)).willReturn(accountResponse); - final AccountResponse reply = accountServiceBlockingStub.getAccountByAccountId(request); + final AccountResponse reply = accountServiceBlockingStub.getAccountByAccountId(request); assertNotNull("Reply should not be null", reply); assertTrue(String.format("Reply should contain profile ID '%s'", Util.ACCOUNT_ID.toString()), @@ -432,8 +502,6 @@ public class AccountServiceTests extends GrpcServerTestBase { .authenticationProviderAlreadyUsedInProfile(Mockito.any(AuthenticationProvider.class))) .willReturn(true); - final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc - .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); final CreatePendingAccountResponse reply = accountServiceBlockingStub.createPendingAccount(request); assertNotNull("Reply should not be null", reply); assertTrue(String.format("Reply should contain account id '%s'", Cause.AUTH_PROVIDER_ALREADY_USED), @@ -606,9 +674,6 @@ public class AccountServiceTests extends GrpcServerTestBase { .completePendingAccountCreation(Mockito.any(CompletePendingAccountCreationRequest.class))) .willReturn(null); - final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc - .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); - final AccountResponse respose = accountServiceBlockingStub.completePendingAccountCreation(request); assertNotNull("Reply should not be null", respose); assertTrue(String.format("Reply should contain cause '%s'", Cause.ERROR_CREATING_ACCOUNT), @@ -688,7 +753,8 @@ public class AccountServiceTests extends GrpcServerTestBase { final DeleteProfileRequest request = DeleteProfileRequest.newBuilder() .setProfileId(Util.PROFILE_ID_NOT_FOUND.toString()).build(); - given(accountRepositoryAdditional.deleteProfile(Util.PROFILE_ID_NOT_FOUND)).willReturn(Optional.of(Cause.ERROR_DELETING_PROFILE)); + given(accountRepositoryAdditional.deleteProfile(Util.PROFILE_ID_NOT_FOUND)) + .willReturn(Optional.of(Cause.ERROR_DELETING_PROFILE)); final StatusResponse reply = accountServiceBlockingStub.deleteProfile(request); assertNotNull("Reply should not be null", reply); @@ -735,7 +801,7 @@ public class AccountServiceTests extends GrpcServerTestBase { Util.PHONE_NUMBER_STREIGHT, AuthenticationType.PHONE.name())) .willReturn(profileByAuthenticationProvider); - final StatusResponse reply = accountServiceBlockingStub.addAuthenticationProviderToProfile(request); + final StatusResponse reply = accountServiceBlockingStub.addAuthenticationProviderToProfile(request); assertNotNull("Reply should not be null", reply); assertTrue(String.format("Reply should contain cause '%s'", Cause.AUTH_PROVIDER_ALREADY_USED), reply.getError().getCause().equals(Cause.AUTH_PROVIDER_ALREADY_USED)); @@ -745,8 +811,7 @@ public class AccountServiceTests extends GrpcServerTestBase { public void testAddAuthenticationProviderToProfileAuthProviderIdMissing() { final AddAuthenticationProviderRequest request = AddAuthenticationProviderRequest.newBuilder() .setAuthenticationProvider(AuthProviderDetails.newBuilder() - .setAuthenticationProvider(Util.PHONE_PROVIDER) - .setAuthenticationType(AuthenticationType.PHONE)) + .setAuthenticationProvider(Util.PHONE_PROVIDER).setAuthenticationType(AuthenticationType.PHONE)) .build(); given(accountRepositoryAdditional.addAuthenticationProvider(Mockito.any(UUID.class), @@ -780,7 +845,7 @@ public class AccountServiceTests extends GrpcServerTestBase { .findByAuthenticationProviderAndAuthenticationProviderType(Util.PHONE_PROVIDER, "PHONE")) .willReturn(null); - final StatusResponse reply = accountServiceBlockingStub.addAuthenticationProviderToProfile(request); + final StatusResponse reply = accountServiceBlockingStub.addAuthenticationProviderToProfile(request); assertNotNull("Reply should not be null", reply); assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_AUTH_PROVIDER_ID), reply.getError().getCause().equals(Cause.MISSING_AUTH_PROVIDER_ID)); @@ -1872,8 +1937,6 @@ public class AccountServiceTests extends GrpcServerTestBase { given(accountProvider.getAccountResponseByLoginOption(AuthenticationType.PHONE, Util.PHONE_PROVIDER)) .willReturn(accountResponse); - final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc - .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); final AccountResponse reply = accountServiceBlockingStub.getAccountByLoginOption(request); assertNotNull("Reply should not be null", reply); @@ -1891,8 +1954,6 @@ public class AccountServiceTests extends GrpcServerTestBase { given(accountProvider.getAccountResponseByLoginOption(AuthenticationType.EMAIL, Util.EMAIL)) .willReturn(accountResponse); - final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc - .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); final AccountResponse reply = accountServiceBlockingStub.getAccountByLoginOption(request); assertNotNull("Reply should not be null", reply); @@ -1908,8 +1969,6 @@ public class AccountServiceTests extends GrpcServerTestBase { given(accountProvider.getAccountResponseByLoginOption(AuthenticationType.EMAIL, Util.EMAIL_NOT_USED)) .willReturn(Optional.empty()); - final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc - .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); final AccountResponse reply = accountServiceBlockingStub.getAccountByLoginOption(request); assertNotNull("Reply should not be null", reply); @@ -1921,8 +1980,6 @@ public class AccountServiceTests extends GrpcServerTestBase { final AuthenticationProviderRequest request = AuthenticationProviderRequest.newBuilder() .setAuthenticationIdentifier(Util.PHONE_PROVIDER).build(); - final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc - .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); final AccountResponse reply = accountServiceBlockingStub.getAccountByLoginOption(request); assertNotNull("Reply should not be null", reply); @@ -1934,8 +1991,6 @@ public class AccountServiceTests extends GrpcServerTestBase { final AuthenticationProviderRequest request = AuthenticationProviderRequest.newBuilder() .setAuthenticationType(AuthenticationType.PHONE).build(); - final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc - .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); final AccountResponse reply = accountServiceBlockingStub.getAccountByLoginOption(request); assertNotNull("Reply should not be null", reply); @@ -1948,8 +2003,6 @@ public class AccountServiceTests extends GrpcServerTestBase { .setAuthenticationIdentifier(Util.INVALID_PHONE_PROVIDER) .setAuthenticationType(AuthenticationType.PHONE).build(); - final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc - .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); final AccountResponse reply = accountServiceBlockingStub.getAccountByLoginOption(request); assertNotNull("Reply should not be null", reply); @@ -1962,8 +2015,6 @@ public class AccountServiceTests extends GrpcServerTestBase { .setAuthenticationIdentifier(Util.INVALID_EMAIL).setAuthenticationType(AuthenticationType.EMAIL) .build(); - final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc - .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); final AccountResponse reply = accountServiceBlockingStub.getAccountByLoginOption(request); assertNotNull("Reply should not be null", reply);