Skip to content

Commit

Permalink
add resource permission to lambda
Browse files Browse the repository at this point in the history
deepanjan90 committed Nov 20, 2024
1 parent 24a716c commit bed5652
Showing 4 changed files with 547 additions and 2 deletions.
237 changes: 237 additions & 0 deletions src/main/java/gyro/aws/lambda/FunctionPermission.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
package gyro.aws.lambda;

import java.util.Iterator;

import com.fasterxml.jackson.databind.JsonNode;
import gyro.aws.Copyable;
import gyro.core.resource.Diffable;
import gyro.core.resource.Updatable;
import gyro.core.validation.Required;
import software.amazon.awssdk.services.lambda.model.AddPermissionRequest;
import software.amazon.awssdk.services.lambda.model.FunctionUrlAuthType;

public class FunctionPermission extends Diffable implements Copyable<AddPermissionRequest> {

private String functionName;
private String statementId;
private String action;
private String principal;
private String sourceArn;
private String sourceAccount;
private String eventSourceToken;
private String qualifier;
private String revisionId;
private String principalOrgID;
private FunctionUrlAuthType functionUrlAuthType;

/**
* The name of the Lambda function.
*/
@Updatable
public String getFunctionName() {
return functionName;
}

public void setFunctionName(String functionName) {
this.functionName = functionName;
}

/**
* A unique statement identifier.
*/
@Required
public String getStatementId() {
return statementId;
}

public void setStatementId(String statementId) {
this.statementId = statementId;
}

/**
* The action that the principal can perform.
*/
@Updatable
public String getAction() {
return action;
}

public void setAction(String action) {
this.action = action;
}

/**
* The principal who is getting this permission.
*/
@Updatable
public String getPrincipal() {
return principal;
}

public void setPrincipal(String principal) {
this.principal = principal;
}

/**
* The ARN of the source that is invoking the function.
*/
@Updatable
public String getSourceArn() {
return sourceArn;
}

public void setSourceArn(String sourceArn) {
this.sourceArn = sourceArn;
}

/**
* The AWS account ID of the source that is invoking the function.
*/
@Updatable
public String getSourceAccount() {
return sourceAccount;
}

public void setSourceAccount(String sourceAccount) {
this.sourceAccount = sourceAccount;
}

/**
* The token that must be present in the request.
*/
@Updatable
public String getEventSourceToken() {
return eventSourceToken;
}

public void setEventSourceToken(String eventSourceToken) {
this.eventSourceToken = eventSourceToken;
}

/**
* The version or alias of the function.
*/
@Updatable
public String getQualifier() {
return qualifier;
}

public void setQualifier(String qualifier) {
this.qualifier = qualifier;
}

/**
* The revision ID of the function.
*/
@Updatable
public String getRevisionId() {
return revisionId;
}

public void setRevisionId(String revisionId) {
this.revisionId = revisionId;
}

/**
* The organization ID of the principal.
*/
@Updatable
public String getPrincipalOrgID() {
return principalOrgID;
}

public void setPrincipalOrgID(String principalOrgID) {
this.principalOrgID = principalOrgID;
}

/**
* The type of authentication to use.
*/
@Updatable
public FunctionUrlAuthType getFunctionUrlAuthType() {
return functionUrlAuthType;
}

public void setFunctionUrlAuthType(FunctionUrlAuthType functionUrlAuthType) {
this.functionUrlAuthType = functionUrlAuthType;
}

@Override
public String primaryKey() {
return getStatementId();
}

@Override
public void copyFrom(AddPermissionRequest model) {
setFunctionName(model.functionName());
setStatementId(model.statementId());
setAction(model.action());
setPrincipal(model.principal());
setSourceArn(model.sourceArn());
setSourceAccount(model.sourceAccount());
setEventSourceToken(model.eventSourceToken());
setQualifier(model.qualifier());
setRevisionId(model.revisionId());
setPrincipalOrgID(model.principalOrgID());
setFunctionUrlAuthType(model.functionUrlAuthType());
}

AddPermissionRequest toAddPermissionRequest() {
return AddPermissionRequest.builder()
.functionName(getFunctionName())
.statementId(getStatementId())
.action(getAction())
.principal(getPrincipal())
.sourceArn(getSourceArn())
.sourceAccount(getSourceAccount())
.eventSourceToken(getEventSourceToken())
.qualifier(getQualifier())
.revisionId(getRevisionId())
.principalOrgID(getPrincipalOrgID())
.functionUrlAuthType(getFunctionUrlAuthType())
.build();
}

protected static AddPermissionRequest getAddPermissionRequest(JsonNode statement) {
AddPermissionRequest.Builder builder = AddPermissionRequest.builder();

builder.statementId(statement.get("Sid").asText())
.action(statement.get("Action").asText())
.principal(statement.get("Principal").get("Service").asText())
.functionName(statement.get("Resource").asText());

if (statement.has("Condition")) {
JsonNode condition = statement.get("Condition");
Iterator<String> fieldNames = condition.fieldNames();

while (fieldNames.hasNext()) {
String conditionKey = fieldNames.next();
JsonNode conditionValue = condition.get(conditionKey);

if (conditionKey.equals("ArnLike") && conditionValue.has("AWS:SourceArn")) {
builder.sourceArn(conditionValue.get("AWS:SourceArn").asText());
} else if (conditionKey.equals("StringEquals") && conditionValue.has("AWS:SourceAccount")) {
builder.sourceAccount(conditionValue.get("AWS:SourceAccount").asText());
}
}
}

if (statement.has("Qualifier")) {
builder.qualifier(statement.get("Qualifier").asText());
}
if (statement.has("FunctionUrlAuthType")) {
builder.functionUrlAuthType(FunctionUrlAuthType.fromValue(statement.get("FunctionUrlAuthType").asText()));
}
if (statement.has("EventSourceToken")) {
builder.eventSourceToken(statement.get("EventSourceToken").asText());
}
if (statement.has("RevisionId")) {
builder.revisionId(statement.get("RevisionId").asText());
}
if (statement.has("PrincipalOrgID")) {
builder.principalOrgID(statement.get("PrincipalOrgID").asText());
}

return builder.build();
}
}
80 changes: 79 additions & 1 deletion src/main/java/gyro/aws/lambda/FunctionResource.java
Original file line number Diff line number Diff line change
@@ -16,6 +16,8 @@

package gyro.aws.lambda;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.psddev.dari.util.ObjectUtils;
import gyro.aws.AwsResource;
import gyro.aws.Copyable;
@@ -38,10 +40,12 @@
import org.apache.commons.codec.digest.DigestUtils;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.services.lambda.LambdaClient;
import software.amazon.awssdk.services.lambda.model.AddPermissionRequest;
import software.amazon.awssdk.services.lambda.model.CreateFunctionRequest;
import software.amazon.awssdk.services.lambda.model.CreateFunctionResponse;
import software.amazon.awssdk.services.lambda.model.FunctionConfiguration;
import software.amazon.awssdk.services.lambda.model.GetFunctionResponse;
import software.amazon.awssdk.services.lambda.model.GetPolicyResponse;
import software.amazon.awssdk.services.lambda.model.ListTagsResponse;
import software.amazon.awssdk.services.lambda.model.ListVersionsByFunctionResponse;
import software.amazon.awssdk.services.lambda.model.ResourceNotFoundException;
@@ -107,6 +111,7 @@ public class FunctionResource extends AwsResource implements Copyable<FunctionCo
private Integer reservedConcurrentExecutions;
private Map<String, String> versionMap;
private Boolean publish;
private Set<FunctionPermission> permission;

// -- Readonly

@@ -438,6 +443,22 @@ public void setPublish(Boolean publish) {
this.publish = publish;
}

/**
* The set of permissions to be associated with the Lambda Function.
*/
@Updatable
public Set<FunctionPermission> getPermission() {
if (permission == null) {
permission = new HashSet<>();
}

return permission;
}

public void setPermission(Set<FunctionPermission> permission) {
this.permission = permission;
}

/**
* The arn for the lambda Lambda Function resource including the version.
*/
@@ -571,6 +592,17 @@ public void copyFrom(FunctionConfiguration configuration) {
);

setReservedConcurrentExecutions(response.concurrency() != null ? response.concurrency().reservedConcurrentExecutions() : null);

getPermission().clear();
try {
GetPolicyResponse policy = client.getPolicy(r -> r.functionName(getName()));

if (policy.policy() != null) {
setPolicy(policy.policy());
}
} catch (ResourceNotFoundException ex) {
// Ignore
}
}

@Override
@@ -647,18 +679,29 @@ public void create(GyroUI ui, State state) {
setVersion(response.version());
setCodeHash(response.codeSha256());

setVersions(client);
state.save();

if (getReservedConcurrentExecutions() != null) {
try {
client.putFunctionConcurrency(
r -> r.functionName(getName())
.reservedConcurrentExecutions(getReservedConcurrentExecutions())
);

state.save();
} catch (Exception ex) {
ui.write("\n@|bold,red Error assigning reserved concurrency executions to lambda function %s. Error - %s|@", getArn(), ex.getMessage());
}
}

setVersions(client);
if (!getPermission().isEmpty()) {
for (FunctionPermission permission : getPermission()) {
client.addPermission(permission.toAddPermissionRequest());
}

state.save();
}
}

@Override
@@ -671,6 +714,23 @@ public void update(GyroUI ui, State state, Resource resource, Set<String> change

Set<String> changeSet = new HashSet<>(changedFieldNames);

if (changeSet.contains("permission")) {
if (!oldResource.getPermission().isEmpty()) {
for (FunctionPermission permission : oldResource.getPermission()) {
client.removePermission(
r -> r.functionName(getName())
.statementId(permission.getStatementId())
);
}
}

for (FunctionPermission permission : getPermission()) {
client.addPermission(permission.toAddPermissionRequest());
}

changeSet.remove("permission");
}

if (changeSet.contains("reserved-concurrent-executions")) {
if (getReservedConcurrentExecutions() != null) {
client.putFunctionConcurrency(
@@ -820,4 +880,22 @@ private void setVersions(LambdaClient client) {
}
}
}

private void setPolicy(String jsonPolicy) {
try {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode policyNode = objectMapper.readTree(jsonPolicy);
JsonNode statements = policyNode.get("Statement");

for (JsonNode statement : statements) {
AddPermissionRequest request = FunctionPermission.getAddPermissionRequest(statement);
FunctionPermission permission = newSubresource(FunctionPermission.class);
permission.copyFrom(request);
getPermission().add(permission);
}

} catch (Exception e) {
throw new GyroException("Error parsing function policy",e);
}
}
}
Loading

0 comments on commit bed5652

Please sign in to comment.