Skip to content

Commit

Permalink
Fix tag handling to account for tagging permissions failures (#29)
Browse files Browse the repository at this point in the history
* Fixes for tests

* fix: add specific failure for tag based access denial

* Update to Java 17 for the build

* Dedupe Maven configuration

---------

Co-authored-by: Christopher Woolum <[email protected]>
Co-authored-by: Albert Winberg <[email protected]>
  • Loading branch information
3 people authored Sep 19, 2024
1 parent 796ed80 commit 702b929
Show file tree
Hide file tree
Showing 41 changed files with 1,748 additions and 1,300 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/java-ci-with-maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
- name: Set up JDK 17
uses: actions/setup-java@v1
with:
java-version: 11
java-version: 17
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
Expand Down
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1 @@
java 11
java 17
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"java.compile.nullAnalysis.mode": "disabled",
"java.configuration.updateBuildConfiguration": "interactive"
"java.configuration.updateBuildConfiguration": "automatic"
}
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

TODO: Fill this README out!

Be sure to:
1. Create a new S3 bucket in your account to store your test artifacts. The step functions that will be executing tests

* Change the title in this README
* Edit your repository description on GitHub
```shell
aws s3api create-bucket --bucket {bucketName} --region {bucketRegion} --create-bucket-configuration LocationConstraint={bucketRegion}
```

## Security

Expand Down
58 changes: 8 additions & 50 deletions aws-amplifyuibuilder-common/pom.xml
Original file line number Diff line number Diff line change
@@ -1,90 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>software.amazon.amplifyuibuilder</groupId>
<artifactId>aws-amplifyuibuilder-handlers</artifactId>
<version>1.0</version>
</parent>

<groupId>software.amazon.amplifyuibuilder.common</groupId>
<artifactId>aws-amplifyuibuilder-common</artifactId>
<name>aws-amplifyuibuilder-common</name>
<version>1.0</version>
<packaging>jar</packaging>

<properties>
<java.version>11</java.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId>
<version>2.17.201</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!--
https://mvnrepository.com/artifact/software.amazon.cloudformation/aws-cloudformation-rpdk-java-plugin -->
<dependency>
<groupId>software.amazon.cloudformation</groupId>
<artifactId>aws-cloudformation-rpdk-java-plugin</artifactId>
<version>[2.0.0,3.0.0)</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-collections4 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.12.2</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.5.0-M1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.6.0</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>3.6.0</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/software.amazon.awssdk/appsync -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>amplifyuibuilder</artifactId>
<version>2.20.81</version>
</dependency>
</dependencies>

Expand All @@ -93,26 +60,17 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<compilerArgs>
<arg>-Xlint:all,-options,-processing</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.4</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.6</version>
<executions>
<execution>
<goals>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ public static <RequestT extends AwsRequest, ResultT extends AwsResponse> AwsResp
if (e.statusCode() == HttpStatus.SC_NOT_FOUND) {
throw new CfnNotFoundException(resourceTypeName, e.getMessage());
}
if (e.statusCode() == HttpStatus.SC_FORBIDDEN) {
throw new CfnAccessDeniedException(resourceTypeName, e);
}
throw new CfnGeneralServiceException(resourceTypeName, e);
throw e;
} catch (AwsServiceException e) {
logger.log("ERROR: " + e.getMessage());
throw new CfnGeneralServiceException(e.getMessage(), e);
throw e;
} catch (Exception e) {
logger.log("GENERAL ERROR: " + e.getMessage());
throw e;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package software.amazon.amplifyuibuilder.common;

import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;

import software.amazon.awssdk.awscore.exception.AwsServiceException;
import software.amazon.awssdk.services.amplifyuibuilder.AmplifyUiBuilderClient;
import software.amazon.awssdk.services.amplifyuibuilder.model.TagResourceRequest;
import software.amazon.awssdk.services.amplifyuibuilder.model.UntagResourceRequest;
import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
import software.amazon.cloudformation.proxy.ProxyClient;
import software.amazon.cloudformation.proxy.Logger;

public class TaggingHelpers {
protected static final String ACCESS_DENIED_ERROR_CODE = "AccessDeniedException";
protected static final String ACESSS_DENIED_MESSAGE_MATCHER = "is not authorized to perform";
protected static final String[] TAGGING_PERMISSIONS = { "amplifyuibuilder:TagResource",
"amplifyuibuilder:UntagResource" };

public static final String SAMPLE_TAGGING_ACCESS_DENIED_MESSAGE = "An error occurred (AccessDeniedException) when calling the CreateCustomActionType operation: User: arn:aws:sts::0123456789:assumed-role/admin/someUser is not authorized to perform: amplifyuibuilder:TagResource on resource: arn:aws:amplifyuibuilder:us-west-2:0123456789:actiontype:Custom/Source/TestingCustomSource/2 with an explicit deny in an identity-based policy";

static String getErrorCode(Exception e) {
if (e instanceof AwsServiceException && ((AwsServiceException) e).awsErrorDetails() != null) {
return ((AwsServiceException) e).awsErrorDetails().errorCode();
}
return e.getMessage();
}

public static Boolean isTagBasedAccessDenied(final Exception e) {
String exceptionMessage = e.getMessage();
boolean isAccessDeniedException = ACCESS_DENIED_ERROR_CODE.equals(getErrorCode(e))
&& exceptionMessage != null
&& exceptionMessage.contains(ACESSS_DENIED_MESSAGE_MATCHER);

if (isAccessDeniedException) {
for (String permission : TAGGING_PERMISSIONS) {
if (e.getMessage().contains(permission)) {
return true;
}
}
}
return false;
}

public static String generateArn(final String region, final String accountId, final String appId, final String environmentName, final String resource, final String id) {
return String.format("arn:aws:amplifyuibuilder:%s:%s:app/%s/environment/%s/%s/%s",region, accountId, appId, environmentName, resource, id);
}

public static void updateTags(final AmazonWebServicesClientProxy proxy,
final ProxyClient<AmplifyUiBuilderClient> proxyClient,
final String appId,
final String resourceArn,
final String typeName,
final Map<String, String> existingTags,
final Map<String, String> desiredTags,
final Logger logger) {

Map<String, String> safeExistingTags = existingTags == null ? Map.of() : existingTags;
Map<String, String> safeDesiredTags = desiredTags == null ? Map.of() : desiredTags;

Map<String, String> tagsToAdd = safeDesiredTags.entrySet().stream()
.filter(entry -> !entry.getValue().equals(safeExistingTags.get(entry.getKey())))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

Map<String, String> tagsToRemove = safeExistingTags.entrySet().stream()
.filter(entry -> !safeDesiredTags.containsKey(entry.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

if (tagsToRemove.size() > 0) {
Collection<String> tagKeys = tagsToRemove.keySet();
final UntagResourceRequest untagResourceRequest = UntagResourceRequest.builder().resourceArn(resourceArn)
.tagKeys(tagKeys).build();
ClientWrapper.execute(proxy, untagResourceRequest, proxyClient.client()::untagResource, typeName,
appId, logger);
}

if (tagsToAdd.size() > 0) {
final TagResourceRequest tagResourceRequest = TagResourceRequest.builder()
.resourceArn(resourceArn).tags(tagsToAdd).build();
ClientWrapper.execute(proxy, tagResourceRequest, proxyClient.client()::tagResource, typeName,
appId, logger);
}
logger.log("INFO: Successfully Updated Tags");
}
}
2 changes: 1 addition & 1 deletion aws-amplifyuibuilder-component/.rpdk-config
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"artifact_type": "RESOURCE",
"typeName": "AWS::AmplifyUIBuilder::Component",
"language": "java",
"runtime": "java11",
"runtime": "java17",
"entrypoint": "software.amazon.amplifyuibuilder.component.HandlerWrapper::handleRequest",
"testEntrypoint": "software.amazon.amplifyuibuilder.component.HandlerWrapper::testEntrypoint",
"settings": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,11 @@
"tagOnCreate": true,
"tagUpdatable": true,
"cloudFormationSystemTags": true,
"tagProperty": "/properties/Tags"
"tagProperty": "/properties/Tags",
"permissions": [
"amplifyuibuilder:TagResource",
"amplifyuibuilder:UntagResource"
]
},
"sourceUrl": "https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-amplifyuibuilder",
"additionalProperties": false
Expand Down
Loading

0 comments on commit 702b929

Please sign in to comment.