mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-30 09:26:44 +00:00
bug4811: working towards MFA enablement
This commit is contained in:
@@ -125,4 +125,8 @@ echo "Patching SDK version in target platform definition ${TARGET_DEFINITION}...
|
||||
sed -i -e 's/<unit id="com.amazon.aws.aws-java-api.feature.group" version="[0-9.]*"\/>/<unit id="com.amazon.aws.aws-java-api.feature.group" version="'${VERSION}'"\/>/' ${TARGET_DEFINITION}
|
||||
echo "You may test your target platform locally by creating race-analysis-p2-local.target by running the script createLocalTargetDef.sh."
|
||||
echo "You can also try a Hudson build with the -v option, generating and using the local target platform during the build."
|
||||
echo "When all this works, update the P2 repository at p2.sapsailing.com using the script uploadAwsApiRepositoryToServer.sh."
|
||||
echo "In this case, start with an unpatched remote target platform (race-analysis-p2-remote.target) and an unpatched"
|
||||
echo "java/com.sap.sse.feature.runtime/feature.xml so that running this script during the Hudson build can resolve"
|
||||
echo "the target platform."
|
||||
echo "When all this works, commit and push the patched to race-analysis-p2-remote.target and feature.xml"
|
||||
echo "and update the P2 repository at p2.sapsailing.com using the script uploadAwsApiRepositoryToServer.sh."
|
||||
|
||||
@@ -22,7 +22,8 @@ Require-Bundle: com.sap.sse.gwt,
|
||||
com.sap.sse.landscape.common,
|
||||
com.sap.sse.landscape.aws.common,
|
||||
com.jcraft.jsch;bundle-version="0.1.54",
|
||||
elemental2;bundle-version="1.1.0"
|
||||
elemental2;bundle-version="1.1.0",
|
||||
org.json.simple
|
||||
Export-Package: com.google.gwt.user.client.rpc.core.com.sap.sse.landscape.aws.common.shared,
|
||||
com.google.gwt.user.client.rpc.core.com.sap.sse.landscape.common.shared,
|
||||
com.sap.sailing.landscape.ui.client,
|
||||
|
||||
+15
@@ -37,4 +37,19 @@ public interface LandscapeManagementWriteService extends RemoteService {
|
||||
AmazonMachineImageDTO upgradeAmazonMachineImage(String awsAccessKey, String awsSecret, String region, String machineImageId) throws Exception;
|
||||
|
||||
void scaleMongo(String awsAccessKey, String awsSecret, String region, MongoScalingInstructionsDTO mongoScalingInstructions) throws Exception;
|
||||
|
||||
/**
|
||||
* For a combination of an AWS access key ID, the corresponding secret plus an MFA token code produces new session
|
||||
* credentials and stores them in the user's preference store from where they can be obtained again using
|
||||
* {@link #getSessionCredentials()}. Any session credentials previously stored in the current user's preference store
|
||||
* will be overwritten by this. The current user must have the {@code LANDSCAPE:MANAGE:AWS} permission.
|
||||
*/
|
||||
void createMfaSessionCredentials(String awsAccessKey, String awsSecret, String mfaTokenCode);
|
||||
|
||||
/**
|
||||
* For the current user who has to have the {@code LANDSCAPE:MANAGE:AWS} permission, clears the preference in the
|
||||
* user's preference store which holds any session credentials created previously using
|
||||
* {@link #createMfaSessionCredentials(String, String, String)}.
|
||||
*/
|
||||
void clearSessionCredentials();
|
||||
}
|
||||
|
||||
+16
@@ -57,4 +57,20 @@ public interface LandscapeManagementWriteServiceAsync {
|
||||
|
||||
void scaleMongo(String awsAccessKey, String awsSecret, String region,
|
||||
MongoScalingInstructionsDTO mongoScalingInstructions, AsyncCallback<Void> asyncCallback);
|
||||
|
||||
/**
|
||||
* For a combination of an AWS access key ID, the corresponding secret plus an MFA token code produces new session
|
||||
* credentials and stores them in the user's preference store from where they can be obtained again using
|
||||
* {@link #getSessionCredentials()}. Any session credentials previously stored in the current user's preference store
|
||||
* will be overwritten by this. The current user must have the {@code LANDSCAPE:MANAGE:AWS} permission.
|
||||
*/
|
||||
void createMfaSessionCredentials(String awsAccessKey, String awsSecret, String mfaTokenCode,
|
||||
AsyncCallback<Void> callback);
|
||||
|
||||
/**
|
||||
* For the current user who has to have the {@code LANDSCAPE:MANAGE:AWS} permission, clears the preference in the
|
||||
* user's preference store which holds any session credentials created previously using
|
||||
* {@link #createMfaSessionCredentials(String, String, String)}.
|
||||
*/
|
||||
void clearSessionCredentials(AsyncCallback<Void> callback);
|
||||
}
|
||||
|
||||
@@ -3,12 +3,19 @@ package com.sap.sailing.landscape.ui.impl;
|
||||
import org.osgi.framework.BundleActivator;
|
||||
import org.osgi.framework.BundleContext;
|
||||
|
||||
import com.sap.sailing.landscape.ui.shared.AwsSessionCredentialsFromUserPreference;
|
||||
import com.sap.sse.security.interfaces.PreferenceConverter;
|
||||
import com.sap.sse.security.util.GenericJSONPreferenceConverter;
|
||||
|
||||
public class Activator implements BundleActivator {
|
||||
private static BundleContext context;
|
||||
|
||||
@Override
|
||||
public void start(BundleContext context) throws Exception {
|
||||
Activator.context = context;
|
||||
context.registerService(PreferenceConverter.class,
|
||||
new GenericJSONPreferenceConverter<>(() -> new AwsSessionCredentialsFromUserPreference()),
|
||||
/* properties */ null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+94
-18
@@ -15,6 +15,9 @@ import com.sap.sailing.landscape.procedures.UpgradeAmi;
|
||||
import com.sap.sailing.landscape.ui.client.LandscapeManagementWriteService;
|
||||
import com.sap.sailing.landscape.ui.impl.Activator;
|
||||
import com.sap.sailing.landscape.ui.shared.AmazonMachineImageDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.AwsSessionCredentialsFromUserPreference;
|
||||
import com.sap.sailing.landscape.ui.shared.AwsSessionCredentialsWithExpiry;
|
||||
import com.sap.sailing.landscape.ui.shared.AwsSessionCredentialsWithExpiryImpl;
|
||||
import com.sap.sailing.landscape.ui.shared.MongoEndpointDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.MongoScalingInstructionsDTO;
|
||||
import com.sap.sailing.landscape.ui.shared.SSHKeyPairDTO;
|
||||
@@ -43,6 +46,7 @@ import com.sap.sse.security.ui.server.SecurityDTOUtil;
|
||||
|
||||
import software.amazon.awssdk.services.ec2.model.InstanceType;
|
||||
import software.amazon.awssdk.services.ec2.model.KeyPairInfo;
|
||||
import software.amazon.awssdk.services.sts.model.Credentials;
|
||||
|
||||
public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRemoteServiceServlet
|
||||
implements LandscapeManagementWriteService {
|
||||
@@ -51,6 +55,8 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
|
||||
private static final Optional<Duration> IMAGE_UPGRADE_TIMEOUT = Optional.of(Duration.ONE_MINUTE.times(10));
|
||||
|
||||
private final FullyInitializedReplicableTracker<SecurityService> securityServiceTracker;
|
||||
|
||||
private static final String USER_PREFERENCE_FOR_SESSION_TOKEN = "___aws.session.token___";
|
||||
|
||||
public <ShardingKey, MetricsT extends ApplicationProcessMetrics,
|
||||
ProcessT extends ApplicationProcess<ShardingKey, MetricsT, ProcessT>> LandscapeManagementWriteServiceImpl() {
|
||||
@@ -66,10 +72,67 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For the logged-in user checks the LANDSCAPE:MANAGE:AWS permission, and if present, tries to obtain the user preference
|
||||
* named like {@link #USER_PREFERENCE_FOR_SESSION_TOKEN}. If found and not yet expired, they are returned. Otherwise,
|
||||
* {@code null} is returned, indicating to the caller that new session credentials shall be obtained which shall then be
|
||||
* stored to the user preference again for future reference.
|
||||
*/
|
||||
private AwsSessionCredentialsWithExpiry getSessionCredentials() {
|
||||
final AwsSessionCredentialsWithExpiry result;
|
||||
checkLandscapeManageAwsPermission();
|
||||
final AwsSessionCredentialsFromUserPreference credentialsPreferences = getSecurityService().getPreferenceObject(
|
||||
getSecurityService().getCurrentUser().getName(), USER_PREFERENCE_FOR_SESSION_TOKEN);
|
||||
if (credentialsPreferences != null) {
|
||||
final AwsSessionCredentialsWithExpiry credentials = credentialsPreferences.getAwsSessionCredentialsWithExpiry();
|
||||
if (credentials.getExpiration().after(TimePoint.now())) {
|
||||
result = null;
|
||||
} else {
|
||||
result = credentials;
|
||||
}
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a combination of an AWS access key ID, the corresponding secret plus an MFA token code produces new session
|
||||
* credentials and stores them in the user's preference store from where they can be obtained again using
|
||||
* {@link #getSessionCredentials()}. Any session credentials previously stored in the current user's preference store
|
||||
* will be overwritten by this. The current user must have the {@code LANDSCAPE:MANAGE:AWS} permission.
|
||||
*/
|
||||
@Override
|
||||
public ArrayList<String> getRegions() {
|
||||
public void createMfaSessionCredentials(String awsAccessKey, String awsSecret, String mfaTokenCode) {
|
||||
checkLandscapeManageAwsPermission();
|
||||
final Credentials credentials = getLandscape(awsAccessKey, awsSecret).getMfaSessionCredentials(mfaTokenCode);
|
||||
final AwsSessionCredentialsWithExpiryImpl result = new AwsSessionCredentialsWithExpiryImpl(
|
||||
credentials.accessKeyId(), credentials.secretAccessKey(), credentials.sessionToken(),
|
||||
TimePoint.of(credentials.expiration().toEpochMilli()));
|
||||
final AwsSessionCredentialsFromUserPreference credentialsPreferences = new AwsSessionCredentialsFromUserPreference(result);
|
||||
getSecurityService().setPreferenceObject(
|
||||
getSecurityService().getCurrentUser().getName(), USER_PREFERENCE_FOR_SESSION_TOKEN, credentialsPreferences);
|
||||
}
|
||||
|
||||
/**
|
||||
* For the current user who has to have the {@code LANDSCAPE:MANAGE:AWS} permission, clears the preference in the
|
||||
* user's preference store which holds any session credentials created previously using
|
||||
* {@link #createMfaSessionCredentials(String, String, String)}.
|
||||
*/
|
||||
@Override
|
||||
public void clearSessionCredentials() {
|
||||
checkLandscapeManageAwsPermission();
|
||||
getSecurityService().unsetPreference(getSecurityService().getCurrentUser().getName(), USER_PREFERENCE_FOR_SESSION_TOKEN);
|
||||
}
|
||||
|
||||
private void checkLandscapeManageAwsPermission() {
|
||||
SecurityUtils.getSubject().checkPermission(SecuredLandscapeTypes.LANDSCAPE.getStringPermissionForTypeRelativeIdentifier(SecuredLandscapeTypes.LandscapeActions.MANAGE,
|
||||
new TypeRelativeObjectIdentifier("AWS")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<String> getRegions() {
|
||||
checkLandscapeManageAwsPermission();
|
||||
final ArrayList<String> result = new ArrayList<>();
|
||||
Util.addAll(Util.map(AwsLandscape.obtain().getRegions(), r->r.getId()), result);
|
||||
return result;
|
||||
@@ -84,10 +147,9 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
|
||||
|
||||
@Override
|
||||
public ArrayList<MongoEndpointDTO> getMongoEndpoints(String awsAccessKey, String awsSecret, String region) {
|
||||
SecurityUtils.getSubject().checkPermission(SecuredLandscapeTypes.LANDSCAPE.getStringPermissionForTypeRelativeIdentifier(SecuredLandscapeTypes.LandscapeActions.MANAGE,
|
||||
new TypeRelativeObjectIdentifier("AWS")));
|
||||
checkLandscapeManageAwsPermission();
|
||||
final ArrayList<MongoEndpointDTO> result = new ArrayList<>();
|
||||
for (final MongoEndpoint mongoEndpoint : AwsLandscape.obtain(awsAccessKey, awsSecret).getMongoEndpoints(new AwsRegion(region))) {
|
||||
for (final MongoEndpoint mongoEndpoint : getLandscape(awsAccessKey, awsSecret).getMongoEndpoints(new AwsRegion(region))) {
|
||||
final MongoEndpointDTO dto;
|
||||
if (mongoEndpoint.isReplicaSet()) {
|
||||
final MongoReplicaSet replicaSet = mongoEndpoint.asMongoReplicaSet();
|
||||
@@ -105,6 +167,23 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
|
||||
return result;
|
||||
}
|
||||
|
||||
private AwsLandscape<String> getLandscape(String awsAccessKey, String awsSecret) {
|
||||
final String keyId;
|
||||
final String secret;
|
||||
final Optional<String> sessionToken;
|
||||
final AwsSessionCredentialsWithExpiry sessionCredentials = getSessionCredentials();
|
||||
if (sessionCredentials != null) {
|
||||
keyId = sessionCredentials.getAccessKeyId();
|
||||
secret = sessionCredentials.getSecretAccessKey();
|
||||
sessionToken = Optional.of(sessionCredentials.getSessionToken());
|
||||
} else {
|
||||
keyId = awsAccessKey;
|
||||
secret = awsSecret;
|
||||
sessionToken = Optional.empty();
|
||||
}
|
||||
return AwsLandscape.obtain(keyId, secret, sessionToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoEndpointDTO getMongoEndpoint(String awsAccessKey, String awsSecret, String region, String replicaSetName) {
|
||||
return getMongoEndpoints(awsAccessKey, awsSecret, region).stream().filter(mep->Util.equalsWithNull(mep.getReplicaSetName(), replicaSetName)).findAny().orElse(null);
|
||||
@@ -118,7 +197,7 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
|
||||
final SSHKeyPair keyPair = getSecurityService().setOwnershipCheckPermissionForObjectCreationAndRevertOnError(dummyKeyPairForSecurityCheck.getPermissionType(),
|
||||
dummyKeyPairForSecurityCheck.getIdentifier().getTypeRelativeObjectIdentifier(), keyName,
|
||||
()->{
|
||||
return AwsLandscape.obtain(awsAccessKey, awsSecret)
|
||||
return getLandscape(awsAccessKey, awsSecret)
|
||||
.createKeyPair(new AwsRegion(regionId), keyName, privateKeyEncryptionPassphrase.getBytes());
|
||||
});
|
||||
return convertToSSHKeyPairDTO(keyPair);
|
||||
@@ -133,7 +212,7 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
|
||||
final SSHKeyPair keyPair = getSecurityService().setOwnershipCheckPermissionForObjectCreationAndRevertOnError(dummyKeyPairForSecurityCheck.getPermissionType(),
|
||||
dummyKeyPairForSecurityCheck.getIdentifier().getTypeRelativeObjectIdentifier(), keyName,
|
||||
()->{
|
||||
return AwsLandscape.obtain(awsAccessKey, awsSecret)
|
||||
return getLandscape(awsAccessKey, awsSecret)
|
||||
.importKeyPair(new AwsRegion(regionId), publicKey.getBytes(), encryptedPrivateKey.getBytes(), keyName);
|
||||
});
|
||||
return convertToSSHKeyPairDTO(keyPair);
|
||||
@@ -148,7 +227,7 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
|
||||
@Override
|
||||
public ArrayList<SSHKeyPairDTO> getSshKeys(String awsAccessKey, String awsSecret, String regionId) {
|
||||
final ArrayList<SSHKeyPairDTO> result = new ArrayList<>();
|
||||
final AwsLandscape<String> landscape = AwsLandscape.obtain(awsAccessKey, awsSecret);
|
||||
final AwsLandscape<String> landscape = getLandscape(awsAccessKey, awsSecret);
|
||||
final AwsRegion region = new AwsRegion(regionId);
|
||||
for (final KeyPairInfo keyPairInfo : landscape.getAllKeyPairInfos(region)) {
|
||||
final SSHKeyPair key = landscape.getSSHKeyPair(region, keyPairInfo.keyName());
|
||||
@@ -164,7 +243,7 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
|
||||
@Override
|
||||
public void removeSshKey(String awsAccessKey, String awsSecret, SSHKeyPairDTO keyPair) {
|
||||
getSecurityService().checkPermissionAndDeleteOwnershipForObjectRemoval(keyPair,
|
||||
()->AwsLandscape.obtain(awsAccessKey, awsSecret).deleteKeyPair(new AwsRegion(keyPair.getRegionId()), keyPair.getName()));
|
||||
()->getLandscape(awsAccessKey, awsSecret).deleteKeyPair(new AwsRegion(keyPair.getRegionId()), keyPair.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -185,11 +264,10 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
|
||||
|
||||
@Override
|
||||
public ArrayList<AmazonMachineImageDTO> getAmazonMachineImages(String awsAccessKey, String awsSecret, String region) {
|
||||
SecurityUtils.getSubject().checkPermission(SecuredLandscapeTypes.LANDSCAPE.getStringPermissionForTypeRelativeIdentifier(SecuredLandscapeTypes.LandscapeActions.MANAGE,
|
||||
new TypeRelativeObjectIdentifier("AWS")));
|
||||
checkLandscapeManageAwsPermission();
|
||||
final ArrayList<AmazonMachineImageDTO> result = new ArrayList<>();
|
||||
final AwsRegion awsRegion = new AwsRegion(region);
|
||||
final AwsLandscape<String> landscape = AwsLandscape.obtain(awsAccessKey, awsSecret);
|
||||
final AwsLandscape<String> landscape = AwsLandscape.obtain(awsAccessKey, awsSecret, /* sessionToken */ Optional.empty());
|
||||
for (final String imageType : landscape.getMachineImageTypes(awsRegion)) {
|
||||
for (final AmazonMachineImage<String> machineImage : landscape.getAllImagesWithType(awsRegion, imageType)) {
|
||||
final AmazonMachineImageDTO dto = new AmazonMachineImageDTO(machineImage.getId(),
|
||||
@@ -203,18 +281,16 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
|
||||
|
||||
@Override
|
||||
public void removeAmazonMachineImage(String awsAccessKey, String awsSecret, String region, String machineImageId) {
|
||||
SecurityUtils.getSubject().checkPermission(SecuredLandscapeTypes.LANDSCAPE.getStringPermissionForTypeRelativeIdentifier(SecuredLandscapeTypes.LandscapeActions.MANAGE,
|
||||
new TypeRelativeObjectIdentifier("AWS")));
|
||||
final AwsLandscape<String> landscape = AwsLandscape.obtain(awsAccessKey, awsSecret);
|
||||
checkLandscapeManageAwsPermission();
|
||||
final AwsLandscape<String> landscape = AwsLandscape.obtain(awsAccessKey, awsSecret, /* sessionToken */ Optional.empty());
|
||||
final AmazonMachineImage<String> ami = landscape.getImage(new AwsRegion(region), machineImageId);
|
||||
ami.delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AmazonMachineImageDTO upgradeAmazonMachineImage(String awsAccessKey, String awsSecret, String region, String machineImageId) throws Exception {
|
||||
SecurityUtils.getSubject().checkPermission(SecuredLandscapeTypes.LANDSCAPE.getStringPermissionForTypeRelativeIdentifier(SecuredLandscapeTypes.LandscapeActions.MANAGE,
|
||||
new TypeRelativeObjectIdentifier("AWS")));
|
||||
final AwsLandscape<String> landscape = AwsLandscape.obtain(awsAccessKey, awsSecret);
|
||||
checkLandscapeManageAwsPermission();
|
||||
final AwsLandscape<String> landscape = AwsLandscape.obtain(awsAccessKey, awsSecret, /* sessionToken */ Optional.empty());
|
||||
final AwsRegion awsRegion = new AwsRegion(region);
|
||||
final AmazonMachineImage<String> ami = landscape.getImage(awsRegion, machineImageId);
|
||||
final UpgradeAmi.Builder<?, String, SailingAnalyticsProcess<String>> upgradeAmiBuilder = UpgradeAmi.builder();
|
||||
@@ -234,7 +310,7 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
|
||||
if (mongoScalingInstructions.getReplicaSetName() == null) {
|
||||
throw new IllegalArgumentException("Can only scale MongoDB Replica Sets, not standalone instances");
|
||||
}
|
||||
final AwsLandscape<String> landscape = AwsLandscape.obtain(awsAccessKey, awsSecret);
|
||||
final AwsLandscape<String> landscape = AwsLandscape.obtain(awsAccessKey, awsSecret, /* sessionToken */ Optional.empty());
|
||||
final AwsRegion region = new AwsRegion(regionId);
|
||||
for (int i=0; i<mongoScalingInstructions.getLaunchParameters().getNumberOfInstances(); i++) {
|
||||
final StartMongoDBServer.Builder<?, String, MongoProcessInReplicaSet> startMongoProcessBuilder = StartMongoDBServer.builder();
|
||||
|
||||
+40
@@ -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()));
|
||||
}
|
||||
}
|
||||
+10
@@ -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();
|
||||
}
|
||||
+39
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
* <p>
|
||||
*
|
||||
* Clients may also create dedicated instances of this service wrapper, using their own credentials. See
|
||||
* {@link #obtain(String, String)}.
|
||||
* {@link #obtain(String, String, Optional)}.
|
||||
* <p>
|
||||
*
|
||||
* This object interacts with an instance of {@link AwsLandscapeState} which keeps persistent and replicable state about
|
||||
@@ -118,7 +119,9 @@ public interface AwsLandscape<ShardingKey> extends Landscape<ShardingKey> {
|
||||
* 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 <ShardingKey, MetricsT extends ApplicationProcessMetrics,
|
||||
ProcessT extends ApplicationProcess<ShardingKey, MetricsT, ProcessT>>
|
||||
@@ -134,8 +137,8 @@ public interface AwsLandscape<ShardingKey> extends Landscape<ShardingKey> {
|
||||
*/
|
||||
static <ShardingKey, MetricsT extends ApplicationProcessMetrics,
|
||||
ProcessT extends ApplicationProcess<ShardingKey, MetricsT, ProcessT>>
|
||||
AwsLandscape<ShardingKey> obtain(String accessKey, String secret) {
|
||||
final AwsLandscape<ShardingKey> result = new AwsLandscapeImpl<>(Activator.getInstance().getLandscapeState(), accessKey, secret);
|
||||
AwsLandscape<ShardingKey> obtain(String accessKey, String secret, Optional<String> mfaTokenCode) {
|
||||
final AwsLandscape<ShardingKey> result = new AwsLandscapeImpl<>(Activator.getInstance().getLandscapeState(), accessKey, secret, mfaTokenCode);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -556,4 +559,10 @@ public interface AwsLandscape<ShardingKey> extends Landscape<ShardingKey> {
|
||||
String tagName, BiFunction<Host, String, ProcessT> processFactoryFromHostAndServerDirectory,
|
||||
Optional<Duration> optionalTimeout, Optional<String> 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);
|
||||
|
||||
}
|
||||
|
||||
+24
-8
@@ -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<ShardingKey> implements AwsLandscape<ShardingKey> {
|
||||
private static final String DEFAULT_TARGET_GROUP_PREFIX = "D";
|
||||
@@ -150,25 +153,26 @@ public class AwsLandscapeImpl<ShardingKey> implements AwsLandscape<ShardingKey>
|
||||
private static final String DEFAULT_NON_DNS_MAPPED_ALB_NAME = "DefDyn";
|
||||
private final String accessKeyId;
|
||||
private final String secretAccessKey;
|
||||
private final Optional<String> 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<String> 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<String> 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<ShardingKey> implements AwsLandscape<ShardingKey>
|
||||
}
|
||||
|
||||
private Route53Client getRoute53Client() {
|
||||
return Route53Client.builder().region(getRegion(globalRegion)).build();
|
||||
return getClient(Route53Client.builder(), getRegion(globalRegion));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -577,8 +581,20 @@ public class AwsLandscapeImpl<ShardingKey> implements AwsLandscape<ShardingKey>
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
-1
@@ -52,7 +52,6 @@ public class PreferenceConverterRegistrationManager implements Stoppable {
|
||||
}
|
||||
|
||||
private class Cutomizer implements ServiceTrackerCustomizer<PreferenceConverter<?>, PreferenceConverter<?>> {
|
||||
|
||||
@Override
|
||||
public PreferenceConverter<?> addingService(ServiceReference<PreferenceConverter<?>> reference) {
|
||||
final String preferenceKey = (String) reference.getProperty(PreferenceConverter.KEY_PARAMETER_NAME);
|
||||
|
||||
+2
-2
@@ -30,8 +30,8 @@ public interface UserStore extends BasicUserStore {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
|
||||
@@ -307,13 +307,13 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica
|
||||
|
||||
/**
|
||||
* Gets a preference object. Always returns null if there is no converter associated with the given key -> see
|
||||
* {@link #registerPreferenceConverter(String, PreferenceConverter)}.
|
||||
* {@link UserStore#registerPreferenceConverter(String, PreferenceConverter)}.
|
||||
*/
|
||||
<T> 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)}.
|
||||
*/
|
||||
<T> Map<String, T> getPreferenceObjectsByKey(String key);
|
||||
|
||||
|
||||
+1
-2
@@ -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<PREF extends GenericSerializableSett
|
||||
public PREF toPreferenceObject(String stringPreference) {
|
||||
return serializer.deserialize(emptyInstanceFactory.get(), stringPreference);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user