diff --git a/README.md b/README.md index 6cfbc28..a7c7a4c 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,54 @@ To learn more about Cerberus, please see the [Cerberus website](http://engineeri ``` Check out ["Working with AWS Credentials"](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html) for more information on how the AWS SDK for Java loads credentials. + +## Manage Safe Deposit Box + +### Create Safe Deposit Box +Your IAM role or user needs to be added to any safe deposit box to be authorized to create a safe deposit box. +``` java + String cerberusUrl = "https://cerberus.example.com"; + String region = "us-west-2"; + CerberusClient cerberusClient = DefaultCerberusClientFactory.getClient(cerberusUrl, region); + String appCategoryId = cerberusClient.getCategoryIdByPath("app"); + Map rolePermissionMap = cerberusClient.getRolePermissionMap(); + CerberusSafeDepositBoxResponse newSdb = cerberusClient.createSafeDepositBox(CerberusSafeDepositBoxRequest.newBuilder() + .withName("cerberus secrets") + .withOwner("very important user group") + .withCategoryId(appCategoryId) + .withRolePermissionMap(rolePermissionMap) + .withIamPrincipalPermission("arn:aws:iam::12345:role/ec2-role", OWNER) + .withUserGroupPermission("readonly group", READ) + .build()); +``` + +### Update Safe Deposit Box +Your IAM role or user needs to be the `owner` of a safe deposit box to update it. +``` java + String cerberusUrl = "https://cerberus.example.com"; + String region = "us-west-2"; + CerberusClient cerberusClient = DefaultCerberusClientFactory.getClient(cerberusUrl, region); + Map rolePermissionMap = cerberusClient.getRolePermissionMap(); + CerberusSafeDepositBoxResponse sdb = cerberusClient.getSafeDepositBoxByName("cerberus secrets"); + cerberusClient.updateSafeDepositBox(CerberusSafeDepositBoxRequest.newBuilder() + .withCerberusSafeDepositBoxResponse(sdb) + .withRolePermissionMap(rolePermissionMap) + .withIamPrincipalPermission("arn:aws:iam::12345:role/lambda-role", READ) + .build()); +``` + +### Delete Safe Deposit Box +Your IAM role or user needs to be the `owner` of a safe deposit box to delete it. +``` java + String cerberusUrl = "https://cerberus.example.com"; + String region = "us-west-2"; + CerberusClient cerberusClient = DefaultCerberusClientFactory.getClient(cerberusUrl, region); + Map rolePermissionMap = cerberusClient.getRolePermissionMap(); + CerberusSafeDepositBoxResponse sdb = cerberusClient.getSafeDepositBoxByName("cerberus secrets"); + cerberusClient.deleteSafeDepositBox(sdb.getId()); +``` + + ## Development ### Run Integration Tests diff --git a/gradle.properties b/gradle.properties index 96f717f..06a54ad 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,6 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # -version=7.3.3 +version=7.4.0 groupId=com.nike artifactId=cerberus-client diff --git a/src/main/java/com/nike/cerberus/client/CerberusClient.java b/src/main/java/com/nike/cerberus/client/CerberusClient.java index 1362c3a..b0ae15b 100644 --- a/src/main/java/com/nike/cerberus/client/CerberusClient.java +++ b/src/main/java/com/nike/cerberus/client/CerberusClient.java @@ -26,10 +26,17 @@ import com.nike.cerberus.client.http.HttpHeader; import com.nike.cerberus.client.http.HttpMethod; import com.nike.cerberus.client.http.HttpStatus; +import com.nike.cerberus.client.model.CerberusCategoryResponse; import com.nike.cerberus.client.model.CerberusListFilesResponse; import com.nike.cerberus.client.model.CerberusListResponse; import com.nike.cerberus.client.model.CerberusResponse; import static io.github.resilience4j.decorators.Decorators.ofSupplier; + +import com.nike.cerberus.client.model.CerberusRolePermission; +import com.nike.cerberus.client.model.CerberusRoleResponse; +import com.nike.cerberus.client.model.CerberusSafeDepositBoxRequest; +import com.nike.cerberus.client.model.CerberusSafeDepositBoxResponse; +import com.nike.cerberus.client.model.CerberusSafeDepositBoxSummaryResponse; import io.github.resilience4j.core.IntervalFunction; import io.github.resilience4j.retry.Retry; import io.github.resilience4j.retry.RetryConfig; @@ -54,8 +61,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.function.Predicate; +import java.util.stream.Collectors; /** * Client for interacting with a Cerberus. @@ -64,14 +70,18 @@ public class CerberusClient { public static final String SECRET_PATH_PREFIX = "v1/secret/"; + public static final String ROLE_PATH = "v1/role"; + + public static final String CATEGORY_PATH = "v1/category"; + + public static final String SAFE_DEPOSIT_BOX_PREFIX = "v2/safe-deposit-box/"; + public static final String SECURE_FILE_PATH_PREFIX = "v1/secure-file/"; public static final MediaType DEFAULT_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8"); protected static final int DEFAULT_NUM_RETRIES = 3; - protected static final int DEFAULT_RETRY_INTERVAL_IN_MILLIS = 200; - private static final RetryConfig RETRY_CONFIG = RetryConfig.custom() .maxAttempts(DEFAULT_NUM_RETRIES) @@ -198,7 +208,7 @@ public CerberusListResponse list(final String path) { * will be thrown with the code and error details. If an unexpected I/O error is * encountered, a {@link CerberusClientException} will be thrown wrapping the underlying exception. *

- * See https://www.github.com/Nike-Inc/cerberus-management-service/blob/master/API.md for details on what the + * See https://github.com/Nike-Inc/cerberus/blob/master/API.md for details on what the * list files operation returns. *

* @@ -215,7 +225,7 @@ public CerberusListFilesResponse listFiles(final String path) { * will be thrown with the code and error details. If an unexpected I/O error is * encountered, a {@link CerberusClientException} will be thrown wrapping the underlying exception. *

- * See https://www.github.com/Nike-Inc/cerberus-management-service/blob/master/API.md for details on what the + * See https://github.com/Nike-Inc/cerberus/blob/master/API.md for details on what the * list files operation returns. *

* @@ -252,21 +262,7 @@ public CerberusListFilesResponse listFiles(final String path, Integer limit, Int * @return Map of the data */ public CerberusResponse read(final String path) { - final HttpUrl httpUrl = buildUrl(SECRET_PATH_PREFIX, path); - logger.debug("read: requestUrl={}", httpUrl); - - final Response response = ofSupplier( - () -> execute(httpUrl, HttpMethod.GET, null) - ) - .withRetry(RETRY) - .decorate() - .get(); - - if (response.code() != HttpStatus.OK) { - parseAndThrowApiErrorResponse(response); - } - - return parseResponseBody(response, CerberusResponse.class); + return buildAndExecuteRequest(SECRET_PATH_PREFIX, path, HttpMethod.GET, null, CerberusResponse.class); } /** @@ -279,20 +275,7 @@ public CerberusResponse read(final String path) { * @return File contents */ public byte[] readFileAsBytes(final String path) { - final HttpUrl httpUrl = buildUrl(SECURE_FILE_PATH_PREFIX, path); - logger.debug("read: requestUrl={}", httpUrl); - - final Response response = ofSupplier( - () -> execute(httpUrl, HttpMethod.GET, null) - ) - .withRetry(RETRY) - .decorate() - .get(); - - if (response.code() != HttpStatus.OK) { - parseAndThrowApiErrorResponse(response); - } - + Response response = buildAndExecuteRequest(SECURE_FILE_PATH_PREFIX, path, HttpMethod.GET, null); return responseBodyAsBytes(response); } @@ -305,19 +288,7 @@ public byte[] readFileAsBytes(final String path) { * @param data Data to be stored */ public void write(final String path, final Map data) { - final HttpUrl httpUrl = buildUrl(SECRET_PATH_PREFIX, path); - logger.debug("write: requestUrl={}", httpUrl); - - final Response response = ofSupplier( - () -> execute(httpUrl, HttpMethod.POST, data) - ) - .withRetry(RETRY) - .decorate() - .get(); - - if (response.code() != HttpStatus.NO_CONTENT) { - parseAndThrowApiErrorResponse(response); - } + buildAndExecuteRequest(SECRET_PATH_PREFIX, path, HttpMethod.POST, data); } /** @@ -362,19 +333,7 @@ public void writeFile(final String path, final byte[] contents) { * @param path Path to file to be deleted */ public void deleteFile(final String path) { - final HttpUrl httpUrl = buildUrl(SECURE_FILE_PATH_PREFIX, path); - logger.debug("delete: requestUrl={}", httpUrl); - - final Response response = ofSupplier( - () -> execute(httpUrl, HttpMethod.DELETE, null) - ) - .withRetry(RETRY) - .decorate() - .get(); - - if (response.code() != HttpStatus.NO_CONTENT) { - parseAndThrowApiErrorResponse(response); - } + buildAndExecuteRequest(SECURE_FILE_PATH_PREFIX, path, HttpMethod.DELETE, null); } /** @@ -385,19 +344,155 @@ public void deleteFile(final String path) { * @param path Path to data to be deleted */ public void delete(final String path) { - final HttpUrl httpUrl = buildUrl(SECRET_PATH_PREFIX, path); - logger.debug("delete: requestUrl={}", httpUrl); + buildAndExecuteRequest(SECRET_PATH_PREFIX, path, HttpMethod.DELETE, null); + } - final Response response = ofSupplier( - () -> execute(httpUrl, HttpMethod.DELETE, null) - ) - .withRetry(RETRY) - .decorate() - .get(); + /** + * Lists all roles that Cerberus supports. + * If Cerberus returns an unexpected response code, a {@link CerberusServerException} will be thrown with the code + * and error details. If an unexpected I/O error is encountered, a {@link CerberusClientException} will be thrown + * wrapping the underlying exception. + * + * @return List of all roles + */ + public List listRoles() { + return buildAndExecuteRequest(ROLE_PATH, "", HttpMethod.GET, null, new TypeToken>(){}.getType()); + } - if (response.code() != HttpStatus.NO_CONTENT) { - parseAndThrowApiErrorResponse(response); + /** + * Gets all roles that Cerberus supports in a more usable {@link CerberusRolePermission} to ID map. + * If Cerberus returns an unexpected response code, a {@link CerberusServerException} will be thrown with the code + * and error details. If an unexpected I/O error is encountered, a {@link CerberusClientException} will be thrown + * wrapping the underlying exception. + * + * @return Map of role permission to ID + */ + public Map getRolePermissionMap() { + return listRoles().stream().collect(Collectors.toMap(roleResponse -> CerberusRolePermission.fromString(roleResponse.getName()), CerberusRoleResponse::getId)); + } + + /** + * Lists all categories that Cerberus supports. + * If Cerberus returns an unexpected response code, a {@link CerberusServerException} will be thrown with the code + * and error details. If an unexpected I/O error is encountered, a {@link CerberusClientException} will be thrown + * wrapping the underlying exception. + * + * @return List of all categories + */ + public List listCategories() { + return buildAndExecuteRequest(CATEGORY_PATH, "", HttpMethod.GET, null, new TypeToken>(){}.getType()); + } + + /** + * Gets all categories that Cerberus supports in a more usable category path to ID map. + * If Cerberus returns an unexpected response code, a {@link CerberusServerException} will be thrown with the code + * and error details. If an unexpected I/O error is encountered, a {@link CerberusClientException} will be thrown + * wrapping the underlying exception. + * + * @return Map of category path to ID + */ + public Map getCategoryMap() { + return listCategories().stream().collect(Collectors.toMap(CerberusCategoryResponse::getPath, CerberusCategoryResponse::getId)); + } + + /** + * Gets the category ID that matches the path. + * If Cerberus returns an unexpected response code, a {@link CerberusServerException} will be thrown with the code + * and error details. If an unexpected I/O error is encountered, a {@link CerberusClientException} will be thrown + * wrapping the underlying exception. + * + * @param path The path of category (e.g. "app") + * @return Map of category path to ID + */ + public String getCategoryIdByPath(String path) { + return listCategories().stream().collect(Collectors.toMap(CerberusCategoryResponse::getPath, CerberusCategoryResponse::getId)).get(path); + } + + /** + * Lists all safe deposit boxes that the authenticated IAM principal has access to. If Cerberus returns an unexpected response code, a {@link CerberusServerException} + * will be thrown with the code and error details. If an unexpected I/O error is + * encountered, a {@link CerberusClientException} will be thrown wrapping the underlying exception. + *

+ * See https://github.com/Nike-Inc/cerberus/blob/master/API.md for details on what the + * list safe deposit box operation returns. + *

+ * + * @return List of safe deposit box summaries + */ + public List listSafeDepositBoxes() { + return buildAndExecuteRequest(SAFE_DEPOSIT_BOX_PREFIX, "", HttpMethod.GET, null, new TypeToken>(){}.getType()); + } + + + /** + * Gets the safe deposit box metadata by its name. + * If Cerberus returns an unexpected response code, a {@link CerberusServerException} will be thrown with the code + * and error details. If an unexpected I/O error is encountered, a {@link CerberusClientException} will be thrown + * wrapping the underlying exception. + * + * @param name The name of the safe deposit box + * @return The safe deposit box metadata + */ + public CerberusSafeDepositBoxResponse getSafeDepositBoxByName(String name) { + List responses = listSafeDepositBoxes().stream() + .filter(sdb -> sdb.getName().equals(name)) + .collect(Collectors.toList()); + if (responses.isEmpty()) { + throw new CerberusClientException("ERROR cannot find safe deposit box with the name " + name); } + return getSafeDepositBoxById(responses.get(0).getId()); + } + + /** + * Gets the safe deposit box metadata by its ID. + * If Cerberus returns an unexpected response code, a {@link CerberusServerException} will be thrown with the code + * and error details. If an unexpected I/O error is encountered, a {@link CerberusClientException} will be thrown + * wrapping the underlying exception. + * + * @param id The ID of the safe deposit box + * @return The safe deposit box metadata + */ + public CerberusSafeDepositBoxResponse getSafeDepositBoxById(String id) { + return buildAndExecuteRequest(SAFE_DEPOSIT_BOX_PREFIX, id, HttpMethod.GET, null, CerberusSafeDepositBoxResponse.class); + } + + /** + * Creates a safe deposit box. + * If Cerberus returns an unexpected response code, a {@link CerberusServerException} will be thrown with the code + * and error details. If an unexpected I/O error is encountered, a {@link CerberusClientException} will be thrown + * wrapping the underlying exception. + * + * @param cerberusSafeDepositBoxRequest The metadata of the safe deposit box + * @return The metadata of the created safe deposit box + */ + public CerberusSafeDepositBoxResponse createSafeDepositBox(CerberusSafeDepositBoxRequest cerberusSafeDepositBoxRequest) { + return buildAndExecuteRequest(SAFE_DEPOSIT_BOX_PREFIX, "", HttpMethod.POST, cerberusSafeDepositBoxRequest, CerberusSafeDepositBoxResponse.class); + } + + + /** + * Updates a safe deposit box. + * If Cerberus returns an unexpected response code, a {@link CerberusServerException} will be thrown with the code + * and error details. If an unexpected I/O error is encountered, a {@link CerberusClientException} will be thrown + * wrapping the underlying exception. + * + * @param cerberusSafeDepositBoxRequest The metadata of the safe deposit box + * @return The metadata of the updated safe deposit box + */ + public CerberusSafeDepositBoxResponse updateSafeDepositBox(String id, CerberusSafeDepositBoxRequest cerberusSafeDepositBoxRequest) { + return buildAndExecuteRequest(SAFE_DEPOSIT_BOX_PREFIX, id, HttpMethod.PUT, cerberusSafeDepositBoxRequest, CerberusSafeDepositBoxResponse.class); + } + + /** + * Deletes a safe deposit box. + * If Cerberus returns an unexpected response code, a + * {@link CerberusServerException} will be thrown with the code and error details. If an unexpected I/O + * error is encountered, a {@link CerberusClientException} will be thrown wrapping the underlying exception. + * + * @param id ID of the safe deposit box to be deleted + */ + public void deleteSafeDepositBox(String id) { + buildAndExecuteRequest(SAFE_DEPOSIT_BOX_PREFIX, id, HttpMethod.DELETE, null); } /** @@ -481,6 +576,34 @@ protected HttpUrl buildUrl(final String prefix, final String path) { return HttpUrl.parse(baseUrl + prefix + path); } + protected M buildAndExecuteRequest(final String prefix, final String path, final String httpMethod, Object requestBody, final Class responseClass) { + final Response response = buildAndExecuteRequest(prefix, path, httpMethod, requestBody); + return parseResponseBody(response, responseClass); + } + + protected M buildAndExecuteRequest(final String prefix, final String path, final String httpMethod, Object requestBody, final Type typeOf) { + final Response response = buildAndExecuteRequest(prefix, path, httpMethod, requestBody); + return parseResponseBody(response, typeOf); + } + + private Response buildAndExecuteRequest(final String prefix, final String path, final String httpMethod, Object requestBody) { + final HttpUrl httpUrl = buildUrl(prefix, path); + logger.debug("requestUrl={}, HTTP method={}", httpUrl, httpMethod); + + final Response response = ofSupplier( + () -> execute(httpUrl, httpMethod, requestBody) + ) + .withRetry(RETRY) + .decorate() + .get(); + + if (!response.isSuccessful()) { + parseAndThrowApiErrorResponse(response); + } + + return response; + } + /** * Executes the HTTP request based on the input parameters. * @@ -684,12 +807,4 @@ protected byte[] responseBodyAsBytes(Response response) { throw new CerberusClientException("ERROR failed to print: " + response.toString()); } } - - private void sleep(long milliseconds) { - try { - TimeUnit.MILLISECONDS.sleep(milliseconds); - } catch (InterruptedException ie) { - logger.warn("Sleep interval interrupted.", ie); - } - } } diff --git a/src/main/java/com/nike/cerberus/client/model/CerberusCategoryResponse.java b/src/main/java/com/nike/cerberus/client/model/CerberusCategoryResponse.java new file mode 100644 index 0000000..0c8555e --- /dev/null +++ b/src/main/java/com/nike/cerberus/client/model/CerberusCategoryResponse.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 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.client.model; + +/** Represents a category. */ +public class CerberusCategoryResponse { + + private String id; + private String displayName; + private String path; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } +} diff --git a/src/main/java/com/nike/cerberus/client/model/CerberusIamPrincipalPermission.java b/src/main/java/com/nike/cerberus/client/model/CerberusIamPrincipalPermission.java new file mode 100644 index 0000000..b37cf09 --- /dev/null +++ b/src/main/java/com/nike/cerberus/client/model/CerberusIamPrincipalPermission.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020 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.client.model; + +/** Represents an IAM principal permission object for a specific safe deposit box. */ +public class CerberusIamPrincipalPermission { + private String iamPrincipalArn; + private String roleId; + + public String getIamPrincipalArn() { + return iamPrincipalArn; + } + + public void setIamPrincipalArn(String iamPrincipalArn) { + this.iamPrincipalArn = iamPrincipalArn; + } + + public String getRoleId() { + return roleId; + } + + public void setRoleId(String roleId) { + this.roleId = roleId; + } +} diff --git a/src/main/java/com/nike/cerberus/client/model/CerberusRolePermission.java b/src/main/java/com/nike/cerberus/client/model/CerberusRolePermission.java new file mode 100644 index 0000000..10a953d --- /dev/null +++ b/src/main/java/com/nike/cerberus/client/model/CerberusRolePermission.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 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.client.model; + +public enum CerberusRolePermission { + OWNER, + WRITE, + READ; + + public static CerberusRolePermission fromString(String value) { + if (value == null) { + throw new IllegalArgumentException("Input cannot be null"); + } + return valueOf(value.toUpperCase()); + } + + @Override + public String toString() { + return name().toLowerCase(); + } +} diff --git a/src/main/java/com/nike/cerberus/client/model/CerberusRoleResponse.java b/src/main/java/com/nike/cerberus/client/model/CerberusRoleResponse.java new file mode 100644 index 0000000..7a0da44 --- /dev/null +++ b/src/main/java/com/nike/cerberus/client/model/CerberusRoleResponse.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020 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.client.model; + +/** Represents a role. */ +public class CerberusRoleResponse { + + private String id; + private String name; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/main/java/com/nike/cerberus/client/model/CerberusSafeDepositBoxRequest.java b/src/main/java/com/nike/cerberus/client/model/CerberusSafeDepositBoxRequest.java new file mode 100644 index 0000000..91b729a --- /dev/null +++ b/src/main/java/com/nike/cerberus/client/model/CerberusSafeDepositBoxRequest.java @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2020 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.client.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** Represents a request for creating and updating a safe deposit box. */ +public class CerberusSafeDepositBoxRequest { + private String name; + private String categoryId; + private String description; + private String owner; + private List userGroupPermissions; + private List iamPrincipalPermissions; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCategoryId() { + return categoryId; + } + + public void setCategoryId(String categoryId) { + this.categoryId = categoryId; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public List getUserGroupPermissions() { + return userGroupPermissions; + } + + public void setUserGroupPermissions(List userGroupPermissions) { + this.userGroupPermissions = userGroupPermissions; + } + + public List getIamPrincipalPermissions() { + return iamPrincipalPermissions; + } + + public void setIamPrincipalPermissions(List iamPrincipalPermissions) { + this.iamPrincipalPermissions = iamPrincipalPermissions; + } + + public static class Builder { + private String name; + private String categoryId; + private String description; + private String owner; + private List userGroupPermissions; + private List iamPrincipalPermissions; + private Map rolePermissionMap; + + public Builder withName(String name){ + this.name = name; + return this; + } + + public Builder withCategoryId(String categoryId){ + this.categoryId = categoryId; + return this; + } + + public Builder withOwner(String owner){ + this.owner = owner; + return this; + } + + public Builder withDescription(String description){ + this.description = description; + return this; + } + + public Builder withRolePermissionMap(Map rolePermissionMap){ + this.rolePermissionMap = rolePermissionMap; + return this; + } + + public Builder withUserGroupPermission(String name, String roleId){ + if (userGroupPermissions == null) { + userGroupPermissions = new ArrayList<>(); + } + Optional any = userGroupPermissions.stream().filter(permission -> name.equals(permission.getName())).findAny(); + if (any.isPresent()){ + any.get().setRoleId(roleId); + } else { + CerberusUserGroupPermission userGroupPermission = new CerberusUserGroupPermission(); + userGroupPermission.setName(name); + userGroupPermission.setRoleId(roleId); + userGroupPermissions.add(userGroupPermission); + } + return this; + } + + public Builder removeUserGroupPermission(String userGroupNameToBeRemoved){ + if (userGroupPermissions != null){ + userGroupPermissions.removeIf(userGroupPermission -> userGroupPermission.getName().equals(userGroupNameToBeRemoved)); + } + return this; + } + + public Builder withIamPrincipalPermission(String iamPrincipalArn, String roleId){ + if (iamPrincipalPermissions == null) { + iamPrincipalPermissions = new ArrayList<>(); + } + Optional any = iamPrincipalPermissions.stream().filter(permission -> iamPrincipalArn.equals(permission.getIamPrincipalArn())).findAny(); + if (any.isPresent()){ + any.get().setRoleId(roleId); + } else { + CerberusIamPrincipalPermission iamPrincipalPermission = new CerberusIamPrincipalPermission(); + iamPrincipalPermission.setIamPrincipalArn(iamPrincipalArn); + iamPrincipalPermission.setRoleId(roleId); + iamPrincipalPermissions.add(iamPrincipalPermission); + } + return this; + } + + public Builder withUserGroupPermission(String name, CerberusRolePermission rolePermission){ + if (rolePermissionMap == null) { + throw new IllegalStateException("withRolePermissionMap() needs to be called before calling this method"); + } + return withUserGroupPermission(name, rolePermissionMap.get(rolePermission)); + } + + public Builder withIamPrincipalPermission(String iamPrincipalArn, CerberusRolePermission rolePermission){ + if (rolePermissionMap == null) { + throw new IllegalStateException("withRolePermissionMap() needs to be called before calling this method"); + } + return withIamPrincipalPermission(iamPrincipalArn, rolePermissionMap.get(rolePermission)); + } + + public Builder removeIamPrincipalPermission(String iamPrincipalArnToBeRemoved){ + if (iamPrincipalPermissions != null){ + iamPrincipalPermissions.removeIf(iamPrincipalPermission -> iamPrincipalPermission.getIamPrincipalArn().equals(iamPrincipalArnToBeRemoved)); + } + return this; + } + + public Builder withCerberusSafeDepositBoxResponse(CerberusSafeDepositBoxResponse sdbResponse) { + this.name = sdbResponse.getName(); + this.description = sdbResponse.getDescription(); + this.owner = sdbResponse.getOwner(); + this.categoryId = sdbResponse.getCategoryId(); + this.userGroupPermissions = sdbResponse.getUserGroupPermissions(); + this.iamPrincipalPermissions = sdbResponse.getIamPrincipalPermissions(); + return this; + } + + public CerberusSafeDepositBoxRequest build() { + if (name == null) { + throw new IllegalArgumentException("Safe deposit box name must be set"); + } + + if (owner == null) { + throw new IllegalArgumentException("Safe deposit box owner must be set"); + } + + return new CerberusSafeDepositBoxRequest(this); + } + + + } + + public static Builder newBuilder() { + return new Builder(); + } + + private CerberusSafeDepositBoxRequest(Builder builder) { + this.name = builder.name; + this.categoryId = builder.categoryId; + this.description = builder.description; + this.owner = builder.owner; + this.userGroupPermissions = builder.userGroupPermissions; + this.iamPrincipalPermissions = builder.iamPrincipalPermissions; + } +} diff --git a/src/main/java/com/nike/cerberus/client/model/CerberusSafeDepositBoxResponse.java b/src/main/java/com/nike/cerberus/client/model/CerberusSafeDepositBoxResponse.java new file mode 100644 index 0000000..2636b54 --- /dev/null +++ b/src/main/java/com/nike/cerberus/client/model/CerberusSafeDepositBoxResponse.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2020 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.client.model; + +import java.util.List; + +/** Represents a specific safe deposit box. */ +public class CerberusSafeDepositBoxResponse { + + private String id; + private String name; + private String path; + private String categoryId; + private String owner; + private String description; + private List userGroupPermissions; + private List iamPrincipalPermissions; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + 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 getCategoryId() { + return categoryId; + } + + public void setCategoryId(String categoryId) { + this.categoryId = categoryId; + } + + 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 List getUserGroupPermissions() { + return userGroupPermissions; + } + + public void setUserGroupPermissions(List userGroupPermissions) { + this.userGroupPermissions = userGroupPermissions; + } + + public List getIamPrincipalPermissions() { + return iamPrincipalPermissions; + } + + public void setIamPrincipalPermissions(List iamPrincipalPermissions) { + this.iamPrincipalPermissions = iamPrincipalPermissions; + } +} diff --git a/src/main/java/com/nike/cerberus/client/model/CerberusSafeDepositBoxSummaryResponse.java b/src/main/java/com/nike/cerberus/client/model/CerberusSafeDepositBoxSummaryResponse.java new file mode 100644 index 0000000..e7fd3dc --- /dev/null +++ b/src/main/java/com/nike/cerberus/client/model/CerberusSafeDepositBoxSummaryResponse.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020 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.client.model; + +/** Represents a summary for a specific safe deposit box. */ +public class CerberusSafeDepositBoxSummaryResponse { + + private String id; + + private String name; + + private String path; + + private String categoryId; + + public String getId() { + return id; + } + + public CerberusSafeDepositBoxSummaryResponse setId(String id) { + this.id = id; + return this; + } + + public String getName() { + return name; + } + + public CerberusSafeDepositBoxSummaryResponse setName(String name) { + this.name = name; + return this; + } + + public String getPath() { + return path; + } + + public CerberusSafeDepositBoxSummaryResponse setPath(String path) { + this.path = path; + return this; + } + + public String getCategoryId() { + return categoryId; + } + + public CerberusSafeDepositBoxSummaryResponse setCategoryId(String categoryId) { + this.categoryId = categoryId; + return this; + } +} diff --git a/src/main/java/com/nike/cerberus/client/model/CerberusUserGroupPermission.java b/src/main/java/com/nike/cerberus/client/model/CerberusUserGroupPermission.java new file mode 100644 index 0000000..34703f8 --- /dev/null +++ b/src/main/java/com/nike/cerberus/client/model/CerberusUserGroupPermission.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020 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.client.model; + +/** Represents a user group permission object for a specific safe deposit box. */ +public class CerberusUserGroupPermission { + private String name; + private String roleId; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getRoleId() { + return roleId; + } + + public void setRoleId(String roleId) { + this.roleId = roleId; + } + + + +} diff --git a/src/test/java/com/nike/cerberus/client/CerberusClientTest.java b/src/test/java/com/nike/cerberus/client/CerberusClientTest.java index c8dd959..ae0299e 100644 --- a/src/test/java/com/nike/cerberus/client/CerberusClientTest.java +++ b/src/test/java/com/nike/cerberus/client/CerberusClientTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Nike, Inc. + * Copyright (c) 2020 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,8 +19,13 @@ import com.nike.cerberus.client.auth.CerberusCredentials; import com.nike.cerberus.client.auth.CerberusCredentialsProvider; import com.nike.cerberus.client.auth.DefaultCerberusCredentialsProviderChain; +import com.nike.cerberus.client.model.CerberusCategoryResponse; import com.nike.cerberus.client.model.CerberusListResponse; import com.nike.cerberus.client.model.CerberusResponse; +import com.nike.cerberus.client.model.CerberusRolePermission; +import com.nike.cerberus.client.model.CerberusRoleResponse; +import com.nike.cerberus.client.model.CerberusSafeDepositBoxResponse; +import com.nike.cerberus.client.model.CerberusSafeDepositBoxSummaryResponse; import okhttp3.Call; import okhttp3.Headers; import okhttp3.HttpUrl; @@ -38,11 +43,13 @@ import java.net.ServerSocket; import java.nio.charset.Charset; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import static com.nike.cerberus.client.CerberusClient.DEFAULT_NUM_RETRIES; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; @@ -129,6 +136,142 @@ public void list_returns_an_empty_response_if_cerberus_returns_a_404() { assertThat(cerberusListResponse.getKeys()).isEmpty(); } + @Test + public void get_roles() { + final MockResponse response = new MockResponse(); + response.setResponseCode(200); + response.setBody(getResponseJson("role")); + mockWebServer.enqueue(response); + + List roles = cerberusClient.listRoles(); + + assertThat(roles).isNotNull(); + assertEquals(3, roles.size()); + assertEquals("ee5c7dea-9c82-4974-b712-086afe589671", roles.get(0).getId()); + assertEquals("owner", roles.get(0).getName()); + assertEquals("753142b9-a07c-47d4-ad3d-f5af0c2e398d", roles.get(1).getId()); + assertEquals("write", roles.get(1).getName()); + assertEquals("451b4c06-3fb5-46b4-ad1b-258349e239ce", roles.get(2).getId()); + assertEquals("read", roles.get(2).getName()); + } + + @Test + public void get_role_permission_map() { + final MockResponse response = new MockResponse(); + response.setResponseCode(200); + response.setBody(getResponseJson("role")); + mockWebServer.enqueue(response); + + Map rolePermissionMap = cerberusClient.getRolePermissionMap(); + + assertThat(rolePermissionMap).isNotNull(); + assertEquals(3, rolePermissionMap.size()); + assertEquals("ee5c7dea-9c82-4974-b712-086afe589671", rolePermissionMap.get(CerberusRolePermission.OWNER)); + assertEquals("753142b9-a07c-47d4-ad3d-f5af0c2e398d", rolePermissionMap.get(CerberusRolePermission.WRITE)); + assertEquals("451b4c06-3fb5-46b4-ad1b-258349e239ce", rolePermissionMap.get(CerberusRolePermission.READ)); + } + + @Test + public void get_categories() { + final MockResponse response = new MockResponse(); + response.setResponseCode(200); + response.setBody(getResponseJson("category")); + mockWebServer.enqueue(response); + + List categories = cerberusClient.listCategories(); + + assertThat(categories).isNotNull(); + assertEquals(2, categories.size()); + assertEquals("053de0f6-7588-44e3-bcf7-1a648d0bc8f2", categories.get(0).getId()); + assertEquals("Applications", categories.get(0).getDisplayName()); + assertEquals("app", categories.get(0).getPath()); + assertEquals("ce2519e2-249a-4adc-a1ce-43ae9a4f9198", categories.get(1).getId()); + assertEquals("Shared", categories.get(1).getDisplayName()); + assertEquals("shared", categories.get(1).getPath()); + } + + @Test + public void get_category_map() { + final MockResponse response = new MockResponse(); + response.setResponseCode(200); + response.setBody(getResponseJson("category")); + mockWebServer.enqueue(response); + + Map categoryMap = cerberusClient.getCategoryMap(); + + assertThat(categoryMap).isNotNull(); + assertEquals(2, categoryMap.size()); + assertEquals("053de0f6-7588-44e3-bcf7-1a648d0bc8f2", categoryMap.get("app")); + assertEquals("ce2519e2-249a-4adc-a1ce-43ae9a4f9198", categoryMap.get("shared")); + } + + @Test + public void list_safe_deposit_boxes() { + final MockResponse response = new MockResponse(); + response.setResponseCode(200); + response.setBody(getResponseJson("list-safe-deposit-boxes")); + mockWebServer.enqueue(response); + + List sdbs = cerberusClient.listSafeDepositBoxes(); + + assertThat(sdbs).isNotNull(); + assertEquals(2, sdbs.size()); + assertEquals("cca549f9-768e-4e0a-b57c-b15098ebcdd8", sdbs.get(0).getId()); + assertEquals("c7e98da8-6d81-4a7d-a318-f26c14d990e1", sdbs.get(0).getCategoryId()); + assertEquals("test sdb 1", sdbs.get(0).getName()); + assertEquals("app/test-sdb-1/", sdbs.get(0).getPath()); + assertEquals("9e3d853c-1144-4b2a-82a1-2987b5e6dea0", sdbs.get(1).getId()); + assertEquals("adb57965-b0ee-449f-8c23-e29b0b72892b", sdbs.get(1).getCategoryId()); + assertEquals("test sdb 2", sdbs.get(1).getName()); + assertEquals("shared/test-sdb-2/", sdbs.get(1).getPath()); + } + + @Test + public void get_safe_deposit_box_by_id() { + final MockResponse response = new MockResponse(); + response.setResponseCode(200); + response.setBody(getResponseJson("safe-deposit-box")); + mockWebServer.enqueue(response); + + CerberusSafeDepositBoxResponse sdb = cerberusClient.getSafeDepositBoxById("cca549f9-768e-4e0a-b57c-b15098ebcdd8"); + + assertThat(sdb).isNotNull(); + assertEquals("cca549f9-768e-4e0a-b57c-b15098ebcdd8", sdb.getId()); + assertEquals("c7e98da8-6d81-4a7d-a318-f26c14d990e1", sdb.getCategoryId()); + assertEquals("test sdb 1", sdb.getName()); + assertEquals("test description", sdb.getDescription()); + assertEquals("app/test-sdb-1/", sdb.getPath()); + assertEquals("owner group", sdb.getOwner()); + assertThat(sdb.getIamPrincipalPermissions()).isNotNull(); + assertEquals("arn:aws:iam::1234567890:role/test-role", sdb.getIamPrincipalPermissions() + .get(0).getIamPrincipalArn()); + assertEquals("eb639d7e-89ad-4084-87ce-4d9b6ee81341", sdb.getIamPrincipalPermissions() + .get(0).getRoleId()); + assertThat(sdb.getUserGroupPermissions()).isNotNull(); + assertEquals("read group", sdb.getUserGroupPermissions() + .get(0).getName()); + assertEquals("152db5e5-68bf-4d5f-a08b-6ca4faaa393e", sdb.getUserGroupPermissions() + .get(0).getRoleId()); + } + + @Test + public void get_safe_deposit_box_by_name() { + final MockResponse listResponse = new MockResponse(); + listResponse.setResponseCode(200); + listResponse.setBody(getResponseJson("list-safe-deposit-boxes")); + mockWebServer.enqueue(listResponse); + + final MockResponse getResponse = new MockResponse(); + getResponse.setResponseCode(200); + getResponse.setBody(getResponseJson("safe-deposit-box")); + mockWebServer.enqueue(getResponse); + + CerberusSafeDepositBoxResponse sdb = cerberusClient.getSafeDepositBoxByName("test sdb 1"); + + assertThat(sdb).isNotNull(); + } + + @Test public void read_returns_map_of_data_for_specified_path_if_exists() { final MockResponse response = new MockResponse(); diff --git a/src/test/java/com/nike/cerberus/client/model/CerberusSafeDepositBoxRequestTest.java b/src/test/java/com/nike/cerberus/client/model/CerberusSafeDepositBoxRequestTest.java new file mode 100644 index 0000000..30714e3 --- /dev/null +++ b/src/test/java/com/nike/cerberus/client/model/CerberusSafeDepositBoxRequestTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020 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.client.model; + +import org.junit.Test; + +import java.util.Collections; + +import static org.junit.Assert.assertEquals; + +public class CerberusSafeDepositBoxRequestTest { + @Test + public void test_withUserGroupPermission_overrides() { + CerberusSafeDepositBoxRequest request = CerberusSafeDepositBoxRequest.newBuilder() + .withName("foo") + .withOwner("bar") + .withUserGroupPermission("user group 1", "role id 1") + .withUserGroupPermission("user group 1", "role id 2") + .build(); + + assertEquals("user group 1", request.getUserGroupPermissions().get(0).getName()); + assertEquals("role id 2", request.getUserGroupPermissions().get(0).getRoleId()); + } + + @Test + public void test_withIamPrincipalPermission_overrides() { + CerberusSafeDepositBoxRequest request = CerberusSafeDepositBoxRequest.newBuilder() + .withName("foo") + .withOwner("bar") + .withIamPrincipalPermission("iam principal 1", "role id 1") + .withIamPrincipalPermission("iam principal 1", "role id 2") + .build(); + + assertEquals("iam principal 1", request.getIamPrincipalPermissions().get(0).getIamPrincipalArn()); + assertEquals("role id 2", request.getIamPrincipalPermissions().get(0).getRoleId()); + } + + @Test + public void test_withRolePermissionMap() { + CerberusSafeDepositBoxRequest request = CerberusSafeDepositBoxRequest.newBuilder() + .withName("foo") + .withOwner("bar") + .withRolePermissionMap(Collections.singletonMap(CerberusRolePermission.OWNER, "role id 1")) + .withUserGroupPermission("user group 1", CerberusRolePermission.OWNER) + .build(); + + assertEquals("user group 1", request.getUserGroupPermissions().get(0).getName()); + assertEquals("role id 1", request.getUserGroupPermissions().get(0).getRoleId()); + } +} \ No newline at end of file diff --git a/src/test/resources/com/nike/cerberus/client/category.json b/src/test/resources/com/nike/cerberus/client/category.json new file mode 100644 index 0000000..ce4a593 --- /dev/null +++ b/src/test/resources/com/nike/cerberus/client/category.json @@ -0,0 +1,20 @@ +[ + { + "id": "053de0f6-7588-44e3-bcf7-1a648d0bc8f2", + "display_name": "Applications", + "path": "app", + "created_ts": "2017-12-20T22:00:54Z", + "last_updated_ts": "2017-12-20T22:00:54Z", + "created_by": "system", + "last_updated_by": "system" + }, + { + "id": "ce2519e2-249a-4adc-a1ce-43ae9a4f9198", + "display_name": "Shared", + "path": "shared", + "created_ts": "2017-12-20T22:00:54Z", + "last_updated_ts": "2017-12-20T22:00:54Z", + "created_by": "system", + "last_updated_by": "system" + } +] \ No newline at end of file diff --git a/src/test/resources/com/nike/cerberus/client/list-safe-deposit-boxes.json b/src/test/resources/com/nike/cerberus/client/list-safe-deposit-boxes.json new file mode 100644 index 0000000..cad3234 --- /dev/null +++ b/src/test/resources/com/nike/cerberus/client/list-safe-deposit-boxes.json @@ -0,0 +1,14 @@ +[ + { + "id": "cca549f9-768e-4e0a-b57c-b15098ebcdd8", + "name": "test sdb 1", + "path": "app/test-sdb-1/", + "category_id": "c7e98da8-6d81-4a7d-a318-f26c14d990e1" + }, + { + "id": "9e3d853c-1144-4b2a-82a1-2987b5e6dea0", + "name": "test sdb 2", + "path": "shared/test-sdb-2/", + "category_id": "adb57965-b0ee-449f-8c23-e29b0b72892b" + } +] \ No newline at end of file diff --git a/src/test/resources/com/nike/cerberus/client/role.json b/src/test/resources/com/nike/cerberus/client/role.json new file mode 100644 index 0000000..cb418d4 --- /dev/null +++ b/src/test/resources/com/nike/cerberus/client/role.json @@ -0,0 +1,26 @@ +[ + { + "id": "ee5c7dea-9c82-4974-b712-086afe589671", + "name": "owner", + "created_ts": "2017-12-20T22:00:54Z", + "last_updated_ts": "2017-12-20T22:00:54Z", + "created_by": "system", + "last_updated_by": "system" + }, + { + "id": "753142b9-a07c-47d4-ad3d-f5af0c2e398d", + "name": "write", + "created_ts": "2017-12-20T22:00:54Z", + "last_updated_ts": "2017-12-20T22:00:54Z", + "created_by": "system", + "last_updated_by": "system" + }, + { + "id": "451b4c06-3fb5-46b4-ad1b-258349e239ce", + "name": "read", + "created_ts": "2017-12-20T22:00:54Z", + "last_updated_ts": "2017-12-20T22:00:54Z", + "created_by": "system", + "last_updated_by": "system" + } +] \ No newline at end of file diff --git a/src/test/resources/com/nike/cerberus/client/safe-deposit-box.json b/src/test/resources/com/nike/cerberus/client/safe-deposit-box.json new file mode 100644 index 0000000..5d7c71d --- /dev/null +++ b/src/test/resources/com/nike/cerberus/client/safe-deposit-box.json @@ -0,0 +1,34 @@ +{ + "id": "cca549f9-768e-4e0a-b57c-b15098ebcdd8", + "category_id": "c7e98da8-6d81-4a7d-a318-f26c14d990e1", + "name": "test sdb 1", + "description": "test description", + "path": "app/test-sdb-1/", + "created_ts": "2017-06-03T00:06:12Z", + "last_updated_ts": "2017-06-03T00:06:12Z", + "created_by": "foo@bar.com", + "last_updated_by": "foo@bar.com", + "owner": "owner group", + "user_group_permissions": [ + { + "id": "867fc41b-f8ff-4e95-9fe7-6d0a8b76ccb2", + "name": "read group", + "role_id": "152db5e5-68bf-4d5f-a08b-6ca4faaa393e", + "created_ts": "2017-12-20T22:03:55Z", + "last_updated_ts": "2019-09-20T02:23:27.934Z", + "created_by": "foo@bar.com", + "last_updated_by": "foo@bar.com" + } + ], + "iam_principal_permissions": [ + { + "id": "e6cc049d-7ddf-4056-b291-6a27c3bd0e4b", + "role_id": "eb639d7e-89ad-4084-87ce-4d9b6ee81341", + "iam_principal_arn": "arn:aws:iam::1234567890:role/test-role", + "created_ts": "2018-01-22T19:01:13Z", + "last_updated_ts": "2019-09-20T02:23:27.934Z", + "created_by": "foo@bar.com", + "last_updated_by": "foo@bar.com" + } + ] +} \ No newline at end of file