Skip to content

Commit 10f553e

Browse files
committed
Fixing IAM policy limits for onboarding service
1 parent 81cef02 commit 10f553e

File tree

6 files changed

+553
-437
lines changed

6 files changed

+553
-437
lines changed

layers/cloudformation-utils/src/main/java/com/amazon/aws/partners/saasfactory/saasboost/CloudFormationResponse.java

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,67 +20,70 @@
2020
import org.slf4j.Logger;
2121
import org.slf4j.LoggerFactory;
2222

23-
import java.io.IOException;
24-
import java.io.OutputStreamWriter;
2523
import java.net.HttpURLConnection;
26-
import java.net.URL;
24+
import java.net.URI;
25+
import java.net.http.HttpClient;
26+
import java.net.http.HttpRequest;
27+
import java.net.http.HttpResponse;
2728
import java.nio.charset.StandardCharsets;
2829
import java.util.HashMap;
2930
import java.util.Map;
3031

3132
public class CloudFormationResponse {
3233

3334
private static final Logger LOGGER = LoggerFactory.getLogger(CloudFormationResponse.class);
35+
private static final HttpClient http = HttpClient.newBuilder().build();
3436

3537
private CloudFormationResponse() {
3638
}
3739

3840
public static void send(Map<String, Object> event, Context context, String responseStatus,
3941
Map<String, Object> responseData) {
40-
Map<String, Object> responseBody = buildResponseBody(event, context, responseStatus, responseData);
42+
send(event, context, responseStatus, responseData, false);
43+
}
44+
45+
public static void send(Map<String, Object> event, Context context, String responseStatus,
46+
Map<String, Object> responseData, boolean noEcho) {
47+
String responseBody = buildResponseBody(event, context, responseStatus, responseData, noEcho);
4148
String responseUrl = (String) event.get("ResponseURL");
49+
LOGGER.info("curl -H 'Content-Type: \"\"' -X PUT -d '" + responseBody + "' \"" + responseUrl + "\"");
50+
51+
HttpRequest request = HttpRequest.newBuilder()
52+
.uri(URI.create(responseUrl))
53+
.setHeader("Content-Type", "")
54+
.PUT(HttpRequest.BodyPublishers.ofString(responseBody, StandardCharsets.UTF_8))
55+
.build();
4256
try {
43-
URL url = new URL(responseUrl);
44-
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
45-
connection.setDoOutput(true);
46-
connection.setRequestProperty("Content-Type", "");
47-
connection.setRequestMethod("PUT");
48-
try (OutputStreamWriter response = new OutputStreamWriter(connection.getOutputStream(),
49-
StandardCharsets.UTF_8)) {
50-
response.write(Utils.toJson(responseBody));
51-
int responseCode = connection.getResponseCode();
52-
if (responseCode < 200 || responseCode > 299) {
53-
LOGGER.error("Response from CFN S3 signed URL failed {}", responseUrl);
54-
LOGGER.error("Response: {} {}", responseCode, connection.getResponseMessage());
55-
}
56-
} catch (IOException ioe) {
57-
LOGGER.error("Failed to complete HTTP request");
58-
LOGGER.error(Utils.getFullStackTrace(ioe));
57+
HttpResponse<String> response = http.send(request,
58+
HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
59+
if (HttpURLConnection.HTTP_OK != response.statusCode()) {
60+
LOGGER.error("Response from CFN S3 signed URL failed {} {}", response.statusCode(), response.body());
5961
}
60-
connection.disconnect();
61-
} catch (IOException e) {
62-
LOGGER.error("Failed to open connection to CFN response URL");
62+
} catch (Exception e) {
63+
LOGGER.error("Failed to complete HTTP request");
6364
LOGGER.error(Utils.getFullStackTrace(e));
6465
}
6566
}
6667

67-
protected static Map<String, Object> buildResponseBody(Map<String, Object> event, Context context,
68-
String responseStatus, Map<String, Object> responseData) {
68+
protected static String buildResponseBody(Map<String, Object> event, Context context, String responseStatus,
69+
Map<String, Object> responseData, boolean noEcho) {
6970
Map<String, Object> responseBody = new HashMap<>();
7071
responseBody.put("Status", responseStatus);
7172
responseBody.put("RequestId", event.get("RequestId"));
7273
responseBody.put("LogicalResourceId", event.get("LogicalResourceId"));
7374
responseBody.put("StackId", event.get("StackId"));
75+
responseBody.put("NoEcho", noEcho);
7476
// If the physical resource id changes between a CREATE and an UPDATE event, CloudFormation will DELETE
7577
// the previous physical resource. Usually this doesn't matter -- unless you're actually creating something
7678
// in your custom resource that you don't want deleted.
7779
responseBody.put("PhysicalResourceId", context.getAwsRequestId());
7880
if (!"FAILED".equals(responseStatus)) {
79-
responseBody.put("Data", responseData);
81+
if (responseData != null && !responseData.isEmpty()) {
82+
responseBody.put("Data", responseData);
83+
}
8084
} else {
8185
responseBody.put("Reason", responseData.get("Reason"));
8286
}
83-
LOGGER.info("Response Body: " + Utils.toJson(responseBody));
84-
return responseBody;
87+
return Utils.toJson(responseBody);
8588
}
8689
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#!/bin/bash
2+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License").
5+
# You may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
if [ -z $1 ]; then
17+
echo "Usage: $0 <Environment> [Lambda Folder]"
18+
exit 2
19+
fi
20+
21+
MY_AWS_REGION=$(aws configure list | grep region | awk '{print $2}')
22+
echo "AWS Region = $MY_AWS_REGION"
23+
24+
ENVIRONMENT=$1
25+
LAMBDA_STAGE_FOLDER=$2
26+
if [ -z $LAMBDA_STAGE_FOLDER ]; then
27+
LAMBDA_STAGE_FOLDER="lambdas"
28+
fi
29+
LAMBDA_CODE=CloudFormationUtils-lambda.zip
30+
LAYER_NAME="sb-${ENVIRONMENT}-cloudformation-utils"
31+
32+
#set this for V2 AWS CLI to disable paging
33+
export AWS_PAGER=""
34+
35+
SAAS_BOOST_BUCKET=$(aws --region $MY_AWS_REGION ssm get-parameter --name "/saas-boost/${ENVIRONMENT}/SAAS_BOOST_BUCKET" --query 'Parameter.Value' --output text)
36+
echo "SaaS Boost Bucket = $SAAS_BOOST_BUCKET"
37+
if [ -z $SAAS_BOOST_BUCKET ]; then
38+
echo "Can't find SAAS_BOOST_BUCKET in Parameter Store"
39+
exit 1
40+
fi
41+
42+
# Do a fresh build of the project
43+
mvn
44+
if [ $? -ne 0 ]; then
45+
echo "Error building project"
46+
exit 1
47+
fi
48+
49+
# And copy it up to S3
50+
aws s3 cp target/$LAMBDA_CODE s3://$SAAS_BOOST_BUCKET/$LAMBDA_STAGE_FOLDER/
51+
52+
# Publish a new version of the layer
53+
PUBLISHED_LAYER=$(aws --region $MY_AWS_REGION lambda publish-layer-version --layer-name "${LAYER_NAME}" --compatible-runtimes java11 --content S3Bucket="${SAAS_BOOST_BUCKET}",S3Key="${LAMBDA_STAGE_FOLDER}/${LAMBDA_CODE}")
54+
55+
# Use eval to deal with the backticks in the filter expression
56+
eval LAYER_VERSION_ARN=\$\("aws lambda list-layers --query 'Layers[?LayerName==\`${LAYER_NAME}\`].LatestMatchingVersion.LayerVersionArn' --output text"\)
57+
echo "Published new layer = $LAYER_VERSION_ARN"
58+
59+
# Find all the functions for this SaaS Boost environment that have layers
60+
eval FUNCTIONS=\$\("aws --region $MY_AWS_REGION lambda list-functions --query 'Functions[?starts_with(FunctionName, \`sb-${ENVIRONMENT}-\`)] | [?Layers != null] | [].FunctionName' --output text"\)
61+
FUNCTIONS=($FUNCTIONS)
62+
#echo "Updating ${#FUNCTIONS[@]} functions with new layer version"
63+
64+
for FX in ${FUNCTIONS[@]}; do
65+
# The order of the function's layers must be maintained. Iterate through this function's layers
66+
# and update this layer's ARN with the newly published version.
67+
FOUND=0
68+
LAYERS=""
69+
for LAYER_ARN in $(aws --region $MY_AWS_REGION lambda get-function --function-name $FX --query 'Configuration.Layers[].Arn' --output text); do
70+
if [[ $LAYER_ARN == *"${LAYER_NAME}"* ]]; then
71+
LAYER_ARN=$LAYER_VERSION_ARN
72+
FOUND=1
73+
fi
74+
if [ ${#LAYERS} -gt 0 ]; then
75+
LAYERS="${LAYERS} "
76+
fi
77+
LAYERS="${LAYERS}${LAYER_ARN}"
78+
done
79+
if (( $FOUND )); then
80+
eval "aws --region $MY_AWS_REGION lambda update-function-configuration --function-name $FX --layers $LAYERS"
81+
fi
82+
done

resources/custom-resources/cidr-dynamodb/src/main/java/com/amazon/aws/partners/saasfactory/saasboost/CidrDynamoDB.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public Object handleRequest(Map<String, Object> event, Context context) {
8787
f.get(context.getRemainingTimeInMillis() - 1000, TimeUnit.MILLISECONDS);
8888
} catch (final TimeoutException | InterruptedException | ExecutionException e) {
8989
// Timed out
90-
LOGGER.error("FAILED unexpected error or request timed out " + e.getMessage());
90+
LOGGER.error("FAILED unexpected error or request timed out", e);
9191
String stackTrace = Utils.getFullStackTrace(e);
9292
LOGGER.error(stackTrace);
9393
responseData.put("Reason", stackTrace);

resources/saas-boost-private-api.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,7 @@ Resources:
409409
Properties:
410410
RestApiId: !Ref PrivateApi
411411
ParentId: !Ref RootResourceId
412+
PathPart: 'onboarding'
412413
OnboardingServiceUpdateResource:
413414
Type: AWS::ApiGateway::Resource
414415
Properties:

resources/saas-boost-public-api.yaml

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,6 @@ Parameters:
5252
OnboardingServiceById:
5353
Description: Onboarding Service get onboarding request by id Lambda ARN
5454
Type: String
55-
OnboardingServiceUpdateStatus:
56-
Description: Onboarding Service update status Lambda ARN
57-
Type: String
5855
SettingsServiceGetAll:
5956
Description: Settings Service get all settings Lambda ARN
6057
Type: String
@@ -632,35 +629,6 @@ Resources:
632629
Action: lambda:InvokeFunction
633630
FunctionName: !Ref OnboardingServiceById
634631
SourceArn: !Sub arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${PublicApi}/*/GET/onboarding/{id}
635-
OnboardingServiceUpdateStatusMethod:
636-
Type: AWS::ApiGateway::Method
637-
Properties:
638-
RestApiId: !Ref PublicApi
639-
ResourceId: !Ref OnboardingServiceByIdResource
640-
HttpMethod: PUT
641-
AuthorizationType: COGNITO_USER_POOLS
642-
AuthorizerId: !Ref CognitoAuthorizer
643-
AuthorizationScopes:
644-
- aws.cognito.signin.user.admin
645-
RequestParameters: {method.request.path.id: true}
646-
Integration:
647-
Type: AWS_PROXY
648-
IntegrationHttpMethod: POST
649-
Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${OnboardingServiceUpdateStatus}/invocations
650-
PassthroughBehavior: WHEN_NO_MATCH
651-
RequestParameters: {integration.request.path.id: 'method.request.path.id'}
652-
MethodResponses:
653-
- StatusCode: '200'
654-
ResponseModels: {application/json: Empty}
655-
ResponseParameters:
656-
method.response.header.Access-Control-Allow-Origin: false
657-
OnboardingServiceUpdateStatusLambdaPermission:
658-
Type: AWS::Lambda::Permission
659-
Properties:
660-
Principal: apigateway.amazonaws.com
661-
Action: lambda:InvokeFunction
662-
FunctionName: !Ref OnboardingServiceUpdateStatus
663-
SourceArn: !Sub arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${PublicApi}/*/PUT/onboarding/{id}
664632
OnboardingServiceByIdResourceCORS:
665633
Type: AWS::ApiGateway::Method
666634
Properties:
@@ -1938,7 +1906,6 @@ Resources:
19381906
- OnboardingServiceStartMethod
19391907
- OnboardingServiceResourceCORS
19401908
- OnboardingServiceByIdMethod
1941-
- OnboardingServiceUpdateStatusMethod
19421909
- OnboardingServiceByIdResourceCORS
19431910
- SettingsServiceGetAllMethod
19441911
- SettingsServiceResourceCORS

0 commit comments

Comments
 (0)