From def476b9e64e9e96565fa91fb1917a8b97d6f884 Mon Sep 17 00:00:00 2001 From: Shaun Ford Date: Tue, 18 Apr 2017 09:27:59 -0700 Subject: [PATCH] Add V2 Safe Deposit Box API with ARNs (#33) * Updates v2 auth endpoint to take 'iam_principal_arn' instead of 'role_arn' * Adds v2 API for SDB * Returns SDB in v2/safe-deposit-box PUT and POST * Drops account id and iam role name columns from DB * Removes AWS account id and role name from IAM role mapper * Allows any IAM principal ARN for v2 IAM auth and SDB APIs --- API.md | 231 +++++++++++++++++- gradle.properties | 2 +- .../cerberus/domain/IamRoleCredentialsV2.java | 14 +- ...rmission.java => IamRolePermissionV1.java} | 29 +-- .../cerberus/domain/IamRolePermissionV2.java | 132 ++++++++++ .../nike/cerberus/domain/SafeDepositBox.java | 189 +------------- .../cerberus/domain/SafeDepositBoxV1.java | 209 ++++++++++++++++ .../cerberus/domain/SafeDepositBoxV2.java | 210 ++++++++++++++++ .../authentication/AuthenticateIamRoleV2.java | 4 +- ...itBox.java => CreateSafeDepositBoxV1.java} | 13 +- .../endpoints/sdb/CreateSafeDepositBoxV2.java | 96 ++++++++ .../endpoints/sdb/DeleteSafeDepositBox.java | 6 +- ...positBox.java => GetSafeDepositBoxV1.java} | 19 +- .../endpoints/sdb/GetSafeDepositBoxV2.java | 98 ++++++++ .../endpoints/sdb/GetSafeDepositBoxes.java | 6 +- ...itBox.java => UpdateSafeDepositBoxV1.java} | 13 +- .../endpoints/sdb/UpdateSafeDepositBoxV2.java | 91 +++++++ .../nike/cerberus/error/DefaultApiError.java | 7 +- .../cerberus/security/VaultAuthPrincipal.java | 2 + .../server/config/guice/CmsGuiceModule.java | 22 +- .../service/AuthenticationService.java | 81 +++--- .../service/IamRolePermissionService.java | 42 ++-- .../cerberus/service/KmsPolicyService.java | 7 - .../cerberus/service/MetadataService.java | 35 +-- .../service/SafeDepositBoxService.java | 202 +++++++++++---- .../cerberus/util/AwsIamRoleArnParser.java | 2 + ...ava => IamRolePermissionsValidatorV1.java} | 15 +- .../IamRolePermissionsValidatorV2.java | 68 ++++++ ...s.java => UniqueIamRolePermissionsV1.java} | 4 +- .../UniqueIamRolePermissionsV2.java | 45 ++++ .../nike/cerberus/mapper/AwsIamRoleMapper.xml | 4 - ...ve_account_id_and_iam_role_name_fields.sql | 3 + .../cerberus/IamRolePermissionV2Test.java | 69 ++++++ .../nike/cerberus/dao/AwsIamRoleDaoTest.java | 5 +- ...Test.java => SafeDepositBoxV1DaoTest.java} | 13 +- .../nike/cerberus/domain/DomainPojoTest.java | 2 +- .../service/AuthenticationServiceTest.java | 108 ++++++++ .../service/KmsPolicyServiceTest.java | 10 +- .../cerberus/service/MetadataServiceTest.java | 31 +-- .../service/SafeDepositBoxServiceTest.java | 158 ++++++++++-- ...=> IamRolePermissionsValidatorV1Test.java} | 18 +- .../IamRolePermissionsValidatorV2Test.java | 77 ++++++ .../validation/UniqueOwnerValidatorTest.java | 49 ++-- 43 files changed, 1961 insertions(+), 480 deletions(-) rename src/main/java/com/nike/cerberus/domain/{IamRolePermission.java => IamRolePermissionV1.java} (80%) create mode 100644 src/main/java/com/nike/cerberus/domain/IamRolePermissionV2.java create mode 100644 src/main/java/com/nike/cerberus/domain/SafeDepositBoxV1.java create mode 100644 src/main/java/com/nike/cerberus/domain/SafeDepositBoxV2.java rename src/main/java/com/nike/cerberus/endpoints/sdb/{CreateSafeDepositBox.java => CreateSafeDepositBoxV1.java} (89%) create mode 100644 src/main/java/com/nike/cerberus/endpoints/sdb/CreateSafeDepositBoxV2.java rename src/main/java/com/nike/cerberus/endpoints/sdb/{GetSafeDepositBox.java => GetSafeDepositBoxV1.java} (85%) create mode 100644 src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxV2.java rename src/main/java/com/nike/cerberus/endpoints/sdb/{UpdateSafeDepositBox.java => UpdateSafeDepositBoxV1.java} (89%) create mode 100644 src/main/java/com/nike/cerberus/endpoints/sdb/UpdateSafeDepositBoxV2.java rename src/main/java/com/nike/cerberus/validation/{IamRolePermissionsValidator.java => IamRolePermissionsValidatorV1.java} (77%) create mode 100644 src/main/java/com/nike/cerberus/validation/IamRolePermissionsValidatorV2.java rename src/main/java/com/nike/cerberus/validation/{UniqueIamRolePermissions.java => UniqueIamRolePermissionsV1.java} (92%) create mode 100644 src/main/java/com/nike/cerberus/validation/UniqueIamRolePermissionsV2.java create mode 100644 src/main/resources/com/nike/cerberus/migration/V1.2.0.0__remove_account_id_and_iam_role_name_fields.sql create mode 100644 src/test/java/com/nike/cerberus/IamRolePermissionV2Test.java rename src/test/java/com/nike/cerberus/dao/{SafeDepositBoxDaoTest.java => SafeDepositBoxV1DaoTest.java} (94%) create mode 100644 src/test/java/com/nike/cerberus/service/AuthenticationServiceTest.java rename src/test/java/com/nike/cerberus/validation/{IamRolePermissionsValidatorTest.java => IamRolePermissionsValidatorV1Test.java} (80%) create mode 100644 src/test/java/com/nike/cerberus/validation/IamRolePermissionsValidatorV2Test.java diff --git a/API.md b/API.md index 2ab25bc92..746c97435 100644 --- a/API.md +++ b/API.md @@ -152,7 +152,7 @@ This endpoint takes IAM ARN information and generates an base 64 encoded KMS enc + Body { - "role_arn" : "arn:aws:iam::111111111:role/cerberus-api-tester", + "iam_principal_arn" : "arn:aws:iam::111111111:role/cerberus-api-tester", "region": "us-west-2" } @@ -165,7 +165,8 @@ This endpoint takes IAM ARN information and generates an base 64 encoded KMS enc "policies" : [ "foo-bar-read", "lookup-self" ], "metadata" : { "aws_region" : "us-west-2", - "username" : "arn:aws:iam::933764306573:role/cerberus-api-tester" + "iam_principal_arn" : "arn:aws:iam::111111111:role/fake-role" + "username" : "arn:aws:iam::111111111:role/fake-role" "is_admin": "false", "groups": "registered-iam-principals" }, @@ -199,7 +200,9 @@ This endpoint takes IAM ARN information and generates an base 64 encoded KMS enc "policies" : [ "health-check-bucket-read", "lookup-self" ], "metadata" : { "aws_region" : "us-west-2", - "username" : "arn:aws:iam::111111111:role/cerberus-api-tester", + "aws_account_id" : "111111111", + "aws_iam_role_name" : "fake-role", + "username" : "arn:aws:iam::111111111:role/fake-role", "is_admin": "false", "groups": "registered-iam-principals" }, @@ -224,7 +227,7 @@ This endpoint will take the users `X-Vault-Token` header and proxy to Vault to r # Group Safe Deposit Box -## Get authorized Safe Deposit Box list [/v1/safe-deposit-box] +## Safe Deposit Box V2 [/v2/safe-deposit-box] ### Get details for each authorized Safe Deposit Box [GET] @@ -259,6 +262,222 @@ This endpoint will list all the Safe Deposit Box a user is authorized to see. This endpoint will create a new Safe Deposit Box ++ Request (application/json) + + + Headers + + X-Vault-Token: 7f6808f1-ede3-2177-aa9d-45f507391310 + + + Body + + { + "name": "Stage", + "description": "Sensitive configuration properties for the stage micro-service.", + "category_id": "f7ff85a0-faaa-11e5-a8a9-7fa3b294cd46", + "owner": "Lst-digital.platform-tools.internal", + "user_group_permissions": [ + { + "name": "Lst-CDT.CloudPlatformEngine.FTE", + "role_id": "f800558e-faaa-11e5-a8a9-7fa3b294cd46" + } + ], + "iam_role_permissions": [ + { + "iam_principal_arn": ""arn:aws:iam::1111111111:role/role-name" + "role_id": "f800558e-faaa-11e5-a8a9-7fa3b294cd46" + } + ] + } + ++ Response 201 (application/json) + + + Headers + + X-Refresh-Token: true + Location: /v1/safe-deposit-box/a7d703da-faac-11e5-a8a9-7fa3b294cd46 + + + Body + + { + "id": "a7d703da-faac-11e5-a8a9-7fa3b294cd46", + "name": "Stage", + "description": "Sensitive configuration properties for the stage micro-service.", + "path": "app/stage", + "category_id": "f7ff85a0-faaa-11e5-a8a9-7fa3b294cd46", + "owner": "Lst-digital.platform-tools.internal", + "user_group_permissions": [ + { + "id": "3fc6455c-faad-11e5-a8a9-7fa3b294cd46", + "name": "Lst-CDT.CloudPlatformEngine.FTE", + "role_id": "f800558e-faaa-11e5-a8a9-7fa3b294cd46" + } + ], + "iam_role_permissions": [ + { + "id": "d05bf72e-faad-11e5-a8a9-7fa3b294cd46", + "iam_principal_arn": "arn:aws:iam::1111111111:role/role-name", + "role_id": "f800558e-faaa-11e5-a8a9-7fa3b294cd46" + } + ] + } + +### Get details for a specific authorized Safe Deposit Box [GET /v2/safe-deposit-box/{id}] + +This endpoint returns details on a specific Safe Deposit Box. + ++ Parameters + + + id (required, string, `a7d703da-faac-11e5-a8a9-7fa3b294cd46`) - The id of the Safe Deposit Box + ++ Request (application/json) + + + Headers + + X-Vault-Token: 7f6808f1-ede3-2177-aa9d-45f507391310 + ++ Response 200 (application/json) + + + body + + { + "id": "a7d703da-faac-11e5-a8a9-7fa3b294cd46", + "name": "Stage", + "description": "Sensitive configuration properties for the stage micro-service.", + "path": "app/stage", + "category_id": "f7ff85a0-faaa-11e5-a8a9-7fa3b294cd46", + "owner": "Lst-digital.platform-tools.internal", + "user_group_permissions": [ + { + "id": "3fc6455c-faad-11e5-a8a9-7fa3b294cd46", + "name": "Lst-CDT.CloudPlatformEngine.FTE", + "role_id": "f800558e-faaa-11e5-a8a9-7fa3b294cd46" + } + ], + "iam_role_permissions": [ + { + "id": "d05bf72e-faad-11e5-a8a9-7fa3b294cd46", + "iam_principal_arn": "arn:aws:iam::1111111111:role/role-name", + "role_id": "f800558e-faaa-11e5-a8a9-7fa3b294cd46" + } + ] + } + + +### Update a specific authorized Safe Deposit Box [PUT] + +This endpoint allows a user to update the description, user group, and iam role mappings + ++ Request (application/json) + + + Headers + + X-Vault-Token: 7f6808f1-ede3-2177-aa9d-45f507391310 + + + Body + + { + "description": "All configuration properties for the stage micro-service.", + "owner": "Lst-Squad.Carebears", + "user_group_permissions": [ + { + "name": "Lst-CDT.CloudPlatformEngine.FTE", + "role_id": "f800558e-faaa-11e5-a8a9-7fa3b294cd46" + } + ], + "iam_role_permissions": [ + { + "iam_principal_arn": ""arn:aws:iam::1111111111:role/role-name2" + "role_id": "f800558e-faaa-11e5-a8a9-7fa3b294cd46" + } + ] + } + ++ Response 200 + + + Headers + + X-Refresh-Token: true + + + body + + { + "id": "a7d703da-faac-11e5-a8a9-7fa3b294cd46", + "name": "Stage", + "description": "Sensitive configuration properties for the stage micro-service.", + "path": "app/stage", + "category_id": "f7ff85a0-faaa-11e5-a8a9-7fa3b294cd46", + "owner": "Lst-digital.platform-tools.internal", + "user_group_permissions": [ + { + "id": "3fc6455c-faad-11e5-a8a9-7fa3b294cd46", + "name": "Lst-CDT.CloudPlatformEngine.FTE", + "role_id": "f800558e-faaa-11e5-a8a9-7fa3b294cd46" + } + ], + "iam_role_permissions": [ + { + "id": "d05bf72e-faad-11e5-a8a9-7fa3b294cd46", + "iam_principal_arn": "arn:aws:iam::1111111111:role/role-name", + "role_id": "f800558e-faaa-11e5-a8a9-7fa3b294cd46" + } + ] + } + +### Delete a specific authorized Safe Deposit Box [DELETE] + +This endpoint allows a user to delete a safe deposit box that they own + ++ Parameters + + + id (required, string, `a7d703da-faac-11e5-a8a9-7fa3b294cd46`) - The id of the Safe Deposit Box + ++ Request (application/json) + + + Headers + + X-Vault-Token: 7f6808f1-ede3-2177-aa9d-45f507391310 + ++ Response 200 + + + Headers + + X-Refresh-Token: true + +## Safe Deposit Box V1 [/v1/safe-deposit-box] + +## Get details for each authorized Safe Deposit Box [GET] + +This endpoint will list all the Safe Deposit Box a user is authorized to see. + ++ Request (application/json) + + + Headers + + X-Vault-Token: 7f6808f1-ede3-2177-aa9d-45f507391310 + ++ Response 200 (application/json) + + + Body + + [ + { + "id": "fb013540-fb5f-11e5-ba72-e899458df21a", + "name": "Web", + "path": "app/web", + "category_id": "f7ff85a0-faaa-11e5-a8a9-7fa3b294cd46" + }, + { + "id": "06f82494-fb60-11e5-ba72-e899458df21a", + "name": "OneLogin", + "path": "shared/onelogin", + "category_id": "f7ffb890-faaa-11e5-a8a9-7fa3b294cd46" + } + ] + +### Create a Safe Deposit Box [POST] + +This endpoint will create a new Safe Deposit Box + + Request (application/json) + Headers @@ -301,9 +520,7 @@ This endpoint will create a new Safe Deposit Box } -## Safe Deposit Box [/v1/safe-deposit-box/{id}] - -### Get details for a specific authorized Safe Deposit Box [GET] +### Get details for a specific authorized Safe Deposit Box [GET /v1/safe-deposit-box/{id}] This endpoint returns details on a specific Safe Deposit Box. diff --git a/gradle.properties b/gradle.properties index 8dabd4b1d..777cedbf6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,6 +14,6 @@ # limitations under the License. # -version=0.15.0 +version=0.16.0 groupId=com.nike.cerberus artifactId=cms diff --git a/src/main/java/com/nike/cerberus/domain/IamRoleCredentialsV2.java b/src/main/java/com/nike/cerberus/domain/IamRoleCredentialsV2.java index 389c95045..e5d6a9c54 100644 --- a/src/main/java/com/nike/cerberus/domain/IamRoleCredentialsV2.java +++ b/src/main/java/com/nike/cerberus/domain/IamRoleCredentialsV2.java @@ -21,25 +21,25 @@ import javax.validation.constraints.Pattern; -import static com.nike.cerberus.util.AwsIamRoleArnParser.AWS_IAM_ROLE_ARN_REGEX; +import static com.nike.cerberus.util.AwsIamRoleArnParser.AWS_IAM_PRINCIPAL_ARN_REGEX; /** * Represents the IAM role credentials sent during authentication. */ public class IamRoleCredentialsV2 { - @Pattern(regexp = AWS_IAM_ROLE_ARN_REGEX, message = "AUTH_IAM_ROLE_NAME_INVALID") - private String roleArn; + @Pattern(regexp = AWS_IAM_PRINCIPAL_ARN_REGEX, message = "AUTH_IAM_PRINCIPAL_INVALID") + private String iamPrincipalArn; @NotBlank(message = "AUTH_IAM_ROLE_AWS_REGION_BLANK") private String region; - public String getRoleArn() { - return roleArn; + public String getIamPrincipalArn() { + return iamPrincipalArn; } - public void setRoleArn(String roleArn) { - this.roleArn = roleArn; + public void setIamPrincipalArn(String iamPrincipalArn) { + this.iamPrincipalArn = iamPrincipalArn; } public String getRegion() { diff --git a/src/main/java/com/nike/cerberus/domain/IamRolePermission.java b/src/main/java/com/nike/cerberus/domain/IamRolePermissionV1.java similarity index 80% rename from src/main/java/com/nike/cerberus/domain/IamRolePermission.java rename to src/main/java/com/nike/cerberus/domain/IamRolePermissionV1.java index 9516a7e3b..7c91005cc 100644 --- a/src/main/java/com/nike/cerberus/domain/IamRolePermission.java +++ b/src/main/java/com/nike/cerberus/domain/IamRolePermissionV1.java @@ -29,7 +29,7 @@ /** * Represents a permission granted to an IAM role with regards to a safe deposit box */ -public class IamRolePermission { +public class IamRolePermissionV1 { private String id; @@ -44,8 +44,6 @@ public class IamRolePermission { @NotBlank(message = "IAM_ROLE_ROLE_ID_INVALID", groups = {Default.class, Updatable.class}) private String roleId; - private String iamRoleArn; - private OffsetDateTime createdTs; private OffsetDateTime lastUpdatedTs; @@ -70,7 +68,7 @@ public void setAccountId(String accountId) { this.accountId = accountId; } - public IamRolePermission withAccountId(String accountId) { + public IamRolePermissionV1 withAccountId(String accountId) { this.accountId = accountId; return this; } @@ -83,7 +81,7 @@ public void setIamRoleName(String iamRoleName) { this.iamRoleName = iamRoleName; } - public IamRolePermission withIamRoleName(String iamRoleName) { + public IamRolePermissionV1 withIamRoleName(String iamRoleName) { this.iamRoleName = iamRoleName; return this; } @@ -96,24 +94,11 @@ public void setRoleId(String roleId) { this.roleId = roleId; } - public IamRolePermission withRoleId(String roleId) { + public IamRolePermissionV1 withRoleId(String roleId) { this.roleId = roleId; return this; } - public String getIamRoleArn() { - return iamRoleArn; - } - - public void setIamRoleArn(String iamRoleArn) { - this.iamRoleArn = iamRoleArn; - } - - public IamRolePermission withIamRoleArn(String iamRoleArn) { - this.iamRoleArn = iamRoleArn; - return this; - } - public OffsetDateTime getCreatedTs() { return createdTs; } @@ -151,11 +136,10 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - IamRolePermission that = (IamRolePermission) o; + IamRolePermissionV1 that = (IamRolePermissionV1) o; if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) return false; - if (iamRoleName != null ? !iamRoleName.equals(that.iamRoleName) : that.iamRoleName == null) return false; - return iamRoleArn != null ? iamRoleArn.equals(that.iamRoleArn) : that.iamRoleArn == null; + return iamRoleName != null ? iamRoleName.equals(that.iamRoleName) : that.iamRoleName == null; } @@ -163,7 +147,6 @@ public boolean equals(Object o) { public int hashCode() { int result = accountId != null ? accountId.hashCode() : 0; result = 31 * result + (iamRoleName != null ? iamRoleName.hashCode() : 0); - result = 31 * result + (iamRoleArn != null ? iamRoleArn.hashCode() : 0); return result; } } diff --git a/src/main/java/com/nike/cerberus/domain/IamRolePermissionV2.java b/src/main/java/com/nike/cerberus/domain/IamRolePermissionV2.java new file mode 100644 index 000000000..7dde8d006 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/IamRolePermissionV2.java @@ -0,0 +1,132 @@ +/* + * 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 com.nike.cerberus.validation.group.Updatable; +import org.hibernate.validator.constraints.NotBlank; + +import javax.validation.constraints.Pattern; +import javax.validation.groups.Default; +import java.time.OffsetDateTime; + +import static com.nike.cerberus.util.AwsIamRoleArnParser.AWS_IAM_PRINCIPAL_ARN_REGEX; +import static com.nike.cerberus.util.AwsIamRoleArnParser.AWS_IAM_ROLE_ARN_REGEX; + +/** + * Represents a permission granted to an IAM role with regards to a safe deposit box + */ +public class IamRolePermissionV2 { + + private String id; + + @NotBlank(message = "IAM_ROLE_ROLE_ID_INVALID", groups = {Default.class, Updatable.class}) + private String roleId; + + @Pattern(regexp = AWS_IAM_PRINCIPAL_ARN_REGEX, message = "SDB_IAM_PRINCIPAL_PERMISSION_ARN_INVALID", groups = {Default.class, Updatable.class}) + private String iamPrincipalArn; + + private OffsetDateTime createdTs; + + private OffsetDateTime lastUpdatedTs; + + private String createdBy; + + private String lastUpdatedBy; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getRoleId() { + return roleId; + } + + public void setRoleId(String roleId) { + this.roleId = roleId; + } + + public IamRolePermissionV2 withRoleId(String roleId) { + this.roleId = roleId; + return this; + } + + public String getIamPrincipalArn() { + return iamPrincipalArn; + } + + public void setIamPrincipalArn(String iamPrincipalArn) { + this.iamPrincipalArn = iamPrincipalArn; + } + + public IamRolePermissionV2 withIamPrincipalArn(String iamRoleArn) { + this.iamPrincipalArn = iamRoleArn; + return this; + } + + public OffsetDateTime getCreatedTs() { + return createdTs; + } + + public void setCreatedTs(OffsetDateTime createdTs) { + this.createdTs = createdTs; + } + + public OffsetDateTime getLastUpdatedTs() { + return lastUpdatedTs; + } + + public void setLastUpdatedTs(OffsetDateTime lastUpdatedTs) { + this.lastUpdatedTs = lastUpdatedTs; + } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public String getLastUpdatedBy() { + return lastUpdatedBy; + } + + public void setLastUpdatedBy(String lastUpdatedBy) { + this.lastUpdatedBy = lastUpdatedBy; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + IamRolePermissionV2 that = (IamRolePermissionV2) o; + + return iamPrincipalArn != null ? iamPrincipalArn.equals(that.iamPrincipalArn) : that.iamPrincipalArn == null; + + } + + @Override + public int hashCode() { + return iamPrincipalArn != null ? iamPrincipalArn.hashCode() : 0; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/SafeDepositBox.java b/src/main/java/com/nike/cerberus/domain/SafeDepositBox.java index 20c4df07b..5a34d7c85 100644 --- a/src/main/java/com/nike/cerberus/domain/SafeDepositBox.java +++ b/src/main/java/com/nike/cerberus/domain/SafeDepositBox.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. @@ -12,198 +12,27 @@ * 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 com.nike.cerberus.validation.UniqueIamRolePermissions; -import com.nike.cerberus.validation.UniqueOwner; -import com.nike.cerberus.validation.UniqueUserGroupPermissions; -import com.nike.cerberus.validation.group.Updatable; -import org.hibernate.validator.constraints.Length; -import org.hibernate.validator.constraints.NotBlank; - -import javax.validation.Valid; -import javax.validation.groups.Default; -import java.time.OffsetDateTime; -import java.util.HashSet; import java.util.Set; /** * Represents a logical grouping of secrets. */ -@UniqueOwner(groups = {Default.class, Updatable.class}) -public class SafeDepositBox { - - private String id; - - @NotBlank(message = "SDB_CATEGORY_ID_INVALID") - private String categoryId; - - @NotBlank(message = "SDB_NAME_BLANK") - @Length(max = 100, message = "SDB_NAME_TOO_LONG") - private String name; - - @Length(max = 1000, message = "SDB_DESCRIPTION_TOO_LONG", groups = {Default.class, Updatable.class}) - private String description; - - private String path; - - private OffsetDateTime createdTs; - - private OffsetDateTime lastUpdatedTs; - - private String createdBy; - - private String lastUpdatedBy; - - @NotBlank(message = "SDB_OWNER_BLANK", groups = {Default.class, Updatable.class}) - @Length(max = 255, message = "SDB_OWNER_TOO_LONG", groups = {Default.class, Updatable.class}) - private String owner; - - @Valid - @UniqueUserGroupPermissions(groups = {Default.class, Updatable.class}) - private Set userGroupPermissions = new HashSet<>(); - - @Valid - @UniqueIamRolePermissions(groups = {Default.class, Updatable.class}) - private Set iamRolePermissions = new HashSet<>(); - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getCategoryId() { - return categoryId; - } - - public void setCategoryId(String categoryId) { - this.categoryId = categoryId; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getPath() { - return path; - } - - public void setPath(String path) { - this.path = path; - } - - public OffsetDateTime getCreatedTs() { - return createdTs; - } - - public void setCreatedTs(OffsetDateTime createdTs) { - this.createdTs = createdTs; - } - - public OffsetDateTime getLastUpdatedTs() { - return lastUpdatedTs; - } - - public void setLastUpdatedTs(OffsetDateTime lastUpdatedTs) { - this.lastUpdatedTs = lastUpdatedTs; - } - - public String getCreatedBy() { - return createdBy; - } - - public void setCreatedBy(String createdBy) { - this.createdBy = createdBy; - } - - public String getLastUpdatedBy() { - return lastUpdatedBy; - } - - public void setLastUpdatedBy(String lastUpdatedBy) { - this.lastUpdatedBy = lastUpdatedBy; - } - - public String getOwner() { - return owner; - } - - public void setOwner(String owner) { - this.owner = owner; - } - - public Set getUserGroupPermissions() { - return userGroupPermissions; - } - - public void setUserGroupPermissions(Set userGroupPermissions) { - this.userGroupPermissions = userGroupPermissions; - } - - public Set getIamRolePermissions() { - return iamRolePermissions; - } +public interface SafeDepositBox { - public void setIamRolePermissions(Set iamRolePermissions) { - this.iamRolePermissions = iamRolePermissions; - } + String getName(); - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + void setName(String name); - SafeDepositBox that = (SafeDepositBox) o; + String getOwner(); - 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; + void setOwner(String owner); - } + Set getUserGroupPermissions(); - @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; - } + void setUserGroupPermissions(Set userGroupPermissions); } diff --git a/src/main/java/com/nike/cerberus/domain/SafeDepositBoxV1.java b/src/main/java/com/nike/cerberus/domain/SafeDepositBoxV1.java new file mode 100644 index 000000000..a5d37a7ac --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/SafeDepositBoxV1.java @@ -0,0 +1,209 @@ +/* + * 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.domain; + +import com.nike.cerberus.validation.UniqueIamRolePermissionsV1; +import com.nike.cerberus.validation.UniqueOwner; +import com.nike.cerberus.validation.UniqueUserGroupPermissions; +import com.nike.cerberus.validation.group.Updatable; +import org.hibernate.validator.constraints.Length; +import org.hibernate.validator.constraints.NotBlank; + +import javax.validation.Valid; +import javax.validation.groups.Default; +import java.time.OffsetDateTime; +import java.util.HashSet; +import java.util.Set; + +/** + * Represents a logical grouping of secrets. + */ +@UniqueOwner(groups = {Default.class, Updatable.class}) +public class SafeDepositBoxV1 implements SafeDepositBox { + + private String id; + + @NotBlank(message = "SDB_CATEGORY_ID_INVALID") + private String categoryId; + + @NotBlank(message = "SDB_NAME_BLANK") + @Length(max = 100, message = "SDB_NAME_TOO_LONG") + private String name; + + @Length(max = 1000, message = "SDB_DESCRIPTION_TOO_LONG", groups = {Default.class, Updatable.class}) + private String description; + + private String path; + + private OffsetDateTime createdTs; + + private OffsetDateTime lastUpdatedTs; + + private String createdBy; + + private String lastUpdatedBy; + + @NotBlank(message = "SDB_OWNER_BLANK", groups = {Default.class, Updatable.class}) + @Length(max = 255, message = "SDB_OWNER_TOO_LONG", groups = {Default.class, Updatable.class}) + private String owner; + + @Valid + @UniqueUserGroupPermissions(groups = {Default.class, Updatable.class}) + private Set userGroupPermissions = new HashSet<>(); + + @Valid + @UniqueIamRolePermissionsV1(groups = {Default.class, Updatable.class}) + private Set iamRolePermissions = new HashSet<>(); + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getCategoryId() { + return categoryId; + } + + public void setCategoryId(String categoryId) { + this.categoryId = categoryId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public OffsetDateTime getCreatedTs() { + return createdTs; + } + + public void setCreatedTs(OffsetDateTime createdTs) { + this.createdTs = createdTs; + } + + public OffsetDateTime getLastUpdatedTs() { + return lastUpdatedTs; + } + + public void setLastUpdatedTs(OffsetDateTime lastUpdatedTs) { + this.lastUpdatedTs = lastUpdatedTs; + } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public String getLastUpdatedBy() { + return lastUpdatedBy; + } + + public void setLastUpdatedBy(String lastUpdatedBy) { + this.lastUpdatedBy = lastUpdatedBy; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public Set getUserGroupPermissions() { + return userGroupPermissions; + } + + public void setUserGroupPermissions(Set userGroupPermissions) { + this.userGroupPermissions = userGroupPermissions; + } + + public Set getIamRolePermissions() { + return iamRolePermissions; + } + + 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; + + SafeDepositBoxV1 that = (SafeDepositBoxV1) 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/SafeDepositBoxV2.java b/src/main/java/com/nike/cerberus/domain/SafeDepositBoxV2.java new file mode 100644 index 000000000..ff3be66ac --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/SafeDepositBoxV2.java @@ -0,0 +1,210 @@ +/* + * 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 com.nike.cerberus.validation.UniqueIamRolePermissionsV2; +import com.nike.cerberus.validation.UniqueOwner; +import com.nike.cerberus.validation.UniqueUserGroupPermissions; +import com.nike.cerberus.validation.group.Updatable; +import org.hibernate.validator.constraints.Length; +import org.hibernate.validator.constraints.NotBlank; + +import javax.validation.Valid; +import javax.validation.groups.Default; +import java.time.OffsetDateTime; +import java.util.HashSet; +import java.util.Set; + +/** + * Represents a logical grouping of secrets. + */ +@UniqueOwner(groups = {Default.class, Updatable.class}) +public class SafeDepositBoxV2 implements SafeDepositBox { + + private String id; + + @NotBlank(message = "SDB_CATEGORY_ID_INVALID") + private String categoryId; + + @NotBlank(message = "SDB_NAME_BLANK") + @Length(max = 100, message = "SDB_NAME_TOO_LONG") + private String name; + + @Length(max = 1000, message = "SDB_DESCRIPTION_TOO_LONG", groups = {Default.class, Updatable.class}) + private String description; + + private String path; + + private OffsetDateTime createdTs; + + private OffsetDateTime lastUpdatedTs; + + private String createdBy; + + private String lastUpdatedBy; + + @NotBlank(message = "SDB_OWNER_BLANK", groups = {Default.class, Updatable.class}) + @Length(max = 255, message = "SDB_OWNER_TOO_LONG", groups = {Default.class, Updatable.class}) + private String owner; + + @Valid + @UniqueUserGroupPermissions(groups = {Default.class, Updatable.class}) + private Set userGroupPermissions = new HashSet<>(); + + @Valid + @UniqueIamRolePermissionsV2(groups = {Default.class, Updatable.class}) + private Set iamRolePermissions = new HashSet<>(); + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getCategoryId() { + return categoryId; + } + + public void setCategoryId(String categoryId) { + this.categoryId = categoryId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public OffsetDateTime getCreatedTs() { + return createdTs; + } + + public void setCreatedTs(OffsetDateTime createdTs) { + this.createdTs = createdTs; + } + + public OffsetDateTime getLastUpdatedTs() { + return lastUpdatedTs; + } + + public void setLastUpdatedTs(OffsetDateTime lastUpdatedTs) { + this.lastUpdatedTs = lastUpdatedTs; + } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public String getLastUpdatedBy() { + return lastUpdatedBy; + } + + public void setLastUpdatedBy(String lastUpdatedBy) { + this.lastUpdatedBy = lastUpdatedBy; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public Set getUserGroupPermissions() { + return userGroupPermissions; + } + + public void setUserGroupPermissions(Set userGroupPermissions) { + this.userGroupPermissions = userGroupPermissions; + } + + public Set getIamRolePermissions() { + return iamRolePermissions; + } + + 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; + + SafeDepositBoxV2 that = (SafeDepositBoxV2) 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/endpoints/authentication/AuthenticateIamRoleV2.java b/src/main/java/com/nike/cerberus/endpoints/authentication/AuthenticateIamRoleV2.java index 7086a8892..f71a671f7 100644 --- a/src/main/java/com/nike/cerberus/endpoints/authentication/AuthenticateIamRoleV2.java +++ b/src/main/java/com/nike/cerberus/endpoints/authentication/AuthenticateIamRoleV2.java @@ -18,10 +18,8 @@ package com.nike.cerberus.endpoints.authentication; import com.nike.cerberus.domain.IamRoleAuthResponse; -import com.nike.cerberus.domain.IamRoleCredentialsV1; import com.nike.cerberus.domain.IamRoleCredentialsV2; import com.nike.cerberus.service.AuthenticationService; -import com.nike.cerberus.util.AwsIamRoleArnParser; import com.nike.riposte.server.http.RequestInfo; import com.nike.riposte.server.http.ResponseInfo; import com.nike.riposte.server.http.StandardEndpoint; @@ -57,7 +55,7 @@ public CompletableFuture> execute(final Reques return CompletableFuture.supplyAsync(() -> { IamRoleCredentialsV2 credentials = request.getContent(); log.info("IAM Auth Event: the IAM principal {} in attempting to authenticate in region {}", - credentials.getRoleArn(), credentials.getRegion()); + credentials.getIamPrincipalArn(), credentials.getRegion()); return ResponseInfo.newBuilder(authenticationService.authenticate(request.getContent())).build(); }, longRunningTaskExecutor); diff --git a/src/main/java/com/nike/cerberus/endpoints/sdb/CreateSafeDepositBox.java b/src/main/java/com/nike/cerberus/endpoints/sdb/CreateSafeDepositBoxV1.java similarity index 89% rename from src/main/java/com/nike/cerberus/endpoints/sdb/CreateSafeDepositBox.java rename to src/main/java/com/nike/cerberus/endpoints/sdb/CreateSafeDepositBoxV1.java index 298820c47..6971ae21d 100644 --- a/src/main/java/com/nike/cerberus/endpoints/sdb/CreateSafeDepositBox.java +++ b/src/main/java/com/nike/cerberus/endpoints/sdb/CreateSafeDepositBoxV1.java @@ -18,7 +18,7 @@ import com.google.common.collect.Maps; import com.nike.backstopper.exception.ApiException; -import com.nike.cerberus.domain.SafeDepositBox; +import com.nike.cerberus.domain.SafeDepositBoxV1; import com.nike.cerberus.error.DefaultApiError; import com.nike.cerberus.security.CmsRequestSecurityValidator; import com.nike.cerberus.security.VaultAuthPrincipal; @@ -46,7 +46,8 @@ /** * Creates a new safe deposit box. Returns the assigned unique identifier. */ -public class CreateSafeDepositBox extends StandardEndpoint> { +@Deprecated +public class CreateSafeDepositBoxV1 extends StandardEndpoint> { private final Logger log = LoggerFactory.getLogger(getClass()); @@ -57,18 +58,18 @@ public class CreateSafeDepositBox extends StandardEndpoint>> execute(final RequestInfo request, + public CompletableFuture>> execute(final RequestInfo request, final Executor longRunningTaskExecutor, final ChannelHandlerContext ctx) { return CompletableFuture.supplyAsync(() -> createSafeDepositBox(request, BASE_PATH), longRunningTaskExecutor); } - private ResponseInfo> createSafeDepositBox(final RequestInfo request, + private ResponseInfo> createSafeDepositBox(final RequestInfo request, final String basePath) { final Optional securityContext = CmsRequestSecurityValidator.getSecurityContextForRequest(request); @@ -80,7 +81,7 @@ private ResponseInfo> createSafeDepositBox(final RequestInfo vaultAuthPrincipal.getName(), request.getContent().getName()); final String id = - safeDepositBoxService.createSafeDepositBox(request.getContent(), vaultAuthPrincipal.getName()); + safeDepositBoxService.createSafeDepositBoxV1(request.getContent(), vaultAuthPrincipal.getName()); final String location = basePath + "/" + id; final Map map = Maps.newHashMap(); diff --git a/src/main/java/com/nike/cerberus/endpoints/sdb/CreateSafeDepositBoxV2.java b/src/main/java/com/nike/cerberus/endpoints/sdb/CreateSafeDepositBoxV2.java new file mode 100644 index 000000000..fc9f40351 --- /dev/null +++ b/src/main/java/com/nike/cerberus/endpoints/sdb/CreateSafeDepositBoxV2.java @@ -0,0 +1,96 @@ +/* + * 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.sdb; + +import com.google.common.collect.Maps; +import com.nike.backstopper.exception.ApiException; +import com.nike.cerberus.domain.SafeDepositBox; +import com.nike.cerberus.domain.SafeDepositBoxV2; +import com.nike.cerberus.error.DefaultApiError; +import com.nike.cerberus.security.CmsRequestSecurityValidator; +import com.nike.cerberus.security.VaultAuthPrincipal; +import com.nike.cerberus.service.SafeDepositBoxService; +import com.nike.riposte.server.http.RequestInfo; +import com.nike.riposte.server.http.ResponseInfo; +import com.nike.riposte.server.http.StandardEndpoint; +import com.nike.riposte.util.Matcher; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpResponseStatus; + +import javax.inject.Inject; +import javax.ws.rs.core.SecurityContext; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +import static io.netty.handler.codec.http.HttpHeaders.Names.LOCATION; + +/** + * Creates a new safe deposit box. Returns the assigned unique identifier. + */ +public class CreateSafeDepositBoxV2 extends StandardEndpoint { + + public static final String BASE_PATH = "/v2/safe-deposit-box"; + + public static final String HEADER_X_REFRESH_TOKEN = "X-Refresh-Token"; + + private final SafeDepositBoxService safeDepositBoxService; + + @Inject + public CreateSafeDepositBoxV2(final SafeDepositBoxService safeDepositBoxService) { + this.safeDepositBoxService = safeDepositBoxService; + } + + @Override + public CompletableFuture> execute(final RequestInfo request, + final Executor longRunningTaskExecutor, + final ChannelHandlerContext ctx) { + return CompletableFuture.supplyAsync(() -> createSafeDepositBox(request, BASE_PATH), longRunningTaskExecutor); + } + + private ResponseInfo createSafeDepositBox(final RequestInfo request, + final String basePath) { + final Optional securityContext = + CmsRequestSecurityValidator.getSecurityContextForRequest(request); + + if (securityContext.isPresent()) { + final VaultAuthPrincipal vaultAuthPrincipal = (VaultAuthPrincipal) securityContext.get().getUserPrincipal(); + final SafeDepositBoxV2 safeDepositBox = + safeDepositBoxService.createSafeDepositBoxV2(request.getContent(), vaultAuthPrincipal.getName()); + + final String location = basePath + "/" + safeDepositBox.getId(); + + return ResponseInfo.newBuilder(safeDepositBox) + .withHeaders(new DefaultHttpHeaders() + .set(LOCATION, location) + .set(HEADER_X_REFRESH_TOKEN, Boolean.TRUE.toString())) + .withHttpStatusCode(HttpResponseStatus.CREATED.code()) + .build(); + } + + throw ApiException.newBuilder().withApiErrors(DefaultApiError.AUTH_BAD_CREDENTIALS).build(); + } + + @Override + public Matcher requestMatcher() { + return Matcher.match(BASE_PATH, HttpMethod.POST); + } +} diff --git a/src/main/java/com/nike/cerberus/endpoints/sdb/DeleteSafeDepositBox.java b/src/main/java/com/nike/cerberus/endpoints/sdb/DeleteSafeDepositBox.java index 8334880b0..a74bceee6 100644 --- a/src/main/java/com/nike/cerberus/endpoints/sdb/DeleteSafeDepositBox.java +++ b/src/main/java/com/nike/cerberus/endpoints/sdb/DeleteSafeDepositBox.java @@ -16,6 +16,7 @@ package com.nike.cerberus.endpoints.sdb; +import com.google.common.collect.Sets; import com.nike.backstopper.exception.ApiException; import com.nike.cerberus.error.DefaultApiError; import com.nike.cerberus.security.CmsRequestSecurityValidator; @@ -25,6 +26,7 @@ import com.nike.riposte.server.http.ResponseInfo; import com.nike.riposte.server.http.StandardEndpoint; import com.nike.riposte.util.Matcher; +import com.nike.riposte.util.MultiMatcher; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.DefaultHttpHeaders; import io.netty.handler.codec.http.HttpMethod; @@ -41,6 +43,7 @@ /** * Endpoint for deleting a safe deposit box. */ +@Deprecated public class DeleteSafeDepositBox extends StandardEndpoint { private final Logger log = LoggerFactory.getLogger(getClass()); @@ -84,6 +87,7 @@ private ResponseInfo deleteSafeDepositBox(final RequestInfo request) @Override public Matcher requestMatcher() { - return Matcher.match("/v1/safe-deposit-box/{id}", HttpMethod.DELETE); + + return MultiMatcher.match(Sets.newHashSet("/v1/safe-deposit-box/{id}","/v2/safe-deposit-box/{id}"),HttpMethod.DELETE); } } diff --git a/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBox.java b/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxV1.java similarity index 85% rename from src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBox.java rename to src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxV1.java index df9e8ab99..edd0b7163 100644 --- a/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBox.java +++ b/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxV1.java @@ -18,7 +18,7 @@ import com.nike.backstopper.apierror.sample.SampleCoreApiError; import com.nike.backstopper.exception.ApiException; -import com.nike.cerberus.domain.SafeDepositBox; +import com.nike.cerberus.domain.SafeDepositBoxV1; import com.nike.cerberus.error.DefaultApiError; import com.nike.cerberus.security.CmsRequestSecurityValidator; import com.nike.cerberus.security.VaultAuthPrincipal; @@ -42,25 +42,26 @@ * Extracts the user groups from the security context for the request and attempts to get details about the safe * deposit box by its unique id. */ -public class GetSafeDepositBox extends StandardEndpoint { +@Deprecated +public class GetSafeDepositBoxV1 extends StandardEndpoint { private final Logger log = LoggerFactory.getLogger(getClass()); private final SafeDepositBoxService safeDepositBoxService; @Inject - public GetSafeDepositBox(final SafeDepositBoxService safeDepositBoxService) { + public GetSafeDepositBoxV1(final SafeDepositBoxService safeDepositBoxService) { this.safeDepositBoxService = safeDepositBoxService; } @Override - public CompletableFuture> execute(final RequestInfo request, - final Executor longRunningTaskExecutor, - final ChannelHandlerContext ctx) { + public CompletableFuture> execute(final RequestInfo request, + final Executor longRunningTaskExecutor, + final ChannelHandlerContext ctx) { return CompletableFuture.supplyAsync(() -> getSafeDepositBox(request), longRunningTaskExecutor); } - public ResponseInfo getSafeDepositBox(final RequestInfo request) { + public ResponseInfo getSafeDepositBox(final RequestInfo request) { final Optional securityContext = CmsRequestSecurityValidator.getSecurityContextForRequest(request); @@ -74,8 +75,8 @@ public ResponseInfo getSafeDepositBox(final RequestInfo re log.info("Read SDB Event: the principal: {} is attempting to read sdb name: '{}' and id: '{}'", vaultAuthPrincipal.getName(), sdbName, sdbId); - final Optional safeDepositBox = - safeDepositBoxService.getAssociatedSafeDepositBox( + final Optional safeDepositBox = + safeDepositBoxService.getAssociatedSafeDepositBoxV1( vaultAuthPrincipal.getUserGroups(), sdbId); diff --git a/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxV2.java b/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxV2.java new file mode 100644 index 000000000..5b4fd8b39 --- /dev/null +++ b/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxV2.java @@ -0,0 +1,98 @@ +/* + * 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.sdb; + +import com.nike.backstopper.apierror.sample.SampleCoreApiError; +import com.nike.backstopper.exception.ApiException; +import com.nike.cerberus.domain.SafeDepositBoxV1; +import com.nike.cerberus.domain.SafeDepositBoxV2; +import com.nike.cerberus.error.DefaultApiError; +import com.nike.cerberus.security.CmsRequestSecurityValidator; +import com.nike.cerberus.security.VaultAuthPrincipal; +import com.nike.cerberus.service.SafeDepositBoxService; +import com.nike.riposte.server.http.RequestInfo; +import com.nike.riposte.server.http.ResponseInfo; +import com.nike.riposte.server.http.StandardEndpoint; +import com.nike.riposte.util.Matcher; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpMethod; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.ws.rs.core.SecurityContext; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +/** + * Extracts the user groups from the security context for the request and attempts to get details about the safe + * deposit box by its unique id. + */ +public class GetSafeDepositBoxV2 extends StandardEndpoint { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final SafeDepositBoxService safeDepositBoxService; + + @Inject + public GetSafeDepositBoxV2(final SafeDepositBoxService safeDepositBoxService) { + this.safeDepositBoxService = safeDepositBoxService; + } + + @Override + public CompletableFuture> execute(final RequestInfo request, + final Executor longRunningTaskExecutor, + final ChannelHandlerContext ctx) { + return CompletableFuture.supplyAsync(() -> getSafeDepositBox(request), longRunningTaskExecutor); + } + + public ResponseInfo getSafeDepositBox(final RequestInfo request) { + final Optional securityContext = + CmsRequestSecurityValidator.getSecurityContextForRequest(request); + + if (securityContext.isPresent()) { + final VaultAuthPrincipal vaultAuthPrincipal = (VaultAuthPrincipal) securityContext.get().getUserPrincipal(); + + String sdbId = request.getPathParam("id"); + Optional sdbNameOptional = safeDepositBoxService.getSafeDepositBoxNameById(sdbId); + String sdbName = sdbNameOptional.isPresent() ? sdbNameOptional.get() : + String.format("(Failed to lookup name from id: %s)", sdbId); + log.info("Read SDB Event: the principal: {} is attempting to read sdb name: '{}' and id: '{}'", + vaultAuthPrincipal.getName(), sdbName, sdbId); + + final Optional safeDepositBox = + safeDepositBoxService.getAssociatedSafeDepositBoxV2( + vaultAuthPrincipal.getUserGroups(), + sdbId); + + if (safeDepositBox.isPresent()) { + return ResponseInfo.newBuilder(safeDepositBox.get()).build(); + } + + throw ApiException.newBuilder().withApiErrors(SampleCoreApiError.NOT_FOUND).build(); + } + + throw ApiException.newBuilder().withApiErrors(DefaultApiError.AUTH_BAD_CREDENTIALS).build(); + } + + @Override + public Matcher requestMatcher() { + return Matcher.match("/v2/safe-deposit-box/{id}", HttpMethod.GET); + } +} diff --git a/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxes.java b/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxes.java index e0da81f6f..d036d3d0a 100644 --- a/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxes.java +++ b/src/main/java/com/nike/cerberus/endpoints/sdb/GetSafeDepositBoxes.java @@ -16,6 +16,7 @@ package com.nike.cerberus.endpoints.sdb; +import com.google.common.collect.Sets; import com.nike.backstopper.exception.ApiException; import com.nike.cerberus.domain.SafeDepositBoxSummary; import com.nike.cerberus.error.DefaultApiError; @@ -26,6 +27,7 @@ import com.nike.riposte.server.http.ResponseInfo; import com.nike.riposte.server.http.StandardEndpoint; import com.nike.riposte.util.Matcher; +import com.nike.riposte.util.MultiMatcher; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.HttpMethod; import org.slf4j.Logger; @@ -42,6 +44,7 @@ * Extracts the user groups from the security context for the request and returns any safe deposit boxes * associated with that list of user groups. */ +@Deprecated public class GetSafeDepositBoxes extends StandardEndpoint> { private final Logger log = LoggerFactory.getLogger(getClass()); @@ -79,6 +82,7 @@ public ResponseInfo> getSafeDepositBoxes(final Reque @Override public Matcher requestMatcher() { - return Matcher.match("/v1/safe-deposit-box", HttpMethod.GET); + + return MultiMatcher.match(Sets.newHashSet("/v1/safe-deposit-box", "/v2/safe-deposit-box"), HttpMethod.GET); } } diff --git a/src/main/java/com/nike/cerberus/endpoints/sdb/UpdateSafeDepositBox.java b/src/main/java/com/nike/cerberus/endpoints/sdb/UpdateSafeDepositBoxV1.java similarity index 89% rename from src/main/java/com/nike/cerberus/endpoints/sdb/UpdateSafeDepositBox.java rename to src/main/java/com/nike/cerberus/endpoints/sdb/UpdateSafeDepositBoxV1.java index b12ce06aa..423167a85 100644 --- a/src/main/java/com/nike/cerberus/endpoints/sdb/UpdateSafeDepositBox.java +++ b/src/main/java/com/nike/cerberus/endpoints/sdb/UpdateSafeDepositBoxV1.java @@ -17,7 +17,7 @@ package com.nike.cerberus.endpoints.sdb; import com.nike.backstopper.exception.ApiException; -import com.nike.cerberus.domain.SafeDepositBox; +import com.nike.cerberus.domain.SafeDepositBoxV1; import com.nike.cerberus.error.DefaultApiError; import com.nike.cerberus.security.CmsRequestSecurityValidator; import com.nike.cerberus.security.VaultAuthPrincipal; @@ -43,7 +43,8 @@ /** * Endpoint for updating a safe deposit box. */ -public class UpdateSafeDepositBox extends StandardEndpoint { +@Deprecated +public class UpdateSafeDepositBoxV1 extends StandardEndpoint { private final Logger log = LoggerFactory.getLogger(getClass()); @@ -52,16 +53,16 @@ public class UpdateSafeDepositBox extends StandardEndpoint private final SafeDepositBoxService safeDepositBoxService; @Inject - public UpdateSafeDepositBox(final SafeDepositBoxService safeDepositBoxService) { + public UpdateSafeDepositBoxV1(final SafeDepositBoxService safeDepositBoxService) { this.safeDepositBoxService = safeDepositBoxService; } @Override - public CompletableFuture> execute(RequestInfo request, Executor longRunningTaskExecutor, ChannelHandlerContext ctx) { + public CompletableFuture> execute(RequestInfo request, Executor longRunningTaskExecutor, ChannelHandlerContext ctx) { return CompletableFuture.supplyAsync(() -> updateSafeDepositBox(request), longRunningTaskExecutor); } - private ResponseInfo updateSafeDepositBox(final RequestInfo request) { + private ResponseInfo updateSafeDepositBox(final RequestInfo request) { final Optional securityContext = CmsRequestSecurityValidator.getSecurityContextForRequest(request); @@ -75,7 +76,7 @@ private ResponseInfo updateSafeDepositBox(final RequestInfo { + + public static final String HEADER_X_REFRESH_TOKEN = "X-Refresh-Token"; + + private final SafeDepositBoxService safeDepositBoxService; + + @Inject + public UpdateSafeDepositBoxV2(final SafeDepositBoxService safeDepositBoxService) { + this.safeDepositBoxService = safeDepositBoxService; + } + + @Override + public CompletableFuture> execute(RequestInfo request, Executor longRunningTaskExecutor, ChannelHandlerContext ctx) { + return CompletableFuture.supplyAsync(() -> updateSafeDepositBox(request), longRunningTaskExecutor); + } + + private ResponseInfo updateSafeDepositBox(final RequestInfo request) { + final Optional securityContext = + CmsRequestSecurityValidator.getSecurityContextForRequest(request); + + if (securityContext.isPresent()) { + final VaultAuthPrincipal vaultAuthPrincipal = (VaultAuthPrincipal) securityContext.get().getUserPrincipal(); + SafeDepositBoxV2 safeDepositBoxV2 = safeDepositBoxService.updateSafeDepositBoxV2(request.getContent(), + vaultAuthPrincipal.getUserGroups(), + vaultAuthPrincipal.getName(), + request.getPathParam("id")); + return ResponseInfo.newBuilder(safeDepositBoxV2) + .withHeaders(new DefaultHttpHeaders().set(HEADER_X_REFRESH_TOKEN, Boolean.TRUE.toString())) + .withHttpStatusCode(HttpResponseStatus.OK.code()) + .build(); + } + + throw ApiException.newBuilder().withApiErrors(DefaultApiError.AUTH_BAD_CREDENTIALS).build(); + } + + @Override + public Matcher requestMatcher() { + return Matcher.match("/v2/safe-deposit-box/{id}", HttpMethod.PUT); + } + + @Override + public Class[] validationGroups(RequestInfo request) { + return new Class[] { + Updatable.class + }; + } +} diff --git a/src/main/java/com/nike/cerberus/error/DefaultApiError.java b/src/main/java/com/nike/cerberus/error/DefaultApiError.java index de189977c..65ebec88d 100644 --- a/src/main/java/com/nike/cerberus/error/DefaultApiError.java +++ b/src/main/java/com/nike/cerberus/error/DefaultApiError.java @@ -149,7 +149,7 @@ public enum DefaultApiError implements ApiError { /** * The IAM Role + Region don't have a KMS key provisioned to encrypt the auth response. */ - AUTH_IAM_ROLE_INVALID(99216, "The specified IAM role is not valid.", HttpServletResponse.SC_BAD_REQUEST), + AUTH_IAM_PRINCIPAL_INVALID(99216, "The specified IAM principal is not valid.", HttpServletResponse.SC_BAD_REQUEST), /** * IAM Role permission on SDB specifies in invalid AWS region. @@ -197,6 +197,11 @@ public enum DefaultApiError implements ApiError { */ FAILED_TO_VALIDATE_KMS_KEY_POLICY(99225, "Failed to validate KMS key policy", HttpServletResponse.SC_INTERNAL_SERVER_ERROR), + /** + * IAM Role permission on SDB specifies in invalid AWS region. + */ + SDB_IAM_PRINCIPAL_PERMISSION_ARN_INVALID(99226, "Invalid AWS IAM role specified for the SDB.", HttpServletResponse.SC_BAD_REQUEST), + /** * Generic not found error. */ diff --git a/src/main/java/com/nike/cerberus/security/VaultAuthPrincipal.java b/src/main/java/com/nike/cerberus/security/VaultAuthPrincipal.java index fba000c98..f6764225e 100644 --- a/src/main/java/com/nike/cerberus/security/VaultAuthPrincipal.java +++ b/src/main/java/com/nike/cerberus/security/VaultAuthPrincipal.java @@ -45,6 +45,8 @@ public class VaultAuthPrincipal implements Principal { public static final String METADATA_KEY_AWS_IAM_ROLE_NAME = "aws_iam_role_name"; + public static final String METADATA_KEY_AWS_IAM_PRINCIPAL_ARN = "aws_iam_principal_arn"; + public static final String METADATA_KEY_AWS_REGION = "aws_region"; private final VaultClientTokenResponse clientToken; 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 b67e60a26..88f1f8a71 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 @@ -36,11 +36,14 @@ import com.nike.cerberus.endpoints.category.GetCategory; import com.nike.cerberus.endpoints.role.GetAllRoles; import com.nike.cerberus.endpoints.role.GetRole; -import com.nike.cerberus.endpoints.sdb.CreateSafeDepositBox; +import com.nike.cerberus.endpoints.sdb.CreateSafeDepositBoxV1; +import com.nike.cerberus.endpoints.sdb.CreateSafeDepositBoxV2; import com.nike.cerberus.endpoints.sdb.DeleteSafeDepositBox; -import com.nike.cerberus.endpoints.sdb.GetSafeDepositBox; +import com.nike.cerberus.endpoints.sdb.GetSafeDepositBoxV1; +import com.nike.cerberus.endpoints.sdb.GetSafeDepositBoxV2; import com.nike.cerberus.endpoints.sdb.GetSafeDepositBoxes; -import com.nike.cerberus.endpoints.sdb.UpdateSafeDepositBox; +import com.nike.cerberus.endpoints.sdb.UpdateSafeDepositBoxV1; +import com.nike.cerberus.endpoints.sdb.UpdateSafeDepositBoxV2; import com.nike.cerberus.error.DefaultApiErrorsImpl; import com.nike.cerberus.auth.connector.AuthConnector; import com.nike.cerberus.security.CmsRequestSecurityValidator; @@ -172,10 +175,13 @@ public Set> appEndpoints( GetAllRoles getAllRoles, GetRole getRole, GetSafeDepositBoxes getSafeDepositBoxes, - GetSafeDepositBox getSafeDepositBox, + GetSafeDepositBoxV1 getSafeDepositBoxV1, + GetSafeDepositBoxV2 getSafeDepositBoxV2, DeleteSafeDepositBox deleteSafeDepositBox, - UpdateSafeDepositBox updateSafeDepositBox, - CreateSafeDepositBox createSafeDepositBox, + UpdateSafeDepositBoxV1 updateSafeDepositBoxV1, + UpdateSafeDepositBoxV2 updateSafeDepositBoxV2, + CreateSafeDepositBoxV1 createSafeDepositBoxV1, + CreateSafeDepositBoxV2 createSafeDepositBoxV2, GetSDBMetadata getSDBMetadata, PutSDBMetadata putSDBMetadata ) { @@ -185,8 +191,8 @@ public Set> appEndpoints( getAllCategories, getCategory, createCategory, deleteCategory, authenticateUser, authenticateIamRoleV2, mfaCheck, refreshUserToken, authenticateIamRole, revokeToken, getAllRoles, getRole, - getSafeDepositBoxes, getSafeDepositBox, - deleteSafeDepositBox, updateSafeDepositBox, createSafeDepositBox, + getSafeDepositBoxes, getSafeDepositBoxV1, getSafeDepositBoxV2, + deleteSafeDepositBox, updateSafeDepositBoxV1, updateSafeDepositBoxV2, createSafeDepositBoxV1, createSafeDepositBoxV2, 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 538f1890d..02b099706 100644 --- a/src/main/java/com/nike/cerberus/service/AuthenticationService.java +++ b/src/main/java/com/nike/cerberus/service/AuthenticationService.java @@ -25,7 +25,6 @@ import com.amazonaws.services.kms.model.EncryptResult; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.inject.Inject; @@ -92,6 +91,7 @@ public class AuthenticationService { private final ObjectMapper objectMapper; private final String adminGroup; private final DateTimeSupplier dateTimeSupplier; + private final AwsIamRoleArnParser awsIamRoleArnParser; @Inject(optional=true) @Named(ADMIN_IAM_ROLES_PROPERTY) @@ -117,7 +117,8 @@ public AuthenticationService(final SafeDepositBoxDao safeDepositBoxDao, final VaultPolicyService vaultPolicyService, final ObjectMapper objectMapper, @Named(ADMIN_GROUP_PROPERTY) final String adminGroup, - final DateTimeSupplier dateTimeSupplier) { + final DateTimeSupplier dateTimeSupplier, + final AwsIamRoleArnParser awsIamRoleArnParser) { this.safeDepositBoxDao = safeDepositBoxDao; this.awsIamRoleDao = awsIamRoleDao; @@ -129,6 +130,7 @@ public AuthenticationService(final SafeDepositBoxDao safeDepositBoxDao, this.objectMapper = objectMapper; this.adminGroup = adminGroup; this.dateTimeSupplier = dateTimeSupplier; + this.awsIamRoleArnParser = awsIamRoleArnParser; } /** @@ -177,17 +179,31 @@ public AuthResponse mfaCheck(final MfaCheckRequest mfaCheckRequest) { */ public IamRoleAuthResponse authenticate(IamRoleCredentialsV1 credentials) { - final String iamRoleArn = String.format(AwsIamRoleArnParser.AWS_IAM_ROLE_ARN_TEMPLATE, credentials.getAccountId(), + final String iamPrincipalArn = String.format(AwsIamRoleArnParser.AWS_IAM_ROLE_ARN_TEMPLATE, credentials.getAccountId(), credentials.getRoleName()); + final String region = credentials.getRegion(); final IamRoleCredentialsV2 iamRoleCredentialsV2 = new IamRoleCredentialsV2(); - iamRoleCredentialsV2.setRoleArn(iamRoleArn); - iamRoleCredentialsV2.setRegion(credentials.getRegion()); + iamRoleCredentialsV2.setIamPrincipalArn(iamPrincipalArn); + iamRoleCredentialsV2.setRegion(region); - return authenticate(iamRoleCredentialsV2); + final Map vaultAuthPrincipalMetadata = generateCommonVaultPrincipalAuthMetadata(iamPrincipalArn, region); + vaultAuthPrincipalMetadata.put(VaultAuthPrincipal.METADATA_KEY_AWS_ACCOUNT_ID,awsIamRoleArnParser.getAccountId(iamPrincipalArn)); + vaultAuthPrincipalMetadata.put(VaultAuthPrincipal.METADATA_KEY_AWS_IAM_ROLE_NAME, awsIamRoleArnParser.getRoleName(iamPrincipalArn)); + + return authenticate(iamRoleCredentialsV2, vaultAuthPrincipalMetadata); } public IamRoleAuthResponse authenticate(IamRoleCredentialsV2 credentials) { + + final String iamPrincipalArn = credentials.getIamPrincipalArn(); + final Map vaultAuthPrincipalMetadata = generateCommonVaultPrincipalAuthMetadata(iamPrincipalArn, credentials.getRegion()); + vaultAuthPrincipalMetadata.put(VaultAuthPrincipal.METADATA_KEY_AWS_IAM_PRINCIPAL_ARN, awsIamRoleArnParser.getRoleName(iamPrincipalArn)); + + return authenticate(credentials, vaultAuthPrincipalMetadata); + } + + public IamRoleAuthResponse authenticate(IamRoleCredentialsV2 credentials, Map vaultAuthPrincipalMetadata) { final String keyId; try { keyId = getKeyId(credentials); @@ -198,33 +214,17 @@ public IamRoleAuthResponse authenticate(IamRoleCredentialsV2 credentials) { .withExceptionCause(e) .withExceptionMessage(String.format( "Failed to lazily provision KMS key for %s in region: %s", - credentials.getRoleArn(), credentials.getRegion())) + credentials.getIamPrincipalArn(), credentials.getRegion())) .build(); } throw e; } - final String iamRoleArn = credentials.getRoleArn(); - final Set policies = buildPolicySet(iamRoleArn); - - final Map meta = Maps.newHashMap(); - meta.put(VaultAuthPrincipal.METADATA_KEY_AWS_REGION, credentials.getRegion()); - meta.put(VaultAuthPrincipal.METADATA_KEY_USERNAME, iamRoleArn); - Set groups = new HashSet<>(); - groups.add("registered-iam-principals"); - - // We will allow specific ARNs access to the user portions of the API - if (getAdminRoleArnSet().contains(iamRoleArn)) { - meta.put(VaultAuthPrincipal.METADATA_KEY_IS_ADMIN, Boolean.toString(true)); - groups.add("admin-iam-principals"); - } else { - meta.put(VaultAuthPrincipal.METADATA_KEY_IS_ADMIN, Boolean.toString(false)); - } - meta.put(VaultAuthPrincipal.METADATA_KEY_GROUPS, StringUtils.join(groups, ',')); + final Set policies = buildPolicySet(credentials.getIamPrincipalArn()); final VaultTokenAuthRequest tokenAuthRequest = new VaultTokenAuthRequest() .setPolicies(policies) - .setMeta(meta) + .setMeta(vaultAuthPrincipalMetadata) .setTtl(iamTokenTTL) .setNoDefaultPolicy(true); @@ -361,13 +361,13 @@ private Set buildPolicySet(final String iamRoleArn) { * @return KMS Key id */ private String getKeyId(IamRoleCredentialsV2 credentials) { - final Optional iamRole = awsIamRoleDao.getIamRole(credentials.getRoleArn()); + final Optional iamRole = awsIamRoleDao.getIamRole(credentials.getIamPrincipalArn()); if (!iamRole.isPresent()) { throw ApiException.newBuilder() - .withApiErrors(DefaultApiError.AUTH_IAM_ROLE_INVALID) + .withApiErrors(DefaultApiError.AUTH_IAM_PRINCIPAL_INVALID) .withExceptionMessage(String.format("The role: %s was not configured for any SDB", - credentials.getRoleArn())) + credentials.getIamPrincipalArn())) .build(); } @@ -376,12 +376,12 @@ private String getKeyId(IamRoleCredentialsV2 credentials) { final String kmsKeyId; if (!kmsKey.isPresent()) { - kmsKeyId = kmsService.provisionKmsKey(iamRole.get().getId(), credentials.getRoleArn(), + kmsKeyId = kmsService.provisionKmsKey(iamRole.get().getId(), credentials.getIamPrincipalArn(), credentials.getRegion(), SYSTEM_USER, dateTimeSupplier.get()); } else { kmsKeyId = kmsKey.get().getAwsKmsKeyId(); String keyRegion = credentials.getRegion(); - kmsService.validatePolicy(kmsKeyId, credentials.getRoleArn(), keyRegion); + kmsService.validatePolicy(kmsKeyId, credentials.getIamPrincipalArn(), keyRegion); } return kmsKeyId; @@ -437,4 +437,25 @@ private Set getAdminRoleArnSet() { } return adminRoleArnSet; } + + protected Map generateCommonVaultPrincipalAuthMetadata(String iamPrincipalArn, String region) { + + Map metadata = Maps.newHashMap(); + metadata.put(VaultAuthPrincipal.METADATA_KEY_AWS_REGION, region); + metadata.put(VaultAuthPrincipal.METADATA_KEY_USERNAME, iamPrincipalArn); + + Set groups = new HashSet<>(); + groups.add("registered-iam-principals"); + + // We will allow specific ARNs access to the user portions of the API + if (getAdminRoleArnSet().contains(iamPrincipalArn)) { + metadata.put(VaultAuthPrincipal.METADATA_KEY_IS_ADMIN, Boolean.toString(true)); + groups.add("admin-iam-principals"); + } else { + metadata.put(VaultAuthPrincipal.METADATA_KEY_IS_ADMIN, Boolean.toString(false)); + } + metadata.put(VaultAuthPrincipal.METADATA_KEY_GROUPS, StringUtils.join(groups, ',')); + + return metadata; + } } diff --git a/src/main/java/com/nike/cerberus/service/IamRolePermissionService.java b/src/main/java/com/nike/cerberus/service/IamRolePermissionService.java index 25730e5ca..d4a4a7858 100644 --- a/src/main/java/com/nike/cerberus/service/IamRolePermissionService.java +++ b/src/main/java/com/nike/cerberus/service/IamRolePermissionService.java @@ -19,7 +19,7 @@ import com.google.common.collect.Sets; import com.nike.backstopper.exception.ApiException; import com.nike.cerberus.dao.AwsIamRoleDao; -import com.nike.cerberus.domain.IamRolePermission; +import com.nike.cerberus.domain.IamRolePermissionV2; import com.nike.cerberus.domain.Role; import com.nike.cerberus.error.DefaultApiError; import com.nike.cerberus.record.AwsIamRolePermissionRecord; @@ -70,10 +70,10 @@ public IamRolePermissionService(final UuidSupplier uuidSupplier, */ @Transactional public void grantIamRolePermissions(final String safeDepositBoxId, - final Set iamRolePermissionSet, + final Set iamRolePermissionSet, final String user, final OffsetDateTime dateTime) { - for (IamRolePermission iamRolePermission : iamRolePermissionSet) { + for (IamRolePermissionV2 iamRolePermission : iamRolePermissionSet) { grantIamRolePermission(safeDepositBoxId, iamRolePermission, user, dateTime); } } @@ -88,17 +88,17 @@ public void grantIamRolePermissions(final String safeDepositBoxId, */ @Transactional public void grantIamRolePermission(final String safeDepositBoxId, - final IamRolePermission iamRolePermission, + final IamRolePermissionV2 iamRolePermission, final String user, final OffsetDateTime dateTime) { final Optional possibleIamRoleRecord = - awsIamRoleDao.getIamRole(iamRolePermission.getIamRoleArn()); + awsIamRoleDao.getIamRole(iamRolePermission.getIamPrincipalArn()); final Optional role = roleService.getRoleById(iamRolePermission.getRoleId()); if (!role.isPresent()) { throw ApiException.newBuilder() - .withApiErrors(DefaultApiError.USER_GROUP_ROLE_ID_INVALID) + .withApiErrors(DefaultApiError.IAM_ROLE_ROLE_ID_INVALID) .build(); } @@ -109,9 +109,7 @@ public void grantIamRolePermission(final String safeDepositBoxId, iamRoleId = uuidSupplier.get(); AwsIamRoleRecord awsIamRoleRecord = new AwsIamRoleRecord(); awsIamRoleRecord.setId(iamRoleId); - awsIamRoleRecord.setAwsAccountId(iamRolePermission.getAccountId()); // TODO: remove - awsIamRoleRecord.setAwsIamRoleName(iamRolePermission.getIamRoleName()); // TODO: remove - awsIamRoleRecord.setAwsIamRoleArn(iamRolePermission.getIamRoleArn()); + awsIamRoleRecord.setAwsIamRoleArn(iamRolePermission.getIamPrincipalArn()); awsIamRoleRecord.setCreatedBy(user); awsIamRoleRecord.setLastUpdatedBy(user); awsIamRoleRecord.setCreatedTs(dateTime); @@ -141,10 +139,10 @@ public void grantIamRolePermission(final String safeDepositBoxId, */ @Transactional public void updateIamRolePermissions(final String safeDepositBoxId, - final Set iamRolePermissionSet, + final Set iamRolePermissionSet, final String user, final OffsetDateTime dateTime) { - for (IamRolePermission iamRolePermission : iamRolePermissionSet) { + for (IamRolePermissionV2 iamRolePermission : iamRolePermissionSet) { updateIamRolePermission(safeDepositBoxId, iamRolePermission, user, dateTime); } } @@ -159,11 +157,11 @@ public void updateIamRolePermissions(final String safeDepositBoxId, */ @Transactional public void updateIamRolePermission(final String safeDepositBoxId, - final IamRolePermission iamRolePermission, + final IamRolePermissionV2 iamRolePermission, final String user, final OffsetDateTime dateTime) { final Optional iamRole = - awsIamRoleDao.getIamRole(iamRolePermission.getIamRoleArn()); + awsIamRoleDao.getIamRole(iamRolePermission.getIamPrincipalArn()); if (!iamRole.isPresent()) { throw ApiException.newBuilder() @@ -191,10 +189,10 @@ public void updateIamRolePermission(final String safeDepositBoxId, */ @Transactional public void revokeIamRolePermissions(final String safeDepositBoxId, - final Set iamRolePermissionSet, + final Set iamRolePermissionSet, final String user, final OffsetDateTime dateTime) { - for (IamRolePermission iamRolePermission : iamRolePermissionSet) { + for (IamRolePermissionV2 iamRolePermission : iamRolePermissionSet) { revokeIamRolePermission(safeDepositBoxId, iamRolePermission, user, dateTime); } } @@ -209,11 +207,11 @@ public void revokeIamRolePermissions(final String safeDepositBoxId, */ @Transactional public void revokeIamRolePermission(final String safeDepositBoxId, - final IamRolePermission iamRolePermission, + final IamRolePermissionV2 iamRolePermission, final String user, final OffsetDateTime dateTime) { final Optional iamRole = - awsIamRoleDao.getIamRole(iamRolePermission.getIamRoleArn()); + awsIamRoleDao.getIamRole(iamRolePermission.getIamPrincipalArn()); if (!iamRole.isPresent()) { throw ApiException.newBuilder() @@ -225,19 +223,17 @@ public void revokeIamRolePermission(final String safeDepositBoxId, awsIamRoleDao.deleteIamRolePermission(safeDepositBoxId, iamRole.get().getId()); } - public Set getIamRolePermissions(final String safeDepositBoxId) { - final Set iamRolePermissionSet = Sets.newHashSet(); + public Set getIamRolePermissions(final String safeDepositBoxId) { + final Set iamRolePermissionSet = Sets.newHashSet(); final List permissionRecords = awsIamRoleDao.getIamRolePermissions(safeDepositBoxId); permissionRecords.forEach(r -> { final Optional iamRoleRecord = awsIamRoleDao.getIamRoleById(r.getAwsIamRoleId()); if (iamRoleRecord.isPresent()) { - final IamRolePermission permission = new IamRolePermission(); + final IamRolePermissionV2 permission = new IamRolePermissionV2(); permission.setId(r.getId()); - permission.setAccountId(awsIamRoleArnParser.getAccountId(iamRoleRecord.get().getAwsIamRoleArn())); // TODO: remove - permission.setIamRoleName(awsIamRoleArnParser.getRoleName(iamRoleRecord.get().getAwsIamRoleArn())); // TODO: remove - permission.setIamRoleArn(iamRoleRecord.get().getAwsIamRoleArn()); + permission.setIamPrincipalArn(iamRoleRecord.get().getAwsIamRoleArn()); permission.setRoleId(r.getRoleId()); permission.setCreatedBy(r.getCreatedBy()); permission.setLastUpdatedBy(r.getLastUpdatedBy()); diff --git a/src/main/java/com/nike/cerberus/service/KmsPolicyService.java b/src/main/java/com/nike/cerberus/service/KmsPolicyService.java index 6fe572408..98a3e2418 100644 --- a/src/main/java/com/nike/cerberus/service/KmsPolicyService.java +++ b/src/main/java/com/nike/cerberus/service/KmsPolicyService.java @@ -21,7 +21,6 @@ import com.amazonaws.auth.policy.Principal; import com.amazonaws.auth.policy.Resource; import com.amazonaws.auth.policy.Statement; -import com.nike.cerberus.util.AwsIamRoleArnParser; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; @@ -30,7 +29,6 @@ import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; -import java.io.IOException; /** * Helpful service for putting together the KMS policy documents to be associated with provisioned KMS keys. @@ -68,11 +66,6 @@ public KmsPolicyService(@Named(ROOT_USER_ARN_PROPERTY) String rootUserArn, objectMapper = new ObjectMapper(); } - public String generateStandardKmsPolicy(final String iamRoleAccountId, final String iamRoleName) { - return generateStandardKmsPolicy(String.format(AwsIamRoleArnParser.AWS_IAM_ROLE_ARN_TEMPLATE, - iamRoleAccountId, iamRoleName)); - } - /*** * Please note that currently in the AWS Core SDK 1.11.108 that Policy.fromJson strips hyphens from AWS ARN Principals * and Hyphens are valid in IAM role names. We will need to manually use JsonNodes and not rely on fromJson diff --git a/src/main/java/com/nike/cerberus/service/MetadataService.java b/src/main/java/com/nike/cerberus/service/MetadataService.java index 4d370eb49..863f1fab3 100644 --- a/src/main/java/com/nike/cerberus/service/MetadataService.java +++ b/src/main/java/com/nike/cerberus/service/MetadataService.java @@ -17,15 +17,14 @@ package com.nike.cerberus.service; import com.nike.backstopper.exception.ApiException; -import com.nike.cerberus.domain.IamRolePermission; +import com.nike.cerberus.domain.IamRolePermissionV2; import com.nike.cerberus.domain.Role; import com.nike.cerberus.domain.SDBMetadata; import com.nike.cerberus.domain.SDBMetadataResult; -import com.nike.cerberus.domain.SafeDepositBox; +import com.nike.cerberus.domain.SafeDepositBoxV2; import com.nike.cerberus.domain.UserGroupPermission; import com.nike.cerberus.error.InvalidCategoryNameApiError; import com.nike.cerberus.error.InvalidRoleNameApiError; -import com.nike.cerberus.util.AwsIamRoleArnParser; import com.nike.cerberus.util.UuidSupplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,25 +49,22 @@ public class MetadataService { private final CategoryService categoryService; private final RoleService roleService; private final UuidSupplier uuidSupplier; - private final AwsIamRoleArnParser awsIamRoleArnParser; @Inject public MetadataService(SafeDepositBoxService safeDepositBoxService, CategoryService categoryService, RoleService roleService, - UuidSupplier uuidSupplier, - AwsIamRoleArnParser awsIamRoleArnParser) { + UuidSupplier uuidSupplier) { this.safeDepositBoxService = safeDepositBoxService; this.categoryService = categoryService; this.roleService = roleService; this.uuidSupplier = uuidSupplier; - this.awsIamRoleArnParser = awsIamRoleArnParser; } /** * Creates or Updates an SDB using saved off metadata. - * This method differs from SafeDepositBoxService::createSafeDepositBox and SafeDepositBoxService::updateSafeDepositBox + * This method differs from SafeDepositBoxService::createSafeDepositBoxV1 and SafeDepositBoxService::updateSafeDepositBoxV1 * 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. @@ -83,9 +79,9 @@ public void restoreMetadata(SDBMetadata sdbMetadata, String adminUser) { String id = getSdbId(sdbMetadata); String categoryId = getCategoryId(sdbMetadata); Set userGroupPermissionSet = getUserGroupPermissionSet(sdbMetadata); - Set iamRolePermissionSet = getIamRolePermissionSet(sdbMetadata); + Set iamRolePermissionSet = getIamRolePermissionSet(sdbMetadata); - SafeDepositBox sdb = new SafeDepositBox(); + SafeDepositBoxV2 sdb = new SafeDepositBoxV2(); sdb.setId(id); sdb.setPath(sdbMetadata.getPath()); sdb.setCategoryId(categoryId); @@ -107,17 +103,12 @@ public void restoreMetadata(SDBMetadata sdbMetadata, String adminUser) { * @param sdbMetadata the sdb metadata * @return IAM Role Permission Set */ - private Set getIamRolePermissionSet(SDBMetadata sdbMetadata) { - Set iamRolePermissionSet = new HashSet<>(); + private Set getIamRolePermissionSet(SDBMetadata sdbMetadata) { + Set iamRolePermissionSet = new HashSet<>(); sdbMetadata.getIamRolePermissions().forEach((iamRoleArn, roleName) -> { - String awsAccountId = awsIamRoleArnParser.getAccountId(iamRoleArn); - String awsIamRoleName = awsIamRoleArnParser.getRoleName(iamRoleArn); - - iamRolePermissionSet.add(new IamRolePermission() - .withAccountId(awsIamRoleArnParser.getAccountId(iamRoleArn)) - .withIamRoleName(awsIamRoleArnParser.getRoleName(iamRoleArn)) - .withIamRoleArn(String.format(AwsIamRoleArnParser.AWS_IAM_ROLE_ARN_TEMPLATE, awsAccountId, awsIamRoleName)) + iamRolePermissionSet.add(new IamRolePermissionV2() + .withIamPrincipalArn(iamRoleArn) .withRoleId(getRoleIdFromName(roleName)) ); }); @@ -213,7 +204,7 @@ protected List getSDBMetadataList(int limit, int offset) { // Collect the roles Map roleIdToStringMap = roleService.getRoleIdToStringMap(); // Collect The SDB Records - List safeDepositBoxes = safeDepositBoxService.getSafeDepositBoxes(limit, offset); + List safeDepositBoxes = safeDepositBoxService.getSafeDepositBoxes(limit, offset); // for each SDB collect the user and iam permissions and add to result safeDepositBoxes.forEach(sdb -> { @@ -252,13 +243,13 @@ protected Map getUserGroupPermissionsMap(Map rol * Retrieves a simplified iam permission map that is only strings so it can be transported across Cerberus environments */ protected Map getIamRolePermissionMap(Map roleIdToStringMap, - Set iamPerms) { + Set iamPerms) { Map iamRoleMap = new HashMap<>(iamPerms.size()); iamPerms.forEach(perm -> { String role = roleIdToStringMap.get(perm.getRoleId()); - iamRoleMap.put(String.format(AwsIamRoleArnParser.AWS_IAM_ROLE_ARN_TEMPLATE, perm.getAccountId(), perm.getIamRoleName()), role); + iamRoleMap.put(perm.getIamPrincipalArn(), role); }); return iamRoleMap; } diff --git a/src/main/java/com/nike/cerberus/service/SafeDepositBoxService.java b/src/main/java/com/nike/cerberus/service/SafeDepositBoxService.java index c01036199..484d1e07a 100644 --- a/src/main/java/com/nike/cerberus/service/SafeDepositBoxService.java +++ b/src/main/java/com/nike/cerberus/service/SafeDepositBoxService.java @@ -22,10 +22,12 @@ import com.nike.cerberus.dao.SafeDepositBoxDao; import com.nike.cerberus.dao.UserGroupDao; import com.nike.cerberus.domain.Category; -import com.nike.cerberus.domain.IamRolePermission; +import com.nike.cerberus.domain.IamRolePermissionV1; +import com.nike.cerberus.domain.IamRolePermissionV2; import com.nike.cerberus.domain.Role; -import com.nike.cerberus.domain.SafeDepositBox; +import com.nike.cerberus.domain.SafeDepositBoxV1; import com.nike.cerberus.domain.SafeDepositBoxSummary; +import com.nike.cerberus.domain.SafeDepositBoxV2; import com.nike.cerberus.domain.UserGroupPermission; import com.nike.cerberus.error.DefaultApiError; import com.nike.cerberus.record.RoleRecord; @@ -82,6 +84,8 @@ public class SafeDepositBoxService { private final DateTimeSupplier dateTimeSupplier; + private final AwsIamRoleArnParser awsIamRoleArnParser; + @Inject public SafeDepositBoxService(final SafeDepositBoxDao safeDepositBoxDao, final UserGroupDao userGroupDao, @@ -93,7 +97,8 @@ public SafeDepositBoxService(final SafeDepositBoxDao safeDepositBoxDao, final UserGroupPermissionService userGroupPermissionService, final IamRolePermissionService iamRolePermissionService, final Slugger slugger, - final DateTimeSupplier dateTimeSupplier) { + final DateTimeSupplier dateTimeSupplier, + final AwsIamRoleArnParser awsIamRoleArnParser) { this.safeDepositBoxDao = safeDepositBoxDao; this.userGroupDao = userGroupDao; this.uuidSupplier = uuidSupplier; @@ -105,6 +110,7 @@ public SafeDepositBoxService(final SafeDepositBoxDao safeDepositBoxDao, this.iamRolePermissionService = iamRolePermissionService; this.slugger = slugger; this.dateTimeSupplier = dateTimeSupplier; + this.awsIamRoleArnParser = awsIamRoleArnParser; } /** @@ -137,7 +143,24 @@ public List getAssociatedSafeDepositBoxes(final Set getAssociatedSafeDepositBox(final Set groups, final String id) { + public Optional getAssociatedSafeDepositBoxV1(final Set groups, final String id) { + + Optional safeDepositBoxV2 = getAssociatedSafeDepositBoxV2(groups, id); + + return safeDepositBoxV2.map(this::convertSafeDepositBoxV2ToV1); + } + + /** + * Queries the data store for the specific safe deposit box by ID. The query also enforces that the specified + * safe deposit box has a linked permission via the user groups supplied in the call. + * + * @param groups Set of user groups that must have at least one matching permission for the specific safe + * deposit box + * @param id The unique identifier for the safe deposit box to lookup + * @return The safe deposit box, if found + */ + public Optional getAssociatedSafeDepositBoxV2(final Set groups, final String id) { + final Optional safeDepositBoxRecord = safeDepositBoxDao.getSafeDepositBox(id); if (safeDepositBoxRecord.isPresent()) { @@ -150,20 +173,20 @@ public Optional getAssociatedSafeDepositBox(final Set gr .build(); } - return Optional.of(getSDBFromRecord(safeDepositBoxRecord.get())); + return Optional.of(getSDBFromRecordV2(safeDepositBoxRecord.get())); } return Optional.empty(); } - protected SafeDepositBox getSDBFromRecord(SafeDepositBoxRecord safeDepositBoxRecord) { + protected SafeDepositBoxV2 getSDBFromRecordV2(SafeDepositBoxRecord safeDepositBoxRecord) { if (safeDepositBoxRecord == null) { throw new IllegalArgumentException("Safe Deposit Box Record must not be null"); } String id = safeDepositBoxRecord.getId(); - - final Set userGroupPermissions = + + final Set userGroupPermissions = userGroupPermissionService.getUserGroupPermissions(id); String owner = null; @@ -175,9 +198,10 @@ protected SafeDepositBox getSDBFromRecord(SafeDepositBoxRecord safeDepositBoxRec owner = possibleOwner.get(); } - final Set iamRolePermissions = iamRolePermissionService.getIamRolePermissions(id); + final Set iamRolePermissions = iamRolePermissionService.getIamRolePermissions(id); - SafeDepositBox safeDepositBox = new SafeDepositBox(); + + SafeDepositBoxV2 safeDepositBox = new SafeDepositBoxV2(); safeDepositBox.setId(safeDepositBoxRecord.getId()); safeDepositBox.setName(safeDepositBoxRecord.getName()); safeDepositBox.setDescription(safeDepositBoxRecord.getDescription()); @@ -203,13 +227,29 @@ protected SafeDepositBox getSDBFromRecord(SafeDepositBoxRecord safeDepositBoxRec * @return ID of the created safe deposit box */ @Transactional - public String createSafeDepositBox(final SafeDepositBox safeDepositBox, final String user) { + public String createSafeDepositBoxV1(final SafeDepositBoxV1 safeDepositBox, final String user) { + + SafeDepositBoxV2 safeDepositBoxV2 = convertSafeDepositBoxV1ToV2(safeDepositBox); + + return createSafeDepositBoxV2(safeDepositBoxV2, user).getId(); + } + + /** + * Creates a safe deposit box and all the appropriate permissions. Policies for each role are also + * created within Vault. + * + * @param safeDepositBox Safe deposit box to create + * @param user User requesting the creation + * @return ID of the created safe deposit box + */ + @Transactional + public SafeDepositBoxV2 createSafeDepositBoxV2(final SafeDepositBoxV2 safeDepositBox, final String user) { final OffsetDateTime now = dateTimeSupplier.get(); final SafeDepositBoxRecord boxRecordToStore = buildBoxToStore(safeDepositBox, user, now); final Set userGroupPermissionSet = safeDepositBox.getUserGroupPermissions(); addOwnerPermission(userGroupPermissionSet, safeDepositBox.getOwner()); - final Set iamRolePermissionSet = addIamRoleArnToPermissions(safeDepositBox.getIamRolePermissions()); + final Set iamRolePermissionSet = safeDepositBox.getIamRolePermissions(); final boolean isPathInUse = safeDepositBoxDao.isPathInUse(boxRecordToStore.getPath()); @@ -235,7 +275,7 @@ public String createSafeDepositBox(final SafeDepositBox safeDepositBox, final St vaultPolicyService.createStandardPolicies(boxRecordToStore.getName(), boxRecordToStore.getPath()); - return boxRecordToStore.getId(); + return getSDBFromRecordV2(boxRecordToStore); } /** @@ -247,9 +287,26 @@ public String createSafeDepositBox(final SafeDepositBox safeDepositBox, final St * @param id Safe deposit box id */ @Transactional - public void updateSafeDepositBox(final SafeDepositBox safeDepositBox, final Set groups, - final String user, final String id) { - final Optional currentBox = getAssociatedSafeDepositBox(groups, id); + public void updateSafeDepositBoxV1(final SafeDepositBoxV1 safeDepositBox, final Set groups, + final String user, final String id) { + + SafeDepositBoxV2 safeDepositBoxV2 = convertSafeDepositBoxV1ToV2(safeDepositBox); + + updateSafeDepositBoxV2(safeDepositBoxV2, groups, user, id); + } + + /** + * Updates a safe deposit box. Currently, only the description, owner and permissions are updatable. + * + * @param safeDepositBox Updated safe deposit box + * @param groups Caller's user groups + * @param user Caller's username + * @param id Safe deposit box id + */ + @Transactional + public SafeDepositBoxV2 updateSafeDepositBoxV2(final SafeDepositBoxV2 safeDepositBox, final Set groups, + final String user, final String id) { + final Optional currentBox = getAssociatedSafeDepositBoxV2(groups, id); if (!currentBox.isPresent()) { throw ApiException.newBuilder() @@ -263,7 +320,7 @@ public void updateSafeDepositBox(final SafeDepositBox safeDepositBox, final Set< final OffsetDateTime now = dateTimeSupplier.get(); final SafeDepositBoxRecord boxToUpdate = buildBoxToUpdate(id, safeDepositBox, user, now); final Set userGroupPermissionSet = safeDepositBox.getUserGroupPermissions(); - final Set iamRolePermissionSet = addIamRoleArnToPermissions(safeDepositBox.getIamRolePermissions()); + final Set iamRolePermissionSet = safeDepositBox.getIamRolePermissions(); if (!StringUtils.equals(currentBox.get().getDescription(), boxToUpdate.getDescription())) { safeDepositBoxDao.updateSafeDepositBox(boxToUpdate); @@ -272,6 +329,16 @@ public void updateSafeDepositBox(final SafeDepositBox safeDepositBox, final Set< updateOwner(currentBox.get().getId(), safeDepositBox.getOwner(), user, now); modifyUserGroupPermissions(currentBox.get(), userGroupPermissionSet, user, now); modifyIamRolePermissions(currentBox.get(), iamRolePermissionSet, user, now); + + Optional updatedSafeDepositBox = getAssociatedSafeDepositBoxV2(groups, id); + if (updatedSafeDepositBox.isPresent()) { + return updatedSafeDepositBox.get(); + } else { + throw ApiException.newBuilder() + .withApiErrors(DefaultApiError.INTERNAL_SERVER_ERROR) + .withExceptionMessage("The updated safe deposit box was not found.") + .build(); + } } /** @@ -281,7 +348,7 @@ public void updateSafeDepositBox(final SafeDepositBox safeDepositBox, final Set< */ @Transactional public void deleteSafeDepositBox(final Set groups, final String id) { - final Optional box = getAssociatedSafeDepositBox(groups, id); + final Optional box = getAssociatedSafeDepositBoxV2(groups, id); if (!box.isPresent()) { throw ApiException.newBuilder() @@ -323,7 +390,7 @@ private Optional extractOwner(Set userGroupPermissi return Optional.of(ownerPermission.get().getName()); } - private void assertIsOwner(final Set groups, final SafeDepositBox box) { + private void assertIsOwner(final Set groups, final SafeDepositBoxV2 box) { if (!groups.contains(box.getOwner())) { throw ApiException.newBuilder() .withApiErrors(DefaultApiError.SDB_CALLER_OWNERSHIP_REQUIRED) @@ -363,7 +430,7 @@ protected void addOwnerPermission(final Set userGroupPermis * @param dateTime The timestamp for the creation * @return The safe deposit box to be stored */ - private SafeDepositBoxRecord buildBoxToStore(final SafeDepositBox requestedBox, + private SafeDepositBoxRecord buildBoxToStore(final SafeDepositBoxV2 requestedBox, final String user, final OffsetDateTime dateTime) { final SafeDepositBoxRecord boxToStore = new SafeDepositBoxRecord(); @@ -398,7 +465,7 @@ private SafeDepositBoxRecord buildBoxToStore(final SafeDepositBox requestedBox, * @return Safe deposit box with only updatable data */ private SafeDepositBoxRecord buildBoxToUpdate(final String id, - final SafeDepositBox safeDepositBox, + final SafeDepositBoxV2 safeDepositBox, final String user, final OffsetDateTime now) { final SafeDepositBoxRecord boxToUpdate = new SafeDepositBoxRecord(); @@ -464,7 +531,7 @@ protected void updateOwner(final String safeDepositBoxId, /** * Sorts out the set of permissions into, grant, update and revoke sets. After that it applies those changes. */ - protected void modifyUserGroupPermissions(final SafeDepositBox currentBox, + protected void modifyUserGroupPermissions(final SafeDepositBoxV2 currentBox, final Set userGroupPermissionSet, final String user, final OffsetDateTime dateTime) { @@ -493,15 +560,15 @@ protected void modifyUserGroupPermissions(final SafeDepositBox currentBox, /** * Sorts out the set of permissions into, grant, update and revoke sets. After that it applies those changes. */ - protected void modifyIamRolePermissions(final SafeDepositBox currentBox, - final Set iamRolePermissionSet, + protected void modifyIamRolePermissions(final SafeDepositBoxV2 currentBox, + final Set iamRolePermissionSet, final String user, final OffsetDateTime dateTime) { - Set toAddSet = Sets.newHashSet(); - Set toUpdateSet = Sets.newHashSet(); - Set toDeleteSet = Sets.newHashSet(); + Set toAddSet = Sets.newHashSet(); + Set toUpdateSet = Sets.newHashSet(); + Set toDeleteSet = Sets.newHashSet(); - for (IamRolePermission iamRolePermission : iamRolePermissionSet) { + for (IamRolePermissionV2 iamRolePermission : iamRolePermissionSet) { if (currentBox.getIamRolePermissions().contains(iamRolePermission)) { toUpdateSet.add(iamRolePermission); } else { @@ -558,18 +625,61 @@ private void deleteAllSecrets(final String path) { } /** - * Populates the ARN field for new Iam Role Permission objects - * @param iamRolePermissions - IAM role permissions to be modified - * @return - Modified IAM role permissions + * Converts a v2 API version safe deposit box into a v1 version + * @param safeDepositBoxV2 - V2 API version safe deposit box + * @return - V1 API version safe deposit box */ - protected Set addIamRoleArnToPermissions(Set iamRolePermissions) { - - return iamRolePermissions.stream() - .map(iamRolePermission -> - iamRolePermission.withIamRoleArn(String.format(AwsIamRoleArnParser.AWS_IAM_ROLE_ARN_TEMPLATE, - iamRolePermission.getAccountId(), - iamRolePermission.getIamRoleName()))) - .collect(Collectors.toSet()); + protected SafeDepositBoxV1 convertSafeDepositBoxV2ToV1(SafeDepositBoxV2 safeDepositBoxV2) { + + final SafeDepositBoxV1 safeDepositBoxV1 = new SafeDepositBoxV1(); + safeDepositBoxV1.setId(safeDepositBoxV2.getId()); + safeDepositBoxV1.setName(safeDepositBoxV2.getName()); + safeDepositBoxV1.setDescription(safeDepositBoxV2.getDescription()); + safeDepositBoxV1.setPath(safeDepositBoxV2.getPath()); + safeDepositBoxV1.setCategoryId(safeDepositBoxV2.getCategoryId()); + safeDepositBoxV1.setCreatedBy(safeDepositBoxV2.getCreatedBy()); + safeDepositBoxV1.setLastUpdatedBy(safeDepositBoxV2.getLastUpdatedBy()); + safeDepositBoxV1.setCreatedTs(safeDepositBoxV2.getCreatedTs()); + safeDepositBoxV1.setLastUpdatedTs(safeDepositBoxV2.getLastUpdatedTs()); + safeDepositBoxV1.setOwner(safeDepositBoxV2.getOwner()); + safeDepositBoxV1.setUserGroupPermissions(safeDepositBoxV2.getUserGroupPermissions()); + safeDepositBoxV1.setIamRolePermissions(safeDepositBoxV2.getIamRolePermissions().stream() + .map(iamRolePermission -> new IamRolePermissionV1() + .withAccountId(awsIamRoleArnParser.getAccountId(iamRolePermission.getIamPrincipalArn())) + .withIamRoleName(awsIamRoleArnParser.getRoleName(iamRolePermission.getIamPrincipalArn())) + .withRoleId(iamRolePermission.getRoleId())) + .collect(Collectors.toSet())); + + return safeDepositBoxV1; + } + + /** + * Converts a v1 API version safe deposit box into a v2 version + * @param safeDepositBoxV1 - V1 API version safe deposit box + * @return - V2 API version safe deposit box + */ + protected SafeDepositBoxV2 convertSafeDepositBoxV1ToV2(SafeDepositBoxV1 safeDepositBoxV1) { + + final SafeDepositBoxV2 safeDepositBoxV2 = new SafeDepositBoxV2(); + safeDepositBoxV2.setId(safeDepositBoxV1.getId()); + safeDepositBoxV2.setName(safeDepositBoxV1.getName()); + safeDepositBoxV2.setDescription(safeDepositBoxV1.getDescription()); + safeDepositBoxV2.setPath(safeDepositBoxV1.getPath()); + safeDepositBoxV2.setCategoryId(safeDepositBoxV1.getCategoryId()); + safeDepositBoxV2.setCreatedBy(safeDepositBoxV1.getCreatedBy()); + safeDepositBoxV2.setLastUpdatedBy(safeDepositBoxV1.getLastUpdatedBy()); + safeDepositBoxV2.setCreatedTs(safeDepositBoxV1.getCreatedTs()); + safeDepositBoxV2.setLastUpdatedTs(safeDepositBoxV1.getLastUpdatedTs()); + safeDepositBoxV2.setOwner(safeDepositBoxV1.getOwner()); + safeDepositBoxV2.setUserGroupPermissions(safeDepositBoxV1.getUserGroupPermissions()); + safeDepositBoxV2.setIamRolePermissions(safeDepositBoxV1.getIamRolePermissions().stream() + .map(iamRolePermission -> new IamRolePermissionV2() + .withIamPrincipalArn(String.format(AwsIamRoleArnParser.AWS_IAM_ROLE_ARN_TEMPLATE, + iamRolePermission.getAccountId(), iamRolePermission.getIamRoleName())) + .withRoleId(iamRolePermission.getRoleId())) + .collect(Collectors.toSet())); + + return safeDepositBoxV2; } /** @@ -580,17 +690,17 @@ public int getTotalNumberOfSafeDepositBoxes() { } /** - * + * * 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) { + public List getSafeDepositBoxes(int limit, int offset) { List records = safeDepositBoxDao.getSafeDepositBoxes(limit, offset); - List result = new LinkedList<>(); + List result = new LinkedList<>(); records.forEach(safeDepositBoxRecord -> { - result.add(getSDBFromRecord(safeDepositBoxRecord)); + result.add(getSDBFromRecordV2(safeDepositBoxRecord)); }); return result; } @@ -617,7 +727,7 @@ public Optional getSafeDepositBoxNameById(String id) { * @param safeDepositBox Safe Deposit Box to restore */ @Transactional - public void restoreSafeDepositBox(SafeDepositBox safeDepositBox, + public void restoreSafeDepositBox(SafeDepositBoxV2 safeDepositBox, String adminUser) { SafeDepositBoxRecord boxToStore = new SafeDepositBoxRecord(); @@ -635,7 +745,7 @@ public void restoreSafeDepositBox(SafeDepositBox safeDepositBox, Optional existingBoxRecord = safeDepositBoxDao.getSafeDepositBox(safeDepositBox.getId()); if (existingBoxRecord.isPresent()) { safeDepositBoxDao.fullUpdateSafeDepositBox(boxToStore); - SafeDepositBox existingBox = getSDBFromRecord(existingBoxRecord.get()); + SafeDepositBoxV2 existingBox = getSDBFromRecordV2(existingBoxRecord.get()); updateOwner(safeDepositBox.getId(), safeDepositBox.getOwner(), adminUser, now); modifyUserGroupPermissions(existingBox, safeDepositBox.getUserGroupPermissions(), adminUser, now); modifyIamRolePermissions(existingBox, safeDepositBox.getIamRolePermissions(), adminUser, now); diff --git a/src/main/java/com/nike/cerberus/util/AwsIamRoleArnParser.java b/src/main/java/com/nike/cerberus/util/AwsIamRoleArnParser.java index 0d4f767b5..1fdca869c 100644 --- a/src/main/java/com/nike/cerberus/util/AwsIamRoleArnParser.java +++ b/src/main/java/com/nike/cerberus/util/AwsIamRoleArnParser.java @@ -33,6 +33,8 @@ public class AwsIamRoleArnParser { public static final String AWS_IAM_ROLE_ARN_REGEX = "^arn:aws:iam::(?\\d+?):role/(?.+)$"; + public static final String AWS_IAM_PRINCIPAL_ARN_REGEX = "^arn:aws:(iam|sts)::.+$"; + private static final Pattern IAM_ROLE_ARN_PATTERN = Pattern.compile(AWS_IAM_ROLE_ARN_REGEX); public String getAccountId(String roleArn) { diff --git a/src/main/java/com/nike/cerberus/validation/IamRolePermissionsValidator.java b/src/main/java/com/nike/cerberus/validation/IamRolePermissionsValidatorV1.java similarity index 77% rename from src/main/java/com/nike/cerberus/validation/IamRolePermissionsValidator.java rename to src/main/java/com/nike/cerberus/validation/IamRolePermissionsValidatorV1.java index c8f534044..94c7a01fc 100644 --- a/src/main/java/com/nike/cerberus/validation/IamRolePermissionsValidator.java +++ b/src/main/java/com/nike/cerberus/validation/IamRolePermissionsValidatorV1.java @@ -16,7 +16,7 @@ package com.nike.cerberus.validation; -import com.nike.cerberus.domain.IamRolePermission; +import com.nike.cerberus.domain.IamRolePermissionV1; import org.apache.commons.lang3.StringUtils; import javax.validation.ConstraintValidator; @@ -28,14 +28,14 @@ /** * Validator class for validating that a set of IAM role permissions contain no duplicate user group names. */ -public class IamRolePermissionsValidator - implements ConstraintValidator> { +public class IamRolePermissionsValidatorV1 + implements ConstraintValidator> { - public void initialize(UniqueIamRolePermissions constraint) { + public void initialize(UniqueIamRolePermissionsV1 constraint) { // no-op } - public boolean isValid(Set iamRolePermissionSet, ConstraintValidatorContext context) { + public boolean isValid(Set iamRolePermissionSet, ConstraintValidatorContext context) { if (iamRolePermissionSet == null || iamRolePermissionSet.isEmpty()) { return true; } @@ -43,7 +43,7 @@ public boolean isValid(Set iamRolePermissionSet, ConstraintVa boolean isValid = true; Set iamRoles = new HashSet<>(); - for (IamRolePermission iamRolePermission : iamRolePermissionSet) { + for (IamRolePermissionV1 iamRolePermission : iamRolePermissionSet) { final String key = buildKey(iamRolePermission); if (iamRoles.contains(key)) { isValid = false; @@ -56,7 +56,7 @@ public boolean isValid(Set iamRolePermissionSet, ConstraintVa return isValid; } - private String buildKey(IamRolePermission iamRolePermission) { + private String buildKey(IamRolePermissionV1 iamRolePermission) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(StringUtils.lowerCase(iamRolePermission.getAccountId(), Locale.ENGLISH)); @@ -65,4 +65,5 @@ private String buildKey(IamRolePermission iamRolePermission) { return stringBuilder.toString(); } + } diff --git a/src/main/java/com/nike/cerberus/validation/IamRolePermissionsValidatorV2.java b/src/main/java/com/nike/cerberus/validation/IamRolePermissionsValidatorV2.java new file mode 100644 index 000000000..6730e00ff --- /dev/null +++ b/src/main/java/com/nike/cerberus/validation/IamRolePermissionsValidatorV2.java @@ -0,0 +1,68 @@ +/* + * 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.validation; + +import com.nike.cerberus.domain.IamRolePermissionV2; +import org.apache.commons.lang3.StringUtils; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; + +/** + * Validator class for validating that a set of IAM role permissions contain no duplicate user group names. + */ +public class IamRolePermissionsValidatorV2 + implements ConstraintValidator> { + + public void initialize(UniqueIamRolePermissionsV2 constraint) { + // no-op + } + + public boolean isValid(Set iamRolePermissionSet, ConstraintValidatorContext context) { + if (iamRolePermissionSet == null || iamRolePermissionSet.isEmpty()) { + return true; + } + + boolean isValid = true; + Set iamRoles = new HashSet<>(); + + for (IamRolePermissionV2 iamRolePermission : iamRolePermissionSet) { + final String key = buildKey(iamRolePermission); + if (iamRoles.contains(key)) { + isValid = false; + break; + } else { + iamRoles.add(key); + } + } + + return isValid; + } + + private String buildKey(IamRolePermissionV2 iamRolePermission) { + StringBuilder stringBuilder = new StringBuilder(); + + stringBuilder.append(StringUtils.lowerCase(iamRolePermission.getIamPrincipalArn(), Locale.ENGLISH)); + + return stringBuilder.toString(); + } + +} diff --git a/src/main/java/com/nike/cerberus/validation/UniqueIamRolePermissions.java b/src/main/java/com/nike/cerberus/validation/UniqueIamRolePermissionsV1.java similarity index 92% rename from src/main/java/com/nike/cerberus/validation/UniqueIamRolePermissions.java rename to src/main/java/com/nike/cerberus/validation/UniqueIamRolePermissionsV1.java index b29992008..20f02c44a 100644 --- a/src/main/java/com/nike/cerberus/validation/UniqueIamRolePermissions.java +++ b/src/main/java/com/nike/cerberus/validation/UniqueIamRolePermissionsV1.java @@ -33,9 +33,9 @@ */ @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) @Retention(RUNTIME) -@Constraint(validatedBy = IamRolePermissionsValidator.class) +@Constraint(validatedBy = IamRolePermissionsValidatorV1.class) @Documented -public @interface UniqueIamRolePermissions { +public @interface UniqueIamRolePermissionsV1 { String message() default "SDB_IAM_ROLE_REPEATED"; Class[] groups() default { }; diff --git a/src/main/java/com/nike/cerberus/validation/UniqueIamRolePermissionsV2.java b/src/main/java/com/nike/cerberus/validation/UniqueIamRolePermissionsV2.java new file mode 100644 index 000000000..da0d997bc --- /dev/null +++ b/src/main/java/com/nike/cerberus/validation/UniqueIamRolePermissionsV2.java @@ -0,0 +1,45 @@ +/* + * 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.validation; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Validation annotation to ensure a set of user group permission objects represent unique groups. + */ +@Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) +@Retention(RUNTIME) +@Constraint(validatedBy = IamRolePermissionsValidatorV2.class) +@Documented +public @interface UniqueIamRolePermissionsV2 { + String message() default "SDB_IAM_ROLE_REPEATED"; + + Class[] groups() default { }; + + Class[] payload() default { }; +} diff --git a/src/main/resources/com/nike/cerberus/mapper/AwsIamRoleMapper.xml b/src/main/resources/com/nike/cerberus/mapper/AwsIamRoleMapper.xml index fb75b2221..76aefde56 100644 --- a/src/main/resources/com/nike/cerberus/mapper/AwsIamRoleMapper.xml +++ b/src/main/resources/com/nike/cerberus/mapper/AwsIamRoleMapper.xml @@ -92,8 +92,6 @@ INSERT INTO AWS_IAM_ROLE ( ID, - AWS_ACCOUNT_ID, - AWS_IAM_ROLE_NAME, AWS_IAM_ROLE_ARN, CREATED_BY, LAST_UPDATED_BY, @@ -102,8 +100,6 @@ ) VALUES ( #{record.id}, - #{record.awsAccountId}, - #{record.awsIamRoleName}, #{record.awsIamRoleArn}, #{record.createdBy}, #{record.lastUpdatedBy}, diff --git a/src/main/resources/com/nike/cerberus/migration/V1.2.0.0__remove_account_id_and_iam_role_name_fields.sql b/src/main/resources/com/nike/cerberus/migration/V1.2.0.0__remove_account_id_and_iam_role_name_fields.sql new file mode 100644 index 000000000..7004237f1 --- /dev/null +++ b/src/main/resources/com/nike/cerberus/migration/V1.2.0.0__remove_account_id_and_iam_role_name_fields.sql @@ -0,0 +1,3 @@ +ALTER TABLE AWS_IAM_ROLE + DROP COLUMN AWS_IAM_ROLE_NAME, + DROP COLUMN AWS_ACCOUNT_ID; \ No newline at end of file diff --git a/src/test/java/com/nike/cerberus/IamRolePermissionV2Test.java b/src/test/java/com/nike/cerberus/IamRolePermissionV2Test.java new file mode 100644 index 000000000..0a0635179 --- /dev/null +++ b/src/test/java/com/nike/cerberus/IamRolePermissionV2Test.java @@ -0,0 +1,69 @@ +/* + * 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; + +import com.nike.cerberus.domain.IamRolePermissionV2; +import org.junit.Test; + +import javax.validation.Validation; +import javax.validation.Validator; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Tests the IamRolePermissionV2 class + */ +public class IamRolePermissionV2Test { + + private Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); + + @Test + public void test_that_IamRolePermissionV2_can_be_constructed_with_a_user_iam_principal_arn() { + + assertTrue(validator.validate( + new IamRolePermissionV2().withIamPrincipalArn("arn:aws:iam::123456789012:user/Bob").withRoleId("role id")).isEmpty()); + + } + + @Test + public void test_that_IamRolePermissionV2_fails_with_invalid_iam_principal_arn() { + + assertFalse(validator.validate( + new IamRolePermissionV2().withIamPrincipalArn("arn:aws:foo::123456789012:user/Bob").withRoleId("role id")).isEmpty()); + + } + + @Test + public void test_that_IamRolePermissionV2_can_be_constructed_with_a_federated_user_iam_principal_arn() { + + assertTrue(validator.validate( + new IamRolePermissionV2().withIamPrincipalArn("arn:aws:sts::123456789012:federated-user/Bob").withRoleId("role id")).isEmpty()); + + } + + @Test + public void test_that_IamRolePermissionV2_can_be_constructed_with_a_assumed_role_iam_principal_arn() { + + assertTrue(validator.validate( + new IamRolePermissionV2().withIamPrincipalArn("arn:aws:sts::123456789012:assumed-role/Accounting-Role/Mary").withRoleId("role id")).isEmpty()); + + } + + +} \ No newline at end of file diff --git a/src/test/java/com/nike/cerberus/dao/AwsIamRoleDaoTest.java b/src/test/java/com/nike/cerberus/dao/AwsIamRoleDaoTest.java index 260b4b1cb..50d5052af 100644 --- a/src/test/java/com/nike/cerberus/dao/AwsIamRoleDaoTest.java +++ b/src/test/java/com/nike/cerberus/dao/AwsIamRoleDaoTest.java @@ -21,7 +21,6 @@ import com.nike.cerberus.record.AwsIamRoleKmsKeyRecord; import com.nike.cerberus.record.AwsIamRolePermissionRecord; import com.nike.cerberus.record.AwsIamRoleRecord; -import com.nike.cerberus.util.AwsIamRoleArnParser; import org.junit.Before; import org.junit.Test; @@ -128,7 +127,7 @@ public void getIamRole_by_id_returns_empty_when_record_not_found() { @Test public void getIamRole_returns_record_when_found() { - String arn = String.format(AwsIamRoleArnParser.AWS_IAM_ROLE_ARN_TEMPLATE, awsAccountId, awsIamRoleName); + String arn = "arn"; when(awsIamRoleMapper.getIamRole(arn)).thenReturn(awsIamRoleRecord); final Optional actual = subject.getIamRole(arn); @@ -158,7 +157,7 @@ public void getIamRole_with_arn_returns_record_when_found() { @Test public void getIamRole_with_arn_returns_empty_when_record_not_found() { - String arn = String.format(AwsIamRoleArnParser.AWS_IAM_ROLE_ARN_TEMPLATE, awsAccountId, awsIamRoleName); + String arn = "arn"; when(awsIamRoleMapper.getIamRole(arn)).thenReturn(null); final Optional actual = subject.getIamRole(arn); diff --git a/src/test/java/com/nike/cerberus/dao/SafeDepositBoxDaoTest.java b/src/test/java/com/nike/cerberus/dao/SafeDepositBoxV1DaoTest.java similarity index 94% rename from src/test/java/com/nike/cerberus/dao/SafeDepositBoxDaoTest.java rename to src/test/java/com/nike/cerberus/dao/SafeDepositBoxV1DaoTest.java index 1607b9567..7ec71705b 100644 --- a/src/test/java/com/nike/cerberus/dao/SafeDepositBoxDaoTest.java +++ b/src/test/java/com/nike/cerberus/dao/SafeDepositBoxV1DaoTest.java @@ -21,7 +21,6 @@ import com.nike.cerberus.mapper.SafeDepositBoxMapper; import com.nike.cerberus.record.SafeDepositBoxRecord; import com.nike.cerberus.record.SafeDepositBoxRoleRecord; -import com.nike.cerberus.util.AwsIamRoleArnParser; import org.junit.Before; import org.junit.Test; @@ -35,7 +34,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -public class SafeDepositBoxDaoTest { +public class SafeDepositBoxV1DaoTest { private final String safeDepositBoxId = "SDB_ID"; @@ -61,9 +60,7 @@ public class SafeDepositBoxDaoTest { private final Set userGroupSet = Sets.newHashSet(userGroup); - private final String awsAccountId = "AWS_ACCOUNT_ID"; - - private final String awsIamRoleName = "AWS_IAM_ROLE_NAME"; + private final String awsIamPrincipalArn = "AWS_IAM_PRINCIPAL_ARN"; private final SafeDepositBoxRecord safeDepositBoxRecord = new SafeDepositBoxRecord() .setId(safeDepositBoxId) @@ -108,13 +105,11 @@ public void getUserAssociatedSafeDepositBoxRoles_returns_list_of_role_records() @Test public void getIamRoleAssociatedSafeDepositBoxRoles_returns_list_of_role_records() { - when(safeDepositBoxMapper.getIamRoleAssociatedSafeDepositBoxRoles(String.format(AwsIamRoleArnParser.AWS_IAM_ROLE_ARN_TEMPLATE, - awsAccountId, awsIamRoleName))) + when(safeDepositBoxMapper.getIamRoleAssociatedSafeDepositBoxRoles(awsIamPrincipalArn)) .thenReturn(safeDepositBoxRoleRecordList); List actual = - subject.getIamRoleAssociatedSafeDepositBoxRoles( - String.format(AwsIamRoleArnParser.AWS_IAM_ROLE_ARN_TEMPLATE, awsAccountId, awsIamRoleName)); + subject.getIamRoleAssociatedSafeDepositBoxRoles(awsIamPrincipalArn); assertThat(actual).isNotEmpty(); assertThat(actual).hasSameElementsAs(safeDepositBoxRoleRecordList); diff --git a/src/test/java/com/nike/cerberus/domain/DomainPojoTest.java b/src/test/java/com/nike/cerberus/domain/DomainPojoTest.java index 3d3dabce4..db869ca48 100644 --- a/src/test/java/com/nike/cerberus/domain/DomainPojoTest.java +++ b/src/test/java/com/nike/cerberus/domain/DomainPojoTest.java @@ -20,7 +20,7 @@ public void test_pojo_structure_and_behavior() { List pojoClasses = PojoClassFactory.getPojoClasses("com.nike.cerberus.domain"); - Assert.assertEquals(15, pojoClasses.size()); + Assert.assertEquals(18, pojoClasses.size()); Validator validator = ValidatorBuilder.create() .with(new GetterMustExistRule()) diff --git a/src/test/java/com/nike/cerberus/service/AuthenticationServiceTest.java b/src/test/java/com/nike/cerberus/service/AuthenticationServiceTest.java new file mode 100644 index 000000000..bc2fb6d31 --- /dev/null +++ b/src/test/java/com/nike/cerberus/service/AuthenticationServiceTest.java @@ -0,0 +1,108 @@ +/* + * 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.fasterxml.jackson.databind.ObjectMapper; +import com.nike.cerberus.auth.connector.AuthConnector; +import com.nike.cerberus.aws.KmsClientFactory; +import com.nike.cerberus.dao.AwsIamRoleDao; +import com.nike.cerberus.dao.SafeDepositBoxDao; +import com.nike.cerberus.security.VaultAuthPrincipal; +import com.nike.cerberus.util.AwsIamRoleArnParser; +import com.nike.cerberus.util.DateTimeSupplier; +import com.nike.vault.client.VaultAdminClient; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +/** + * Tests the AuthenticationService class + */ +public class AuthenticationServiceTest { + + @Mock + private SafeDepositBoxDao safeDepositBoxDao; + + @Mock + private AwsIamRoleDao awsIamRoleDao; + + @Mock + private AuthConnector authConnector; + + @Mock + private KmsService kmsService; + + @Mock + private KmsClientFactory kmsClientFactory; + + @Mock + private VaultAdminClient vaultAdminClient; + + @Mock + private VaultPolicyService vaultPolicyService; + + @Mock + private ObjectMapper objectMapper; + +// @Named(ADMIN_GROUP_PROPERTY) final String adminGroup, + + @Mock + private DateTimeSupplier dateTimeSupplier; + + @Mock + private AwsIamRoleArnParser awsIamRoleArnParser; + + @InjectMocks + private AuthenticationService authenticationService; + + @Before + public void setup() { + + initMocks(this); + } + + @Test + public void tests_that_generateCommonVaultPrincipalAuthMetadata_contains_expected_fields() { + + String principalArn = "principal arn"; + String region = "region"; + + Map result = authenticationService.generateCommonVaultPrincipalAuthMetadata(principalArn, region); + + assertTrue(result.containsKey(VaultAuthPrincipal.METADATA_KEY_USERNAME)); + assertEquals(principalArn, result.get(VaultAuthPrincipal.METADATA_KEY_USERNAME)); + + assertTrue(result.containsKey(VaultAuthPrincipal.METADATA_KEY_AWS_REGION)); + assertEquals(region, result.get(VaultAuthPrincipal.METADATA_KEY_AWS_REGION)); + + assertTrue(result.containsKey(VaultAuthPrincipal.METADATA_KEY_GROUPS)); + + assertTrue(result.containsKey(VaultAuthPrincipal.METADATA_KEY_IS_ADMIN)); + } + +} \ No newline at end of file diff --git a/src/test/java/com/nike/cerberus/service/KmsPolicyServiceTest.java b/src/test/java/com/nike/cerberus/service/KmsPolicyServiceTest.java index 8aba04967..cc3d35efa 100644 --- a/src/test/java/com/nike/cerberus/service/KmsPolicyServiceTest.java +++ b/src/test/java/com/nike/cerberus/service/KmsPolicyServiceTest.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.nike.cerberus.util.AwsIamRoleArnParser; import org.apache.commons.io.IOUtils; import org.junit.Before; import org.junit.Test; @@ -21,11 +20,7 @@ */ public class KmsPolicyServiceTest { - private static final String CERBERUS_CONSUMER_ACCOUNT_ID = "1234567890"; - private static final String CERBERUS_CONSUMER_ROLE_NAME = "cerberus-consumer"; - private static final String CERBERUS_CONSUMER_IAM_ROLE_ARN = - String.format(AwsIamRoleArnParser.AWS_IAM_ROLE_ARN_TEMPLATE, - CERBERUS_CONSUMER_ACCOUNT_ID, CERBERUS_CONSUMER_ROLE_NAME); + private static final String CERBERUS_CONSUMER_IAM_ROLE_ARN = "arn:aws:iam::1234567890:role/cerberus-consumer"; private KmsPolicyService kmsPolicyService; private ObjectMapper objectMapper; @@ -48,8 +43,7 @@ public void test_generateStandardKmsPolicy() throws IOException { String minifiedPolicyJsonAsString = expectedPolicy.toString(); // invoke method under test - String actualPolicyJsonAsString = kmsPolicyService.generateStandardKmsPolicy(CERBERUS_CONSUMER_ACCOUNT_ID, - CERBERUS_CONSUMER_ROLE_NAME); + String actualPolicyJsonAsString = kmsPolicyService.generateStandardKmsPolicy(CERBERUS_CONSUMER_IAM_ROLE_ARN); assertEquals(minifiedPolicyJsonAsString, actualPolicyJsonAsString); } diff --git a/src/test/java/com/nike/cerberus/service/MetadataServiceTest.java b/src/test/java/com/nike/cerberus/service/MetadataServiceTest.java index b34a9cade..60a239bd0 100644 --- a/src/test/java/com/nike/cerberus/service/MetadataServiceTest.java +++ b/src/test/java/com/nike/cerberus/service/MetadataServiceTest.java @@ -17,15 +17,14 @@ package com.nike.cerberus.service; import com.fasterxml.jackson.databind.ObjectMapper; -import com.nike.cerberus.domain.IamRolePermission; +import com.nike.cerberus.domain.IamRolePermissionV2; import com.nike.cerberus.domain.Role; import com.nike.cerberus.domain.SDBMetadata; import com.nike.cerberus.domain.SDBMetadataResult; -import com.nike.cerberus.domain.SafeDepositBox; +import com.nike.cerberus.domain.SafeDepositBoxV2; import com.nike.cerberus.domain.UserGroupPermission; import com.nike.cerberus.record.RoleRecord; import com.nike.cerberus.server.config.CmsConfig; -import com.nike.cerberus.util.AwsIamRoleArnParser; import com.nike.cerberus.util.UuidSupplier; import org.junit.Before; import org.junit.Test; @@ -44,8 +43,6 @@ import java.util.Set; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -72,9 +69,6 @@ public class MetadataServiceTest { @Mock private UuidSupplier uuidSupplier; - @Mock - private AwsIamRoleArnParser awsIamRoleArnParser; - @Before public void before() { initMocks(this); @@ -137,8 +131,7 @@ public void test_that_get_sdb_metadata_list_returns_valid_list() { String grumpyBearsId = "111-def"; String ownerId = "000"; String readId = "111"; - String acctId = "12345"; - String roleName = "foo-role"; + String arn = "arn:aws:iam::12345:role/foo-role"; OffsetDateTime offsetDateTime = OffsetDateTime.now(); @@ -152,7 +145,7 @@ public void test_that_get_sdb_metadata_list_returns_valid_list() { when(roleService.getRoleIdToStringMap()).thenReturn(roleIdToStringMap); when(categoryService.getCategoryIdToCategoryNameMap()).thenReturn(catMap); - SafeDepositBox box = new SafeDepositBox(); + SafeDepositBoxV2 box = new SafeDepositBoxV2(); box.setId(sdbId); box.setName(name); box.setPath(path); @@ -168,8 +161,8 @@ public void test_that_get_sdb_metadata_list_returns_valid_list() { userPerms.add(new UserGroupPermission().withName(grumpyBearsGroup).withRoleId(readId)); box.setUserGroupPermissions(userPerms); - Set iamPerms = new HashSet<>(); - iamPerms.add(new IamRolePermission().withAccountId(acctId).withIamRoleName(roleName).withRoleId(readId)); + Set iamPerms = new HashSet<>(); + iamPerms.add(new IamRolePermissionV2().withIamPrincipalArn(arn).withRoleId(readId)); box.setIamRolePermissions(iamPerms); when(safeDepositBoxService.getSafeDepositBoxes(1,0)).thenReturn(Arrays.asList(box)); @@ -187,7 +180,7 @@ public void test_that_get_sdb_metadata_list_returns_valid_list() { assertEquals("updated ts should match record", offsetDateTime, data.getLastUpdatedTs()); Map expectedIamPermMap = new HashMap<>(); - expectedIamPermMap.put(String.format(AwsIamRoleArnParser.AWS_IAM_ROLE_ARN_TEMPLATE, acctId, roleName), RoleRecord.ROLE_READ); + expectedIamPermMap.put(arn, RoleRecord.ROLE_READ); assertEquals("iam role perm map should match what is returned by getIamRolePermissionMap", expectedIamPermMap, data.getIamRolePermissions()); @@ -218,12 +211,10 @@ public void test_that_restore_metadata_calls_the_sdb_service_with_expected_sdb_b Role readRole = new Role(); readRole.setId(readId); when(roleService.getRoleByName(RoleRecord.ROLE_READ)).thenReturn(Optional.of(readRole)); - when(awsIamRoleArnParser.getAccountId(anyString())).thenCallRealMethod(); - when(awsIamRoleArnParser.getRoleName(anyString())).thenCallRealMethod(); metadataService.restoreMetadata(sdbMetadata, user); - SafeDepositBox expectedSdb = new SafeDepositBox(); + SafeDepositBoxV2 expectedSdb = new SafeDepositBoxV2(); expectedSdb.setId(id); expectedSdb.setPath("app/health-check-bucket/"); expectedSdb.setCategoryId(categoryId); @@ -240,9 +231,9 @@ public void test_that_restore_metadata_calls_the_sdb_service_with_expected_sdb_b userPerms.add(new UserGroupPermission().withName("Lst-NIKE.FOO.ISL").withRoleId(readId)); expectedSdb.setUserGroupPermissions(userPerms); - Set iamPerms = new HashSet<>(); - String arn = String.format(AwsIamRoleArnParser.AWS_IAM_ROLE_ARN_TEMPLATE, "1111111111", "lambda_prod_healthcheck"); - iamPerms.add(new IamRolePermission().withAccountId("1111111111").withIamRoleName("lambda_prod_healthcheck").withIamRoleArn(arn).withRoleId(readId)); + Set iamPerms = new HashSet<>(); + String arn = "arn:aws:iam::1111111111:role/lambda_prod_healthcheck"; + iamPerms.add(new IamRolePermissionV2().withIamPrincipalArn(arn).withRoleId(readId)); expectedSdb.setIamRolePermissions(iamPerms); expectedSdb.setUserGroupPermissions(userPerms); diff --git a/src/test/java/com/nike/cerberus/service/SafeDepositBoxServiceTest.java b/src/test/java/com/nike/cerberus/service/SafeDepositBoxServiceTest.java index 28bef9748..402dc4dd9 100644 --- a/src/test/java/com/nike/cerberus/service/SafeDepositBoxServiceTest.java +++ b/src/test/java/com/nike/cerberus/service/SafeDepositBoxServiceTest.java @@ -18,8 +18,10 @@ 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.IamRolePermissionV1; +import com.nike.cerberus.domain.IamRolePermissionV2; +import com.nike.cerberus.domain.SafeDepositBoxV1; +import com.nike.cerberus.domain.SafeDepositBoxV2; import com.nike.cerberus.domain.UserGroupPermission; import com.nike.cerberus.record.SafeDepositBoxRecord; import com.nike.cerberus.util.AwsIamRoleArnParser; @@ -83,6 +85,9 @@ public class SafeDepositBoxServiceTest { @Mock private DateTimeSupplier dateTimeSupplier; + @Mock + private AwsIamRoleArnParser awsIamRoleArnParser; + @InjectMocks private SafeDepositBoxService safeDepositBoxService; @@ -102,7 +107,7 @@ public void test_that_restore_safe_deposit_box_creates_with_expected_sdb_record_ String readId = "333"; String sdbName = "HEALTH CHECK BUCKET"; - SafeDepositBox sdbObject = new SafeDepositBox(); + SafeDepositBoxV2 sdbObject = new SafeDepositBoxV2(); sdbObject.setId(id); sdbObject.setPath("app/health-check-bucket/"); sdbObject.setCategoryId(categoryId); @@ -119,8 +124,8 @@ public void test_that_restore_safe_deposit_box_creates_with_expected_sdb_record_ 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)); + Set iamPerms = new HashSet<>(); + iamPerms.add(new IamRolePermissionV2().withIamPrincipalArn("arn:aws:iam::1111111111:role/lambda_prod_healthcheck").withRoleId(readId)); sdbObject.setIamRolePermissions(iamPerms); sdbObject.setUserGroupPermissions(userPerms); @@ -153,7 +158,7 @@ public void test_that_restore_safe_deposit_box_updates_with_expected_sdb_record_ String sdbName = "HEALTH CHECK BUCKET"; String sdbId = "asdf-1231-23sad-asd"; - SafeDepositBox sdbObject = new SafeDepositBox(); + SafeDepositBoxV2 sdbObject = new SafeDepositBoxV2(); sdbObject.setId(id); sdbObject.setPath("app/health-check-bucket/"); sdbObject.setCategoryId(categoryId); @@ -170,8 +175,8 @@ public void test_that_restore_safe_deposit_box_updates_with_expected_sdb_record_ 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)); + Set iamPerms = new HashSet<>(); + iamPerms.add(new IamRolePermissionV2().withIamPrincipalArn("arn:aws:iam::1111111111:role/lambda_prod_healthcheck").withRoleId(readId)); sdbObject.setIamRolePermissions(iamPerms); sdbObject.setUserGroupPermissions(userPerms); @@ -194,7 +199,7 @@ public void test_that_restore_safe_deposit_box_updates_with_expected_sdb_record_ 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()); + doReturn(sdbObject).when(safeDepositBoxServiceSpy).getSDBFromRecordV2(any()); safeDepositBoxServiceSpy.restoreSafeDepositBox(sdbObject, "admin-user"); @@ -202,19 +207,130 @@ public void test_that_restore_safe_deposit_box_updates_with_expected_sdb_record_ } @Test - public void test_that_addIamRoleArnToPermissions_adds_arn_to_role_permissions() { - - String accountId = "account id"; - String roleName = "role name"; - IamRolePermission iamRolePermission = new IamRolePermission().withAccountId(accountId).withIamRoleName(roleName); - - String expectedArn = String.format(AwsIamRoleArnParser.AWS_IAM_ROLE_ARN_TEMPLATE, accountId, roleName); - IamRolePermission expectedPerm = new IamRolePermission().withAccountId(accountId).withIamRoleName(roleName).withIamRoleArn(expectedArn); - Set permissions = Sets.newHashSet(); - permissions.add(iamRolePermission); + public void test_that_convertSafeDepositBoxV1ToV2_creates_expected_safe_deposit_box_v2() { + + String id = "id"; + String name = "name"; + String description = "description"; + String path = "path"; + String categoryId = "category id"; + String createdBy = "created by"; + String lastUpdatedBy = "last updated by"; + OffsetDateTime createdTs = OffsetDateTime.now(); + OffsetDateTime lastUpdatedTs = OffsetDateTime.now(); + String owner = "owner"; + String accountId = "123"; + String roleName = "abc"; + String arn = "arn:aws:iam::123:role/abc"; + String roleId = "role id"; + + Set userGroupPermissions = Sets.newHashSet(); + UserGroupPermission userGroupPermission = new UserGroupPermission(); + userGroupPermissions.add(userGroupPermission); + + Set iamRolePermissions = Sets.newHashSet(); + IamRolePermissionV2 iamRolePermission = new IamRolePermissionV2().withIamPrincipalArn(arn).withRoleId(roleId); + iamRolePermissions.add(iamRolePermission); + + SafeDepositBoxV2 safeDepositBoxV2 = new SafeDepositBoxV2(); + safeDepositBoxV2.setId(id); + safeDepositBoxV2.setName(name); + safeDepositBoxV2.setDescription(description); + safeDepositBoxV2.setPath(path); + safeDepositBoxV2.setCategoryId(categoryId); + safeDepositBoxV2.setCreatedBy(createdBy); + safeDepositBoxV2.setLastUpdatedBy(lastUpdatedBy); + safeDepositBoxV2.setCreatedTs(createdTs); + safeDepositBoxV2.setLastUpdatedTs(lastUpdatedTs); + safeDepositBoxV2.setOwner(owner); + safeDepositBoxV2.setUserGroupPermissions(userGroupPermissions); + safeDepositBoxV2.setIamRolePermissions(iamRolePermissions); + + when(awsIamRoleArnParser.getAccountId(arn)).thenReturn(accountId); + when(awsIamRoleArnParser.getRoleName(arn)).thenReturn(roleName); + + SafeDepositBoxV1 resultantSDBV1 = safeDepositBoxService.convertSafeDepositBoxV2ToV1(safeDepositBoxV2); + + SafeDepositBoxV1 expectedSdbV1 = new SafeDepositBoxV1(); + expectedSdbV1.setId(id); + expectedSdbV1.setName(name); + expectedSdbV1.setDescription(description); + expectedSdbV1.setPath(path); + expectedSdbV1.setCategoryId(categoryId); + expectedSdbV1.setCreatedBy(createdBy); + expectedSdbV1.setLastUpdatedBy(lastUpdatedBy); + expectedSdbV1.setCreatedTs(createdTs); + expectedSdbV1.setLastUpdatedTs(lastUpdatedTs); + expectedSdbV1.setOwner(owner); + expectedSdbV1.setUserGroupPermissions(userGroupPermissions); + Set expectedIamRolePermissionsV1 = Sets.newHashSet(); + IamRolePermissionV1 expectedIamRolePermissionV1 = new IamRolePermissionV1().withAccountId(accountId).withIamRoleName(roleName).withRoleId(roleId); + expectedIamRolePermissionsV1.add(expectedIamRolePermissionV1); + expectedSdbV1.setIamRolePermissions(expectedIamRolePermissionsV1); + + assertEquals(expectedSdbV1, resultantSDBV1); + } - Set result = safeDepositBoxServiceSpy.addIamRoleArnToPermissions(permissions); - assertEquals(expectedPerm, result.toArray()[0]); + @Test + public void test_that_convertSafeDepositBoxV2ToV1_creates_expected_safe_deposit_box_v1() { + + String id = "id"; + String name = "name"; + String description = "description"; + String path = "path"; + String categoryId = "category id"; + String createdBy = "created by"; + String lastUpdatedBy = "last updated by"; + OffsetDateTime createdTs = OffsetDateTime.now(); + OffsetDateTime lastUpdatedTs = OffsetDateTime.now(); + String owner = "owner"; + String accountId = "123"; + String roleName = "abc"; + String arn = "arn:aws:iam::123:role/abc"; + String roleId = "role id"; + + Set userGroupPermissions = Sets.newHashSet(); + UserGroupPermission userGroupPermission = new UserGroupPermission(); + userGroupPermissions.add(userGroupPermission); + + Set iamRolePermissions = Sets.newHashSet(); + IamRolePermissionV1 iamRolePermission = new IamRolePermissionV1().withAccountId(accountId).withIamRoleName(roleName).withRoleId(roleId); + iamRolePermissions.add(iamRolePermission); + + SafeDepositBoxV1 safeDepositBoxV1 = new SafeDepositBoxV1(); + safeDepositBoxV1.setId(id); + safeDepositBoxV1.setName(name); + safeDepositBoxV1.setDescription(description); + safeDepositBoxV1.setPath(path); + safeDepositBoxV1.setCategoryId(categoryId); + safeDepositBoxV1.setCreatedBy(createdBy); + safeDepositBoxV1.setLastUpdatedBy(lastUpdatedBy); + safeDepositBoxV1.setCreatedTs(createdTs); + safeDepositBoxV1.setLastUpdatedTs(lastUpdatedTs); + safeDepositBoxV1.setOwner(owner); + safeDepositBoxV1.setUserGroupPermissions(userGroupPermissions); + safeDepositBoxV1.setIamRolePermissions(iamRolePermissions); + + SafeDepositBoxV2 resultantSDBV1 = safeDepositBoxService.convertSafeDepositBoxV1ToV2(safeDepositBoxV1); + + SafeDepositBoxV2 expectedSdbV2 = new SafeDepositBoxV2(); + expectedSdbV2.setId(id); + expectedSdbV2.setName(name); + expectedSdbV2.setDescription(description); + expectedSdbV2.setPath(path); + expectedSdbV2.setCategoryId(categoryId); + expectedSdbV2.setCreatedBy(createdBy); + expectedSdbV2.setLastUpdatedBy(lastUpdatedBy); + expectedSdbV2.setCreatedTs(createdTs); + expectedSdbV2.setLastUpdatedTs(lastUpdatedTs); + expectedSdbV2.setOwner(owner); + expectedSdbV2.setUserGroupPermissions(userGroupPermissions); + Set expectedIamRolePermissionsV2 = Sets.newHashSet(); + IamRolePermissionV2 expectedIamRolePermissionV2 = new IamRolePermissionV2().withIamPrincipalArn(arn).withRoleId(roleId); + expectedIamRolePermissionsV2.add(expectedIamRolePermissionV2); + expectedSdbV2.setIamRolePermissions(expectedIamRolePermissionsV2); + + assertEquals(expectedSdbV2, resultantSDBV1); } } diff --git a/src/test/java/com/nike/cerberus/validation/IamRolePermissionsValidatorTest.java b/src/test/java/com/nike/cerberus/validation/IamRolePermissionsValidatorV1Test.java similarity index 80% rename from src/test/java/com/nike/cerberus/validation/IamRolePermissionsValidatorTest.java rename to src/test/java/com/nike/cerberus/validation/IamRolePermissionsValidatorV1Test.java index 259798db3..fe4b854bd 100644 --- a/src/test/java/com/nike/cerberus/validation/IamRolePermissionsValidatorTest.java +++ b/src/test/java/com/nike/cerberus/validation/IamRolePermissionsValidatorV1Test.java @@ -16,7 +16,7 @@ package com.nike.cerberus.validation; -import com.nike.cerberus.domain.IamRolePermission; +import com.nike.cerberus.domain.IamRolePermissionV1; import org.junit.Before; import org.junit.Test; import org.mockito.internal.util.collections.Sets; @@ -28,18 +28,18 @@ import static org.mockito.Mockito.mock; /** - * Tests the IamRolePermissionsValidator class + * Tests the IamRolePermissionsValidatorV1 class */ -public class IamRolePermissionsValidatorTest { +public class IamRolePermissionsValidatorV1Test { private ConstraintValidatorContext mockConstraintValidatorContext; - private IamRolePermissionsValidator subject; + private IamRolePermissionsValidatorV1 subject; @Before public void setup() { mockConstraintValidatorContext = mock(ConstraintValidatorContext.class); - subject = new IamRolePermissionsValidator(); + subject = new IamRolePermissionsValidatorV1(); } @Test @@ -54,10 +54,10 @@ public void empty_set_is_valid() { @Test public void unique_set_is_valid() { - IamRolePermission a = new IamRolePermission(); + IamRolePermissionV1 a = new IamRolePermissionV1(); a.setAccountId("123"); a.setIamRoleName("abc"); - IamRolePermission b = new IamRolePermission(); + IamRolePermissionV1 b = new IamRolePermissionV1(); b.setAccountId("123"); b.setIamRoleName("def"); @@ -66,10 +66,10 @@ public void unique_set_is_valid() { @Test public void duplicate_set_is_invalid() { - IamRolePermission a = new IamRolePermission(); + IamRolePermissionV1 a = new IamRolePermissionV1(); a.setAccountId("123"); a.setIamRoleName("abc"); - IamRolePermission b = new IamRolePermission(); + IamRolePermissionV1 b = new IamRolePermissionV1(); b.setAccountId("123"); b.setIamRoleName("ABC"); diff --git a/src/test/java/com/nike/cerberus/validation/IamRolePermissionsValidatorV2Test.java b/src/test/java/com/nike/cerberus/validation/IamRolePermissionsValidatorV2Test.java new file mode 100644 index 000000000..d498f879c --- /dev/null +++ b/src/test/java/com/nike/cerberus/validation/IamRolePermissionsValidatorV2Test.java @@ -0,0 +1,77 @@ +/* + * 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.validation; + +import com.nike.cerberus.domain.IamRolePermissionV2; +import org.junit.Before; +import org.junit.Test; +import org.mockito.internal.util.collections.Sets; + +import javax.validation.ConstraintValidatorContext; +import java.util.HashSet; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Tests the IamRolePermissionsValidatorV1 class + */ +public class IamRolePermissionsValidatorV2Test { + + private ConstraintValidatorContext mockConstraintValidatorContext; + + private IamRolePermissionsValidatorV2 subject; + + @Before + public void setup() { + mockConstraintValidatorContext = mock(ConstraintValidatorContext.class); + subject = new IamRolePermissionsValidatorV2(); + } + + @Test + public void null_set_is_valid() { + assertThat(subject.isValid(null, mockConstraintValidatorContext)).isTrue(); + } + + @Test + public void empty_set_is_valid() { + assertThat(subject.isValid(new HashSet<>(), mockConstraintValidatorContext)).isTrue(); + } + + @Test + public void unique_set_is_valid() { + + IamRolePermissionV2 a = new IamRolePermissionV2(); + a.withIamPrincipalArn("arn:aws:iam::123:role/abc"); + IamRolePermissionV2 b = new IamRolePermissionV2(); + b.withIamPrincipalArn("arn:aws:iam::123:role/def"); + + assertThat(subject.isValid(Sets.newSet(a, b), mockConstraintValidatorContext)).isTrue(); + } + + @Test + public void duplicate_set_is_invalid() { + + IamRolePermissionV2 a = new IamRolePermissionV2(); + a.withIamPrincipalArn("arn:aws:iam::123:role/abc"); + IamRolePermissionV2 b = new IamRolePermissionV2(); + b.withIamPrincipalArn("arn:aws:iam::123:role/ABC"); + + assertThat(subject.isValid(Sets.newSet(a, b), mockConstraintValidatorContext)).isFalse(); + } +} \ No newline at end of file diff --git a/src/test/java/com/nike/cerberus/validation/UniqueOwnerValidatorTest.java b/src/test/java/com/nike/cerberus/validation/UniqueOwnerValidatorTest.java index 27da96100..330654d1b 100644 --- a/src/test/java/com/nike/cerberus/validation/UniqueOwnerValidatorTest.java +++ b/src/test/java/com/nike/cerberus/validation/UniqueOwnerValidatorTest.java @@ -16,7 +16,8 @@ package com.nike.cerberus.validation; -import com.nike.cerberus.domain.SafeDepositBox; +import com.nike.cerberus.domain.SafeDepositBoxV1; +import com.nike.cerberus.domain.SafeDepositBoxV2; import com.nike.cerberus.domain.UserGroupPermission; import org.junit.Before; import org.junit.Test; @@ -43,37 +44,55 @@ public void setup() { @Test public void blank_owner_returns_valid() { - SafeDepositBox safeDepositBox = new SafeDepositBox(); - safeDepositBox.setOwner(" "); - assertThat(subject.isValid(safeDepositBox, mockConstraintValidatorContext)).isTrue(); + SafeDepositBoxV1 safeDepositBox1 = new SafeDepositBoxV1(); + safeDepositBox1.setOwner(" "); + SafeDepositBoxV2 safeDepositBox2 = new SafeDepositBoxV2(); + safeDepositBox2.setOwner(" "); + assertThat(subject.isValid(safeDepositBox1, mockConstraintValidatorContext)).isTrue(); + assertThat(subject.isValid(safeDepositBox2, mockConstraintValidatorContext)).isTrue(); } @Test public void empty_user_group_set_is_valid() { - SafeDepositBox safeDepositBox = new SafeDepositBox(); - safeDepositBox.setOwner("owner"); - assertThat(subject.isValid(safeDepositBox, mockConstraintValidatorContext)).isTrue(); + SafeDepositBoxV1 safeDepositBox1 = new SafeDepositBoxV1(); + safeDepositBox1.setOwner("owner"); + SafeDepositBoxV2 safeDepositBox2 = new SafeDepositBoxV2(); + safeDepositBox2.setOwner("owner"); + assertThat(subject.isValid(safeDepositBox1, mockConstraintValidatorContext)).isTrue(); + assertThat(subject.isValid(safeDepositBox2, mockConstraintValidatorContext)).isTrue(); } @Test public void unique_owner_is_valid() { UserGroupPermission userGroupPermission = new UserGroupPermission(); userGroupPermission.setName("group"); - SafeDepositBox safeDepositBox = new SafeDepositBox(); - safeDepositBox.setOwner("owner"); - safeDepositBox.getUserGroupPermissions().add(userGroupPermission); - assertThat(subject.isValid(safeDepositBox, mockConstraintValidatorContext)).isTrue(); + SafeDepositBoxV1 safeDepositBox1 = new SafeDepositBoxV1(); + safeDepositBox1.setOwner("owner"); + safeDepositBox1.getUserGroupPermissions().add(userGroupPermission); + + SafeDepositBoxV2 safeDepositBox2 = new SafeDepositBoxV2(); + safeDepositBox2.setOwner("owner"); + safeDepositBox2.getUserGroupPermissions().add(userGroupPermission); + + assertThat(subject.isValid(safeDepositBox1, mockConstraintValidatorContext)).isTrue(); + assertThat(subject.isValid(safeDepositBox2, mockConstraintValidatorContext)).isTrue(); } @Test public void owner_in_group_permissions_is_invalid() { UserGroupPermission userGroupPermission = new UserGroupPermission(); userGroupPermission.setName("owner"); - SafeDepositBox safeDepositBox = new SafeDepositBox(); - safeDepositBox.setOwner("owner"); - safeDepositBox.getUserGroupPermissions().add(userGroupPermission); - assertThat(subject.isValid(safeDepositBox, mockConstraintValidatorContext)).isFalse(); + SafeDepositBoxV1 safeDepositBox1 = new SafeDepositBoxV1(); + safeDepositBox1.setOwner("owner"); + safeDepositBox1.getUserGroupPermissions().add(userGroupPermission); + + SafeDepositBoxV2 safeDepositBox2 = new SafeDepositBoxV2(); + safeDepositBox2.setOwner("owner"); + safeDepositBox2.getUserGroupPermissions().add(userGroupPermission); + + assertThat(subject.isValid(safeDepositBox1, mockConstraintValidatorContext)).isFalse(); + assertThat(subject.isValid(safeDepositBox2, mockConstraintValidatorContext)).isFalse(); } } \ No newline at end of file