diff --git a/API.md b/API.md index cf7e1f173..9600a9b75 100644 --- a/API.md +++ b/API.md @@ -474,4 +474,81 @@ Returns basic stats about each safe deposit box (name, owner, last updated ts). } ], "safe_deposit_box_total": 2 + } + +# Group Metadata + +## SDB Metadata [/v1/metadata?limit={limit}&offset={offset}] + +### Get metadata [GET] + +Returns pageable metadata for all SDBs +You can use has_next and next_offset from the response to paginate through all records + ++ Parameters + + limit (number) - OPTIONAL: The number of records to include in the metadata result. Defaults to 100 + + offset (number) - OPTIONAL: The offset to use when paginating records. Defaults to 0 + ++ Response 200 (application/json) + + + Headers + + X-Vault-Token: 7f6808f1-ede3-2177-aa9d-45f507391310 + + + Body + + { + "has_next": false, + "next_offset": 0, + "limit": 10, + "offset": 0, + "sdb_count_in_result": 3, + "total_sdbcount": 3, + "safe_deposit_box_meta_data": [ + { + "name": "dev demo", + "path": "app/dev-demo/", + "category": "Applications", + "owner": "Lst-Squad.Carebears", + "description": "test", + "created_ts": "2017-01-04T23:18:40-08:00", + "created_by": "justin.field@nike.com", + "last_updated_ts": "2017-01-04T23:18:40-08:00", + "last_updated_by": "justin.field@nike.com", + "user_group_permissions": { + "Application.FOO.User": "read" + }, + "iam_role_permissions": { + "arn:aws:iam::265866363820:role/asdf": "write" + } + }, + { + "name": "nike dev foo bar", + "path": "app/nike-dev-foo-bar/", + "category": "Applications", + "owner": "Lst-Squad.Carebears", + "description": "adsfasdfadsfasdf", + "created_ts": "2017-01-04T23:19:03-08:00", + "created_by": "justin.field@nike.com", + "last_updated_ts": "2017-01-04T23:19:03-08:00", + "last_updated_by": "justin.field@nike.com", + "user_group_permissions": { + "Lst-FOO-bar": "read" + }, + "iam_role_permissions": {} + }, + { + "name": "IaM W d WASD", + "path": "shared/iam-w-d-wasd/", + "category": "Shared", + "owner": "Lst-Squad.Carebears", + "description": "CAREBERS", + "created_ts": "2017-01-04T23:19:19-08:00", + "created_by": "justin.field@nike.com", + "last_updated_ts": "2017-01-04T23:19:19-08:00", + "last_updated_by": "justin.field@nike.com", + "user_group_permissions": {}, + "iam_role_permissions": {} + } + ] } \ No newline at end of file diff --git a/src/main/java/com/nike/cerberus/dao/SafeDepositBoxDao.java b/src/main/java/com/nike/cerberus/dao/SafeDepositBoxDao.java index 8de25bafe..7e4fe62ff 100644 --- a/src/main/java/com/nike/cerberus/dao/SafeDepositBoxDao.java +++ b/src/main/java/com/nike/cerberus/dao/SafeDepositBoxDao.java @@ -50,8 +50,12 @@ public List getUserAssociatedSafeDepositBoxes(final Set getSafeDepositBoxes() { - return safeDepositBoxMapper.getSafeDepositBoxes(); + public List getSafeDepositBoxes(final int limit, final int offset) { + return safeDepositBoxMapper.getSafeDepositBoxes(limit, offset); + } + + public Integer getSafeDepositBoxCount() { + return safeDepositBoxMapper.count(); } public Optional getSafeDepositBox(final String id) { diff --git a/src/main/java/com/nike/cerberus/domain/SDBMetaData.java b/src/main/java/com/nike/cerberus/domain/SDBMetaData.java new file mode 100644 index 000000000..15823064b --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/SDBMetaData.java @@ -0,0 +1,124 @@ +/* + * 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.domain; + +import java.time.OffsetDateTime; +import java.util.Date; +import java.util.Map; + +public class SDBMetaData { + + private String name; + private String path; + private String category; + private String owner; + private String description; + private OffsetDateTime createdTs; + private String createdBy; + private OffsetDateTime lastUpdatedTs; + private String lastUpdatedBy; + private Map userGroupPermissions; + private Map iamRolePermissions; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public OffsetDateTime getCreatedTs() { + return createdTs; + } + + public void setCreatedTs(OffsetDateTime createdTs) { + this.createdTs = createdTs; + } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public OffsetDateTime getLastUpdatedTs() { + return lastUpdatedTs; + } + + public void setLastUpdatedTs(OffsetDateTime lastUpdatedTs) { + this.lastUpdatedTs = lastUpdatedTs; + } + + public String getLastUpdatedBy() { + return lastUpdatedBy; + } + + public void setLastUpdatedBy(String lastUpdatedBy) { + this.lastUpdatedBy = lastUpdatedBy; + } + + public Map getUserGroupPermissions() { + return userGroupPermissions; + } + + public void setUserGroupPermissions(Map userGroupPermissions) { + this.userGroupPermissions = userGroupPermissions; + } + + public Map getIamRolePermissions() { + return iamRolePermissions; + } + + public void setIamRolePermissions(Map iamRolePermissions) { + this.iamRolePermissions = iamRolePermissions; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/SDBMetaDataResult.java b/src/main/java/com/nike/cerberus/domain/SDBMetaDataResult.java new file mode 100644 index 000000000..cdc97277c --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/SDBMetaDataResult.java @@ -0,0 +1,86 @@ +/* + * 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.domain; + +import java.util.List; + +public class SDBMetaDataResult { + + private boolean hasNext = false; + private int nextOffset = 0; + private int limit = 0; + private int offset = 0; + private int sdbCountInResult; + private int totalSDBCount; + private List safeDepositBoxMetaData; + + public boolean isHasNext() { + return hasNext; + } + + public void setHasNext(boolean hasNext) { + this.hasNext = hasNext; + } + + public int getNextOffset() { + return nextOffset; + } + + public void setNextOffset(int nextOffset) { + this.nextOffset = nextOffset; + } + + public int getLimit() { + return limit; + } + + public void setLimit(int limit) { + this.limit = limit; + } + + public int getOffset() { + return offset; + } + + public void setOffset(int offset) { + this.offset = offset; + } + + public int getSdbCountInResult() { + return sdbCountInResult; + } + + public void setSdbCountInResult(int sdbCountInResult) { + this.sdbCountInResult = sdbCountInResult; + } + + public int getTotalSDBCount() { + return totalSDBCount; + } + + public void setTotalSDBCount(int totalSDBCount) { + this.totalSDBCount = totalSDBCount; + } + + public List getSafeDepositBoxMetaData() { + return safeDepositBoxMetaData; + } + + public void setSafeDepositBoxMetaData(List safeDepositBoxMetaData) { + this.safeDepositBoxMetaData = safeDepositBoxMetaData; + } +} diff --git a/src/main/java/com/nike/cerberus/endpoints/AdminStandardEndpoint.java b/src/main/java/com/nike/cerberus/endpoints/AdminStandardEndpoint.java index 1595f9030..f045a54da 100644 --- a/src/main/java/com/nike/cerberus/endpoints/AdminStandardEndpoint.java +++ b/src/main/java/com/nike/cerberus/endpoints/AdminStandardEndpoint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * 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. diff --git a/src/main/java/com/nike/cerberus/endpoints/admin/GetSDBMetaData.java b/src/main/java/com/nike/cerberus/endpoints/admin/GetSDBMetaData.java new file mode 100644 index 000000000..a1b938093 --- /dev/null +++ b/src/main/java/com/nike/cerberus/endpoints/admin/GetSDBMetaData.java @@ -0,0 +1,139 @@ +/* + * 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.backstopper.apierror.ApiErrorBase; +import com.nike.backstopper.exception.ApiException; +import com.nike.cerberus.domain.SDBMetaDataResult; +import com.nike.cerberus.endpoints.AdminStandardEndpoint; +import com.nike.cerberus.domain.Stats; +import com.nike.cerberus.error.DefaultApiError; +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 org.apache.commons.lang3.StringUtils; + +import javax.ws.rs.core.SecurityContext; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +/** + * Returns meta data for all SDBs in CMS + */ +public class GetSDBMetaData extends AdminStandardEndpoint { + + protected static final String OFFSET_QUERY_KEY = "offset"; + protected static final String LIMIT_QUERY_KEY = "limit"; + protected static final int DEFAULT_OFFSET = 0; + protected static final int DEFAULT_LIMIT = 100; + + private final MetaDataService metaDataService; + + @Inject + public GetSDBMetaData(MetaDataService metaDataService) { + this.metaDataService = metaDataService; + } + + @SuppressWarnings("ConstantConditions") // it lies + @Override + public CompletableFuture> doExecute(final RequestInfo request, + final Executor longRunningTaskExecutor, + final ChannelHandlerContext ctx, + final SecurityContext securityContext) { + + return CompletableFuture.supplyAsync( + () -> ResponseInfo.newBuilder(metaDataService.getSDBMetaData(getLimit(request), getOffset(request))) + .build(), longRunningTaskExecutor + ); + } + + /** + * Parses and validates limit query param + * @param request The request + * @return default or parsed vaule + */ + protected int getLimit(RequestInfo request) { + String limitQueryValue = request.getQueryParamSingle(LIMIT_QUERY_KEY); + int limit = DEFAULT_LIMIT; + + if (limitQueryValue != null) { + validateLimitQuery(limitQueryValue); + limit = Integer.parseInt(limitQueryValue); + } + + return limit; + } + + /** + * validates that the limit query is a valid number >= 1 + * @param limitQueryValue + */ + protected void validateLimitQuery(String limitQueryValue) { + if (! StringUtils.isNumeric(limitQueryValue) || Integer.parseInt(limitQueryValue) < 1) { + throw ApiException.newBuilder() + .withApiErrors(new ApiErrorBase( + DefaultApiError.INVALID_QUERY_PARAMS.getName(), + DefaultApiError.INVALID_QUERY_PARAMS.getErrorCode(), + String.format("limit query param must be an int >= 1, '%s' given", limitQueryValue), + DefaultApiError.INVALID_QUERY_PARAMS.getHttpStatusCode() + )).build(); + } + } + + /** + * Parses and validates offset query param + * @param request The request + * @return default or parsed vaule + */ + protected int getOffset(RequestInfo request) { + String offsetQueryValue = request.getQueryParamSingle(OFFSET_QUERY_KEY); + int offset = DEFAULT_OFFSET; + + if (offsetQueryValue != null) { + validateOffsetQuery(offsetQueryValue); + offset = Integer.parseInt(offsetQueryValue); + } + + return offset; + } + + /** + * Validates that the offset value is a number that is >= 0 + * @param offsetQueryValue + */ + protected void validateOffsetQuery(String offsetQueryValue) { + if (! StringUtils.isNumeric(offsetQueryValue)) { + throw ApiException.newBuilder() + .withApiErrors(new ApiErrorBase( + DefaultApiError.INVALID_QUERY_PARAMS.getName(), + DefaultApiError.INVALID_QUERY_PARAMS.getErrorCode(), + String.format("offset query param must be an int >= 0, '%s' given", offsetQueryValue), + DefaultApiError.INVALID_QUERY_PARAMS.getHttpStatusCode() + )).build(); + } + } + + @Override + public Matcher requestMatcher() { + return Matcher.match("/v1/metadata", HttpMethod.GET); + } + +} diff --git a/src/main/java/com/nike/cerberus/endpoints/stats/GetStats.java b/src/main/java/com/nike/cerberus/endpoints/admin/GetStats.java similarity index 82% rename from src/main/java/com/nike/cerberus/endpoints/stats/GetStats.java rename to src/main/java/com/nike/cerberus/endpoints/admin/GetStats.java index c598457c4..53d6d341b 100644 --- a/src/main/java/com/nike/cerberus/endpoints/stats/GetStats.java +++ b/src/main/java/com/nike/cerberus/endpoints/admin/GetStats.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * 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. @@ -14,11 +14,11 @@ * limitations under the License. */ -package com.nike.cerberus.endpoints.stats; +package com.nike.cerberus.endpoints.admin; -import com.nike.cerberus.domain.Stats; import com.nike.cerberus.endpoints.AdminStandardEndpoint; -import com.nike.cerberus.service.StatsService; +import com.nike.cerberus.domain.Stats; +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; @@ -33,13 +33,14 @@ /** * Returns basic stats about the safe deposit boxes in Cerberus. */ +@Deprecated public class GetStats extends AdminStandardEndpoint { - private final StatsService statsService; + private final MetaDataService metaDataService; @Inject - public GetStats(final StatsService statsService) { - this.statsService = statsService; + public GetStats(final MetaDataService metaDataService) { + this.metaDataService = metaDataService; } @Override @@ -48,7 +49,7 @@ public CompletableFuture> doExecute(final RequestInfo final ChannelHandlerContext ctx, final SecurityContext securityContext) { return CompletableFuture.supplyAsync( - () -> ResponseInfo.newBuilder(statsService.getStats()).build(), + () -> ResponseInfo.newBuilder(metaDataService.getStats()).build(), longRunningTaskExecutor ); } diff --git a/src/main/java/com/nike/cerberus/error/DefaultApiError.java b/src/main/java/com/nike/cerberus/error/DefaultApiError.java index bc177b642..6dccb52c5 100644 --- a/src/main/java/com/nike/cerberus/error/DefaultApiError.java +++ b/src/main/java/com/nike/cerberus/error/DefaultApiError.java @@ -187,6 +187,11 @@ public enum DefaultApiError implements ApiError { */ AUTH_IAM_ROLE_REJECTED(99223, "KMS rejected the IAM Role ARN with an InvalidArnException.", HttpServletResponse.SC_INTERNAL_SERVER_ERROR), + /** + * The IAM Role + Region don't have a KMS key provisioned to encrypt the auth response. + */ + INVALID_QUERY_PARAMS(99224, "Invalid query params", HttpServletResponse.SC_BAD_REQUEST), + /** * Generic not found error. */ diff --git a/src/main/java/com/nike/cerberus/mapper/SafeDepositBoxMapper.java b/src/main/java/com/nike/cerberus/mapper/SafeDepositBoxMapper.java index b745f116e..2081cea59 100644 --- a/src/main/java/com/nike/cerberus/mapper/SafeDepositBoxMapper.java +++ b/src/main/java/com/nike/cerberus/mapper/SafeDepositBoxMapper.java @@ -45,5 +45,7 @@ List getIamRoleAssociatedSafeDepositBoxRoles(@Param("a int deleteSafeDepositBox(@Param("id") String id); - List getSafeDepositBoxes(); + List getSafeDepositBoxes(@Param("limit") int limit, @Param("offset") int offset); + + int count(); } 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 bec30fd1f..52c0172c2 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 @@ -21,6 +21,7 @@ import com.nike.backstopper.apierror.projectspecificinfo.ProjectApiErrors; import com.nike.cerberus.config.CmsEnvPropertiesLoader; import com.nike.cerberus.endpoints.HealthCheckEndpoint; +import com.nike.cerberus.endpoints.admin.GetSDBMetaData; import com.nike.cerberus.endpoints.authentication.AuthenticateIamRole; import com.nike.cerberus.endpoints.authentication.AuthenticateUser; import com.nike.cerberus.endpoints.authentication.MfaCheck; @@ -37,7 +38,7 @@ import com.nike.cerberus.endpoints.sdb.GetSafeDepositBox; import com.nike.cerberus.endpoints.sdb.GetSafeDepositBoxes; import com.nike.cerberus.endpoints.sdb.UpdateSafeDepositBox; -import com.nike.cerberus.endpoints.stats.GetStats; +import com.nike.cerberus.endpoints.admin.GetStats; import com.nike.cerberus.error.DefaultApiErrorsImpl; import com.nike.cerberus.auth.connector.AuthConnector; import com.nike.cerberus.security.CmsRequestSecurityValidator; @@ -172,7 +173,8 @@ public Set> appEndpoints( DeleteSafeDepositBox deleteSafeDepositBox, UpdateSafeDepositBox updateSafeDepositBox, CreateSafeDepositBox createSafeDepositBox, - GetStats getStats + GetStats getStats, + GetSDBMetaData getSDBMetaData ) { return new LinkedHashSet<>(Arrays.>asList( healthCheckEndpoint, @@ -182,7 +184,7 @@ public Set> appEndpoints( getAllRoles, getRole, getSafeDepositBoxes, getSafeDepositBox, deleteSafeDepositBox, updateSafeDepositBox, createSafeDepositBox, - getStats + getStats, getSDBMetaData )); } diff --git a/src/main/java/com/nike/cerberus/service/MetaDataService.java b/src/main/java/com/nike/cerberus/service/MetaDataService.java new file mode 100644 index 000000000..ac6beec55 --- /dev/null +++ b/src/main/java/com/nike/cerberus/service/MetaDataService.java @@ -0,0 +1,221 @@ +/* + * 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.backstopper.exception.ApiException; +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.Role; +import com.nike.cerberus.domain.SDBMetaData; +import com.nike.cerberus.domain.SDBMetaDataResult; +import com.nike.cerberus.domain.SafeDepositBoxStats; +import com.nike.cerberus.domain.Stats; +import com.nike.cerberus.error.DefaultApiError; +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 com.nike.cerberus.record.UserGroupRecord; +import com.nike.cerberus.util.DateTimeSupplier; +import org.apache.commons.lang3.StringUtils; +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; + +/** + * Provides general stats about safe deposit boxes. + */ +public class MetaDataService { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private final RoleService roleService; + private final SafeDepositBoxDao safeDepositBoxDao; + private final UserGroupDao userGroupDao; + private final DateTimeSupplier dateTimeSupplier; + private final CategoryDao categoryDao; + private final RoleDao roleDao; + private final AwsIamRoleDao awsIamRoleDao; + + @Inject + public MetaDataService(RoleService roleService, + SafeDepositBoxDao safeDepositBoxDao, + UserGroupDao userGroupDao, + DateTimeSupplier dateTimeSupplier, + CategoryDao categoryDao, + RoleDao roleDao, + AwsIamRoleDao awsIamRoleDao) { + + this.roleService = roleService; + this.safeDepositBoxDao = safeDepositBoxDao; + this.userGroupDao = userGroupDao; + this.dateTimeSupplier = dateTimeSupplier; + this.categoryDao = categoryDao; + this.roleDao = roleDao; + this.awsIamRoleDao = awsIamRoleDao; + } + + @Deprecated // Use getSDBMetaData + public Stats getStats() { + final Optional ownerRole = roleService.getRoleByName(RoleRecord.ROLE_OWNER); + + if (!ownerRole.isPresent()) { + throw ApiException.newBuilder() + .withApiErrors(DefaultApiError.MISCONFIGURED_APP) + .withExceptionMessage("Owner role doesn't exist!") + .build(); + } + + final Set safeDepositBoxStats = new HashSet<>(); + final List safeDepositBoxRecords = safeDepositBoxDao.getSafeDepositBoxes(1000, 0); + + safeDepositBoxRecords.forEach(r -> { + final List userGroupOwnerRecords = + userGroupDao.getUserGroupsByRole(r.getId(), ownerRole.get().getId()); + + if (userGroupOwnerRecords.size() != 1) { + throw ApiException.newBuilder() + .withApiErrors(DefaultApiError.SDB_TOO_MANY_OWNERS) + .withExceptionMessage("SDB has more than one owner!") + .build(); + } + + final SafeDepositBoxStats sdbStats = new SafeDepositBoxStats(); + sdbStats.setName(r.getName()); + sdbStats.setOwner(userGroupOwnerRecords.get(0).getName()); + sdbStats.setLastUpdatedTs(r.getLastUpdatedTs()); + safeDepositBoxStats.add(sdbStats); + }); + + return new Stats().setSafeDepositBoxStats(safeDepositBoxStats).setGeneratedTs(dateTimeSupplier.get()); + } + + /** + * Method for retrieving meta data about SDBs sorted by created date. + * + * @param limit The int limit for paginating. + * @param offset The int offset for paginating. + * @return SDBMetaDataResult of meta data. + */ + public SDBMetaDataResult getSDBMetaData(int limit, int offset) { + SDBMetaDataResult result = new SDBMetaDataResult(); + result.setLimit(limit); + result.setOffset(offset); + result.setTotalSDBCount(safeDepositBoxDao.getSafeDepositBoxCount()); + result.setHasNext(result.getTotalSDBCount() > (offset + limit)); + if (result.isHasNext()) { + result.setNextOffset(offset + limit); + } + 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<>(); + + // Collect the categories. + Map catIdToStringMap = getCategoryIdToStringMap(); + // Collect the roles + Map roleIdToStringMap = getRoleIdToStringMap(); + // Collect The SDB Records + final List safeDepositBoxRecords = safeDepositBoxDao.getSafeDepositBoxes(limit, offset); + + // for each SDB collect the user and iam permissions and add to result + safeDepositBoxRecords.forEach(sdb -> { + SDBMetaData data = new SDBMetaData(); + data.setName(sdb.getName()); + data.setPath(sdb.getPath()); + data.setDescription(sdb.getDescription()); + data.setCategory(catIdToStringMap.get(sdb.getCategoryId())); + data.setCreatedBy(sdb.getCreatedBy()); + 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())); + + 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); + } + }); + + data.setUserGroupPermissions(groupRoleMap); + } + + protected Map getIamRolePermissionMap(Map roleIdToStringMap, String sdbId) { + List iamPerms = awsIamRoleDao.getIamRolePermissions(sdbId); + Map iamRoleMap = new HashMap<>(iamPerms.size()); + iamPerms.forEach(record -> { + AwsIamRoleRecord iam = awsIamRoleDao.getIamRole(record.getAwsIamRoleId()).get(); + String role = roleIdToStringMap.get(record.getRoleId()); + + iamRoleMap.put(String.format("arn:aws:iam::%s:role/%s", + iam.getAwsAccountId(), iam.getAwsIamRoleName()), role); + }); + return iamRoleMap; + } +} diff --git a/src/main/java/com/nike/cerberus/service/StatsService.java b/src/main/java/com/nike/cerberus/service/StatsService.java deleted file mode 100644 index 4959c8b1f..000000000 --- a/src/main/java/com/nike/cerberus/service/StatsService.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2016 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.backstopper.exception.ApiException; -import com.nike.cerberus.dao.SafeDepositBoxDao; -import com.nike.cerberus.dao.UserGroupDao; -import com.nike.cerberus.domain.Role; -import com.nike.cerberus.domain.SafeDepositBoxStats; -import com.nike.cerberus.domain.Stats; -import com.nike.cerberus.error.DefaultApiError; -import com.nike.cerberus.record.RoleRecord; -import com.nike.cerberus.record.SafeDepositBoxRecord; -import com.nike.cerberus.record.UserGroupRecord; -import com.nike.cerberus.util.DateTimeSupplier; - -import javax.inject.Inject; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -/** - * Provides general stats about safe deposit boxes. - */ -public class StatsService { - - private final RoleService roleService; - - private final SafeDepositBoxDao safeDepositBoxDao; - - private final UserGroupDao userGroupDao; - - private final DateTimeSupplier dateTimeSupplier; - - @Inject - public StatsService(final RoleService roleService, - final SafeDepositBoxDao safeDepositBoxDao, - final UserGroupDao userGroupDao, - final DateTimeSupplier dateTimeSupplier) { - this.roleService = roleService; - this.safeDepositBoxDao = safeDepositBoxDao; - this.userGroupDao = userGroupDao; - this.dateTimeSupplier = dateTimeSupplier; - } - - public Stats getStats() { - final Optional ownerRole = roleService.getRoleByName(RoleRecord.ROLE_OWNER); - - if (!ownerRole.isPresent()) { - throw ApiException.newBuilder() - .withApiErrors(DefaultApiError.MISCONFIGURED_APP) - .withExceptionMessage("Owner role doesn't exist!") - .build(); - } - - final Set safeDepositBoxStats = new HashSet<>(); - final List safeDepositBoxRecords = safeDepositBoxDao.getSafeDepositBoxes(); - - safeDepositBoxRecords.forEach(r -> { - final List userGroupOwnerRecords = - userGroupDao.getUserGroupsByRole(r.getId(), ownerRole.get().getId()); - - if (userGroupOwnerRecords.size() != 1) { - throw ApiException.newBuilder() - .withApiErrors(DefaultApiError.SDB_TOO_MANY_OWNERS) - .withExceptionMessage("SDB has more than one owner!") - .build(); - } - - final SafeDepositBoxStats sdbStats = new SafeDepositBoxStats(); - sdbStats.setName(r.getName()); - sdbStats.setOwner(userGroupOwnerRecords.get(0).getName()); - sdbStats.setLastUpdatedTs(r.getLastUpdatedTs()); - safeDepositBoxStats.add(sdbStats); - }); - - return new Stats().setSafeDepositBoxStats(safeDepositBoxStats).setGeneratedTs(dateTimeSupplier.get()); - } -} diff --git a/src/main/resources/com/nike/cerberus/mapper/SafeDepositBoxMapper.xml b/src/main/resources/com/nike/cerberus/mapper/SafeDepositBoxMapper.xml index b477e7537..e83774ccf 100644 --- a/src/main/resources/com/nike/cerberus/mapper/SafeDepositBoxMapper.xml +++ b/src/main/resources/com/nike/cerberus/mapper/SafeDepositBoxMapper.xml @@ -71,6 +71,9 @@ LAST_UPDATED_TS FROM SAFE_DEPOSIT_BOX + ORDER BY CREATED_TS ASC + LIMIT #{limit} + OFFSET #{offset} + +