Skip to content

Commit 914b59d

Browse files
author
Martin Charlesworth
committed
Adds support for IAM task role credentials, and AWS session credentials.
1 parent 5128ce0 commit 914b59d

File tree

8 files changed

+145
-64
lines changed

8 files changed

+145
-64
lines changed

AmazonSQS/AmazonSQSExample/src/main/java/fish/payara/cloud/connectors/amazonsqs/example/NewTimerSessionBean.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,13 @@
4444
import com.amazonaws.services.sqs.model.SendMessageRequest;
4545
import fish.payara.cloud.connectors.amazonsqs.api.AmazonSQSConnection;
4646
import fish.payara.cloud.connectors.amazonsqs.api.AmazonSQSConnectionFactory;
47-
import java.util.Date;
47+
4848
import javax.annotation.Resource;
4949
import javax.ejb.Schedule;
5050
import javax.ejb.Stateless;
5151
import javax.resource.ConnectionFactoryDefinition;
5252
import javax.resource.spi.TransactionSupport.TransactionSupportLevel;
53+
import java.time.LocalTime;
5354

5455
/**
5556
*
@@ -63,17 +64,23 @@
6364
transactionSupport = TransactionSupportLevel.NoTransaction,
6465
properties = {"awsAccessKeyId=${ENV=accessKey}",
6566
"awsSecretKey=${ENV=secretKey}",
66-
"region=eu-west-2"})
67+
"awsSessionToken=${ENV=sessionToken}",
68+
"profileName=${ENV=profileName}",
69+
"region=${ENV=region}"})
6770
@Stateless
6871
public class NewTimerSessionBean {
6972

7073
@Resource(lookup="java:comp/env/SQSConnectionFactory")
7174
AmazonSQSConnectionFactory factory;
7275

73-
@Schedule(second = "*/1", hour="*", minute="*")
76+
@Schedule(second = "*/3", hour="*", minute="*")
7477
public void myTimer() {
7578
try (AmazonSQSConnection connection = factory.getConnection()) {
76-
connection.sendMessage(new SendMessageRequest(System.getenv("queueURL"), "Hello World"));
77-
} catch (Exception e) {}
79+
String msg = "Hello World @ " + LocalTime.now();
80+
System.out.println("<< Sending message: " + msg);
81+
connection.sendMessage(new SendMessageRequest(System.getenv("queueURL"), msg));
82+
} catch (Exception e) {
83+
e.printStackTrace();
84+
}
7885
}
7986
}

AmazonSQS/AmazonSQSExample/src/main/java/fish/payara/cloud/connectors/amazonsqs/example/ReceiveSQSMessage.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,23 +54,25 @@
5454
@MessageDriven(activationConfig = {
5555
@ActivationConfigProperty(propertyName = "awsAccessKeyId", propertyValue = "${ENV=accessKey}"),
5656
@ActivationConfigProperty(propertyName = "awsSecretKey", propertyValue = "${ENV=secretKey}"),
57-
@ActivationConfigProperty(propertyName = "queueURL", propertyValue = "${ENV=queueURL}"),
57+
@ActivationConfigProperty(propertyName = "awsSessionToken", propertyValue = "${ENV=sessionToken}"),
58+
@ActivationConfigProperty(propertyName = "profileName", propertyValue = "${ENV=profileName}"),
59+
@ActivationConfigProperty(propertyName = "queueURL", propertyValue = "${ENV=queueURL}"),
5860
@ActivationConfigProperty(propertyName = "pollInterval", propertyValue = "1000"),
59-
@ActivationConfigProperty(propertyName = "region", propertyValue = "eu-west-2")
61+
@ActivationConfigProperty(propertyName = "region", propertyValue = "${ENV=region}")
6062
})
6163
public class ReceiveSQSMessage implements AmazonSQSListener {
6264

6365
@OnSQSMessage
6466
public void receiveMessage(Message message) {
65-
System.out.println("Got message " + message.getBody());
67+
System.out.println(">> Got message " + message.getBody());
6668
Map<String,MessageAttributeValue> mattrs = message.getMessageAttributes();
6769
for (String key : mattrs.keySet()) {
68-
System.out.println("Got Message attribute " + key + "," + mattrs.get(key).getStringValue());
70+
System.out.println(">> Got Message attribute " + key + "," + mattrs.get(key).getStringValue());
6971
}
7072

7173
Map<String,String> attrs = message.getAttributes();
7274
for (String key : attrs.keySet()) {
73-
System.out.println("Got attribute " + key + "," + attrs.get(key));
75+
System.out.println(">> Got attribute " + key + "," + attrs.get(key));
7476
}
7577
}
7678
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
awsSecretKey=
2+
awsAccessKeyId=
3+
awsSessionToken=
4+
region=us-east-1
5+
profileName=
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package fish.payara.cloud.connectors.amazonsqs.api;
2+
3+
import com.amazonaws.auth.*;
4+
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
5+
import com.amazonaws.util.StringUtils;
6+
7+
/**
8+
* @author Martin Charlesworth
9+
*/
10+
public class AwsCredentialsProviderUtils {
11+
12+
/**
13+
* Creates the appropriate type of {@link AWSCredentialsProvider} depending on the configuration provided.
14+
*/
15+
public static AWSCredentialsProvider getProvider(String accessKey,
16+
String secretKey,
17+
String sessionToken,
18+
String profileName) {
19+
AWSCredentialsProvider awsCredentialsProvider = null;
20+
if (isValidParam(profileName)) {
21+
// uses specified credentials profile
22+
awsCredentialsProvider = new ProfileCredentialsProvider(profileName);
23+
24+
} else {
25+
if (isValidParam(accessKey) && isValidParam(secretKey)) {
26+
if (isValidParam(sessionToken)) {
27+
// uses temporary session based credentials
28+
awsCredentialsProvider = new AWSStaticCredentialsProvider(new BasicSessionCredentials(accessKey, secretKey, sessionToken));
29+
} else {
30+
// uses basic access key + secret key credentials
31+
awsCredentialsProvider = new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey));
32+
}
33+
}
34+
}
35+
if (awsCredentialsProvider == null) {
36+
// if neither credentials nor profile are provided, fall back on the default provider chain, which will
37+
// eventually give us a EC2ContainerCredentialsProviderWrapper, if running in ECS.
38+
awsCredentialsProvider = DefaultAWSCredentialsProviderChain.getInstance();
39+
}
40+
41+
return awsCredentialsProvider;
42+
}
43+
44+
private static boolean isValidParam(String value) {
45+
return !StringUtils.isNullOrEmpty(value) && !value.startsWith("${ENV");
46+
}
47+
48+
}

AmazonSQS/AmazonSQSJCAAPI/src/main/java/fish/payara/cloud/connectors/amazonsqs/api/inbound/AmazonSQSActivationSpec.java

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,17 @@
3939
*/
4040
package fish.payara.cloud.connectors.amazonsqs.api.inbound;
4141

42-
import com.amazonaws.auth.AWSCredentials;
43-
import com.amazonaws.auth.AWSCredentialsProvider;
44-
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
42+
import com.amazonaws.auth.*;
4543
import com.amazonaws.util.StringUtils;
4644
import fish.payara.cloud.connectors.amazonsqs.api.AmazonSQSListener;
45+
import fish.payara.cloud.connectors.amazonsqs.api.AwsCredentialsProviderUtils;
4746

4847
import javax.resource.ResourceException;
4948
import javax.resource.spi.Activation;
5049
import javax.resource.spi.ActivationSpec;
5150
import javax.resource.spi.InvalidPropertyException;
5251
import javax.resource.spi.ResourceAdapter;
52+
import java.util.logging.Logger;
5353

5454
/**
5555
* Activation Specification for Amazon SQS
@@ -63,6 +63,7 @@ public class AmazonSQSActivationSpec implements ActivationSpec, AWSCredentialsPr
6363

6464
private String awsAccessKeyId;
6565
private String awsSecretKey;
66+
private String awsSessionToken;
6667
private String queueURL;
6768
private String region;
6869
private Integer maxMessages = 10;
@@ -79,17 +80,8 @@ public void validate() throws InvalidPropertyException {
7980
throw new InvalidPropertyException("region must be specified");
8081
}
8182

82-
// Validate profileName if present, skip validation off other keys
83-
if (StringUtils.isNullOrEmpty(profileName)) {
84-
85-
// validate keys if profileName isn't available
86-
if (StringUtils.isNullOrEmpty(awsAccessKeyId)) {
87-
throw new InvalidPropertyException("awsAccessKeyId must be specified");
88-
}
89-
90-
if (StringUtils.isNullOrEmpty(awsSecretKey)) {
91-
throw new InvalidPropertyException("awsSecretKey must be specified");
92-
}
83+
if (profileName != null && profileName.startsWith("${ENV")) {
84+
throw new InvalidPropertyException("invalid profileName");
9385
}
9486

9587
if (StringUtils.isNullOrEmpty(queueURL)) {
@@ -123,6 +115,14 @@ public void setAwsSecretKey(String awsSecretKey) {
123115
this.awsSecretKey = awsSecretKey;
124116
}
125117

118+
public String getAwsSessionToken() {
119+
return awsSessionToken;
120+
}
121+
122+
public void setAwsSessionToken(String awsSessionToken) {
123+
this.awsSessionToken = awsSessionToken;
124+
}
125+
126126
public String getQueueURL() {
127127
return queueURL;
128128
}
@@ -195,30 +195,19 @@ public void setProfileName(String profileName) {
195195
this.profileName = profileName;
196196
}
197197

198+
private boolean isValidProfile() {
199+
return !StringUtils.isNullOrEmpty(profileName) && !profileName.startsWith("${ENV");
200+
}
201+
198202
@Override
199203
public AWSCredentials getCredentials() {
200-
// Return Credentials based on what is present, profileName taking priority.
201-
if (StringUtils.isNullOrEmpty(getProfileName())) {
202-
return new AWSCredentials() {
203-
@Override
204-
public String getAWSAccessKeyId() {
205-
return awsAccessKeyId;
206-
}
207-
208-
@Override
209-
public String getAWSSecretKey() {
210-
return awsSecretKey;
211-
}
212-
};
213-
} else {
214-
return new ProfileCredentialsProvider(getProfileName()).getCredentials();
215-
}
204+
return AwsCredentialsProviderUtils.getProvider(awsAccessKeyId, awsSecretKey, awsSessionToken, profileName)
205+
.getCredentials();
216206
}
217207

218208
@Override
219209
public void refresh() {
220210

221211
}
222212

223-
224213
}

AmazonSQS/AmazonSQSJCAAPI/src/main/java/fish/payara/cloud/connectors/amazonsqs/api/outbound/AmazonSQSManagedConnection.java

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,16 @@
3939
*/
4040
package fish.payara.cloud.connectors.amazonsqs.api.outbound;
4141

42-
import com.amazonaws.auth.AWSCredentials;
4342
import com.amazonaws.auth.AWSCredentialsProvider;
44-
import com.amazonaws.auth.AWSStaticCredentialsProvider;
45-
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
4643
import com.amazonaws.services.sqs.AmazonSQS;
4744
import com.amazonaws.services.sqs.AmazonSQSClientBuilder;
4845
import com.amazonaws.services.sqs.model.SendMessageBatchRequest;
4946
import com.amazonaws.services.sqs.model.SendMessageBatchRequestEntry;
5047
import com.amazonaws.services.sqs.model.SendMessageBatchResult;
5148
import com.amazonaws.services.sqs.model.SendMessageRequest;
5249
import com.amazonaws.services.sqs.model.SendMessageResult;
53-
import com.amazonaws.util.StringUtils;
5450
import fish.payara.cloud.connectors.amazonsqs.api.AmazonSQSConnection;
51+
import fish.payara.cloud.connectors.amazonsqs.api.AwsCredentialsProviderUtils;
5552

5653
import javax.resource.NotSupportedException;
5754
import javax.resource.ResourceException;
@@ -80,22 +77,12 @@ public class AmazonSQSManagedConnection implements ManagedConnection, AmazonSQSC
8077
private final AmazonSQS sqsClient;
8178

8279
AmazonSQSManagedConnection(Subject subject, ConnectionRequestInfo cxRequestInfo, AmazonSQSManagedConnectionFactory aThis) {
83-
AWSCredentialsProvider credentialsProvider;
84-
if (StringUtils.isNullOrEmpty(aThis.getProfileName())) {
85-
credentialsProvider = new AWSStaticCredentialsProvider(new AWSCredentials() {
86-
@Override
87-
public String getAWSAccessKeyId() {
88-
return aThis.getAwsAccessKeyId();
89-
}
90-
91-
@Override
92-
public String getAWSSecretKey() {
93-
return aThis.getAwsSecretKey();
94-
}
95-
});
96-
} else {
97-
credentialsProvider = new ProfileCredentialsProvider(aThis.getProfileName());
98-
}
80+
81+
AWSCredentialsProvider credentialsProvider = AwsCredentialsProviderUtils.getProvider(
82+
aThis.getAwsAccessKeyId(),
83+
aThis.getAwsSecretKey(),
84+
aThis.getAwsSessionToken(),
85+
aThis.getProfileName());
9986

10087
sqsClient = AmazonSQSClientBuilder.standard().withRegion(aThis.getRegion()).withCredentials(credentialsProvider).build();
10188
}

AmazonSQS/AmazonSQSJCAAPI/src/main/java/fish/payara/cloud/connectors/amazonsqs/api/outbound/AmazonSQSManagedConnectionFactory.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ public class AmazonSQSManagedConnectionFactory implements ManagedConnectionFacto
7070
@ConfigProperty(description = "AWS Access Key", type = String.class)
7171
private String awsAccessKeyId;
7272

73+
@ConfigProperty(description = "AWS Session Token", type = String.class)
74+
private String awsSessionToken;
75+
7376
@ConfigProperty(description = "Region hosting the queue", type = String.class)
7477
private String region;
7578

@@ -94,6 +97,14 @@ public void setAwsAccessKeyId(String awsAccessKey) {
9497
this.awsAccessKeyId = awsAccessKey;
9598
}
9699

100+
public String getAwsSessionToken() {
101+
return awsSessionToken;
102+
}
103+
104+
public void setAwsSessionToken(String awsSessionToken) {
105+
this.awsSessionToken = awsSessionToken;
106+
}
107+
97108
public String getRegion() {
98109
return region;
99110
}

AmazonSQS/README.md

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,46 @@ To use the JCA adapter the AmazonSQSRAR-<version>.rar should be deployed to your
99

1010
To deploy the JCA adapter on Payara Micro use the following commands.
1111

12-
The example uses a number of environment variables for connecting to your Amazon account.
12+
To run the example, first set your queue ARN and AWS region:
13+
14+
```shell
15+
export queueURL=<your SQS queue ARN>
16+
export region=us-east-1
17+
```
18+
19+
A number of different ways are supported to connect to your AWS account.
20+
21+
## AWS Profile
22+
23+
A profile from your ~/.aws/credentials file can be used:
24+
25+
```shell
26+
export profileName="<your profile>"
27+
```
28+
29+
## Session based credentials
30+
31+
Alternatively, temporary session keys can be used, if your AWS account uses federated login:
32+
33+
```shell
34+
export accessKey="<your amazon access key>"
35+
export secretKey="<your amazon secret key>"
36+
export sessionToken="<your session token>"
37+
```
38+
39+
## Basic Credentials
40+
41+
Standard access key ID and secret key credentials can be provided:
1342

1443
```shell
1544
export accessKey="<your amazon access key>"
16-
export queueURL="<URL of the SQS queue in your amazon account>"
1745
export secretKey="<your amazon secret key>"
1846
```
1947

48+
## DefaultAWSCredentialsProviderChain
49+
50+
If no credentials or profile are provided as environment variables, the [DefaultAWSCredentialsProviderChain](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html) will be used.
51+
2052
These environment variables must be set before running the example
2153

2254
```shell
@@ -87,7 +119,7 @@ It is also possible to send messages to the queue using a defined connection fac
87119
A full example of this is shown below;
88120
```java
89121
try (AmazonSQSConnection connection = factory.getConnection()) {
90-
connection.sendMessage(new SendMessageRequest("queueURL", "Hello World"));
122+
connection.sendMessage(new SendMessageRequest("queueURL", "Hello World"));
91123
} catch (Exception e) {}
92124

93125
```

0 commit comments

Comments
 (0)