From 53d82e4f2db8033cd612cd862efcd299b1893003 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Wed, 28 Jun 2017 11:16:55 -0700 Subject: [PATCH] Allow IAM Principals to perform full CRUD on SBDs (#50) Allow IAM Principals to list and delete sdbs so that they can perform full CRUD on SDBs --- .../nike/cerberus/dao/SafeDepositBoxDao.java | 4 + .../endpoints/sdb/DeleteSafeDepositBox.java | 2 +- .../endpoints/sdb/GetSafeDepositBoxV1.java | 13 +- .../endpoints/sdb/GetSafeDepositBoxV2.java | 14 +- .../endpoints/sdb/GetSafeDepositBoxes.java | 2 +- .../endpoints/sdb/UpdateSafeDepositBoxV1.java | 6 +- .../endpoints/sdb/UpdateSafeDepositBoxV2.java | 3 +- .../cerberus/mapper/SafeDepositBoxMapper.java | 2 + .../cerberus/security/VaultAuthPrincipal.java | 16 ++ .../service/AuthenticationService.java | 7 +- .../service/SafeDepositBoxService.java | 183 ++++++++++-------- .../cerberus/mapper/SafeDepositBoxMapper.xml | 21 ++ .../service/AuthenticationServiceTest.java | 2 +- 13 files changed, 165 insertions(+), 110 deletions(-) diff --git a/src/main/java/com/nike/cerberus/dao/SafeDepositBoxDao.java b/src/main/java/com/nike/cerberus/dao/SafeDepositBoxDao.java index 23305d8e8..3b71df086 100644 --- a/src/main/java/com/nike/cerberus/dao/SafeDepositBoxDao.java +++ b/src/main/java/com/nike/cerberus/dao/SafeDepositBoxDao.java @@ -49,6 +49,10 @@ public List getUserAssociatedSafeDepositBoxes(final Set getIamPrincipalAssociatedSafeDepositBoxes(final String iamPrincipalArn) { + return safeDepositBoxMapper.getIamPrincipalAssociatedSafeDepositBoxes(iamPrincipalArn); + } + public List getSafeDepositBoxes(final int limit, final int offset) { return safeDepositBoxMapper.getSafeDepositBoxes(limit, offset); } diff --git a/src/main/java/com/nike/cerberus/endpoints/sdb/DeleteSafeDepositBox.java b/src/main/java/com/nike/cerberus/endpoints/sdb/DeleteSafeDepositBox.java index 0eef88f87..ad0e8dd7b 100644 --- a/src/main/java/com/nike/cerberus/endpoints/sdb/DeleteSafeDepositBox.java +++ b/src/main/java/com/nike/cerberus/endpoints/sdb/DeleteSafeDepositBox.java @@ -79,7 +79,7 @@ private ResponseInfo deleteSafeDepositBox(final RequestInfo request) log.info("Delete SDB Event: the principal: {} is attempting to delete sdb name: '{}' and id: '{}'", vaultAuthPrincipal.getName(), sdbName, sdbId); - safeDepositBoxService.deleteSafeDepositBox(vaultAuthPrincipal.getUserGroups(), sdbId); + safeDepositBoxService.deleteSafeDepositBox(vaultAuthPrincipal, sdbId); return ResponseInfo.newBuilder().withHttpStatusCode(HttpResponseStatus.OK.code()) .withHeaders(new DefaultHttpHeaders().set(HEADER_X_REFRESH_TOKEN, Boolean.TRUE.toString())) .build(); diff --git a/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxV1.java b/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxV1.java index 2295468b7..70696ada8 100644 --- a/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxV1.java +++ b/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxV1.java @@ -16,7 +16,6 @@ package com.nike.cerberus.endpoints.sdb; -import com.nike.backstopper.apierror.sample.SampleCoreApiError; import com.nike.backstopper.exception.ApiException; import com.nike.cerberus.domain.SafeDepositBoxV1; import com.nike.cerberus.error.DefaultApiError; @@ -79,16 +78,12 @@ public ResponseInfo getSafeDepositBox(final RequestInfo log.info("Read SDB Event: the principal: {} is attempting to read sdb name: '{}' and id: '{}'", vaultAuthPrincipal.getName(), sdbName, sdbId); - final Optional safeDepositBox = - safeDepositBoxService.getAssociatedSafeDepositBoxV1( - vaultAuthPrincipal.getUserGroups(), + final SafeDepositBoxV1 safeDepositBox = + safeDepositBoxService.getSDBAndValidatePrincipalAssociationV1( + vaultAuthPrincipal, sdbId); - if (safeDepositBox.isPresent()) { - return ResponseInfo.newBuilder(safeDepositBox.get()).build(); - } - - throw ApiException.newBuilder().withApiErrors(SampleCoreApiError.NOT_FOUND).build(); + return ResponseInfo.newBuilder(safeDepositBox).build(); } throw ApiException.newBuilder().withApiErrors(DefaultApiError.AUTH_BAD_CREDENTIALS).build(); diff --git a/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxV2.java b/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxV2.java index d86a08810..fef7777b0 100644 --- a/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxV2.java +++ b/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxV2.java @@ -17,9 +17,7 @@ package com.nike.cerberus.endpoints.sdb; -import com.nike.backstopper.apierror.sample.SampleCoreApiError; import com.nike.backstopper.exception.ApiException; -import com.nike.cerberus.domain.SafeDepositBoxV1; import com.nike.cerberus.domain.SafeDepositBoxV2; import com.nike.cerberus.error.DefaultApiError; import com.nike.cerberus.security.CmsRequestSecurityValidator; @@ -80,16 +78,12 @@ public ResponseInfo getSafeDepositBox(final RequestInfo log.info("Read SDB Event: the principal: {} is attempting to read sdb name: '{}' and id: '{}'", vaultAuthPrincipal.getName(), sdbName, sdbId); - final Optional safeDepositBox = - safeDepositBoxService.getAssociatedSafeDepositBoxV2( - vaultAuthPrincipal.getUserGroups(), + final SafeDepositBoxV2 safeDepositBox = + safeDepositBoxService.getSDBAndValidatePrincipalAssociationV2( + vaultAuthPrincipal, sdbId); - if (safeDepositBox.isPresent()) { - return ResponseInfo.newBuilder(safeDepositBox.get()).build(); - } - - throw ApiException.newBuilder().withApiErrors(SampleCoreApiError.NOT_FOUND).build(); + return ResponseInfo.newBuilder(safeDepositBox).build(); } throw ApiException.newBuilder().withApiErrors(DefaultApiError.AUTH_BAD_CREDENTIALS).build(); diff --git a/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxes.java b/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxes.java index b8ef841c9..3b17f5f82 100644 --- a/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxes.java +++ b/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxes.java @@ -77,7 +77,7 @@ public ResponseInfo> getSafeDepositBoxes(final Reque vaultAuthPrincipal.getName()); return ResponseInfo.newBuilder( - safeDepositBoxService.getAssociatedSafeDepositBoxes(vaultAuthPrincipal.getUserGroups())).build(); + safeDepositBoxService.getAssociatedSafeDepositBoxes(vaultAuthPrincipal)).build(); } throw ApiException.newBuilder().withApiErrors(DefaultApiError.AUTH_BAD_CREDENTIALS).build(); diff --git a/src/main/java/com/nike/cerberus/endpoints/sdb/UpdateSafeDepositBoxV1.java b/src/main/java/com/nike/cerberus/endpoints/sdb/UpdateSafeDepositBoxV1.java index 57ff3da47..2f546cbbf 100644 --- a/src/main/java/com/nike/cerberus/endpoints/sdb/UpdateSafeDepositBoxV1.java +++ b/src/main/java/com/nike/cerberus/endpoints/sdb/UpdateSafeDepositBoxV1.java @@ -77,14 +77,12 @@ private ResponseInfo updateSafeDepositBox(final RequestInfo sdbNameOptional = safeDepositBoxService.getSafeDepositBoxNameById(sdbId); - String sdbName = sdbNameOptional.isPresent() ? sdbNameOptional.get() : - String.format("(Failed to lookup name from id: %s)", sdbId); + String sdbName = sdbNameOptional.orElseGet(() -> String.format("(Failed to lookup name from id: %s)", sdbId)); log.info("Update SDB Event: the principal: {} is attempting to update sdb name: '{}' and id: '{}'", vaultAuthPrincipal.getName(), sdbName, sdbId); safeDepositBoxService.updateSafeDepositBoxV1(request.getContent(), - vaultAuthPrincipal.getUserGroups(), - vaultAuthPrincipal.getName(), + vaultAuthPrincipal, sdbId); return ResponseInfo.newBuilder().withHttpStatusCode(HttpResponseStatus.NO_CONTENT.code()) .withHeaders(new DefaultHttpHeaders().set(HEADER_X_REFRESH_TOKEN, Boolean.TRUE.toString())) diff --git a/src/main/java/com/nike/cerberus/endpoints/sdb/UpdateSafeDepositBoxV2.java b/src/main/java/com/nike/cerberus/endpoints/sdb/UpdateSafeDepositBoxV2.java index a3ef86499..d29a59fed 100644 --- a/src/main/java/com/nike/cerberus/endpoints/sdb/UpdateSafeDepositBoxV2.java +++ b/src/main/java/com/nike/cerberus/endpoints/sdb/UpdateSafeDepositBoxV2.java @@ -71,8 +71,7 @@ private ResponseInfo updateSafeDepositBox(final RequestInfo getUserAssociatedSafeDepositBoxes(@Param("userGroups") Set userGroups); + List getIamPrincipalAssociatedSafeDepositBoxes(@Param("iamPrincipalArn") final String iamPrincipalArn); + SafeDepositBoxRecord getSafeDepositBox(@Param("id") String id); int countByPath(@Param("path") String path); diff --git a/src/main/java/com/nike/cerberus/security/VaultAuthPrincipal.java b/src/main/java/com/nike/cerberus/security/VaultAuthPrincipal.java index f6764225e..1c35986a6 100644 --- a/src/main/java/com/nike/cerberus/security/VaultAuthPrincipal.java +++ b/src/main/java/com/nike/cerberus/security/VaultAuthPrincipal.java @@ -47,6 +47,8 @@ public class VaultAuthPrincipal implements Principal { public static final String METADATA_KEY_AWS_IAM_PRINCIPAL_ARN = "aws_iam_principal_arn"; + public static final String METADATA_KEY_IS_IAM_PRINCIPAL = "is_iam_principal"; + public static final String METADATA_KEY_AWS_REGION = "aws_region"; private final VaultClientTokenResponse clientToken; @@ -57,11 +59,21 @@ public class VaultAuthPrincipal implements Principal { private final Set roles; + private final boolean isIamPrincipal; + public VaultAuthPrincipal(VaultClientTokenResponse clientToken) { this.clientToken = clientToken; this.roles = buildRoles(clientToken); this.userGroupSet = extractUserGroups(clientToken); this.username = extractUsername(clientToken); + this.isIamPrincipal = extractIsIamPrincipal(clientToken); + } + + private boolean extractIsIamPrincipal(VaultClientTokenResponse clientToken) { + final Map meta = clientToken.getMeta(); + // if a Token that is the root token or created outside of CMS, + // then meta might be null and there will be no value set + return meta == null ? false : Boolean.valueOf(meta.get(METADATA_KEY_IS_IAM_PRINCIPAL)); } private Set buildRoles(VaultClientTokenResponse clientToken) { @@ -114,4 +126,8 @@ public boolean hasRole(final String role) { public Set getUserGroups() { return userGroupSet; } + + public boolean isIamPrincipal() { + return isIamPrincipal; + } } diff --git a/src/main/java/com/nike/cerberus/service/AuthenticationService.java b/src/main/java/com/nike/cerberus/service/AuthenticationService.java index 29e85ba8f..aeb93ff71 100644 --- a/src/main/java/com/nike/cerberus/service/AuthenticationService.java +++ b/src/main/java/com/nike/cerberus/service/AuthenticationService.java @@ -197,7 +197,7 @@ public IamRoleAuthResponse authenticate(IamRoleCredentials credentials) { iamPrincipalCredentials.setIamPrincipalArn(iamPrincipalArn); iamPrincipalCredentials.setRegion(region); - final Map vaultAuthPrincipalMetadata = generateCommonVaultPrincipalAuthMetadata(iamPrincipalArn, region); + final Map vaultAuthPrincipalMetadata = generateCommonIamPrincipalAuthMetadata(iamPrincipalArn, region); vaultAuthPrincipalMetadata.put(VaultAuthPrincipal.METADATA_KEY_AWS_ACCOUNT_ID, awsIamRoleArnParser.getAccountId(iamPrincipalArn)); vaultAuthPrincipalMetadata.put(VaultAuthPrincipal.METADATA_KEY_AWS_IAM_ROLE_NAME, awsIamRoleArnParser.getRoleName(iamPrincipalArn)); @@ -207,7 +207,7 @@ public IamRoleAuthResponse authenticate(IamRoleCredentials credentials) { public IamRoleAuthResponse authenticate(IamPrincipalCredentials credentials) { final String iamPrincipalArn = credentials.getIamPrincipalArn(); - final Map vaultAuthPrincipalMetadata = generateCommonVaultPrincipalAuthMetadata(iamPrincipalArn, credentials.getRegion()); + final Map vaultAuthPrincipalMetadata = generateCommonIamPrincipalAuthMetadata(iamPrincipalArn, credentials.getRegion()); vaultAuthPrincipalMetadata.put(VaultAuthPrincipal.METADATA_KEY_AWS_IAM_PRINCIPAL_ARN, iamPrincipalArn); return authenticate(credentials, vaultAuthPrincipalMetadata); @@ -534,10 +534,11 @@ private Set getAdminRoleArnSet() { * @param region - The AWS region * @return - Map of token metadata */ - protected Map generateCommonVaultPrincipalAuthMetadata(final String iamPrincipalArn, final String region) { + protected Map generateCommonIamPrincipalAuthMetadata(final String iamPrincipalArn, final String region) { Map metadata = Maps.newHashMap(); metadata.put(VaultAuthPrincipal.METADATA_KEY_AWS_REGION, region); metadata.put(VaultAuthPrincipal.METADATA_KEY_USERNAME, iamPrincipalArn); + metadata.put(VaultAuthPrincipal.METADATA_KEY_IS_IAM_PRINCIPAL, Boolean.TRUE.toString()); Set groups = new HashSet<>(); groups.add("registered-iam-principals"); diff --git a/src/main/java/com/nike/cerberus/service/SafeDepositBoxService.java b/src/main/java/com/nike/cerberus/service/SafeDepositBoxService.java index acdc10e9d..f5d99ba4a 100644 --- a/src/main/java/com/nike/cerberus/service/SafeDepositBoxService.java +++ b/src/main/java/com/nike/cerberus/service/SafeDepositBoxService.java @@ -33,6 +33,7 @@ import com.nike.cerberus.record.RoleRecord; import com.nike.cerberus.record.SafeDepositBoxRecord; import com.nike.cerberus.record.UserGroupRecord; +import com.nike.cerberus.security.VaultAuthPrincipal; import com.nike.cerberus.util.AwsIamRoleArnParser; import com.nike.cerberus.util.UuidSupplier; import com.nike.cerberus.util.DateTimeSupplier; @@ -116,20 +117,26 @@ public SafeDepositBoxService(final SafeDepositBoxDao safeDepositBoxDao, /** * Queries the data store for all safe deposit box associated with the user groups supplied. * - * @param userGroups Set of user groups to find associated safe deposit boxes with + * @param vaultAuthPrincipal The authenticated principal * @return Collection of summaries for each associated safe deposit box */ - public List getAssociatedSafeDepositBoxes(final Set userGroups) { - final List records = safeDepositBoxDao.getUserAssociatedSafeDepositBoxes(userGroups); - final List summaries = Lists.newArrayListWithCapacity(records.size()); - - records.forEach(r -> { - summaries.add(new SafeDepositBoxSummary() - .setId(r.getId()) - .setName(r.getName()) - .setCategoryId(r.getCategoryId()) - .setPath(r.getPath())); - }); + public List getAssociatedSafeDepositBoxes(final VaultAuthPrincipal vaultAuthPrincipal) { + + List sdbRecords; + if (vaultAuthPrincipal.isIamPrincipal()) { + sdbRecords = safeDepositBoxDao.getIamPrincipalAssociatedSafeDepositBoxes(vaultAuthPrincipal.getName()); + } else { + sdbRecords = safeDepositBoxDao.getUserAssociatedSafeDepositBoxes(vaultAuthPrincipal.getUserGroups()); + } + + final List summaries = Lists.newArrayListWithCapacity(sdbRecords.size()); + + sdbRecords.forEach(r -> + summaries.add(new SafeDepositBoxSummary() + .setId(r.getId()) + .setName(r.getName()) + .setCategoryId(r.getCategoryId()) + .setPath(r.getPath()))); return summaries; } @@ -138,45 +145,59 @@ public List getAssociatedSafeDepositBoxes(final Set getAssociatedSafeDepositBoxV1(final Set groups, final String id) { - - Optional safeDepositBoxV2 = getAssociatedSafeDepositBoxV2(groups, id); - - return safeDepositBoxV2.map(this::convertSafeDepositBoxV2ToV1); + public SafeDepositBoxV1 getSDBAndValidatePrincipalAssociationV1(VaultAuthPrincipal vaultAuthPrincipal, String id) { + return convertSafeDepositBoxV2ToV1(getSDBAndValidatePrincipalAssociationV2(vaultAuthPrincipal, id)); } /** * Queries the data store for the specific safe deposit box by ID. The query also enforces that the specified * safe deposit box has a linked permission via the user groups supplied in the call. * - * @param groups Set of user groups that must have at least one matching permission for the specific safe - * deposit box - * @param id The unique identifier for the safe deposit box to lookup - * @return The safe deposit box, if found + * @param vaultAuthPrincipal The authenticated principal, which must have an association with the requested SDB + * @param sdbId The unique identifier for the safe deposit box to lookup + * @return The safe deposit box + * @throws ApiException Throws an exception if the requesting principal has no permissions associated with the requested SDB. + * @throws ApiException Throws an exception if the SDB Id is invalid */ - public Optional getAssociatedSafeDepositBoxV2(final Set groups, final String id) { + public SafeDepositBoxV2 getSDBAndValidatePrincipalAssociationV2(VaultAuthPrincipal vaultAuthPrincipal, String sdbId) { - final Optional safeDepositBoxRecord = safeDepositBoxDao.getSafeDepositBox(id); + Optional safeDepositBoxRecordOptional = safeDepositBoxDao.getSafeDepositBox(sdbId); - if (safeDepositBoxRecord.isPresent()) { - final Set userGroupPermissions = userGroupPermissionService.getUserGroupPermissions(id); + if (! safeDepositBoxRecordOptional.isPresent()) { + throw ApiException.newBuilder() + .withApiErrors(DefaultApiError.ENTITY_NOT_FOUND) + .build(); + } - final long count = userGroupPermissions.stream().filter(perm -> groups.contains(perm.getName())).count(); - if (count == 0) { - throw ApiException.newBuilder() - .withApiErrors(DefaultApiError.ACCESS_DENIED) - .build(); - } + SafeDepositBoxRecord safeDepositBoxRecord = safeDepositBoxRecordOptional.get(); + + boolean principalHasPermissionAssociationWithSdb; + // if the authenticated principal is an IAM Principal check to see that the iam principal is associated with the requested sdb + if (vaultAuthPrincipal.isIamPrincipal()) { + principalHasPermissionAssociationWithSdb = iamPrincipalPermissionService.getIamPrincipalPermissions(sdbId) + .stream() + .filter(perm -> perm.getIamPrincipalArn().equals(vaultAuthPrincipal.getName())) // filter for permissions on the SDB that match the principals arn + .count() > 0; // if there is more than one, then the SDB is associated with the principal arn. + } else { + // if the the principal is a user principal ensure that one of the users groups is associated with the sdb + Set userGroupPermissions = userGroupPermissionService.getUserGroupPermissions(sdbId); + principalHasPermissionAssociationWithSdb = userGroupPermissions + .stream() + .filter(perm -> vaultAuthPrincipal.getUserGroups().contains(perm.getName())) // filter for permissions on the sdb that have groups that the authenticated user belongs too. + .count() > 0; + } - return Optional.of(getSDBFromRecordV2(safeDepositBoxRecord.get())); + if (! principalHasPermissionAssociationWithSdb) { + throw ApiException.newBuilder() + .withApiErrors(DefaultApiError.ACCESS_DENIED) + .build(); } - return Optional.empty(); + return getSDBFromRecordV2(safeDepositBoxRecord); } protected SafeDepositBoxV2 getSDBFromRecordV2(SafeDepositBoxRecord safeDepositBoxRecord) { @@ -282,63 +303,50 @@ public SafeDepositBoxV2 createSafeDepositBoxV2(final SafeDepositBoxV2 safeDeposi * Updates a safe deposit box. Currently, only the description, owner and permissions are updatable. * * @param safeDepositBox Updated safe deposit box - * @param groups Caller's user groups - * @param user Caller's username + * @param vaultAuthPrincipal The authenticated principal * @param id Safe deposit box id */ @Transactional - public void updateSafeDepositBoxV1(final SafeDepositBoxV1 safeDepositBox, final Set groups, - final String user, final String id) { + public void updateSafeDepositBoxV1(final SafeDepositBoxV1 safeDepositBox, + final VaultAuthPrincipal vaultAuthPrincipal, + final String id) { SafeDepositBoxV2 safeDepositBoxV2 = convertSafeDepositBoxV1ToV2(safeDepositBox); - updateSafeDepositBoxV2(safeDepositBoxV2, groups, user, id); + updateSafeDepositBoxV2(safeDepositBoxV2, vaultAuthPrincipal, id); } /** * Updates a safe deposit box. Currently, only the description, owner and permissions are updatable. * * @param safeDepositBox Updated safe deposit box - * @param groups Caller's user groups - * @param user Caller's username + * @param vaultAuthPrincipal The authenticated principal * @param id Safe deposit box id */ @Transactional - public SafeDepositBoxV2 updateSafeDepositBoxV2(final SafeDepositBoxV2 safeDepositBox, final Set groups, - final String user, final String id) { - final Optional currentBox = getAssociatedSafeDepositBoxV2(groups, id); + public SafeDepositBoxV2 updateSafeDepositBoxV2(final SafeDepositBoxV2 safeDepositBox, + final VaultAuthPrincipal vaultAuthPrincipal, + final String id) { - if (!currentBox.isPresent()) { - throw ApiException.newBuilder() - .withApiErrors(DefaultApiError.ENTITY_NOT_FOUND) - .withExceptionMessage("The specified safe deposit box was not found.") - .build(); - } + final SafeDepositBoxV2 currentBox = getSDBAndValidatePrincipalAssociationV2(vaultAuthPrincipal, id); - assertIsOwner(groups, currentBox.get()); + assertPrincipalHasOwnerPermissions(vaultAuthPrincipal, currentBox); + String principalName = vaultAuthPrincipal.getName(); final OffsetDateTime now = dateTimeSupplier.get(); - final SafeDepositBoxRecord boxToUpdate = buildBoxToUpdate(id, safeDepositBox, user, now); + final SafeDepositBoxRecord boxToUpdate = buildBoxToUpdate(id, safeDepositBox, principalName, now); final Set userGroupPermissionSet = safeDepositBox.getUserGroupPermissions(); final Set iamRolePermissionSet = safeDepositBox.getIamPrincipalPermissions(); - if (!StringUtils.equals(currentBox.get().getDescription(), boxToUpdate.getDescription())) { + if (!StringUtils.equals(currentBox.getDescription(), boxToUpdate.getDescription())) { safeDepositBoxDao.updateSafeDepositBox(boxToUpdate); } - updateOwner(currentBox.get().getId(), safeDepositBox.getOwner(), user, now); - modifyUserGroupPermissions(currentBox.get(), userGroupPermissionSet, user, now); - modifyIamPrincipalPermissions(currentBox.get(), iamRolePermissionSet, user, now); + updateOwner(currentBox.getId(), safeDepositBox.getOwner(), principalName, now); + modifyUserGroupPermissions(currentBox, userGroupPermissionSet, principalName, now); + modifyIamPrincipalPermissions(currentBox, iamRolePermissionSet, principalName, now); - Optional updatedSafeDepositBox = getAssociatedSafeDepositBoxV2(groups, id); - if (updatedSafeDepositBox.isPresent()) { - return updatedSafeDepositBox.get(); - } else { - throw ApiException.newBuilder() - .withApiErrors(DefaultApiError.INTERNAL_SERVER_ERROR) - .withExceptionMessage("The updated safe deposit box was not found.") - .build(); - } + return getSDBAndValidatePrincipalAssociationV2(vaultAuthPrincipal, id); } /** @@ -347,16 +355,10 @@ public SafeDepositBoxV2 updateSafeDepositBoxV2(final SafeDepositBoxV2 safeDeposi * @param id The unique identifier for the safe deposit box */ @Transactional - public void deleteSafeDepositBox(final Set groups, final String id) { - final Optional box = getAssociatedSafeDepositBoxV2(groups, id); + public void deleteSafeDepositBox(VaultAuthPrincipal vaultAuthPrincipal, final String id) { + final SafeDepositBoxV2 box = getSDBAndValidatePrincipalAssociationV2(vaultAuthPrincipal, id); - if (!box.isPresent()) { - throw ApiException.newBuilder() - .withApiErrors(DefaultApiError.ENTITY_NOT_FOUND) - .build(); - } - - assertIsOwner(groups, box.get()); + assertPrincipalHasOwnerPermissions(vaultAuthPrincipal, box); // 1. Remove permissions and metadata from database. iamPrincipalPermissionService.deleteIamPrincipalPermissions(id); @@ -364,10 +366,10 @@ public void deleteSafeDepositBox(final Set groups, final String id) { safeDepositBoxDao.deleteSafeDepositBox(id); // 2. Recursively delete all secrets from the safe deposit box Vault path. - deleteAllSecrets(box.get().getPath()); + deleteAllSecrets(box.getPath()); // 3. Delete the standard policies from Vault for this safe deposit box. - vaultPolicyService.deleteStandardPolicies(box.get().getName()); + vaultPolicyService.deleteStandardPolicies(box.getName()); } private Optional extractOwner(Set userGroupPermissions) { @@ -390,8 +392,31 @@ private Optional extractOwner(Set userGroupPermissi return Optional.of(ownerPermission.get().getName()); } - private void assertIsOwner(final Set groups, final SafeDepositBoxV2 box) { - if (!groups.contains(box.getOwner())) { + /** + * Asserts that the given principal has owner permissions on the given SDB + * @param principal The authenticated principal + * @param sdb The SDB that the principal is trying to access + */ + private void assertPrincipalHasOwnerPermissions(final VaultAuthPrincipal principal, final SafeDepositBoxV2 sdb) { + + boolean principalHasOwnerPermissions = false; + if (principal.isIamPrincipal()) { + Optional ownerRole = roleService.getRoleByName(RoleRecord.ROLE_OWNER); + for (IamPrincipalPermission perm : sdb.getIamPrincipalPermissions()) { + String roleId = perm.getRoleId(); + Optional attachedRole = roleService.getRoleById(roleId); + if (attachedRole.get().getId().equals(ownerRole.get().getId())) { + principalHasOwnerPermissions = true; + } + } + + } else { + if (principal.getUserGroups().contains(sdb.getOwner())) { + principalHasOwnerPermissions = true; + } + } + + if (! principalHasOwnerPermissions) { throw ApiException.newBuilder() .withApiErrors(DefaultApiError.SDB_CALLER_OWNERSHIP_REQUIRED) .build(); diff --git a/src/main/resources/com/nike/cerberus/mapper/SafeDepositBoxMapper.xml b/src/main/resources/com/nike/cerberus/mapper/SafeDepositBoxMapper.xml index a8ec2ffd1..2b7b34bfc 100644 --- a/src/main/resources/com/nike/cerberus/mapper/SafeDepositBoxMapper.xml +++ b/src/main/resources/com/nike/cerberus/mapper/SafeDepositBoxMapper.xml @@ -102,6 +102,27 @@ SDB.ID ASC + +