startMongoProcessBuilder = StartMongoDBServer.builder();
diff --git a/java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/shared/AwsSessionCredentialsFromUserPreference.java b/java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/shared/AwsSessionCredentialsFromUserPreference.java
new file mode 100755
index 00000000000..e052ed8edd3
--- /dev/null
+++ b/java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/shared/AwsSessionCredentialsFromUserPreference.java
@@ -0,0 +1,40 @@
+package com.sap.sailing.landscape.ui.shared;
+
+import com.sap.sse.common.TimePoint;
+import com.sap.sse.common.settings.generic.AbstractGenericSerializableSettings;
+import com.sap.sse.common.settings.generic.LongSetting;
+import com.sap.sse.common.settings.generic.StringSetting;
+
+public class AwsSessionCredentialsFromUserPreference extends AbstractGenericSerializableSettings {
+ private static final long serialVersionUID = -3250243915670349222L;
+
+ private StringSetting accessKeyId;
+ private StringSetting secretAccessKey;
+ private StringSetting sessionToken;
+ private LongSetting expiry;
+
+ @Override
+ protected void addChildSettings() {
+ accessKeyId = new StringSetting("accessKeyId", this);
+ secretAccessKey = new StringSetting("secretAccessKey", this);
+ sessionToken = new StringSetting("sessionToken", this);
+ expiry = new LongSetting("expiry", this);
+ }
+
+ /**
+ * The default settings
+ */
+ public AwsSessionCredentialsFromUserPreference() {
+ }
+
+ public AwsSessionCredentialsFromUserPreference(AwsSessionCredentialsWithExpiry awsSessionCredentialsWithExpiry) {
+ this.accessKeyId.setValue(awsSessionCredentialsWithExpiry.getAccessKeyId());
+ this.secretAccessKey.setValue(awsSessionCredentialsWithExpiry.getSecretAccessKey());
+ this.sessionToken.setValue(awsSessionCredentialsWithExpiry.getSessionToken());
+ this.expiry.setValue(awsSessionCredentialsWithExpiry.getExpiration().asMillis());
+ }
+
+ public AwsSessionCredentialsWithExpiry getAwsSessionCredentialsWithExpiry() {
+ return new AwsSessionCredentialsWithExpiryImpl(accessKeyId.getValue(), secretAccessKey.getValue(), sessionToken.getValue(), TimePoint.of(expiry.getValue()));
+ }
+}
diff --git a/java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/shared/AwsSessionCredentialsWithExpiry.java b/java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/shared/AwsSessionCredentialsWithExpiry.java
new file mode 100755
index 00000000000..b68f0313b19
--- /dev/null
+++ b/java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/shared/AwsSessionCredentialsWithExpiry.java
@@ -0,0 +1,10 @@
+package com.sap.sailing.landscape.ui.shared;
+
+import com.sap.sse.common.TimePoint;
+
+public interface AwsSessionCredentialsWithExpiry {
+ String getAccessKeyId();
+ String getSecretAccessKey();
+ String getSessionToken();
+ TimePoint getExpiration();
+}
diff --git a/java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/shared/AwsSessionCredentialsWithExpiryImpl.java b/java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/shared/AwsSessionCredentialsWithExpiryImpl.java
new file mode 100755
index 00000000000..f7a903cf007
--- /dev/null
+++ b/java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/shared/AwsSessionCredentialsWithExpiryImpl.java
@@ -0,0 +1,39 @@
+package com.sap.sailing.landscape.ui.shared;
+
+import com.sap.sse.common.TimePoint;
+
+public class AwsSessionCredentialsWithExpiryImpl implements AwsSessionCredentialsWithExpiry {
+ private final String accessKeyId;
+ private final String secretAccessKey;
+ private final String sessionToken;
+ private final TimePoint expiration;
+
+ public AwsSessionCredentialsWithExpiryImpl(String accessKeyId, String secretAccessKey, String sessionToken,
+ TimePoint expiration) {
+ super();
+ this.accessKeyId = accessKeyId;
+ this.secretAccessKey = secretAccessKey;
+ this.sessionToken = sessionToken;
+ this.expiration = expiration;
+ }
+
+ @Override
+ public String getAccessKeyId() {
+ return accessKeyId;
+ }
+
+ @Override
+ public String getSecretAccessKey() {
+ return secretAccessKey;
+ }
+
+ @Override
+ public String getSessionToken() {
+ return sessionToken;
+ }
+
+ @Override
+ public TimePoint getExpiration() {
+ return expiration;
+ }
+}
diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java
index eb6f9cf9ed6..aafa19b9e4f 100755
--- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java
+++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java
@@ -71,6 +71,7 @@ import com.sap.sse.security.SecurityService;
import com.sap.sse.security.SecurityUrlPathProvider;
import com.sap.sse.security.interfaces.PreferenceConverter;
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
+import com.sap.sse.security.util.GenericJSONPreferenceConverter;
import com.sap.sse.security.shared.HasPermissionsProvider;
import com.sap.sse.security.shared.RoleDefinition;
import com.sap.sse.util.ClearStateTestSupport;
diff --git a/java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/AwsLandscape.java b/java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/AwsLandscape.java
index 3472fd42bc0..ef9a32c237f 100755
--- a/java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/AwsLandscape.java
+++ b/java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/AwsLandscape.java
@@ -47,6 +47,7 @@ import software.amazon.awssdk.services.elasticloadbalancingv2.model.TargetHealth
import software.amazon.awssdk.services.route53.Route53Client;
import software.amazon.awssdk.services.route53.model.ChangeInfo;
import software.amazon.awssdk.services.route53.model.RRType;
+import software.amazon.awssdk.services.sts.model.Credentials;
/**
* A simplified, largely stateless view onto the AWS SDK API that is geared towards specific ways and patterns of
@@ -64,7 +65,7 @@ import software.amazon.awssdk.services.route53.model.RRType;
*
*
* Clients may also create dedicated instances of this service wrapper, using their own credentials. See
- * {@link #obtain(String, String)}.
+ * {@link #obtain(String, String, Optional)}.
*
*
* This object interacts with an instance of {@link AwsLandscapeState} which keeps persistent and replicable state about
@@ -118,7 +119,9 @@ public interface AwsLandscape extends Landscape {
* Based on system properties for the AWS access key ID and the secret access key (see
* {@link #ACCESS_KEY_ID_SYSTEM_PROPERTY_NAME} and {@link #SECRET_ACCESS_KEY_SYSTEM_PROPERTY_NAME}), this method
* returns a landscape object which internally has access to the clients for the underlying AWS landscape, such as
- * an EC2 client, a Route53 client, etc.
+ * an EC2 client, a Route53 client, etc. Note that this way no multi-factor authentication (MFA) is possible. If
+ * the system properties described above are not set or not valid, an unauthenticated landscape object will result;
+ * some rudimentary things may still work, such as querying the set of regions.
*/
static >
@@ -134,8 +137,8 @@ public interface AwsLandscape extends Landscape {
*/
static >
- AwsLandscape obtain(String accessKey, String secret) {
- final AwsLandscape result = new AwsLandscapeImpl<>(Activator.getInstance().getLandscapeState(), accessKey, secret);
+ AwsLandscape obtain(String accessKey, String secret, Optional mfaTokenCode) {
+ final AwsLandscape result = new AwsLandscapeImpl<>(Activator.getInstance().getLandscapeState(), accessKey, secret, mfaTokenCode);
return result;
}
@@ -556,4 +559,10 @@ public interface AwsLandscape extends Landscape {
String tagName, BiFunction processFactoryFromHostAndServerDirectory,
Optional optionalTimeout, Optional optionalKeyName, byte[] privateKeyEncryptionPassphrase) throws Exception;
+ /**
+ * Obtains session credentials using an MFA token code valid for the user for which this landscape object was authenticated
+ * during its creation with an access key ID and a secret.
+ */
+ Credentials getMfaSessionCredentials(String nonEmptyMfaTokenCode);
+
}
diff --git a/java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/impl/AwsLandscapeImpl.java b/java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/impl/AwsLandscapeImpl.java
index b8750c9ae2b..4a4f5883782 100755
--- a/java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/impl/AwsLandscapeImpl.java
+++ b/java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/impl/AwsLandscapeImpl.java
@@ -62,6 +62,7 @@ import com.sap.sse.security.SessionUtils;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentials;
+import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.regions.Region;
@@ -135,6 +136,8 @@ import software.amazon.awssdk.services.route53.model.GetChangeRequest;
import software.amazon.awssdk.services.route53.model.RRType;
import software.amazon.awssdk.services.route53.model.ResourceRecord;
import software.amazon.awssdk.services.route53.model.ResourceRecordSet;
+import software.amazon.awssdk.services.sts.StsClient;
+import software.amazon.awssdk.services.sts.model.Credentials;
public class AwsLandscapeImpl implements AwsLandscape {
private static final String DEFAULT_TARGET_GROUP_PREFIX = "D";
@@ -150,25 +153,26 @@ public class AwsLandscapeImpl implements AwsLandscape
private static final String DEFAULT_NON_DNS_MAPPED_ALB_NAME = "DefDyn";
private final String accessKeyId;
private final String secretAccessKey;
+ private final Optional sessionToken;
private final AwsRegion globalRegion;
private final AwsLandscapeState landscapeState;
public AwsLandscapeImpl(AwsLandscapeState awsLandscapeState) {
this(awsLandscapeState,
- System.getProperty(ACCESS_KEY_ID_SYSTEM_PROPERTY_NAME), System.getProperty(SECRET_ACCESS_KEY_SYSTEM_PROPERTY_NAME));
+ System.getProperty(ACCESS_KEY_ID_SYSTEM_PROPERTY_NAME), System.getProperty(SECRET_ACCESS_KEY_SYSTEM_PROPERTY_NAME), Optional.empty());
}
- public AwsLandscapeImpl(AwsLandscapeState awsLandscapeState, String accessKeyId, String secretAccessKey) {
- this(accessKeyId, secretAccessKey,
+ public AwsLandscapeImpl(AwsLandscapeState awsLandscapeState, String accessKeyId, String secretAccessKey, Optional mfaTokenCode) {
+ this(accessKeyId, secretAccessKey, mfaTokenCode,
// by using MongoDBService.INSTANCE the default test configuration will be used if nothing else is configured
- PersistenceFactory.INSTANCE.getDomainObjectFactory(MongoDBService.INSTANCE),
- PersistenceFactory.INSTANCE.getMongoObjectFactory(MongoDBService.INSTANCE), awsLandscapeState);
+ PersistenceFactory.INSTANCE.getDomainObjectFactory(MongoDBService.INSTANCE), PersistenceFactory.INSTANCE.getMongoObjectFactory(MongoDBService.INSTANCE), awsLandscapeState);
}
public AwsLandscapeImpl(String accessKeyId, String secretAccessKey,
- DomainObjectFactory domainObjectFactory, MongoObjectFactory mongoObjectFactory, AwsLandscapeState landscapeState) {
+ Optional sessionToken, DomainObjectFactory domainObjectFactory, MongoObjectFactory mongoObjectFactory, AwsLandscapeState landscapeState) {
this.accessKeyId = accessKeyId;
this.secretAccessKey = secretAccessKey;
+ this.sessionToken = sessionToken;
this.globalRegion = new AwsRegion(Region.AWS_GLOBAL);
this.landscapeState = landscapeState;
}
@@ -397,7 +401,7 @@ public class AwsLandscapeImpl implements AwsLandscape
}
private Route53Client getRoute53Client() {
- return Route53Client.builder().region(getRegion(globalRegion)).build();
+ return getClient(Route53Client.builder(), getRegion(globalRegion));
}
@Override
@@ -577,8 +581,20 @@ public class AwsLandscapeImpl implements AwsLandscape
};
}
+ /**
+ * If a {@link #sessionToken} was provided to this landscape, use it to create {@link AwsSessionCredentials}; otherwise
+ * an {@link AwsBasicCredentials} object will be produced from the {@link #accessKeyId} and the {@link #secretAccessKey}.
+ * @return
+ */
private AwsCredentials getCredentials() {
- return AwsBasicCredentials.create(accessKeyId, secretAccessKey);
+ return sessionToken.map(nonEmptySessionToken->(AwsCredentials) AwsSessionCredentials.create(accessKeyId, secretAccessKey, sessionToken.get()))
+ .orElse(AwsBasicCredentials.create(accessKeyId, secretAccessKey));
+ }
+
+ @Override
+ public Credentials getMfaSessionCredentials(String nonEmptyMfaTokenCode) {
+ return StsClient.builder().credentialsProvider(()->AwsBasicCredentials.create(accessKeyId, secretAccessKey)).build()
+ .getSessionToken(b->b.tokenCode(nonEmptyMfaTokenCode)).credentials();
}
@Override
diff --git a/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/PreferenceConverterRegistrationManager.java b/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/PreferenceConverterRegistrationManager.java
index 7bd7a0e647f..8f6a3bc4369 100644
--- a/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/PreferenceConverterRegistrationManager.java
+++ b/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/PreferenceConverterRegistrationManager.java
@@ -52,7 +52,6 @@ public class PreferenceConverterRegistrationManager implements Stoppable {
}
private class Cutomizer implements ServiceTrackerCustomizer, PreferenceConverter>> {
-
@Override
public PreferenceConverter> addingService(ServiceReference> reference) {
final String preferenceKey = (String) reference.getProperty(PreferenceConverter.KEY_PARAMETER_NAME);
diff --git a/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/UserStore.java b/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/UserStore.java
index 3f54ad88b6d..bfbc4779106 100644
--- a/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/UserStore.java
+++ b/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/UserStore.java
@@ -30,8 +30,8 @@ public interface UserStore extends BasicUserStore {
/**
*
- * In an OSGi environment, this shouldn't be called manually, but instead automatically managed by setting a
- * {@link PreferenceConverterRegistrationManager} up. {@link PreferenceConverter}s should be registered in the OSGi
+ * In an OSGi environment, this shouldn't be called manually, but instead automatically managed by setting up a
+ * {@link PreferenceConverterRegistrationManager}. {@link PreferenceConverter}s should be registered in the OSGi
* service registry with {@link PreferenceConverter#KEY_PARAMETER_NAME} containing the associated preference key
* added as property of the service registration.
*
diff --git a/java/com.sap.sse.security/src/com/sap/sse/security/SecurityService.java b/java/com.sap.sse.security/src/com/sap/sse/security/SecurityService.java
index 1c31a5a4a6e..c8225267d10 100644
--- a/java/com.sap.sse.security/src/com/sap/sse/security/SecurityService.java
+++ b/java/com.sap.sse.security/src/com/sap/sse/security/SecurityService.java
@@ -307,13 +307,13 @@ public interface SecurityService extends ReplicableWithObjectInputStream see
- * {@link #registerPreferenceConverter(String, PreferenceConverter)}.
+ * {@link UserStore#registerPreferenceConverter(String, PreferenceConverter)}.
*/
T getPreferenceObject(String username, String key);
/**
* Gets all preference objects resolving to a certain key. Always returns a valid map. May be empty.
- * {@link #registerPreferenceConverter(String, PreferenceConverter)}.
+ * {@link UserStore#registerPreferenceConverter(String, PreferenceConverter)}.
*/
Map getPreferenceObjectsByKey(String key);
diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/GenericJSONPreferenceConverter.java b/java/com.sap.sse.security/src/com/sap/sse/security/util/GenericJSONPreferenceConverter.java
similarity index 96%
rename from java/com.sap.sailing.server/src/com/sap/sailing/server/impl/GenericJSONPreferenceConverter.java
rename to java/com.sap.sse.security/src/com/sap/sse/security/util/GenericJSONPreferenceConverter.java
index ba600330f45..58294ad2514 100644
--- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/GenericJSONPreferenceConverter.java
+++ b/java/com.sap.sse.security/src/com/sap/sse/security/util/GenericJSONPreferenceConverter.java
@@ -1,4 +1,4 @@
-package com.sap.sailing.server.impl;
+package com.sap.sse.security.util;
import java.util.function.Supplier;
@@ -24,5 +24,4 @@ public class GenericJSONPreferenceConverter