Skip to content

Commit 4240108

Browse files
aditya-goyal01phiz71
authored andcommitted
feat: secret support for fields
1 parent bdbe259 commit 4240108

File tree

11 files changed

+424
-75
lines changed

11 files changed

+424
-75
lines changed

README.adoc

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,13 @@ By default, the lambda is called in addition to the backend, meaning the consume
6363

6464
.^|accessKey
6565
^.^|
66-
|AWS Access Key
66+
|AWS Access Key. It can be stored in vault and referred via the Gravitee expression language.
6767
^.^|string
6868
^.^|-
6969

7070
.^|secretKey
7171
^.^|
72-
|AWS Secret Key
72+
|AWS Secret Key. It can be stored in vault and referred via the Gravitee expression language.
7373
^.^|string
7474
^.^|-
7575

@@ -119,7 +119,7 @@ DryRun – Validate parameter values and verify that the user or role has permis
119119

120120
.^|roleArn
121121
^.^|-
122-
|The arn of the role to be assumed. This is used when authentication is relying on the AWS Security Token Service (STS) to assume a Role and create temporary, short-lived sessions to use for authentication.
122+
|The arn of the role to be assumed. This is used when authentication is relying on the AWS Security Token Service (STS) to assume a Role and create temporary, short-lived sessions to use for authentication. It can be stored in vault and referred via the Gravitee expression language.
123123
^.^|string
124124
^.^|-
125125

@@ -132,10 +132,11 @@ DryRun – Validate parameter values and verify that the user or role has permis
132132
|===
133133

134134
== Examples
135-
135+
Without expression language:
136136
[source, json]
137137
----
138-
"configuration": {
138+
{
139+
"configuration": {
139140
"variables": [
140141
{
141142
"name": "lambdaResponse",
@@ -150,9 +151,32 @@ DryRun – Validate parameter values and verify that the user or role has permis
150151
"region": "us-east-1",
151152
"sendToConsumer": true,
152153
"endpoint": "http://aws-lambda-url/function"
154+
}
155+
}
156+
----
157+
With expression language support:
158+
[source, json]
159+
----
160+
{
161+
"configuration": {
162+
"variables": [
163+
{
164+
"name": "lambdaResponse",
165+
"value": "{#jsonPath(#lambdaResponse.content, '$')}"
166+
}
167+
],
168+
"secretKey": "{#secrets.get('/vault/secret/aws:secretKey')}",
169+
"accessKey": "{#secrets.get('/vault/secret/aws:accessKey')}",
170+
"roleArn": " {#secrets.get('/vault/secret/aws:roleArn')}",
171+
"payload": "{ \"key\": \"value\" }",
172+
"scope": "REQUEST",
173+
"function": "lambda-example",
174+
"region": "us-east-1",
175+
"sendToConsumer": true,
176+
"endpoint": "http://aws-lambda-url/function"
177+
}
153178
}
154179
----
155-
156180
== Errors
157181

158182
=== Default error

pom.xml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,15 @@
3636

3737
<properties>
3838
<gravitee-apim-bom.version>4.8.9</gravitee-apim-bom.version>
39+
<gravitee-plugin.version>4.10.0</gravitee-plugin.version>
3940
<gravitee-entrypoint-http-get.version>2.1.0</gravitee-entrypoint-http-get.version>
4041
<gravitee-reactor-message.version>7.0.1</gravitee-reactor-message.version>
42+
<gravitee-secretprovider-hc-vault.version>2.1.0</gravitee-secretprovider-hc-vault.version>
43+
<gravitee-service-secrets.version>2.0.1</gravitee-service-secrets.version>
44+
<gravitee-secret-api.version>2.0.0</gravitee-secret-api.version>
45+
<testcontainers.version>1.21.3</testcontainers.version>
4146
<aws-java-sdk.version>2.31.26</aws-java-sdk.version>
47+
<hibernate-validator.version>8.0.3.Final</hibernate-validator.version>
4248

4349
<maven-plugin-assembly.version>3.8.0</maven-plugin-assembly.version>
4450
<maven-plugin-properties.version>1.2.1</maven-plugin-properties.version>
@@ -137,6 +143,27 @@
137143
<scope>provided</scope>
138144
</dependency>
139145

146+
<dependency>
147+
<groupId>io.gravitee.secret</groupId>
148+
<artifactId>gravitee-secret-api</artifactId>
149+
<version>${gravitee-secret-api.version}</version>
150+
<scope>provided</scope>
151+
</dependency>
152+
153+
<dependency>
154+
<groupId>io.gravitee.plugin</groupId>
155+
<artifactId>gravitee-plugin-annotation-processors</artifactId>
156+
<version>${gravitee-plugin.version}</version>
157+
<scope>provided</scope>
158+
</dependency>
159+
160+
<dependency>
161+
<groupId>org.hibernate.validator</groupId>
162+
<artifactId>hibernate-validator</artifactId>
163+
<version>${hibernate-validator.version}</version>
164+
<scope>provided</scope>
165+
</dependency>
166+
140167
<!-- Test scope -->
141168
<dependency>
142169
<groupId>io.gravitee.apim.gateway</groupId>
@@ -168,6 +195,30 @@
168195
<version>${gravitee-reactor-message.version}</version>
169196
<scope>test</scope>
170197
</dependency>
198+
<dependency>
199+
<groupId>com.graviteesource.secretprovider</groupId>
200+
<artifactId>gravitee-secret-provider-hc-vault</artifactId>
201+
<version>${gravitee-secretprovider-hc-vault.version}</version>
202+
<scope>test</scope>
203+
</dependency>
204+
<dependency>
205+
<groupId>com.graviteesource.service</groupId>
206+
<artifactId>gravitee-service-secrets</artifactId>
207+
<version>${gravitee-service-secrets.version}</version>
208+
<scope>test</scope>
209+
</dependency>
210+
<dependency>
211+
<groupId>org.testcontainers</groupId>
212+
<artifactId>vault</artifactId>
213+
<version>${testcontainers.version}</version>
214+
<scope>test</scope>
215+
</dependency>
216+
<dependency>
217+
<groupId>org.testcontainers</groupId>
218+
<artifactId>junit-jupiter</artifactId>
219+
<version>${testcontainers.version}</version>
220+
<scope>test</scope>
221+
</dependency>
171222
</dependencies>
172223

173224
<build>

src/main/java/io/gravitee/policy/aws/lambda/AwsLambdaPolicy.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import io.gravitee.gateway.reactor.ReactableApi;
3232
import io.gravitee.policy.aws.lambda.configuration.AwsLambdaError;
3333
import io.gravitee.policy.aws.lambda.configuration.AwsLambdaPolicyConfiguration;
34+
import io.gravitee.policy.aws.lambda.configuration.AwsLambdaPolicyConfigurationEvaluator;
3435
import io.gravitee.policy.aws.lambda.el.LambdaResponse;
3536
import io.gravitee.policy.aws.lambda.invokers.LambdaInvoker;
3637
import io.reactivex.rxjava3.core.Completable;
@@ -48,8 +49,11 @@
4849
@Slf4j
4950
public class AwsLambdaPolicy extends AwsLambdaPolicyV3 implements HttpPolicy {
5051

52+
private final AwsLambdaPolicyConfigurationEvaluator evaluator;
53+
5154
public AwsLambdaPolicy(AwsLambdaPolicyConfiguration configuration) {
5255
super(configuration);
56+
this.evaluator = new AwsLambdaPolicyConfigurationEvaluator(configuration);
5357
}
5458

5559
@Override
@@ -98,23 +102,26 @@ public Completable onResponse(HttpPlainExecutionContext ctx) {
98102

99103
private <T extends HttpBaseExecutionContext> Single<InvokeResponse> invokeAndHandleLambda(T ctx) {
100104
return Single
101-
.fromFuture(invokeLambda(ctx.getTemplateEngine()))
105+
.fromFuture(invokeLambda(this.evaluator.eval(ctx)))
102106
.subscribeOn(Schedulers.io())
103107
.flatMap(invokeResponse -> {
104108
log.debug("AWS Lambda function has been invoked successfully");
109+
105110
String functionError = invokeResponse.functionError();
111+
106112
if (functionError != null && List.of("Handled", "Unhandled").contains(functionError)) {
107113
return processError(ctx, null, AwsLambdaError.AWS_LAMBDA_INVALID_RESPONSE);
108114
} else if (invokeResponse.statusCode() >= 200 && invokeResponse.statusCode() < 300) {
109115
return processSuccess(ctx, invokeResponse);
110116
}
117+
111118
return processError(ctx, null, AWS_LAMBDA_INVALID_STATUS_CODE);
112119
})
113120
.onErrorResumeNext(throwable -> processError(ctx, throwable, AwsLambdaError.AWS_LAMBDA_INVALID_RESPONSE));
114121
}
115122

116123
private <T extends HttpBaseExecutionContext> Single<InvokeResponse> processError(T ctx, Throwable throwable, AwsLambdaError error) {
117-
log.debug("An error occurs while invoking lambda function", throwable);
124+
log.error("An error occurs while invoking lambda function", throwable);
118125

119126
ExecutionFailure failure = new ExecutionFailure(error.getStatusCode())
120127
.key(error.name())

src/main/java/io/gravitee/policy/aws/lambda/AwsLambdaPolicyV3.java

Lines changed: 48 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@
3434
import io.gravitee.policy.api.annotations.OnRequestContent;
3535
import io.gravitee.policy.api.annotations.OnResponse;
3636
import io.gravitee.policy.api.annotations.OnResponseContent;
37-
import io.gravitee.policy.aws.lambda.configuration.AwsLambdaError;
3837
import io.gravitee.policy.aws.lambda.configuration.AwsLambdaPolicyConfiguration;
3938
import io.gravitee.policy.aws.lambda.configuration.PolicyScope;
4039
import io.gravitee.policy.aws.lambda.el.LambdaResponse;
4140
import io.gravitee.policy.aws.lambda.invokers.LambdaInvokerV3;
41+
import io.reactivex.rxjava3.core.Single;
4242
import io.vertx.core.Context;
4343
import io.vertx.core.Vertx;
4444
import java.util.concurrent.CompletableFuture;
@@ -64,16 +64,17 @@
6464
@Slf4j
6565
public class AwsLambdaPolicyV3 {
6666

67+
private LambdaAsyncClient lambdaClient;
68+
6769
protected final AwsLambdaPolicyConfiguration configuration;
70+
6871
protected static final String TEMPLATE_VARIABLE = "lambdaResponse";
69-
private final LambdaAsyncClient lambdaClient;
7072
private static final String LAMBDA_RESULT_ATTR = "LAMBDA_RESULT";
7173
private static final String REQUEST_TEMPLATE_VARIABLE = "request";
7274
private static final String RESPONSE_TEMPLATE_VARIABLE = "response";
7375

7476
public AwsLambdaPolicyV3(AwsLambdaPolicyConfiguration configuration) {
7577
this.configuration = configuration;
76-
lambdaClient = initLambdaClient();
7778
}
7879

7980
@OnRequest
@@ -223,63 +224,69 @@ public void end() {
223224
};
224225
}
225226

226-
protected CompletableFuture<InvokeResponse> invokeLambda(TemplateEngine templateEngine) {
227-
return CompletableFuture
228-
.supplyAsync(() -> {
229-
InvokeRequest.Builder awsRequest = InvokeRequest
230-
.builder()
231-
.functionName(configuration.getFunction())
232-
.invocationType(configuration.getInvocationType())
233-
.qualifier(configuration.getQualifier())
234-
.logType(configuration.getLogType());
235-
236-
if (configuration.getPayload() != null && !configuration.getPayload().isEmpty()) {
237-
String payload = templateEngine.evalNow(configuration.getPayload(), String.class);
238-
awsRequest.payload(SdkBytes.fromUtf8String(payload));
239-
}
227+
protected CompletableFuture<InvokeResponse> invokeLambda(Single<AwsLambdaPolicyConfiguration> configuration) {
228+
return configuration
229+
.flatMap(config -> {
230+
lambdaClient = initLambdaClient(config);
231+
InvokeRequest.Builder awsRequest = buildRequest(config);
240232

241-
return awsRequest;
233+
return Single.fromFuture(lambdaClient.invoke(awsRequest.build()));
242234
})
243-
.thenCompose(awsRequest -> {
244-
// invoke the lambda function and inspect the result...
245-
return lambdaClient.invoke(awsRequest.build());
246-
});
235+
.toCompletionStage()
236+
.toCompletableFuture();
237+
}
238+
239+
private static InvokeRequest.Builder buildRequest(AwsLambdaPolicyConfiguration config) {
240+
InvokeRequest.Builder awsRequest = InvokeRequest
241+
.builder()
242+
.functionName(config.getFunction())
243+
.invocationType(config.getInvocationType())
244+
.qualifier(config.getQualifier())
245+
.logType(config.getLogType());
246+
247+
if (config.getPayload() != null && !config.getPayload().isEmpty()) {
248+
awsRequest.payload(SdkBytes.fromUtf8String(config.getPayload()));
249+
}
250+
251+
return awsRequest;
247252
}
248253

249-
protected LambdaAsyncClient initLambdaClient() {
254+
protected LambdaAsyncClient initLambdaClient(AwsLambdaPolicyConfiguration config) {
250255
AwsCredentialsProvider awsCredentialsProvider;
256+
String accessKey = config.getAccessKey();
257+
String secretKey = config.getSecretKey();
258+
String roleArn = config.getRoleArn();
251259

252-
if (configuration.getRoleArn() != null && !configuration.getRoleArn().isEmpty()) {
253-
awsCredentialsProvider = createSTSCredentialsProvider();
260+
if (roleArn != null && !roleArn.isEmpty()) {
261+
awsCredentialsProvider = createSTSCredentialsProvider(accessKey, secretKey, roleArn);
254262
} else {
255-
awsCredentialsProvider = getAWSCredentialsProvider();
263+
awsCredentialsProvider = getAWSCredentialsProvider(accessKey, secretKey);
256264
}
257265

258-
return LambdaAsyncClient.builder().credentialsProvider(awsCredentialsProvider).region(Region.of(configuration.getRegion())).build();
266+
String region = config.getRegion();
267+
268+
return LambdaAsyncClient.builder().credentialsProvider(awsCredentialsProvider).region(Region.of(region)).build();
259269
}
260270

261-
private StsAssumeRoleCredentialsProvider createSTSCredentialsProvider() {
271+
private StsAssumeRoleCredentialsProvider createSTSCredentialsProvider(String accessKey, String secretKey, String roleArn) {
262272
return StsAssumeRoleCredentialsProvider
263273
.builder()
264-
.refreshRequest(() ->
265-
AssumeRoleRequest.builder().roleArn(configuration.getRoleArn()).roleSessionName(configuration.getRoleSessionName()).build()
266-
)
274+
.refreshRequest(() -> AssumeRoleRequest.builder().roleArn(roleArn).roleSessionName(configuration.getRoleSessionName()).build())
267275
.stsClient(
268-
StsClient.builder().credentialsProvider(getAWSCredentialsProvider()).region(Region.of(configuration.getRegion())).build()
276+
StsClient
277+
.builder()
278+
.credentialsProvider(getAWSCredentialsProvider(accessKey, secretKey))
279+
.region(Region.of(configuration.getRegion()))
280+
.build()
269281
)
270282
.build();
271283
}
272284

273-
private AwsCredentialsProvider getAWSCredentialsProvider() {
285+
private AwsCredentialsProvider getAWSCredentialsProvider(String accessKey, String secretKey) {
274286
AwsBasicCredentials credentials = null;
275287

276-
if (
277-
configuration.getAccessKey() != null &&
278-
!configuration.getAccessKey().isEmpty() &&
279-
configuration.getSecretKey() != null &&
280-
!configuration.getSecretKey().isEmpty()
281-
) {
282-
credentials = AwsBasicCredentials.create(configuration.getAccessKey(), configuration.getSecretKey());
288+
if (accessKey != null && !accessKey.isEmpty() && secretKey != null && !secretKey.isEmpty()) {
289+
credentials = AwsBasicCredentials.create(accessKey, secretKey);
283290
}
284291

285292
if (credentials != null) {
@@ -291,7 +298,7 @@ private AwsCredentialsProvider getAWSCredentialsProvider() {
291298
}
292299

293300
private void invokeLambda(ExecutionContext context, Consumer<InvokeResponse> onSuccess, Consumer<PolicyResult> onError) {
294-
invokeLambda(context.getTemplateEngine())
301+
invokeLambda(Single.just(configuration))
295302
.whenCompleteAsync((InvokeResponse result, Throwable throwable) -> {
296303
// Lambda will return an HTTP status code will be in the 200 range for successful
297304
// request, even if an error occurred in the Lambda function itself. Here, we check

src/main/java/io/gravitee/policy/aws/lambda/configuration/AwsLambdaPolicyConfiguration.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
*/
1616
package io.gravitee.policy.aws.lambda.configuration;
1717

18+
import io.gravitee.plugin.annotation.ConfigurationEvaluator;
1819
import io.gravitee.policy.api.PolicyConfiguration;
20+
import io.gravitee.secrets.api.annotation.Secret;
21+
import io.gravitee.secrets.api.el.FieldKind;
1922
import java.util.ArrayList;
2023
import java.util.List;
2124
import lombok.Getter;
@@ -29,13 +32,18 @@
2932
*/
3033
@Getter
3134
@Setter
35+
@ConfigurationEvaluator(attributePrefix = "gravitee.attributes.secret.aws")
3236
public class AwsLambdaPolicyConfiguration implements PolicyConfiguration {
3337

3438
private PolicyScope scope = PolicyScope.REQUEST;
3539

3640
private String region = "us-east-1";
3741

38-
private String accessKey, secretKey;
42+
@Secret(FieldKind.GENERIC)
43+
private String accessKey;
44+
45+
@Secret(FieldKind.GENERIC)
46+
private String secretKey;
3947

4048
private String function;
4149

@@ -45,6 +53,7 @@ public class AwsLambdaPolicyConfiguration implements PolicyConfiguration {
4553

4654
private String qualifier;
4755

56+
@Secret(FieldKind.GENERIC)
4857
private String roleArn;
4958

5059
private String roleSessionName = "gravitee";

0 commit comments

Comments
 (0)