diff --git a/pom.xml b/pom.xml
index 7a41f5d74bc1599557bcc375794b4a8ce12f2456..3299435feff6405e4709acfab8a848d8f4caf572 100644
--- a/pom.xml
+++ b/pom.xml
@@ -93,6 +93,19 @@
+
+ com.datastax.cassandra
+ cassandra-driver-mapping
+ 3.4.0
+
+
+
+
+ org.springframework.data
+ spring-data-cassandra
+ 2.1.3.RELEASE
+
+
com.google.guava
guava
@@ -145,6 +158,13 @@
3.8.1
+
+
+ org.springframework.data
+ spring-data-commons-core
+ 1.4.1.RELEASE
+
+
com.sun.mail
javax.mail
diff --git a/src/main/java/biz/nynja/account/accesspoints/AccessPoint.java b/src/main/java/biz/nynja/account/accesspoints/AccessPoint.java
index 978d6e18398521800bd5a706400663827dc62a9b..a794bebef0dd35307253a795a4ec8319768c7597 100644
--- a/src/main/java/biz/nynja/account/accesspoints/AccessPoint.java
+++ b/src/main/java/biz/nynja/account/accesspoints/AccessPoint.java
@@ -8,18 +8,27 @@ 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
+import com.datastax.driver.mapping.annotations.ClusteringColumn;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+//import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn;
+import com.datastax.driver.mapping.annotations.Table;
+
+@Table(name = "accesspoint")
public class AccessPoint implements Serializable {
- @PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED)
+ @PartitionKey
+ @Column(name = "accountid")
private UUID accountId;
- @PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.CLUSTERED)
+ @ClusteringColumn
+ @Column(name = "accesstoken")
private String accessToken;
+ @Column(name = "deviceid")
private String deviceId;
+ @Column(name = "creationtimestamp")
private Long creationTimestamp;
+ @Column(name = "expirationtimestamp")
private Long expirationTimestamp;
public String getDeviceId() {
diff --git a/src/main/java/biz/nynja/account/accesspoints/AccessPointRepository.java b/src/main/java/biz/nynja/account/accesspoints/AccessPointRepository.java
index 6888c6df5cea70596cc543c8bdda533c5ef1b7bc..15a20bb98f2cd32ec172d6b672185121d03e1951 100644
--- a/src/main/java/biz/nynja/account/accesspoints/AccessPointRepository.java
+++ b/src/main/java/biz/nynja/account/accesspoints/AccessPointRepository.java
@@ -1,17 +1,54 @@
-/**
- * Copyright (C) 2018 Nynja Inc. All rights reserved.
- */
package biz.nynja.account.accesspoints;
-
+import java.util.List;
import java.util.UUID;
-import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.stereotype.Repository;
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Session;
+import com.datastax.driver.core.querybuilder.QueryBuilder;
+import com.datastax.driver.mapping.Mapper;
+import com.datastax.driver.mapping.MappingManager;
+
@Repository
-public interface AccessPointRepository
- extends CassandraRepository {
-
- AccessPoint findByAccountIdAndAccessToken(UUID accountId, String accessToken);
+public class AccessPointRepository {
+
+ private Mapper mapper;
+ private Session session;
+
+ private static final String TABLE = "accesspoint";
+
+ public AccessPointRepository(MappingManager mappingManager) {
+ createTable(mappingManager.getSession());
+ this.mapper = mappingManager.mapper(AccessPoint.class);
+ this.session = mappingManager.getSession();
+ }
+
+ private void createTable(Session session) {
+ // use SchemaBuilder to create table
+ }
+
+ public AccessPoint find(UUID accountId, String accessToken) {
+ return mapper.get(accountId, accessToken);
+ }
+
+// public List findAll() {
+// final ResultSet result = session.execute(QueryBuilder.select().all().from(TABLE));
+// return mapper.map(result).all();
+// }
+
+// public AccessPoint findByAccountIdAndAccessToken(UUID accountId, String accessToken) {
+// final ResultSet result = session.execute(QueryBuilder.select().all().from(TABLE)
+// .where(QueryBuilder.eq("accountid", accountId)).and(QueryBuilder.eq("accesstoken", accessToken)));
+// return mapper.map(result).all();
+// }
+
+// public void delete(String country, String firstName, String secondName, UUID id) {
+// mapper.delete(country, firstName, secondName, id);
+// }
+// public AccessPoint save(AccessPoint person) {
+// mapper.save(person);
+// return person;
+// }
}
diff --git a/src/main/java/biz/nynja/account/accesspoints/AccessPointService.java b/src/main/java/biz/nynja/account/accesspoints/AccessPointService.java
index ca24bd22e1bb812ec898871c76dd9cdd1d7793ae..8d07433c6994c3ac4d2c7b5c27a89b277dd1080f 100644
--- a/src/main/java/biz/nynja/account/accesspoints/AccessPointService.java
+++ b/src/main/java/biz/nynja/account/accesspoints/AccessPointService.java
@@ -21,7 +21,7 @@ public class AccessPointService {
}
public Optional getAccessPoint(UUID accountId, String accessToken) {
- AccessPoint accessPoint = accessPointRepository.findByAccountIdAndAccessToken(accountId, accessToken);
+ AccessPoint accessPoint = accessPointRepository.find(accountId, accessToken);
if (accessPoint != null) {
return Optional.of(accessPoint);
}
diff --git a/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java b/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java
index 20be301ffa8a81bd42aaea6160e46f4d00a1885e..10032abd4822a58213b4432a41192f40fa435d42 100644
--- a/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java
+++ b/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java
@@ -18,20 +18,23 @@ import com.datastax.driver.core.UDTValue;
import com.datastax.driver.core.UserType;
import biz.nynja.account.codecs.AuthenticationProviderCodec;
-import biz.nynja.account.configuration.CassandraAccountConfig;
+//import biz.nynja.account.configuration.CassandraAccountConfig;
+import biz.nynja.account.configuration.CassandraConfig;
import biz.nynja.account.models.AuthenticationProvider;
@Configuration
public class PreparedStatementsCache {
- private final CassandraAccountConfig cassandraConfig;
+ private final CassandraConfig cassandraConfig;
+// private final CassandraAccountConfig cassandraConfig;
private final Session session;
private static Map statementsCache;
@Autowired
- public PreparedStatementsCache(Session session, CassandraAccountConfig cassandraConfig) {
+// public PreparedStatementsCache(Session session, CassandraAccountConfig cassandraConfig) {
+ public PreparedStatementsCache(Session session, CassandraConfig 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
index 1c67b4d8dcadc45e77c751658ae04a84f0fb6696..50034936f8a4589a423489d9d103e2829a5ac582 100644
--- a/src/main/java/biz/nynja/account/configuration/CassandraAccessPointConfig.java
+++ b/src/main/java/biz/nynja/account/configuration/CassandraAccessPointConfig.java
@@ -1,26 +1,61 @@
-//package biz.nynja.account.configuration;
-//
-//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.repository.config.EnableCassandraRepositories;
-//
-//@Configuration
-//@EnableCassandraRepositories(basePackages = { "biz.nynja.account.accesspoints" })
-//@ConditionalOnMissingClass("org.springframework.test.context.junit4.SpringRunner")
-//public class CassandraAccessPointConfig extends CassandraBaseConfig {
-//
-// @Value("${spring.data.cassandra.accesspoint.keyspace-name}")
-// private String keyspaceName;
-//
-// @Override
-// protected String getKeyspaceName() {
-// return keyspaceName;
-// }
-//
-// @Override
-// public String[] getEntityBasePackages() {
-// return new String[] { "biz.nynja.account.accesspoints" };
-// }
-//
-//}
+package biz.nynja.account.configuration;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories;
+
+import com.datastax.driver.core.Cluster;
+import com.datastax.driver.core.Session;
+import com.datastax.driver.core.schemabuilder.SchemaBuilder;
+import com.datastax.driver.mapping.DefaultNamingStrategy;
+import com.datastax.driver.mapping.DefaultPropertyMapper;
+import com.datastax.driver.mapping.MappingConfiguration;
+import com.datastax.driver.mapping.MappingManager;
+import com.datastax.driver.mapping.NamingConventions;
+import com.datastax.driver.mapping.PropertyMapper;
+
+@Configuration
+@EnableCassandraRepositories( basePackages = { "biz.nynja.account.accesspoints" } )
+public class CassandraAccessPointConfig {
+
+ @Bean
+ public Cluster cluster(
+ @Value("${spring.data.cassandra.contact-points}") String host,
+// @Value("${cassandra.cluster.name:cluster}") String clusterName,
+ @Value("${spring.data.cassandra.port}") int port) {
+// return Cluster.builder().addContactPoint(host).withPort(port).withClusterName(clusterName).build();
+ return Cluster.builder().addContactPoint(host).withPort(port).build();
+ }
+
+ @Bean
+ public Session session(Cluster cluster,
+ @Value("${spring.data.cassandra.accesspoint.keyspace-name}") String keyspace) throws IOException {
+ final Session session = cluster.connect();
+ setupKeyspace(session, keyspace);
+ return session;
+ }
+
+ private void setupKeyspace(Session session, String keyspace) throws IOException {
+ final Map replication = new HashMap<>();
+ replication.put("class", "SimpleStrategy");
+ replication.put("replication_factor", 1);
+// session.execute(SchemaBuilder.createKeyspace(keyspace).ifNotExists().with().replication(replication));
+ session.execute("USE " + keyspace);
+ // String[] statements = split(IOUtils.toString(getClass().getResourceAsStream("/cql/setup.cql")), ";");
+ // Arrays.stream(statements).map(statement -> normalizeSpace(statement) + ";").forEach(session::execute);
+ }
+
+ @Bean
+ public MappingManager mappingManager(Session session) {
+ final PropertyMapper propertyMapper = new DefaultPropertyMapper().setNamingStrategy(
+ new DefaultNamingStrategy(NamingConventions.LOWER_CAMEL_CASE, NamingConventions.LOWER_SNAKE_CASE));
+ final MappingConfiguration configuration = MappingConfiguration.builder().withPropertyMapper(propertyMapper)
+ .build();
+ return new MappingManager(session, configuration);
+ }
+}
\ 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
deleted file mode 100644
index 547b5353b18030c44129fdbed96f2594da05fdd6..0000000000000000000000000000000000000000
--- a/src/main/java/biz/nynja/account/configuration/CassandraAccountConfig.java
+++ /dev/null
@@ -1,90 +0,0 @@
-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.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Primary;
-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.core.CassandraAdminTemplate;
-import org.springframework.data.cassandra.core.cql.keyspace.CreateKeyspaceSpecification;
-import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories;
-
-import biz.nynja.account.StartupScriptsListener;
-
-@Configuration
-@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;
- }
-
- @Override
- public String[] getEntityBasePackages() {
- return new String[] { "biz.nynja.account.models", "biz.nynja.account.repositories" };
- }
-
- @Override
- public SchemaAction getSchemaAction() {
- return SchemaAction.CREATE_IF_NOT_EXISTS;
- }
-
- /**
- * See {@link StartupScriptsListener} for scripts
- * that require JPA annotated tables
- */
- @Override
- protected List getStartupScripts() {
- return super.getStartupScripts();
- }
-
- public String getConfiguredKeyspaceName() {
- return getKeyspaceName();
- }
-
- @Override
- protected List getKeyspaceCreations() {
- CreateKeyspaceSpecification specification = CreateKeyspaceSpecification.createKeyspace(getKeyspaceName())
- .ifNotExists().withSimpleReplication();
- return Arrays.asList(specification);
- }
-
- @Override
-// @Primary
- @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;
- }
-}
diff --git a/src/main/java/biz/nynja/account/configuration/CassandraBaseConfig.java b/src/main/java/biz/nynja/account/configuration/CassandraBaseConfig.java
deleted file mode 100644
index 98dcffb57bdd02f56192736d5975772f2d78251a..0000000000000000000000000000000000000000
--- a/src/main/java/biz/nynja/account/configuration/CassandraBaseConfig.java
+++ /dev/null
@@ -1,51 +0,0 @@
-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.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.core.cql.keyspace.CreateKeyspaceSpecification;
-import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories;
-
-@Configuration
-@EnableCassandraRepositories(basePackages = { "biz.nynja.account.accesspoints" }) // to be moved to CassandraAccessPointConfig
-@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;
- }
-
- public CassandraAdminOperations cassandraTemplate(CassandraSessionFactoryBean session) throws Exception {
- return null;
- }
-
-}
diff --git a/src/main/java/biz/nynja/account/configuration/CassandraConfig.java b/src/main/java/biz/nynja/account/configuration/CassandraConfig.java
index 35906d76639a696e7429abea2ecf138b195d0f81..f5aedc7e42a3ef4c992266748803f4ef7ec27a91 100644
--- a/src/main/java/biz/nynja/account/configuration/CassandraConfig.java
+++ b/src/main/java/biz/nynja/account/configuration/CassandraConfig.java
@@ -1,77 +1,77 @@
-///**
-// * 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.account.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();
-// }
-//}
\ No newline at end of file
+/**
+ * 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.account.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();
+ }
+}
diff --git a/src/main/java/biz/nynja/account/permissions/PermissionsInterceptor.java b/src/main/java/biz/nynja/account/permissions/PermissionsInterceptor.java
index b67ce520a1b210f0b2d2529906c641c8fd505a1f..962d7f1215b60032407a3cf28428b973643af7f5 100644
--- a/src/main/java/biz/nynja/account/permissions/PermissionsInterceptor.java
+++ b/src/main/java/biz/nynja/account/permissions/PermissionsInterceptor.java
@@ -1,177 +1,178 @@
-///**
-// * Copyright (C) 2018 Nynja Inc. All rights reserved.
-// */
-//package biz.nynja.account.permissions;
-//
-//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:
-// * - 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;
-// * - if rpc has either @Permitted(role = RoleConstants.ANY) - PERMISSION GRANTED;
-// * - if rpc has an annotation @PerformPermissionCheck - @Permitted annotations are checked and an additional check is performed in the rpc;
-// * - if no role from the request matches any Permitted annotation for the rpc - PERMISSION DENIED
-// */
-//
-//@GRpcGlobalInterceptor
-//public class PermissionsInterceptor implements ServerInterceptor {
-//
-// private static final Logger logger = LoggerFactory.getLogger(PermissionsInterceptor.class);
-// private static final Class SERVICE_CLASS = AccountServiceImpl.class;
-//
-// public static final Metadata.Key ACCESS_TOKEN_METADATA = Metadata.Key.of("Authorization",
-// ASCII_STRING_MARSHALLER);
-// public static final Context.Key ACCESS_TOKEN_CTX = Context.key("accessToken");
-//
-// 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);
-//
-// /*
-// * 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 && 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) {
-// // Get name of endpoint/rpc called
-// String callName = call.getMethodDescriptor().getFullMethodName();
-// return callName.substring(callName.lastIndexOf('/') + 1);
-// }
-//
-//// 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;
-//
-// Claim claim = decodedToken.getClaim("roles");
-// if (claim != null) {
-// requestingRoles = claim.asArray(String.class);
-// }
-// return requestingRoles;
-// }
-//
-// private Method getMethod(String rpc) {
-// // Get the rpc method called
-// Method[] allMethods = SERVICE_CLASS.getDeclaredMethods();
-//
-// for (Method method : allMethods) {
-// if (method.getName().equals(rpc)) {
-// return method;
-// }
-// }
-// return null;
-// }
-//
-// private boolean checkPermissions(String[] requestingRoles, Permitted[] permittedRoles) {
-//
-// for (Permitted permitted : permittedRoles) {
-// if (permitted.role().equals(RoleConstants.ANY)) {
-// return true;
-// }
-// for (String role : requestingRoles) {
-// if (role.equals(permitted.role()) || role.equals(RoleConstants.ACCOUNT_ADMIN)
-// || role.equals(RoleConstants.AUTH_SERVICE)) {
-// return true;
-// }
-// }
-// }
-// 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;
-// }
-//
-//}
+/**
+ * Copyright (C) 2018 Nynja Inc. All rights reserved.
+ */
+package biz.nynja.account.permissions;
+
+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.accesspoints.AccessPoint;
+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:
+ * - 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;
+ * - if rpc has either @Permitted(role = RoleConstants.ANY) - PERMISSION GRANTED;
+ * - if rpc has an annotation @PerformPermissionCheck - @Permitted annotations are checked and an additional check is performed in the rpc;
+ * - if no role from the request matches any Permitted annotation for the rpc - PERMISSION DENIED
+ */
+
+@GRpcGlobalInterceptor
+public class PermissionsInterceptor implements ServerInterceptor {
+
+ private static final Logger logger = LoggerFactory.getLogger(PermissionsInterceptor.class);
+ private static final Class SERVICE_CLASS = AccountServiceImpl.class;
+
+ public static final Metadata.Key ACCESS_TOKEN_METADATA = Metadata.Key.of("Authorization",
+ ASCII_STRING_MARSHALLER);
+ public static final Context.Key ACCESS_TOKEN_CTX = Context.key("accessToken");
+
+ 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);
+
+ /*
+ * 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 && 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) {
+ // Get name of endpoint/rpc called
+ String callName = call.getMethodDescriptor().getFullMethodName();
+ return callName.substring(callName.lastIndexOf('/') + 1);
+ }
+
+ 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;
+
+ Claim claim = decodedToken.getClaim("roles");
+ if (claim != null) {
+ requestingRoles = claim.asArray(String.class);
+ }
+ return requestingRoles;
+ }
+
+ private Method getMethod(String rpc) {
+ // Get the rpc method called
+ Method[] allMethods = SERVICE_CLASS.getDeclaredMethods();
+
+ for (Method method : allMethods) {
+ if (method.getName().equals(rpc)) {
+ return method;
+ }
+ }
+ return null;
+ }
+
+ private boolean checkPermissions(String[] requestingRoles, Permitted[] permittedRoles) {
+
+ for (Permitted permitted : permittedRoles) {
+ if (permitted.role().equals(RoleConstants.ANY)) {
+ return true;
+ }
+ for (String role : requestingRoles) {
+ if (role.equals(permitted.role()) || role.equals(RoleConstants.ACCOUNT_ADMIN)
+ || role.equals(RoleConstants.AUTH_SERVICE)) {
+ return true;
+ }
+ }
+ }
+ 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;
+ }
+
+}