From b80225f05bf951152a6f62965092affdd9434273 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Tue, 21 Feb 2017 09:53:42 -0800 Subject: [PATCH] Refactor get metadata to use services rather than DAOs directly and add new endpoint for restoring metadata --- .../com/nike/cerberus/dao/CategoryDao.java | 4 + .../nike/cerberus/dao/SafeDepositBoxDao.java | 7 + .../cerberus/domain/IamRolePermission.java | 15 ++ .../{SdbMetadata.java => SDBMetadata.java} | 2 +- .../cerberus/domain/SDBMetadataResult.java | 6 +- .../nike/cerberus/domain/SafeDepositBox.java | 42 ++++ .../cerberus/domain/UserGroupPermission.java | 10 + .../endpoints/admin/PutSDBMetadata.java | 67 +++++ .../error/InvalidCategoryNameApiError.java | 56 +++++ .../error/InvalidIamRoleArnApiError.java | 56 +++++ .../error/InvalidRoleNameApiError.java | 56 +++++ .../nike/cerberus/mapper/CategoryMapper.java | 2 + .../cerberus/mapper/SafeDepositBoxMapper.java | 4 + .../cerberus/record/SafeDepositBoxRecord.java | 34 +++ .../cerberus/server/config/CmsConfig.java | 2 +- .../server/config/guice/CmsGuiceModule.java | 6 +- .../service/AuthenticationService.java | 3 +- .../cerberus/service/CategoryService.java | 20 ++ .../cerberus/service/KmsPolicyService.java | 2 +- .../cerberus/service/MetadataService.java | 228 ++++++++++------- .../nike/cerberus/service/RoleService.java | 9 + .../service/SafeDepositBoxService.java | 154 +++++++++--- .../nike/cerberus/mapper/CategoryMapper.xml | 9 + .../cerberus/mapper/SafeDepositBoxMapper.xml | 20 +- .../cerberus/service/CategoryServiceTest.java | 71 ++++++ .../cerberus/service/MetadataServiceTest.java | 234 +++++++++--------- .../cerberus/service/RoleServiceTest.java | 59 +++++ .../service/SafeDepositBoxServiceTest.java | 201 +++++++++++++++ .../cerberus/service/sdb_metadata_backup.json | 23 ++ 29 files changed, 1159 insertions(+), 243 deletions(-) rename src/main/java/com/nike/cerberus/domain/{SdbMetadata.java => SDBMetadata.java} (99%) create mode 100644 src/main/java/com/nike/cerberus/endpoints/admin/PutSDBMetadata.java create mode 100644 src/main/java/com/nike/cerberus/error/InvalidCategoryNameApiError.java create mode 100644 src/main/java/com/nike/cerberus/error/InvalidIamRoleArnApiError.java create mode 100644 src/main/java/com/nike/cerberus/error/InvalidRoleNameApiError.java create mode 100644 src/test/java/com/nike/cerberus/service/CategoryServiceTest.java create mode 100644 src/test/java/com/nike/cerberus/service/RoleServiceTest.java create mode 100644 src/test/java/com/nike/cerberus/service/SafeDepositBoxServiceTest.java create mode 100644 src/test/resources/com/nike/cerberus/service/sdb_metadata_backup.json diff --git a/src/main/java/com/nike/cerberus/dao/CategoryDao.java b/src/main/java/com/nike/cerberus/dao/CategoryDao.java index 2b0a24766..7c058a8f8 100644 --- a/src/main/java/com/nike/cerberus/dao/CategoryDao.java +++ b/src/main/java/com/nike/cerberus/dao/CategoryDao.java @@ -50,4 +50,8 @@ public int createCategory(final CategoryRecord record) { public int deleteCategory(final String id) { return categoryMapper.deleteCategory(id); } + + public String getCategoryIdByName(String categoryName) { + return categoryMapper.getCategoryIdByName(categoryName); + } } diff --git a/src/main/java/com/nike/cerberus/dao/SafeDepositBoxDao.java b/src/main/java/com/nike/cerberus/dao/SafeDepositBoxDao.java index 7e4fe62ff..917e8c0d8 100644 --- a/src/main/java/com/nike/cerberus/dao/SafeDepositBoxDao.java +++ b/src/main/java/com/nike/cerberus/dao/SafeDepositBoxDao.java @@ -74,9 +74,16 @@ public int updateSafeDepositBox(final SafeDepositBoxRecord safeDepositBox) { return safeDepositBoxMapper.updateSafeDepositBox(safeDepositBox); } + public int fullUpdateSafeDepositBox(final SafeDepositBoxRecord safeDepositBox) { + return safeDepositBoxMapper.fullUpdateSafeDepositBox(safeDepositBox); + } + public int deleteSafeDepositBox(final String id) { return safeDepositBoxMapper.deleteSafeDepositBox(id); } + public String getSafeDepositBoxIdByName(String name) { + return safeDepositBoxMapper.getSafeDepositBoxIdByName(name); + } } diff --git a/src/main/java/com/nike/cerberus/domain/IamRolePermission.java b/src/main/java/com/nike/cerberus/domain/IamRolePermission.java index 3ffb8bd08..d9f8ea460 100644 --- a/src/main/java/com/nike/cerberus/domain/IamRolePermission.java +++ b/src/main/java/com/nike/cerberus/domain/IamRolePermission.java @@ -66,6 +66,11 @@ public void setAccountId(String accountId) { this.accountId = accountId; } + public IamRolePermission withAccountId(String accountId) { + this.accountId = accountId; + return this; + } + public String getIamRoleName() { return iamRoleName; } @@ -74,6 +79,11 @@ public void setIamRoleName(String iamRoleName) { this.iamRoleName = iamRoleName; } + public IamRolePermission withIamRoleName(String iamRoleName) { + this.iamRoleName = iamRoleName; + return this; + } + public String getRoleId() { return roleId; } @@ -82,6 +92,11 @@ public void setRoleId(String roleId) { this.roleId = roleId; } + public IamRolePermission withRoleId(String roleId) { + this.roleId = roleId; + return this; + } + public OffsetDateTime getCreatedTs() { return createdTs; } diff --git a/src/main/java/com/nike/cerberus/domain/SdbMetadata.java b/src/main/java/com/nike/cerberus/domain/SDBMetadata.java similarity index 99% rename from src/main/java/com/nike/cerberus/domain/SdbMetadata.java rename to src/main/java/com/nike/cerberus/domain/SDBMetadata.java index 66fdf99cd..79abcdf2b 100644 --- a/src/main/java/com/nike/cerberus/domain/SdbMetadata.java +++ b/src/main/java/com/nike/cerberus/domain/SDBMetadata.java @@ -20,7 +20,7 @@ import java.util.Date; import java.util.Map; -public class SdbMetadata { +public class SDBMetadata { private String name; private String path; diff --git a/src/main/java/com/nike/cerberus/domain/SDBMetadataResult.java b/src/main/java/com/nike/cerberus/domain/SDBMetadataResult.java index 282597cfe..1f2e08ab4 100644 --- a/src/main/java/com/nike/cerberus/domain/SDBMetadataResult.java +++ b/src/main/java/com/nike/cerberus/domain/SDBMetadataResult.java @@ -26,7 +26,7 @@ public class SDBMetadataResult { private int offset = 0; private int sdbCountInResult; private int totalSDBCount; - private List safeDepositBoxMetadata; + private List safeDepositBoxMetadata; public boolean isHasNext() { return hasNext; @@ -76,11 +76,11 @@ public void setTotalSDBCount(int totalSDBCount) { this.totalSDBCount = totalSDBCount; } - public List getSafeDepositBoxMetadata() { + public List getSafeDepositBoxMetadata() { return safeDepositBoxMetadata; } - public void setSafeDepositBoxMetadata(List safeDepositBoxMetadata) { + public void setSafeDepositBoxMetadata(List safeDepositBoxMetadata) { this.safeDepositBoxMetadata = safeDepositBoxMetadata; } } diff --git a/src/main/java/com/nike/cerberus/domain/SafeDepositBox.java b/src/main/java/com/nike/cerberus/domain/SafeDepositBox.java index 43bff666a..20c4df07b 100644 --- a/src/main/java/com/nike/cerberus/domain/SafeDepositBox.java +++ b/src/main/java/com/nike/cerberus/domain/SafeDepositBox.java @@ -164,4 +164,46 @@ public Set getIamRolePermissions() { public void setIamRolePermissions(Set iamRolePermissions) { this.iamRolePermissions = iamRolePermissions; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SafeDepositBox that = (SafeDepositBox) o; + + if (id != null ? !id.equals(that.id) : that.id != null) return false; + if (categoryId != null ? !categoryId.equals(that.categoryId) : that.categoryId != null) return false; + if (name != null ? !name.equals(that.name) : that.name != null) return false; + if (description != null ? !description.equals(that.description) : that.description != null) return false; + if (path != null ? !path.equals(that.path) : that.path != null) return false; + if (createdTs != null ? !createdTs.equals(that.createdTs) : that.createdTs != null) return false; + if (lastUpdatedTs != null ? !lastUpdatedTs.equals(that.lastUpdatedTs) : that.lastUpdatedTs != null) + return false; + if (createdBy != null ? !createdBy.equals(that.createdBy) : that.createdBy != null) return false; + if (lastUpdatedBy != null ? !lastUpdatedBy.equals(that.lastUpdatedBy) : that.lastUpdatedBy != null) + return false; + if (owner != null ? !owner.equals(that.owner) : that.owner != null) return false; + if (userGroupPermissions != null ? !userGroupPermissions.equals(that.userGroupPermissions) : that.userGroupPermissions != null) + return false; + return iamRolePermissions != null ? iamRolePermissions.equals(that.iamRolePermissions) : that.iamRolePermissions == null; + + } + + @Override + public int hashCode() { + int result = id != null ? id.hashCode() : 0; + result = 31 * result + (categoryId != null ? categoryId.hashCode() : 0); + result = 31 * result + (name != null ? name.hashCode() : 0); + result = 31 * result + (description != null ? description.hashCode() : 0); + result = 31 * result + (path != null ? path.hashCode() : 0); + result = 31 * result + (createdTs != null ? createdTs.hashCode() : 0); + result = 31 * result + (lastUpdatedTs != null ? lastUpdatedTs.hashCode() : 0); + result = 31 * result + (createdBy != null ? createdBy.hashCode() : 0); + result = 31 * result + (lastUpdatedBy != null ? lastUpdatedBy.hashCode() : 0); + result = 31 * result + (owner != null ? owner.hashCode() : 0); + result = 31 * result + (userGroupPermissions != null ? userGroupPermissions.hashCode() : 0); + result = 31 * result + (iamRolePermissions != null ? iamRolePermissions.hashCode() : 0); + return result; + } } diff --git a/src/main/java/com/nike/cerberus/domain/UserGroupPermission.java b/src/main/java/com/nike/cerberus/domain/UserGroupPermission.java index 82a35b82f..0e3747548 100644 --- a/src/main/java/com/nike/cerberus/domain/UserGroupPermission.java +++ b/src/main/java/com/nike/cerberus/domain/UserGroupPermission.java @@ -59,6 +59,11 @@ public void setName(String name) { this.name = name; } + public UserGroupPermission withName(String name) { + this.name = name; + return this; + } + public String getRoleId() { return roleId; } @@ -67,6 +72,11 @@ public void setRoleId(String roleId) { this.roleId = roleId; } + public UserGroupPermission withRoleId(String roleId) { + this.roleId = roleId; + return this; + } + public OffsetDateTime getCreatedTs() { return createdTs; } diff --git a/src/main/java/com/nike/cerberus/endpoints/admin/PutSDBMetadata.java b/src/main/java/com/nike/cerberus/endpoints/admin/PutSDBMetadata.java new file mode 100644 index 000000000..4c5946dd3 --- /dev/null +++ b/src/main/java/com/nike/cerberus/endpoints/admin/PutSDBMetadata.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017 Nike, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.nike.cerberus.endpoints.admin; + +import com.google.inject.Inject; +import com.nike.cerberus.domain.SDBMetadata; +import com.nike.cerberus.endpoints.AdminStandardEndpoint; +import com.nike.cerberus.security.VaultAuthPrincipal; +import com.nike.cerberus.service.MetadataService; +import com.nike.riposte.server.http.RequestInfo; +import com.nike.riposte.server.http.ResponseInfo; +import com.nike.riposte.util.Matcher; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpResponseStatus; + +import javax.ws.rs.core.SecurityContext; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +/** + * Allows an Admin to restore (create or update) Metadata for an SDB + */ +public class PutSDBMetadata extends AdminStandardEndpoint { + + private final MetadataService metadataService; + + @Inject + public PutSDBMetadata(MetadataService metadataService) { + this.metadataService = metadataService; + } + + @Override + public CompletableFuture> doExecute(RequestInfo request, + Executor longRunningTaskExecutor, + ChannelHandlerContext ctx, + SecurityContext securityContext) { + + return CompletableFuture.supplyAsync(() -> { + VaultAuthPrincipal vaultAuthPrincipal = (VaultAuthPrincipal) securityContext.getUserPrincipal(); + metadataService.restoreMetadata(request.getContent(), vaultAuthPrincipal.getName()); + + return ResponseInfo.newBuilder() + .withHttpStatusCode(HttpResponseStatus.NO_CONTENT.code()) + .build(); + }, longRunningTaskExecutor); + } + + @Override + public Matcher requestMatcher() { + return Matcher.match("/v1/metadata", HttpMethod.PUT); + } +} diff --git a/src/main/java/com/nike/cerberus/error/InvalidCategoryNameApiError.java b/src/main/java/com/nike/cerberus/error/InvalidCategoryNameApiError.java new file mode 100644 index 000000000..44cc986c2 --- /dev/null +++ b/src/main/java/com/nike/cerberus/error/InvalidCategoryNameApiError.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 Nike, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.nike.cerberus.error; + +import com.nike.backstopper.apierror.ApiError; + +import javax.servlet.http.HttpServletResponse; +import java.util.Map; + +public class InvalidCategoryNameApiError implements ApiError { + + private final String categoryName; + + public InvalidCategoryNameApiError(String categoryName) { + this.categoryName = categoryName; + } + + @Override + public String getName() { + return "InvalidCategoryNameApiError"; + } + + @Override + public String getErrorCode() { + return DefaultApiError.ENTITY_NOT_FOUND.getErrorCode(); + } + + @Override + public String getMessage() { + return String.format("The category %s could not be mapped to a valid category id", categoryName); + } + + @Override + public Map getMetadata() { + return null; + } + + @Override + public int getHttpStatusCode() { + return HttpServletResponse.SC_BAD_REQUEST; + } +} diff --git a/src/main/java/com/nike/cerberus/error/InvalidIamRoleArnApiError.java b/src/main/java/com/nike/cerberus/error/InvalidIamRoleArnApiError.java new file mode 100644 index 000000000..166f97253 --- /dev/null +++ b/src/main/java/com/nike/cerberus/error/InvalidIamRoleArnApiError.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 Nike, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.nike.cerberus.error; + +import com.nike.backstopper.apierror.ApiError; + +import javax.servlet.http.HttpServletResponse; +import java.util.Map; + +public class InvalidIamRoleArnApiError implements ApiError { + + private final String arn; + + public InvalidIamRoleArnApiError(String arn) { + this.arn = arn; + } + + @Override + public String getName() { + return "InvalidIamRoleArnApiError"; + } + + @Override + public String getErrorCode() { + return DefaultApiError.ENTITY_NOT_FOUND.getErrorCode(); + } + + @Override + public String getMessage() { + return String.format("The arn %s was not a valid arn in 'arn:aws:iam::(.*?):role/(.*)' format", arn); + } + + @Override + public Map getMetadata() { + return null; + } + + @Override + public int getHttpStatusCode() { + return HttpServletResponse.SC_BAD_REQUEST; + } +} diff --git a/src/main/java/com/nike/cerberus/error/InvalidRoleNameApiError.java b/src/main/java/com/nike/cerberus/error/InvalidRoleNameApiError.java new file mode 100644 index 000000000..0b6e5f123 --- /dev/null +++ b/src/main/java/com/nike/cerberus/error/InvalidRoleNameApiError.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 Nike, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.nike.cerberus.error; + +import com.nike.backstopper.apierror.ApiError; + +import javax.servlet.http.HttpServletResponse; +import java.util.Map; + +public class InvalidRoleNameApiError implements ApiError { + + private final String roleName; + + public InvalidRoleNameApiError(String roleName) { + this.roleName = roleName; + } + + @Override + public String getName() { + return "InvalidRoleNameApiError"; + } + + @Override + public String getErrorCode() { + return DefaultApiError.ENTITY_NOT_FOUND.getErrorCode(); + } + + @Override + public String getMessage() { + return String.format("The role %s could not be mapped to a valid role id", roleName); + } + + @Override + public Map getMetadata() { + return null; + } + + @Override + public int getHttpStatusCode() { + return HttpServletResponse.SC_BAD_REQUEST; + } +} diff --git a/src/main/java/com/nike/cerberus/mapper/CategoryMapper.java b/src/main/java/com/nike/cerberus/mapper/CategoryMapper.java index e778b3ef0..ac6eac2cc 100644 --- a/src/main/java/com/nike/cerberus/mapper/CategoryMapper.java +++ b/src/main/java/com/nike/cerberus/mapper/CategoryMapper.java @@ -33,4 +33,6 @@ public interface CategoryMapper { int createCategory(@Param("record") final CategoryRecord record); int deleteCategory(@Param("id") final String id); + + String getCategoryIdByName(@Param("categoryName") String categoryName); } diff --git a/src/main/java/com/nike/cerberus/mapper/SafeDepositBoxMapper.java b/src/main/java/com/nike/cerberus/mapper/SafeDepositBoxMapper.java index 2081cea59..5fe731376 100644 --- a/src/main/java/com/nike/cerberus/mapper/SafeDepositBoxMapper.java +++ b/src/main/java/com/nike/cerberus/mapper/SafeDepositBoxMapper.java @@ -43,9 +43,13 @@ List getIamRoleAssociatedSafeDepositBoxRoles(@Param("a int updateSafeDepositBox(@Param("record") SafeDepositBoxRecord record); + int fullUpdateSafeDepositBox(@Param("record") SafeDepositBoxRecord record); + int deleteSafeDepositBox(@Param("id") String id); List getSafeDepositBoxes(@Param("limit") int limit, @Param("offset") int offset); int count(); + + String getSafeDepositBoxIdByName(@Param("name") String name); } diff --git a/src/main/java/com/nike/cerberus/record/SafeDepositBoxRecord.java b/src/main/java/com/nike/cerberus/record/SafeDepositBoxRecord.java index 7a2788082..f422416a7 100644 --- a/src/main/java/com/nike/cerberus/record/SafeDepositBoxRecord.java +++ b/src/main/java/com/nike/cerberus/record/SafeDepositBoxRecord.java @@ -121,4 +121,38 @@ public SafeDepositBoxRecord setLastUpdatedTs(OffsetDateTime lastUpdatedTs) { this.lastUpdatedTs = lastUpdatedTs; return this; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SafeDepositBoxRecord record = (SafeDepositBoxRecord) o; + + if (id != null ? !id.equals(record.id) : record.id != null) return false; + if (categoryId != null ? !categoryId.equals(record.categoryId) : record.categoryId != null) return false; + if (name != null ? !name.equals(record.name) : record.name != null) return false; + if (description != null ? !description.equals(record.description) : record.description != null) return false; + if (path != null ? !path.equals(record.path) : record.path != null) return false; + if (createdBy != null ? !createdBy.equals(record.createdBy) : record.createdBy != null) return false; + if (lastUpdatedBy != null ? !lastUpdatedBy.equals(record.lastUpdatedBy) : record.lastUpdatedBy != null) + return false; + if (createdTs != null ? !createdTs.equals(record.createdTs) : record.createdTs != null) return false; + return lastUpdatedTs != null ? lastUpdatedTs.equals(record.lastUpdatedTs) : record.lastUpdatedTs == null; + + } + + @Override + public int hashCode() { + int result = id != null ? id.hashCode() : 0; + result = 31 * result + (categoryId != null ? categoryId.hashCode() : 0); + result = 31 * result + (name != null ? name.hashCode() : 0); + result = 31 * result + (description != null ? description.hashCode() : 0); + result = 31 * result + (path != null ? path.hashCode() : 0); + result = 31 * result + (createdBy != null ? createdBy.hashCode() : 0); + result = 31 * result + (lastUpdatedBy != null ? lastUpdatedBy.hashCode() : 0); + result = 31 * result + (createdTs != null ? createdTs.hashCode() : 0); + result = 31 * result + (lastUpdatedTs != null ? lastUpdatedTs.hashCode() : 0); + return result; + } } diff --git a/src/main/java/com/nike/cerberus/server/config/CmsConfig.java b/src/main/java/com/nike/cerberus/server/config/CmsConfig.java index ee7fce822..d3161e5f5 100644 --- a/src/main/java/com/nike/cerberus/server/config/CmsConfig.java +++ b/src/main/java/com/nike/cerberus/server/config/CmsConfig.java @@ -113,7 +113,7 @@ protected List getAppGuiceModules(Config appConfig) { ); } - protected ObjectMapper configureObjectMapper() { + public static ObjectMapper configureObjectMapper() { final ObjectMapper om = new ObjectMapper(); om.findAndRegisterModules(); om.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); diff --git a/src/main/java/com/nike/cerberus/server/config/guice/CmsGuiceModule.java b/src/main/java/com/nike/cerberus/server/config/guice/CmsGuiceModule.java index 1b7dfd89a..2dede284d 100644 --- a/src/main/java/com/nike/cerberus/server/config/guice/CmsGuiceModule.java +++ b/src/main/java/com/nike/cerberus/server/config/guice/CmsGuiceModule.java @@ -23,6 +23,7 @@ import com.nike.cerberus.config.CmsEnvPropertiesLoader; import com.nike.cerberus.endpoints.HealthCheckEndpoint; import com.nike.cerberus.endpoints.admin.GetSDBMetadata; +import com.nike.cerberus.endpoints.admin.PutSDBMetadata; import com.nike.cerberus.endpoints.authentication.AuthenticateIamRole; import com.nike.cerberus.endpoints.authentication.AuthenticateUser; import com.nike.cerberus.endpoints.authentication.MfaCheck; @@ -173,7 +174,8 @@ public Set> appEndpoints( DeleteSafeDepositBox deleteSafeDepositBox, UpdateSafeDepositBox updateSafeDepositBox, CreateSafeDepositBox createSafeDepositBox, - GetSDBMetadata getSDBMetadata + GetSDBMetadata getSDBMetadata, + PutSDBMetadata putSDBMetadata ) { return new LinkedHashSet<>(Arrays.>asList( healthCheckEndpoint, @@ -183,7 +185,7 @@ public Set> appEndpoints( getAllRoles, getRole, getSafeDepositBoxes, getSafeDepositBox, deleteSafeDepositBox, updateSafeDepositBox, createSafeDepositBox, - getSDBMetadata + getSDBMetadata, putSDBMetadata )); } diff --git a/src/main/java/com/nike/cerberus/service/AuthenticationService.java b/src/main/java/com/nike/cerberus/service/AuthenticationService.java index 09841c518..72c7aeec3 100644 --- a/src/main/java/com/nike/cerberus/service/AuthenticationService.java +++ b/src/main/java/com/nike/cerberus/service/AuthenticationService.java @@ -79,6 +79,7 @@ public class AuthenticationService { public static final String IAM_TOKEN_TTL_OVERRIDE = "cms.iam.token.ttl.override"; public static final String LOOKUP_SELF_POLICY = "lookup-self"; public static final String DEFAULT_TOKEN_TTL = "1h"; + public static final String AWS_IAM_ROLE_ARN_TEMPLATE = "arn:aws:iam::%s:role/%s"; private final SafeDepositBoxDao safeDepositBoxDao; private final AwsIamRoleDao awsIamRoleDao; @@ -191,7 +192,7 @@ public IamRoleAuthResponse authenticate(IamRoleCredentials credentials) { } final Set policies = buildPolicySet(credentials.getAccountId(), credentials.getRoleName()); - String arn = String.format("arn:aws:iam::%s:role/%s", credentials.getAccountId(), credentials.getRoleName()); + String arn = String.format(AWS_IAM_ROLE_ARN_TEMPLATE, credentials.getAccountId(), credentials.getRoleName()); final Map meta = Maps.newHashMap(); meta.put(VaultAuthPrincipal.METADATA_KEY_AWS_ACCOUNT_ID, credentials.getAccountId()); diff --git a/src/main/java/com/nike/cerberus/service/CategoryService.java b/src/main/java/com/nike/cerberus/service/CategoryService.java index 7b79fa8e1..cf9444cf9 100644 --- a/src/main/java/com/nike/cerberus/service/CategoryService.java +++ b/src/main/java/com/nike/cerberus/service/CategoryService.java @@ -28,7 +28,9 @@ import javax.inject.Inject; import javax.inject.Singleton; import java.time.OffsetDateTime; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; /** @@ -136,4 +138,22 @@ public boolean deleteCategory(final String id) { final Integer count = categoryDao.deleteCategory(id); return count != null && count > 0; } + + /** + * Collects all the categories and creates an id to name map + * @return map of category ids to category name + */ + public Map getCategoryIdToCategoryNameMap() { + List categoryRecords = categoryDao.getAllCategories(); + Map catIdToStringMap = new HashMap<>(categoryRecords.size()); + categoryRecords.forEach(categoryRecord -> + catIdToStringMap.put(categoryRecord.getId(), categoryRecord.getDisplayName()) + ); + return catIdToStringMap; + } + + + public Optional getCategoryIdByName(String categoryName) { + return Optional.ofNullable(categoryDao.getCategoryIdByName(categoryName)); + } } \ No newline at end of file diff --git a/src/main/java/com/nike/cerberus/service/KmsPolicyService.java b/src/main/java/com/nike/cerberus/service/KmsPolicyService.java index 9d6c8615f..c86e36616 100644 --- a/src/main/java/com/nike/cerberus/service/KmsPolicyService.java +++ b/src/main/java/com/nike/cerberus/service/KmsPolicyService.java @@ -83,7 +83,7 @@ public String generateStandardKmsPolicy(final String iamRoleAccountId, final Str Statement iamRoleUsageStatement = new Statement(Statement.Effect.Allow); iamRoleUsageStatement.withId("Target IAM Role Has Decrypt Action"); iamRoleUsageStatement.withPrincipals( - new Principal(AWS_PROVIDER, String.format("arn:aws:iam::%s:role/%s", iamRoleAccountId, iamRoleName), false)); + new Principal(AWS_PROVIDER, String.format(AuthenticationService.AWS_IAM_ROLE_ARN_TEMPLATE, iamRoleAccountId, iamRoleName), false)); iamRoleUsageStatement.withActions(KmsActions.DecryptAction); iamRoleUsageStatement.withResources(new Resource("*")); diff --git a/src/main/java/com/nike/cerberus/service/MetadataService.java b/src/main/java/com/nike/cerberus/service/MetadataService.java index f2433d935..3b1538bae 100644 --- a/src/main/java/com/nike/cerberus/service/MetadataService.java +++ b/src/main/java/com/nike/cerberus/service/MetadataService.java @@ -16,58 +16,146 @@ package com.nike.cerberus.service; -import com.nike.cerberus.dao.AwsIamRoleDao; -import com.nike.cerberus.dao.CategoryDao; -import com.nike.cerberus.dao.RoleDao; -import com.nike.cerberus.dao.SafeDepositBoxDao; -import com.nike.cerberus.dao.UserGroupDao; -import com.nike.cerberus.domain.SdbMetadata; +import com.nike.backstopper.exception.ApiException; +import com.nike.cerberus.domain.IamRolePermission; +import com.nike.cerberus.domain.Role; +import com.nike.cerberus.domain.SDBMetadata; import com.nike.cerberus.domain.SDBMetadataResult; -import com.nike.cerberus.record.AwsIamRolePermissionRecord; -import com.nike.cerberus.record.AwsIamRoleRecord; -import com.nike.cerberus.record.CategoryRecord; -import com.nike.cerberus.record.RoleRecord; -import com.nike.cerberus.record.SafeDepositBoxRecord; -import com.nike.cerberus.record.UserGroupPermissionRecord; -import org.apache.commons.lang3.StringUtils; +import com.nike.cerberus.domain.SafeDepositBox; +import com.nike.cerberus.domain.UserGroupPermission; +import com.nike.cerberus.error.InvalidCategoryNameApiError; +import com.nike.cerberus.error.InvalidIamRoleArnApiError; +import com.nike.cerberus.error.InvalidRoleNameApiError; +import com.nike.cerberus.util.UuidSupplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** - * Provides general stats about safe deposit boxes. + * A service that can perform admin tasks around SDB metadata */ public class MetadataService { private final Logger logger = LoggerFactory.getLogger(this.getClass()); - private final SafeDepositBoxDao safeDepositBoxDao; - private final UserGroupDao userGroupDao; - private final CategoryDao categoryDao; - private final RoleDao roleDao; - private final AwsIamRoleDao awsIamRoleDao; + private final SafeDepositBoxService safeDepositBoxService; + private final CategoryService categoryService; + private final RoleService roleService; + private final UuidSupplier uuidSupplier; @Inject - public MetadataService(SafeDepositBoxDao safeDepositBoxDao, - UserGroupDao userGroupDao, - CategoryDao categoryDao, - RoleDao roleDao, - AwsIamRoleDao awsIamRoleDao) { - - this.safeDepositBoxDao = safeDepositBoxDao; - this.userGroupDao = userGroupDao; - this.categoryDao = categoryDao; - this.roleDao = roleDao; - this.awsIamRoleDao = awsIamRoleDao; + public MetadataService(SafeDepositBoxService safeDepositBoxService, + CategoryService categoryService, + RoleService roleService, + UuidSupplier uuidSupplier) { + + this.safeDepositBoxService = safeDepositBoxService; + this.categoryService = categoryService; + this.roleService = roleService; + this.uuidSupplier = uuidSupplier; } /** - * Method for retrieving meta data about SDBs sorted by created date. + * Creates or Updates an SDB using saved off metadata. + * This method differs from SafeDepositBoxService::createSafeDepositBox and SafeDepositBoxService::updateSafeDepositBox + * only in that this method sets the created by and last updated fields which are normally sourced automatically. + * + * This is an admin function so that backed up SDB metadata can easily be restored. + * An example would be a cross region recovery event where you are restoring backed up data from a different + * region / cerberus environment + * + * @param sdbMetadata SDB Payload to restore + */ + public void restoreMetadata(SDBMetadata sdbMetadata, String adminUser) { + logger.info("Restoring metadata for SDB: {}", sdbMetadata.getName()); + + Optional sdbId = safeDepositBoxService.getSafeDepositBoxIdByName(sdbMetadata.getName()); + String id; + if (sdbId.isPresent()) { + id = sdbId.get(); + + logger.info("Found existing SDB for {} with id {}, forcing restore", sdbMetadata.getName(), id); + } else { + // create + id = uuidSupplier.get(); + logger.info("No SDB found for {}, creating new SDB", sdbMetadata.getName()); + } + + // Map the string category name to a category id + Optional categoryOpt = categoryService.getCategoryIdByName(sdbMetadata.getCategory()); + if (! categoryOpt.isPresent()) { + throw ApiException.newBuilder() + .withApiErrors(new InvalidCategoryNameApiError(sdbMetadata.getCategory())) + .build(); + } + String categoryId = categoryOpt.get(); + + Set userGroupPermissionSet = new HashSet<>(); + sdbMetadata.getUserGroupPermissions().forEach((groupName, roleName) -> { + userGroupPermissionSet.add(new UserGroupPermission() + .withName(groupName) + .withRoleId(getRoleIdFromName(roleName)) + ); + }); + + Set iamRolePermissionSet = new HashSet<>(); + sdbMetadata.getIamRolePermissions().forEach((iamRoleArn, roleName) -> { + Pattern iamRoleArnParserPattern = Pattern.compile("arn:aws:iam::(?.*?):role/(?.*)"); + Matcher iamRoleArnParserMatcher = iamRoleArnParserPattern.matcher(iamRoleArn); + if (! iamRoleArnParserMatcher.find()) { + throw ApiException.newBuilder() + .withApiErrors(new InvalidIamRoleArnApiError(sdbMetadata.getCategory())) + .build(); + } + + iamRolePermissionSet.add(new IamRolePermission() + .withAccountId(iamRoleArnParserMatcher.group("accountId")) + .withIamRoleName(iamRoleArnParserMatcher.group("roleName")) + .withRoleId(getRoleIdFromName(roleName)) + ); + }); + + + SafeDepositBox sdb = new SafeDepositBox(); + sdb.setId(id); + sdb.setPath(sdbMetadata.getPath()); + sdb.setCategoryId(categoryId); + sdb.setName(sdbMetadata.getName()); + sdb.setOwner(sdbMetadata.getOwner()); + sdb.setDescription(sdbMetadata.getDescription()); + sdb.setCreatedTs(sdbMetadata.getCreatedTs()); + sdb.setLastUpdatedTs(sdbMetadata.getLastUpdatedTs()); + sdb.setCreatedBy(sdbMetadata.getCreatedBy()); + sdb.setLastUpdatedBy(sdbMetadata.getLastUpdatedBy()); + sdb.setUserGroupPermissions(userGroupPermissionSet); + sdb.setIamRolePermissions(iamRolePermissionSet); + + safeDepositBoxService.restoreSafeDepositBox(sdb, adminUser); + } + + private String getRoleIdFromName(String roleName) { + // map the string role name to a role id + Optional role = roleService.getRoleByName(roleName); + if (! role.isPresent()) { + throw ApiException.newBuilder() + .withApiErrors(new InvalidRoleNameApiError(roleName)) + .build(); + } + return role.get().getId(); + } + + /** + * Method for retrieving metadata about SDBs sorted by created date. * * @param limit The int limit for paginating. * @param offset The int offset for paginating. @@ -77,47 +165,31 @@ public SDBMetadataResult getSDBMetadata(int limit, int offset) { SDBMetadataResult result = new SDBMetadataResult(); result.setLimit(limit); result.setOffset(offset); - result.setTotalSDBCount(safeDepositBoxDao.getSafeDepositBoxCount()); + result.setTotalSDBCount(safeDepositBoxService.getTotalNumberOfSafeDepositBoxes()); result.setHasNext(result.getTotalSDBCount() > (offset + limit)); if (result.isHasNext()) { result.setNextOffset(offset + limit); } - List sdbMetadataList = getSDBMetadataList(limit, offset); + List sdbMetadataList = getSDBMetadataList(limit, offset); result.setSafeDepositBoxMetadata(sdbMetadataList); result.setSdbCountInResult(sdbMetadataList.size()); return result; } - protected Map getCategoryIdToStringMap() { - List categoryRecords = categoryDao.getAllCategories(); - Map catIdToStringMap = new HashMap<>(categoryRecords.size()); - categoryRecords.forEach(categoryRecord -> - catIdToStringMap.put(categoryRecord.getId(), categoryRecord.getDisplayName()) - ); - return catIdToStringMap; - } - - protected Map getRoleIdToStringMap() { - List roleRecords = roleDao.getAllRoles(); - Map roleIdToStringMap = new HashMap<>(roleRecords.size()); - roleRecords.forEach(roleRecord -> roleIdToStringMap.put(roleRecord.getId(), roleRecord.getName())); - return roleIdToStringMap; - } - - protected List getSDBMetadataList(int limit, int offset) { - List sdbs = new LinkedList<>(); + protected List getSDBMetadataList(int limit, int offset) { + List sdbs = new LinkedList<>(); // Collect the categories. - Map catIdToStringMap = getCategoryIdToStringMap(); + Map catIdToStringMap = categoryService.getCategoryIdToCategoryNameMap(); // Collect the roles - Map roleIdToStringMap = getRoleIdToStringMap(); + Map roleIdToStringMap = roleService.getRoleIdToStringMap(); // Collect The SDB Records - final List safeDepositBoxRecords = safeDepositBoxDao.getSafeDepositBoxes(limit, offset); + List safeDepositBoxes = safeDepositBoxService.getSafeDepositBoxes(limit, offset); // for each SDB collect the user and iam permissions and add to result - safeDepositBoxRecords.forEach(sdb -> { - SdbMetadata data = new SdbMetadata(); + safeDepositBoxes.forEach(sdb -> { + SDBMetadata data = new SDBMetadata(); data.setName(sdb.getName()); data.setPath(sdb.getPath()); data.setDescription(sdb.getDescription()); @@ -126,44 +198,34 @@ protected List getSDBMetadataList(int limit, int offset) { data.setCreatedTs(sdb.getCreatedTs()); data.setLastUpdatedBy(sdb.getLastUpdatedBy()); data.setLastUpdatedTs(sdb.getLastUpdatedTs()); - - // set the owner and map group to roles - processGroupData(roleIdToStringMap, data, sdb.getId()); - - data.setIamRolePermissions(getIamRolePermissionMap(roleIdToStringMap, sdb.getId())); - + data.setOwner(sdb.getOwner()); + data.setUserGroupPermissions(getUserGroupPermissionsMap(roleIdToStringMap, sdb.getUserGroupPermissions())); + data.setIamRolePermissions(getIamRolePermissionMap(roleIdToStringMap, sdb.getIamRolePermissions())); sdbs.add(data); }); return sdbs; } - protected void processGroupData(Map roleIdToStringMap, SdbMetadata data, String sdbId) { - List userPerms = userGroupDao.getUserGroupPermissions(sdbId); - Map groupRoleMap = new HashMap<>(userPerms.size() - 1); - userPerms.forEach(record -> { - String group = userGroupDao.getUserGroup(record.getUserGroupId()).get().getName(); - String role = roleIdToStringMap.get(record.getRoleId()); - - if (StringUtils.equals(role, RoleRecord.ROLE_OWNER)) { - data.setOwner(group); - } else { - groupRoleMap.put(group, role); - } - }); + protected Map getUserGroupPermissionsMap(Map roleIdToStringMap, + Set permissions) { + + Map permissionsMap = new HashMap<>(); + permissions.forEach(permission -> + permissionsMap.put(permission.getName(), roleIdToStringMap.get(permission.getRoleId()))); - data.setUserGroupPermissions(groupRoleMap); + return permissionsMap; } - protected Map getIamRolePermissionMap(Map roleIdToStringMap, String sdbId) { - List iamPerms = awsIamRoleDao.getIamRolePermissions(sdbId); + protected Map getIamRolePermissionMap(Map roleIdToStringMap, + Set iamPerms) { + Map iamRoleMap = new HashMap<>(iamPerms.size()); - iamPerms.forEach(record -> { - AwsIamRoleRecord iam = awsIamRoleDao.getIamRole(record.getAwsIamRoleId()).get(); - String role = roleIdToStringMap.get(record.getRoleId()); + iamPerms.forEach(perm -> { + String role = roleIdToStringMap.get(perm.getRoleId()); - iamRoleMap.put(String.format("arn:aws:iam::%s:role/%s", - iam.getAwsAccountId(), iam.getAwsIamRoleName()), role); + iamRoleMap.put(String.format(AuthenticationService.AWS_IAM_ROLE_ARN_TEMPLATE, + perm.getAccountId(), perm.getIamRoleName()), role); }); return iamRoleMap; } diff --git a/src/main/java/com/nike/cerberus/service/RoleService.java b/src/main/java/com/nike/cerberus/service/RoleService.java index 019dd4f40..c2f68e86b 100644 --- a/src/main/java/com/nike/cerberus/service/RoleService.java +++ b/src/main/java/com/nike/cerberus/service/RoleService.java @@ -23,7 +23,9 @@ import javax.inject.Inject; import javax.inject.Singleton; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; /** @@ -102,4 +104,11 @@ public Optional getRoleByName(final String name) { return Optional.empty(); } + + public Map getRoleIdToStringMap() { + List roleRecords = roleDao.getAllRoles(); + Map roleIdToStringMap = new HashMap<>(roleRecords.size()); + roleRecords.forEach(roleRecord -> roleIdToStringMap.put(roleRecord.getId(), roleRecord.getName())); + return roleIdToStringMap; + } } diff --git a/src/main/java/com/nike/cerberus/service/SafeDepositBoxService.java b/src/main/java/com/nike/cerberus/service/SafeDepositBoxService.java index 5d51c352f..39b41b493 100644 --- a/src/main/java/com/nike/cerberus/service/SafeDepositBoxService.java +++ b/src/main/java/com/nike/cerberus/service/SafeDepositBoxService.java @@ -45,6 +45,7 @@ import javax.inject.Inject; import javax.inject.Singleton; import java.time.OffsetDateTime; +import java.util.LinkedList; import java.util.List; import java.util.Optional; import java.util.Set; @@ -136,50 +137,62 @@ public List getAssociatedSafeDepositBoxes(final Set getAssociatedSafeDepositBox(final Set groups, final String id) { - SafeDepositBox safeDepositBox = null; - final Optional safeDepositBoxRecord = - safeDepositBoxDao.getSafeDepositBox(id); + final Optional safeDepositBoxRecord = safeDepositBoxDao.getSafeDepositBox(id); if (safeDepositBoxRecord.isPresent()) { final Set userGroupPermissions = userGroupPermissionService.getUserGroupPermissions(id); final long count = userGroupPermissions.stream().filter(perm -> groups.contains(perm.getName())).count(); - if (count == 0) { throw ApiException.newBuilder() .withApiErrors(DefaultApiError.ACCESS_DENIED) .build(); } - String owner = null; - final Optional possibleOwner = extractOwner(userGroupPermissions); + return Optional.of(getSDBFromRecord(safeDepositBoxRecord.get())); + } - if (!possibleOwner.isPresent()) { - logger.error("Detected Safe Deposit Box without owner! ID={}", id); - } else { - owner = possibleOwner.get(); - } + return Optional.empty(); + } - final Set iamRolePermissions = iamRolePermissionService.getIamRolePermissions(id); - - safeDepositBox = new SafeDepositBox(); - safeDepositBox.setId(safeDepositBoxRecord.get().getId()); - safeDepositBox.setName(safeDepositBoxRecord.get().getName()); - safeDepositBox.setDescription(safeDepositBoxRecord.get().getDescription()); - safeDepositBox.setPath(safeDepositBoxRecord.get().getPath()); - safeDepositBox.setCategoryId(safeDepositBoxRecord.get().getCategoryId()); - safeDepositBox.setCreatedBy(safeDepositBoxRecord.get().getCreatedBy()); - safeDepositBox.setLastUpdatedBy(safeDepositBoxRecord.get().getLastUpdatedBy()); - safeDepositBox.setCreatedTs(safeDepositBoxRecord.get().getCreatedTs()); - safeDepositBox.setLastUpdatedTs(safeDepositBoxRecord.get().getLastUpdatedTs()); - safeDepositBox.setOwner(owner); - safeDepositBox.setUserGroupPermissions(userGroupPermissions); - safeDepositBox.setIamRolePermissions(iamRolePermissions); + protected SafeDepositBox getSDBFromRecord(SafeDepositBoxRecord safeDepositBoxRecord) { + if (safeDepositBoxRecord == null) { + throw new IllegalArgumentException("Safe Deposit Box Record must not be null"); } - return Optional.ofNullable(safeDepositBox); - } + String id = safeDepositBoxRecord.getId(); + + final Set userGroupPermissions = + userGroupPermissionService.getUserGroupPermissions(id); + + String owner = null; + final Optional possibleOwner = extractOwner(userGroupPermissions); + + if (!possibleOwner.isPresent()) { + logger.error("Detected Safe Deposit Box without owner! ID={}", id); + } else { + owner = possibleOwner.get(); + } + final Set iamRolePermissions = iamRolePermissionService.getIamRolePermissions(id); + + SafeDepositBox safeDepositBox = new SafeDepositBox(); + safeDepositBox.setId(safeDepositBoxRecord.getId()); + safeDepositBox.setName(safeDepositBoxRecord.getName()); + safeDepositBox.setDescription(safeDepositBoxRecord.getDescription()); + safeDepositBox.setPath(safeDepositBoxRecord.getPath()); + safeDepositBox.setCategoryId(safeDepositBoxRecord.getCategoryId()); + safeDepositBox.setCreatedBy(safeDepositBoxRecord.getCreatedBy()); + safeDepositBox.setLastUpdatedBy(safeDepositBoxRecord.getLastUpdatedBy()); + safeDepositBox.setCreatedTs(safeDepositBoxRecord.getCreatedTs()); + safeDepositBox.setLastUpdatedTs(safeDepositBoxRecord.getLastUpdatedTs()); + safeDepositBox.setOwner(owner); + safeDepositBox.setUserGroupPermissions(userGroupPermissions); + safeDepositBox.setIamRolePermissions(iamRolePermissions); + + return safeDepositBox; + } + /** * Creates a safe deposit box and all the appropriate permissions. Policies for each role are also * created within Vault. @@ -320,7 +333,7 @@ private void assertIsOwner(final Set groups, final SafeDepositBox box) { * @param userGroupPermissionSet Set to add the owner to * @param owner Owner to be added */ - private void addOwnerPermission(final Set userGroupPermissionSet, final String owner) { + protected void addOwnerPermission(final Set userGroupPermissionSet, final String owner) { UserGroupPermission ownerPermission = new UserGroupPermission(); ownerPermission.setId(uuidSupplier.get()); ownerPermission.setName(owner); @@ -407,7 +420,7 @@ private String buildPath(final String name, final String categoryPath) { /** * Updates the owner if its changed. */ - private void updateOwner(final String safeDepositBoxId, + protected void updateOwner(final String safeDepositBoxId, final String newOwner, final String user, final OffsetDateTime dateTime) { @@ -447,7 +460,7 @@ private void updateOwner(final String safeDepositBoxId, /** * Sorts out the set of permissions into, grant, update and revoke sets. After that it applies those changes. */ - private void modifyUserGroupPermissions(final SafeDepositBox currentBox, + protected void modifyUserGroupPermissions(final SafeDepositBox currentBox, final Set userGroupPermissionSet, final String user, final OffsetDateTime dateTime) { @@ -476,7 +489,7 @@ private void modifyUserGroupPermissions(final SafeDepositBox currentBox, /** * Sorts out the set of permissions into, grant, update and revoke sets. After that it applies those changes. */ - private void modifyIamRolePermissions(final SafeDepositBox currentBox, + protected void modifyIamRolePermissions(final SafeDepositBox currentBox, final Set iamRolePermissionSet, final String user, final OffsetDateTime dateTime) { @@ -538,4 +551,81 @@ private void deleteAllSecrets(final String path) { .build(); } } + + /** + * @return The total number of safe deposit boxes. + */ + public int getTotalNumberOfSafeDepositBoxes() { + return safeDepositBoxDao.getSafeDepositBoxCount(); + } + + /** + * + * A paginatable method for iterating retrieving all SDBs + * + * @param limit The maximum number of SDBs to fetch + * @param offset The offset to paginate with + */ + public List getSafeDepositBoxes(int limit, int offset) { + List records = safeDepositBoxDao.getSafeDepositBoxes(limit, offset); + List result = new LinkedList<>(); + records.forEach(safeDepositBoxRecord -> { + result.add(getSDBFromRecord(safeDepositBoxRecord)); + }); + return result; + } + + /** + * @param name Safe Deposit Box name + * @return The id for the box + */ + public Optional getSafeDepositBoxIdByName(String name) { + return Optional.ofNullable(safeDepositBoxDao.getSafeDepositBoxIdByName(name)); + } + + /** + * Admin method for restoring sdb + * @param safeDepositBox Safe Deposit Box to restore + */ + @Transactional + public void restoreSafeDepositBox(SafeDepositBox safeDepositBox, + String adminUser) { + + SafeDepositBoxRecord boxToStore = new SafeDepositBoxRecord(); + boxToStore.setId(safeDepositBox.getId()); + boxToStore.setPath(safeDepositBox.getPath()); + boxToStore.setCategoryId(safeDepositBox.getCategoryId()); + boxToStore.setName(safeDepositBox.getName()); + boxToStore.setDescription(safeDepositBox.getDescription()); + boxToStore.setCreatedTs(safeDepositBox.getCreatedTs()); + boxToStore.setLastUpdatedTs(safeDepositBox.getLastUpdatedTs()); + boxToStore.setCreatedBy(safeDepositBox.getCreatedBy()); + boxToStore.setLastUpdatedBy(safeDepositBox.getLastUpdatedBy()); + + OffsetDateTime now = dateTimeSupplier.get(); + Optional existingBoxRecord = safeDepositBoxDao.getSafeDepositBox(safeDepositBox.getId()); + if (existingBoxRecord.isPresent()) { + safeDepositBoxDao.fullUpdateSafeDepositBox(boxToStore); + SafeDepositBox existingBox = getSDBFromRecord(existingBoxRecord.get()); + updateOwner(safeDepositBox.getId(), safeDepositBox.getOwner(), adminUser, now); + modifyUserGroupPermissions(existingBox, safeDepositBox.getUserGroupPermissions(), adminUser, now); + modifyIamRolePermissions(existingBox, safeDepositBox.getIamRolePermissions(), adminUser, now); + } else { + safeDepositBoxDao.createSafeDepositBox(boxToStore); + addOwnerPermission(safeDepositBox.getUserGroupPermissions(), safeDepositBox.getOwner()); + userGroupPermissionService.grantUserGroupPermissions( + safeDepositBox.getId(), + safeDepositBox.getUserGroupPermissions(), + adminUser, + now); + + iamRolePermissionService.grantIamRolePermissions( + safeDepositBox.getId(), + safeDepositBox.getIamRolePermissions(), + adminUser, + now); + + vaultPolicyService.createStandardPolicies(safeDepositBox.getName(), safeDepositBox.getPath()); + } + } } diff --git a/src/main/resources/com/nike/cerberus/mapper/CategoryMapper.xml b/src/main/resources/com/nike/cerberus/mapper/CategoryMapper.xml index 5363eb949..8ddaafaf1 100644 --- a/src/main/resources/com/nike/cerberus/mapper/CategoryMapper.xml +++ b/src/main/resources/com/nike/cerberus/mapper/CategoryMapper.xml @@ -48,6 +48,15 @@ ID = #{id} + + INSERT INTO CATEGORY diff --git a/src/main/resources/com/nike/cerberus/mapper/SafeDepositBoxMapper.xml b/src/main/resources/com/nike/cerberus/mapper/SafeDepositBoxMapper.xml index e83774ccf..c11b60c93 100644 --- a/src/main/resources/com/nike/cerberus/mapper/SafeDepositBoxMapper.xml +++ b/src/main/resources/com/nike/cerberus/mapper/SafeDepositBoxMapper.xml @@ -71,7 +71,7 @@ LAST_UPDATED_TS FROM SAFE_DEPOSIT_BOX - ORDER BY CREATED_TS ASC + ORDER BY CREATED_TS DESC LIMIT #{limit} OFFSET #{offset} @@ -170,6 +170,20 @@ ID = #{record.id} + + UPDATE + SAFE_DEPOSIT_BOX + SET + CATEGORY_ID = #{record.categoryId}, + DESCRIPTION = #{record.description}, + CREATED_BY = #{record.createdBy}, + LAST_UPDATED_BY = #{record.lastUpdatedBy}, + CREATED_TS = #{record.createdTs}, + LAST_UPDATED_TS = #{record.lastUpdatedTs} + WHERE + ID = #{record.id} + + DELETE FROM SAFE_DEPOSIT_BOX @@ -177,4 +191,8 @@ ID = #{id} + + \ No newline at end of file diff --git a/src/test/java/com/nike/cerberus/service/CategoryServiceTest.java b/src/test/java/com/nike/cerberus/service/CategoryServiceTest.java new file mode 100644 index 000000000..61343af39 --- /dev/null +++ b/src/test/java/com/nike/cerberus/service/CategoryServiceTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2017 Nike, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.nike.cerberus.service; + +import com.nike.cerberus.dao.CategoryDao; +import com.nike.cerberus.record.CategoryRecord; +import com.nike.cerberus.util.DateTimeSupplier; +import com.nike.cerberus.util.Slugger; +import com.nike.cerberus.util.UuidSupplier; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +public class CategoryServiceTest { + + @Mock + private CategoryDao categoryDao; + + @Mock + private UuidSupplier uuidSupplier; + + @Mock + private Slugger slugger; + + @Mock + private DateTimeSupplier dateTimeSupplier; + + @InjectMocks + private CategoryService categoryService; + + @Before + public void before() { + initMocks(this); + } + + @Test + public void test_that_getCategoryIdToStringMap_returns_valid_map() { + Map expected = new HashMap<>(); + expected.put("abc", "foo"); + + when(categoryDao.getAllCategories()) + .thenReturn(Arrays.asList(new CategoryRecord().setId("abc").setDisplayName("foo"))); + + Map actual = categoryService.getCategoryIdToCategoryNameMap(); + assertEquals(expected, actual); + } + +} diff --git a/src/test/java/com/nike/cerberus/service/MetadataServiceTest.java b/src/test/java/com/nike/cerberus/service/MetadataServiceTest.java index ab6dfed39..3357f4db3 100644 --- a/src/test/java/com/nike/cerberus/service/MetadataServiceTest.java +++ b/src/test/java/com/nike/cerberus/service/MetadataServiceTest.java @@ -16,39 +16,37 @@ package com.nike.cerberus.service; -import com.nike.cerberus.dao.AwsIamRoleDao; -import com.nike.cerberus.dao.CategoryDao; -import com.nike.cerberus.dao.RoleDao; -import com.nike.cerberus.dao.SafeDepositBoxDao; -import com.nike.cerberus.dao.UserGroupDao; -import com.nike.cerberus.domain.SdbMetadata; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nike.cerberus.domain.IamRolePermission; +import com.nike.cerberus.domain.Role; +import com.nike.cerberus.domain.SDBMetadata; import com.nike.cerberus.domain.SDBMetadataResult; -import com.nike.cerberus.record.CategoryRecord; +import com.nike.cerberus.domain.SafeDepositBox; +import com.nike.cerberus.domain.UserGroupPermission; import com.nike.cerberus.record.RoleRecord; -import com.nike.cerberus.record.SafeDepositBoxRecord; -import com.nike.cerberus.record.UserGroupPermissionRecord; -import com.nike.cerberus.record.UserGroupRecord; -import com.nike.cerberus.util.DateTimeSupplier; +import com.nike.cerberus.server.config.CmsConfig; +import com.nike.cerberus.util.UuidSupplier; import org.junit.Before; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.Mock; +import java.io.IOException; +import java.io.InputStream; import java.time.OffsetDateTime; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyMap; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.isA; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; @@ -60,25 +58,16 @@ public class MetadataServiceTest { private MetadataService metadataServiceSpy; @Mock - private RoleService roleService; - - @Mock - private SafeDepositBoxDao safeDepositBoxDao; - - @Mock - private UserGroupDao userGroupDao; - - @Mock - private DateTimeSupplier dateTimeSupplier; + private SafeDepositBoxService safeDepositBoxService; @Mock - private CategoryDao categoryDao; + private CategoryService categoryService; @Mock - private RoleDao roleDao; + private RoleService roleService; @Mock - private AwsIamRoleDao awsIamRoleDao; + private UuidSupplier uuidSupplier; @Before public void before() { @@ -88,14 +77,14 @@ public void before() { } @Test - public void test_that_getSdbMetadata_properly_sets_result_meta_data() { + public void test_that_get_sdb_metadata_properly_sets_result_metadata() { int limit = 5; int offset = 0; int totalSDBs = 20; - when(safeDepositBoxDao.getSafeDepositBoxCount()).thenReturn(totalSDBs); + when(safeDepositBoxService.getTotalNumberOfSafeDepositBoxes()).thenReturn(totalSDBs); - SdbMetadata sdbMD = new SdbMetadata(); + SDBMetadata sdbMD = new SDBMetadata(); doReturn(Arrays.asList(sdbMD)).when(metadataServiceSpy).getSDBMetadataList(limit, offset); SDBMetadataResult actual = metadataServiceSpy.getSDBMetadata(limit, offset); @@ -109,13 +98,13 @@ public void test_that_getSdbMetadata_properly_sets_result_meta_data() { } @Test - public void test_that_getSdbMetadata_set_has_next_to_non_when_done_paging() { + public void test_that_get_sdb_metadata_set_has_next_to_no_when_done_paging() { int limit = 5; int offset = 15; int totalSDBs = 20; - when(safeDepositBoxDao.getSafeDepositBoxCount()).thenReturn(totalSDBs); - doReturn(Arrays.asList(new SdbMetadata())).when(metadataServiceSpy).getSDBMetadataList(limit, offset); + when(safeDepositBoxService.getTotalNumberOfSafeDepositBoxes()).thenReturn(totalSDBs); + doReturn(Arrays.asList(new SDBMetadata())).when(metadataServiceSpy).getSDBMetadataList(limit, offset); SDBMetadataResult actual = metadataServiceSpy.getSDBMetadata(limit, offset); @@ -128,31 +117,7 @@ public void test_that_getSdbMetadata_set_has_next_to_non_when_done_paging() { } @Test - public void test_that_getCategoryIdToStringMap_returns_valid_map() { - Map expected = new HashMap<>(); - expected.put("abc", "foo"); - - when(categoryDao.getAllCategories()) - .thenReturn(Arrays.asList(new CategoryRecord().setId("abc").setDisplayName("foo"))); - - Map actual = metadataService.getCategoryIdToStringMap(); - assertEquals(expected, actual); - } - - @Test - public void test_that_getRoleIdToStringMap_returns_valid_map() { - Map expected = new HashMap<>(); - expected.put("abc", "foo"); - - when(roleDao.getAllRoles()) - .thenReturn(Arrays.asList(new RoleRecord().setId("abc").setName("foo"))); - - Map actual = metadataService.getRoleIdToStringMap(); - assertEquals(expected, actual); - } - - @Test - public void test_that_getSDBMetadataList_returns_valid_list() { + public void test_that_get_sdb_metadata_list_returns_valid_list() { String sdbId = "123"; String categoryName = "foo"; String categoryId = "321"; @@ -160,35 +125,52 @@ public void test_that_getSDBMetadataList_returns_valid_list() { String path = "app/test-name"; String desc = "blah blah blah"; String by = "justin.field@nike.com"; + String careBearsGroup = "care-bears"; + String careBearsId = "000-abc"; + String grumpyBearsGroup = "grumpy-bears"; + String grumpyBearsId = "111-def"; + String ownerId = "000"; + String readId = "111"; + String acctId = "12345"; + String roleName = "foo-role"; + OffsetDateTime offsetDateTime = OffsetDateTime.now(); Map catMap = new HashMap<>(); catMap.put(categoryId, categoryName); - Map roleMap = new HashMap<>(); - doReturn(catMap).when(metadataServiceSpy).getCategoryIdToStringMap(); - doReturn(roleMap).when(metadataServiceSpy).getRoleIdToStringMap(); + Map roleIdToStringMap = new HashMap<>(); + roleIdToStringMap.put(ownerId, RoleRecord.ROLE_OWNER); + roleIdToStringMap.put(readId, RoleRecord.ROLE_READ); + + when(roleService.getRoleIdToStringMap()).thenReturn(roleIdToStringMap); + when(categoryService.getCategoryIdToCategoryNameMap()).thenReturn(catMap); - when(safeDepositBoxDao.getSafeDepositBoxes(1,0)).thenReturn(Arrays.asList(new SafeDepositBoxRecord() - .setId(sdbId) - .setName(name) - .setPath(path) - .setDescription(desc) - .setCategoryId(categoryId) - .setCreatedBy(by) - .setLastUpdatedBy(by) - .setCreatedTs(offsetDateTime) - .setLastUpdatedTs(offsetDateTime))); + SafeDepositBox box = new SafeDepositBox(); + box.setId(sdbId); + box.setName(name); + box.setPath(path); + box.setDescription(desc); + box.setCategoryId(categoryId); + box.setCreatedBy(by); + box.setLastUpdatedBy(by); + box.setCreatedTs(offsetDateTime); + box.setLastUpdatedTs(offsetDateTime); + box.setOwner(careBearsGroup); - doNothing().when(metadataServiceSpy) - .processGroupData(anyMap(), isA(SdbMetadata.class), anyString()); + Set userPerms = new HashSet<>(); + userPerms.add(new UserGroupPermission().withName(grumpyBearsGroup).withRoleId(readId)); + box.setUserGroupPermissions(userPerms); - HashMap rPermMap = new HashMap<>(); - doReturn(rPermMap).when(metadataServiceSpy).getIamRolePermissionMap(roleMap, sdbId); + Set iamPerms = new HashSet<>(); + iamPerms.add(new IamRolePermission().withAccountId(acctId).withIamRoleName(roleName).withRoleId(readId)); + box.setIamRolePermissions(iamPerms); - List actual = metadataServiceSpy.getSDBMetadataList(1,0); + when(safeDepositBoxService.getSafeDepositBoxes(1,0)).thenReturn(Arrays.asList(box)); + + List actual = metadataService.getSDBMetadataList(1,0); assertEquals("List should have 1 entry", 1, actual.size()); - SdbMetadata data = actual.get(0); + SDBMetadata data = actual.get(0); assertEquals("Name should match record", name, data.getName()); assertEquals("path should match record", path, data.getPath()); assertEquals("", categoryName, data.getCategory()); @@ -197,50 +179,66 @@ public void test_that_getSDBMetadataList_returns_valid_list() { assertEquals("last updated by should match record", by, data.getLastUpdatedBy()); assertEquals("created ts should match record", offsetDateTime, data.getCreatedTs()); assertEquals("updated ts should match record", offsetDateTime, data.getLastUpdatedTs()); - assertEquals("iam role perm map should match what is returned by getIamRolePermissionMap", - rPermMap, data.getIamRolePermissions()); - } - @Test - public void test_that_processGroupData_sets_owner_and_adds_user_perms_map() { - String careBearsGroup = "care-bears"; - String careBearsId = "000-abc"; - String grumpyBearsGroup = "grumpy-bears"; - String grumpyBearsId = "111-def"; - String ownerId = "000"; - String readId = "111"; - String sdbId = "abc-123-cdf"; - - Map roleIdToStringMap = new HashMap<>(); - roleIdToStringMap.put(ownerId, RoleRecord.ROLE_OWNER); - roleIdToStringMap.put(readId, RoleRecord.ROLE_READ); - - SdbMetadata metadata = new SdbMetadata(); - - when(userGroupDao.getUserGroupPermissions(sdbId)).thenReturn(Arrays.asList( - new UserGroupPermissionRecord().setRoleId(ownerId).setUserGroupId(careBearsId), - new UserGroupPermissionRecord().setRoleId(readId).setUserGroupId(grumpyBearsId) - )); - - when(userGroupDao.getUserGroup(careBearsId)) - .thenReturn(Optional.of(new UserGroupRecord().setName(careBearsGroup))); - when(userGroupDao.getUserGroup(grumpyBearsId)) - .thenReturn(Optional.of(new UserGroupRecord().setName(grumpyBearsGroup))); - - metadataService.processGroupData(roleIdToStringMap, metadata, sdbId); - - Map expectedMap = new HashMap<>(); - expectedMap.put(grumpyBearsGroup, RoleRecord.ROLE_READ); + Map expectedIamPermMap = new HashMap<>(); + expectedIamPermMap.put(String.format(AuthenticationService.AWS_IAM_ROLE_ARN_TEMPLATE, acctId, roleName), RoleRecord.ROLE_READ); + assertEquals("iam role perm map should match what is returned by getIamRolePermissionMap", + expectedIamPermMap, data.getIamRolePermissions()); - assertEquals("Owner group should be care-bears", careBearsGroup, metadata.getOwner()); + Map expectedGroupPermMap = new HashMap<>(); + expectedGroupPermMap.put(grumpyBearsGroup, RoleRecord.ROLE_READ); + assertEquals("Owner group should be care-bears", careBearsGroup, data.getOwner()); assertEquals("The user group perms should match the expected map", - expectedMap, metadata.getUserGroupPermissions()); + expectedGroupPermMap, data.getUserGroupPermissions()); } @Test - public void test_that_getIamRolePermissionMap_returns_valid_map() { - Map roleMap = new HashMap<>(); - roleMap.put("123", "read"); + public void test_that_restore_metadata_calls_the_sdb_service_with_expected_sdb_box() throws IOException { + String user = "unit-test-user"; + String id = "111"; + String categoryId = "222"; + String categoryName = "Applications"; + String readId = "333"; + String sdbName = "HEALTH CHECK BUCKET"; + + ObjectMapper mapper = CmsConfig.configureObjectMapper(); + InputStream metadataStream = getClass().getClassLoader() + .getResourceAsStream("com/nike/cerberus/service/sdb_metadata_backup.json"); + SDBMetadata sdbMetadata = mapper.readValue(metadataStream, SDBMetadata.class); + + when(safeDepositBoxService.getSafeDepositBoxIdByName(sdbName)).thenReturn(Optional.ofNullable(null)); + when(uuidSupplier.get()).thenReturn(id); + when(categoryService.getCategoryIdByName(categoryName)).thenReturn(Optional.of(categoryId)); + Role readRole = new Role(); + readRole.setId(readId); + when(roleService.getRoleByName(RoleRecord.ROLE_READ)).thenReturn(Optional.of(readRole)); + + metadataService.restoreMetadata(sdbMetadata, user); + + SafeDepositBox expectedSdb = new SafeDepositBox(); + expectedSdb.setId(id); + expectedSdb.setPath("app/health-check-bucket/"); + expectedSdb.setCategoryId(categoryId); + expectedSdb.setName(sdbName); + expectedSdb.setOwner("Lst-Squad.Carebears"); + expectedSdb.setDescription("This SDB is read by the Health Check Lambda..."); + expectedSdb.setCreatedTs(OffsetDateTime.parse("2016-09-08T15:39:31Z")); + expectedSdb.setLastUpdatedTs(OffsetDateTime.parse("2016-12-13T17:28:00Z")); + expectedSdb.setCreatedBy("justin.field@nike.com"); + expectedSdb.setLastUpdatedBy("todd.lisonbee@nike.com"); + + Set userPerms = new HashSet<>(); + userPerms.add(new UserGroupPermission().withName("Foundation.Prod.Support").withRoleId(readId)); + userPerms.add(new UserGroupPermission().withName("Lst-NIKE.FOO.ISL").withRoleId(readId)); + expectedSdb.setUserGroupPermissions(userPerms); + + Set iamPerms = new HashSet<>(); + iamPerms.add(new IamRolePermission().withAccountId("1111111111").withIamRoleName("lambda_prod_healthcheck").withRoleId(readId)); + expectedSdb.setIamRolePermissions(iamPerms); + + expectedSdb.setUserGroupPermissions(userPerms); + expectedSdb.setIamRolePermissions(iamPerms); + + verify(safeDepositBoxService, times(1)).restoreSafeDepositBox(expectedSdb, user); } - } diff --git a/src/test/java/com/nike/cerberus/service/RoleServiceTest.java b/src/test/java/com/nike/cerberus/service/RoleServiceTest.java new file mode 100644 index 000000000..97d3e3a0e --- /dev/null +++ b/src/test/java/com/nike/cerberus/service/RoleServiceTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017 Nike, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.nike.cerberus.service; + +import com.nike.cerberus.dao.RoleDao; +import com.nike.cerberus.record.RoleRecord; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +public class RoleServiceTest { + + @Mock + private RoleDao roleDao; + + @InjectMocks + private RoleService roleService; + + @Before + public void before() { + initMocks(this); + } + + @Test + public void test_that_getRoleIdToStringMap_returns_valid_map() { + Map expected = new HashMap<>(); + expected.put("abc", "foo"); + + when(roleDao.getAllRoles()) + .thenReturn(Arrays.asList(new RoleRecord().setId("abc").setName("foo"))); + + Map actual = roleService.getRoleIdToStringMap(); + assertEquals(expected, actual); + } + +} diff --git a/src/test/java/com/nike/cerberus/service/SafeDepositBoxServiceTest.java b/src/test/java/com/nike/cerberus/service/SafeDepositBoxServiceTest.java new file mode 100644 index 000000000..743d736f2 --- /dev/null +++ b/src/test/java/com/nike/cerberus/service/SafeDepositBoxServiceTest.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2017 Nike, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.nike.cerberus.service; + +import com.nike.cerberus.dao.SafeDepositBoxDao; +import com.nike.cerberus.dao.UserGroupDao; +import com.nike.cerberus.domain.IamRolePermission; +import com.nike.cerberus.domain.SafeDepositBox; +import com.nike.cerberus.domain.UserGroupPermission; +import com.nike.cerberus.record.SafeDepositBoxRecord; +import com.nike.cerberus.util.DateTimeSupplier; +import com.nike.cerberus.util.Slugger; +import com.nike.cerberus.util.UuidSupplier; +import com.nike.vault.client.VaultAdminClient; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.time.OffsetDateTime; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +public class SafeDepositBoxServiceTest { + + @Mock + private SafeDepositBoxDao safeDepositBoxDao; + + @Mock + private UserGroupDao userGroupDao; + + @Mock + private UuidSupplier uuidSupplier; + + @Mock + private CategoryService categoryService; + + @Mock + private RoleService roleService; + + @Mock + private VaultAdminClient vaultAdminClient; + + @Mock + private VaultPolicyService vaultPolicyService; + + @Mock + private UserGroupPermissionService userGroupPermissionService; + + @Mock + private IamRolePermissionService iamRolePermissionService; + + @Mock + private Slugger slugger; + + @Mock + private DateTimeSupplier dateTimeSupplier; + + @InjectMocks + private SafeDepositBoxService safeDepositBoxService; + + private SafeDepositBoxService safeDepositBoxServiceSpy; + + @Before + public void before() { + initMocks(this); + + safeDepositBoxServiceSpy = spy(safeDepositBoxService); + } + + @Test + public void test_that_restore_safe_deposit_box_creates_with_expected_sdb_record_from_safe_depot_box_object() { + String id = "111"; + String categoryId = "222"; + String readId = "333"; + String sdbName = "HEALTH CHECK BUCKET"; + + SafeDepositBox sdbObject = new SafeDepositBox(); + sdbObject.setId(id); + sdbObject.setPath("app/health-check-bucket/"); + sdbObject.setCategoryId(categoryId); + sdbObject.setName(sdbName); + sdbObject.setOwner("Lst-Squad.Carebears"); + sdbObject.setDescription("This SDB is read by the Health Check Lambda..."); + sdbObject.setCreatedTs(OffsetDateTime.parse("2016-09-08T15:39:31Z")); + sdbObject.setLastUpdatedTs(OffsetDateTime.parse("2016-12-13T17:28:00Z")); + sdbObject.setCreatedBy("justin.field@nike.com"); + sdbObject.setLastUpdatedBy("todd.lisonbee@nike.com"); + + Set userPerms = new HashSet<>(); + userPerms.add(new UserGroupPermission().withName("Foundation.Prod.Support").withRoleId(readId)); + userPerms.add(new UserGroupPermission().withName("Lst-NIKE.FOO.ISL").withRoleId(readId)); + sdbObject.setUserGroupPermissions(userPerms); + + Set iamPerms = new HashSet<>(); + iamPerms.add(new IamRolePermission().withAccountId("1111111111").withIamRoleName("lambda_prod_healthcheck").withRoleId(readId)); + sdbObject.setIamRolePermissions(iamPerms); + + sdbObject.setUserGroupPermissions(userPerms); + sdbObject.setIamRolePermissions(iamPerms); + + SafeDepositBoxRecord boxToStore = new SafeDepositBoxRecord(); + boxToStore.setId(sdbObject.getId()); + boxToStore.setPath(sdbObject.getPath()); + boxToStore.setCategoryId(sdbObject.getCategoryId()); + boxToStore.setName(sdbObject.getName()); + boxToStore.setDescription(sdbObject.getDescription()); + boxToStore.setCreatedTs(sdbObject.getCreatedTs()); + boxToStore.setLastUpdatedTs(sdbObject.getLastUpdatedTs()); + boxToStore.setCreatedBy(sdbObject.getCreatedBy()); + boxToStore.setLastUpdatedBy(sdbObject.getLastUpdatedBy()); + + when(safeDepositBoxDao.getSafeDepositBox(sdbObject.getId())).thenReturn(Optional.ofNullable(null)); + doNothing().when(safeDepositBoxServiceSpy).addOwnerPermission(any(), any()); + + safeDepositBoxServiceSpy.restoreSafeDepositBox(sdbObject, "admin-user"); + + verify(safeDepositBoxDao, times(1)).createSafeDepositBox(boxToStore); + } + + @Test + public void test_that_restore_safe_deposit_box_updates_with_expected_sdb_record_from_safe_depot_box_object_when_the_sdb_already_exists() { + String id = "111"; + String categoryId = "222"; + String readId = "333"; + String sdbName = "HEALTH CHECK BUCKET"; + String sdbId = "asdf-1231-23sad-asd"; + + SafeDepositBox sdbObject = new SafeDepositBox(); + sdbObject.setId(id); + sdbObject.setPath("app/health-check-bucket/"); + sdbObject.setCategoryId(categoryId); + sdbObject.setName(sdbName); + sdbObject.setOwner("Lst-Squad.Carebears"); + sdbObject.setDescription("This SDB is read by the Health Check Lambda..."); + sdbObject.setCreatedTs(OffsetDateTime.parse("2016-09-08T15:39:31Z")); + sdbObject.setLastUpdatedTs(OffsetDateTime.parse("2016-12-13T17:28:00Z")); + sdbObject.setCreatedBy("justin.field@nike.com"); + sdbObject.setLastUpdatedBy("todd.lisonbee@nike.com"); + + Set userPerms = new HashSet<>(); + userPerms.add(new UserGroupPermission().withName("Foundation.Prod.Support").withRoleId(readId)); + userPerms.add(new UserGroupPermission().withName("Lst-NIKE.FOO.ISL").withRoleId(readId)); + sdbObject.setUserGroupPermissions(userPerms); + + Set iamPerms = new HashSet<>(); + iamPerms.add(new IamRolePermission().withAccountId("1111111111").withIamRoleName("lambda_prod_healthcheck").withRoleId(readId)); + sdbObject.setIamRolePermissions(iamPerms); + + sdbObject.setUserGroupPermissions(userPerms); + sdbObject.setIamRolePermissions(iamPerms); + + SafeDepositBoxRecord boxToStore = new SafeDepositBoxRecord(); + boxToStore.setId(sdbObject.getId()); + boxToStore.setPath(sdbObject.getPath()); + boxToStore.setCategoryId(sdbObject.getCategoryId()); + boxToStore.setName(sdbObject.getName()); + boxToStore.setDescription(sdbObject.getDescription()); + boxToStore.setCreatedTs(sdbObject.getCreatedTs()); + boxToStore.setLastUpdatedTs(sdbObject.getLastUpdatedTs()); + boxToStore.setCreatedBy(sdbObject.getCreatedBy()); + boxToStore.setLastUpdatedBy(sdbObject.getLastUpdatedBy()); + + SafeDepositBoxRecord existingRecord = new SafeDepositBoxRecord(); + existingRecord.setId(sdbId); + when(safeDepositBoxDao.getSafeDepositBox(sdbObject.getId())).thenReturn(Optional.of(existingRecord)); + doNothing().when(safeDepositBoxServiceSpy).updateOwner(any(), any(), any(), any()); + doNothing().when(safeDepositBoxServiceSpy).modifyUserGroupPermissions(any(), any(), any(), any()); + doNothing().when(safeDepositBoxServiceSpy).modifyIamRolePermissions(any(), any(), any(), any()); + doReturn(sdbObject).when(safeDepositBoxServiceSpy).getSDBFromRecord(any()); + + safeDepositBoxServiceSpy.restoreSafeDepositBox(sdbObject, "admin-user"); + + verify(safeDepositBoxDao, times(1)).fullUpdateSafeDepositBox(boxToStore); + } + +} diff --git a/src/test/resources/com/nike/cerberus/service/sdb_metadata_backup.json b/src/test/resources/com/nike/cerberus/service/sdb_metadata_backup.json new file mode 100644 index 000000000..22559b978 --- /dev/null +++ b/src/test/resources/com/nike/cerberus/service/sdb_metadata_backup.json @@ -0,0 +1,23 @@ +{ + "category":"Applications", + "created_by":"justin.field@nike.com", + "created_ts":"2016-09-08T15:39:31Z", + "data":{ + "app/health-check-bucket/healthcheck":{ + "value":"I am healthy" + } + }, + "description":"This SDB is read by the Health Check Lambda...", + "iam_role_permissions":{ + "arn:aws:iam::1111111111:role/lambda_prod_healthcheck":"read" + }, + "last_updated_by":"todd.lisonbee@nike.com", + "last_updated_ts":"2016-12-13T17:28:00Z", + "name":"HEALTH CHECK BUCKET", + "owner":"Lst-Squad.Carebears", + "path":"app/health-check-bucket/", + "user_group_permissions":{ + "Foundation.Prod.Support":"read", + "Lst-NIKE.FOO.ISL":"read" + } +} \ No newline at end of file