From 65d950c9201f28ce42fbf719f005c573d5829e78 Mon Sep 17 00:00:00 2001 From: Stoyan Tzenkov Date: Wed, 30 Jan 2019 15:33:29 +0200 Subject: [PATCH 1/3] NY-6804: Delete ACCOUNT_ADMIN accesspoints when ACCUNT_ADMIN role is taken away from user. Signed-off-by: Stoyan Tzenkov --- .../accesspoints/AccessPointRepository.java | 9 +++ .../accesspoints/AccessPointService.java | 41 +++++++++++++ .../AccountRepositoryAdditionalImpl.java | 57 +++++++++++++++++-- 3 files changed, 101 insertions(+), 6 deletions(-) diff --git a/src/main/java/biz/nynja/account/accesspoints/AccessPointRepository.java b/src/main/java/biz/nynja/account/accesspoints/AccessPointRepository.java index 84b4c0d..3fe188d 100644 --- a/src/main/java/biz/nynja/account/accesspoints/AccessPointRepository.java +++ b/src/main/java/biz/nynja/account/accesspoints/AccessPointRepository.java @@ -24,4 +24,13 @@ public interface AccessPointRepository @Query("delete from auth.accesspoint where accountid=:accountId;") void deleteById(@Param("accountId") UUID accountId); + + @Query("delete from auth.accesspoint where accountid=:accountId and accesstoken=:accessToken;") + void deleteByPrimary(@Param("accountId") UUID accountId, @Param("accessToken") String accessToken); + + @Query("INSERT INTO auth.accesspoint (accountid, deviceid, accesstoken, creationtimestamp, expirationtimestamp) VALUES ( :accountId, :deviceId, :accessToken, :creationTimestamp, :expirationTimestamp ) USING TTL :expiresIn;") + void save(@Param("accountId") UUID accountId, @Param("deviceId") String deviceId, + @Param("accessToken") String accessToken, @Param("creationTimestamp") long creationTimestamp, + @Param("expirationTimestamp") long expirationTimestamp, @Param("expiresIn") int expiresIn); + } diff --git a/src/main/java/biz/nynja/account/accesspoints/AccessPointService.java b/src/main/java/biz/nynja/account/accesspoints/AccessPointService.java index 74cc4af..3c02e91 100644 --- a/src/main/java/biz/nynja/account/accesspoints/AccessPointService.java +++ b/src/main/java/biz/nynja/account/accesspoints/AccessPointService.java @@ -4,6 +4,7 @@ package biz.nynja.account.accesspoints; import java.util.Date; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -11,6 +12,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; +import biz.nynja.account.accesspoints.AccessPoint; + @Service public class AccessPointService { private final Logger logger = LoggerFactory.getLogger(AccessPointService.class); @@ -20,6 +23,19 @@ public class AccessPointService { this.accessPointRepository = accessPointRepository; } + public Optional saveAccessPoint(AccessPoint accessPoint) { + int expiresIn = (int) (accessPoint.getExpirationTimestamp() - accessPoint.getCreationTimestamp()); + try { + accessPointRepository.save(accessPoint.getAccountId(), accessPoint.getDeviceId(), + accessPoint.getAccessToken(), accessPoint.getCreationTimestamp(), + accessPoint.getExpirationTimestamp(), expiresIn); + } catch (Exception e) { + logger.error("Error saving accesspoint record for accoint ID {}. Error message: {}", + accessPoint.getAccountId(), e.getMessage()); + } + return Optional.empty(); + } + public Optional getAccessPoint(UUID accountId, String accessToken) { try { AccessPoint accessPoint = accessPointRepository.findByAccountIdAndAccessToken(accountId, accessToken); @@ -33,6 +49,31 @@ public class AccessPointService { return Optional.empty(); } + public Optional> getAllAccessPointsForAccount(UUID accountId) { + try { + List accessPointsForAccount = accessPointRepository.findAllByAccountId(accountId); + if (accessPointsForAccount != null) { + return Optional.of(accessPointsForAccount); + } + } catch (Exception e) { + logger.error("Error getting accesspoints for account {}. Message: {}, cause: {}", accountId, e.getMessage(), + e.getCause()); + } + return Optional.empty(); + } + + public boolean deleteAccessPoint(UUID accountId, String accessToken) { + try { + accessPointRepository.deleteByPrimary(accountId, accessToken); + logger.info("AccessPoint successfully deleted from the DB."); + return true; + } catch (Exception e) { + logger.error("Error deleting access point record for account {} from DB. Message: {}, cause: {}.", accountId, + e.getMessage(), e.getCause()); + return false; + } + } + public boolean deleteAccessPointsForAccount(UUID accountId) { try { accessPointRepository.deleteById(accountId); diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java index 48aaa80..d5639c4 100644 --- a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java @@ -29,12 +29,16 @@ import org.springframework.data.cassandra.core.WriteResult; import org.springframework.data.cassandra.core.cql.CqlOperations; import org.springframework.stereotype.Service; +import com.auth0.jwt.JWT; +import com.auth0.jwt.interfaces.Claim; +import com.auth0.jwt.interfaces.DecodedJWT; import com.datastax.driver.core.BatchStatement; import com.datastax.driver.core.BoundStatement; import com.datastax.driver.core.PreparedStatement; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.Session; +import biz.nynja.account.accesspoints.AccessPoint; import biz.nynja.account.accesspoints.AccessPointService; import biz.nynja.account.components.AccountServiceHelper; import biz.nynja.account.components.StatementsPool; @@ -113,7 +117,7 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio this.erlangAccountBridge = erlangAccountBridge; this.permissionsValidator = permissionsValidator; this.accountDataConfiguration = accountDataConfiguration; - this.accessPointService= accessPointService; + this.accessPointService = accessPointService; } @PostConstruct @@ -217,18 +221,18 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio public Account updateAccount(UpdateAccountRequest request) { Transaction sagaTransaction = new SagaTransaction(cassandraTemplate); - + Account existingAccount = accountRepository.findByAccountId(UUID.fromString(request.getAccountId())); if (existingAccount == null) { logger.error("Existing account with the provided id {} was not found.", request.getAccountId()); logger.debug("Existing account with the provided id {} was not found.", request.getAccountId()); return null; } - + + Set existingRoles = existingAccount.getRoles().stream().map(Role::valueOf).collect(Collectors.toSet()); if (!permissionsValidator.isAdminToken()) { // No permission to update roles, load old ones - Set roles = existingAccount.getRoles().stream().map(Role::valueOf).collect(Collectors.toSet()); - request = UpdateAccountRequest.newBuilder(request).clearRoles().addAllRoles(roles).build(); +// request = UpdateAccountRequest.newBuilder(request).clearRoles().addAllRoles(existingRoles).build(); } Long timeUpdated = Instant.now().toEpochMilli(); WriteResult wr = null; @@ -262,6 +266,14 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio || request.getAccessStatus().equals(AccessStatus.SUSPENDED)) { if (!accessPointService.deleteAccessPointsForAccount(UUID.fromString(request.getAccountId()))) { logger.error("Error deleting accesspoints from the DB for account {}.", request.getAccountId()); + return null; + } + } + Set newRoles = new HashSet(request.getRolesList()); + if (existingRoles.contains(Role.ACCOUNT_ADMIN) && newRoles.size() == 1 + && newRoles.contains(Role.USER)) { + if (!deleteADMINAccessPoints(request.getAccountId())) { + return null; } } Account updatedAccount = accountRepository.findByAccountId(UUID.fromString(request.getAccountId())); @@ -271,6 +283,38 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio return null; } + private boolean deleteADMINAccessPoints(String accountId) { + Optional> accessPointsForAccount = accessPointService + .getAllAccessPointsForAccount(UUID.fromString(accountId)); + if (accessPointsForAccount.isPresent()) { + for (AccessPoint accessPoint : accessPointsForAccount.get()) { + String accessToken = accessPoint.getAccessToken(); + DecodedJWT decodedToken = JWT.decode(accessToken); + List accesspointRoles = getRolesFromAccessToken(decodedToken); + if (accesspointRoles.contains(Role.ACCOUNT_ADMIN.name())) { + if (!accessPointService.deleteAccessPoint(UUID.fromString(accountId), accessToken)) { + logger.error("Error deleting accesspoint record from DB for account with id {} and role {}.", + accountId, Role.ACCOUNT_ADMIN.name()); + return false; + } + + } + } + } + return true; + } + + private List getRolesFromAccessToken(DecodedJWT decodedToken) { + // Get roles from access token + List requestingRoles = null; + + Claim claim = decodedToken.getClaim("roles"); + if (claim != null) { + requestingRoles = claim.asList(String.class); + } + return requestingRoles; + } + public boolean deleteAccount(UUID accountId) { Transaction sagaTransaction = new SagaTransaction(cassandraTemplate); Account existingAccount = accountRepository.findByAccountId(accountId); @@ -959,7 +1003,8 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio return Optional.empty(); } if (!profileByAuthProvider.getSearchable().booleanValue()) { - logger.info("Record found in DB but is not searchable for the profileId: {}", profileByAuthProvider.getProfileId()); + logger.info("Record found in DB but is not searchable for the profileId: {}", + profileByAuthProvider.getProfileId()); return Optional.empty(); } -- GitLab From 7c75713c89684c927dda9e544d3c7add48e33908 Mon Sep 17 00:00:00 2001 From: Stoyan Tzenkov Date: Wed, 30 Jan 2019 16:02:50 +0200 Subject: [PATCH 2/3] NY-6804: Some corrections. Signed-off-by: Stoyan Tzenkov --- .../repositories/AccountRepositoryAdditionalImpl.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java index d5639c4..dccb8f8 100644 --- a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java @@ -232,7 +232,7 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio Set existingRoles = existingAccount.getRoles().stream().map(Role::valueOf).collect(Collectors.toSet()); if (!permissionsValidator.isAdminToken()) { // No permission to update roles, load old ones -// request = UpdateAccountRequest.newBuilder(request).clearRoles().addAllRoles(existingRoles).build(); + request = UpdateAccountRequest.newBuilder(request).clearRoles().addAllRoles(existingRoles).build(); } Long timeUpdated = Instant.now().toEpochMilli(); WriteResult wr = null; @@ -270,8 +270,8 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio } } Set newRoles = new HashSet(request.getRolesList()); - if (existingRoles.contains(Role.ACCOUNT_ADMIN) && newRoles.size() == 1 - && newRoles.contains(Role.USER)) { + if (existingRoles.size() > 1 && existingRoles.contains(Role.USER) + && newRoles.size() == 1 && newRoles.contains(Role.USER)) { if (!deleteADMINAccessPoints(request.getAccountId())) { return null; } @@ -291,7 +291,7 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio String accessToken = accessPoint.getAccessToken(); DecodedJWT decodedToken = JWT.decode(accessToken); List accesspointRoles = getRolesFromAccessToken(decodedToken); - if (accesspointRoles.contains(Role.ACCOUNT_ADMIN.name())) { + if (accesspointRoles.contains(Role.ACCOUNT_ADMIN.name()) || accesspointRoles.contains(Role.AUTHENTICATION_ADMIN.name())) { if (!accessPointService.deleteAccessPoint(UUID.fromString(accountId), accessToken)) { logger.error("Error deleting accesspoint record from DB for account with id {} and role {}.", accountId, Role.ACCOUNT_ADMIN.name()); -- GitLab From 0572d204a100f5ac1767480b8545e318c797200c Mon Sep 17 00:00:00 2001 From: Stoyan Tzenkov Date: Wed, 30 Jan 2019 16:33:46 +0200 Subject: [PATCH 3/3] NY-6804: Condition refined. Signed-off-by: Stoyan Tzenkov --- .../account/repositories/AccountRepositoryAdditionalImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java index dccb8f8..5b1c412 100644 --- a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java @@ -270,8 +270,7 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio } } Set newRoles = new HashSet(request.getRolesList()); - if (existingRoles.size() > 1 && existingRoles.contains(Role.USER) - && newRoles.size() == 1 && newRoles.contains(Role.USER)) { + if (newRoles.size() != existingRoles.size() || !existingRoles.containsAll(newRoles)) { if (!deleteADMINAccessPoints(request.getAccountId())) { return null; } -- GitLab