Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix tag handling to account for tagging permissions failures #29

Merged
merged 5 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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}
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see this as a security risk, but maybe not a useful inclusion to have in the readme


## 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
Loading