bug4811: started to make SSHKeyPair a secured type and adding security-aware UI support for SSH key management

This commit is contained in:
Axel Uhl
2021-01-14 11:18:11 +01:00
parent f0e12ad1a3
commit 885c490f9a
13 changed files with 134 additions and 37 deletions
@@ -6,3 +6,6 @@ Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Automatic-Module-Name: com.sap.sailing.landscape.common
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Require-Bundle: com.sap.sse.security.common,
com.sap.sse.common
Export-Package: com.sap.sailing.landscape.common
@@ -0,0 +1,50 @@
package com.sap.sailing.landscape.common;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import com.sap.sse.security.shared.HasPermissions;
import com.sap.sse.security.shared.impl.HasPermissionsImpl;
import com.sap.sse.security.shared.impl.SecuredSecurityTypes;
/**
* The basic types of logical objects provided by the landscape bundle that themselves have permissions governing how
* users may deal with them.
*
* @author Axel Uhl (d043530)
*
*/
public class SecuredLandscapeTypes extends HasPermissionsImpl {
private static final long serialVersionUID = -5052828472297142038L;
private static Set<HasPermissions> allInstances = new HashSet<>();
public SecuredLandscapeTypes(String logicalTypeName, Action... availableActions) {
super(logicalTypeName, availableActions);
allInstances.add(this);
}
public SecuredLandscapeTypes(String logicalTypeName) {
super(logicalTypeName);
allInstances.add(this);
}
public static Iterable<HasPermissions> getAllInstances() {
return Collections.unmodifiableSet(allInstances);
}
/**
* The type-relative identifier consists of the region ID and the key name.
*/
public static final HasPermissions SSH_KEY = new SecuredLandscapeTypes("SSH_KEY");
public static enum LandscapeActions implements Action {
MANAGE;
private static final Action[] ALL_ACTIONS = new Action[] { MANAGE,
DefaultActions.CHANGE_OWNERSHIP, DefaultActions.CHANGE_ACL };
}
public static final HasPermissions LANDSCAPE = new SecuredSecurityTypes("LANDSCAPE", LandscapeActions.ALL_ACTIONS);
}
@@ -0,0 +1,6 @@
package com.sap.sailing.landscape.ui.client;
public interface AwsAccessKeyProvider {
String getAwsAccessKeyId();
String getAwsSecret();
}
@@ -60,7 +60,7 @@ import com.sap.sse.security.ui.client.UserService;
* @author Axel Uhl (D043530)
*
*/
public class LandscapeManagementPanel extends VerticalPanel {
public class LandscapeManagementPanel extends VerticalPanel implements AwsAccessKeyProvider {
private final LandscapeManagementWriteServiceAsync landscapeManagementService;
private final TableWrapperWithSingleSelectionAndFilter<String, StringMessages, AdminConsoleTableResources> regionsTable;
private final TableWrapperWithSingleSelectionAndFilter<MongoEndpointDTO, StringMessages, AdminConsoleTableResources> mongoEndpointsTable;
@@ -104,7 +104,8 @@ public class LandscapeManagementPanel extends VerticalPanel {
awsCredentialsGrid.setWidget(1, 0, new Label(stringMessages.awsSecret()));
awsSecretPasswordTextBox = new PasswordTextBox();
awsCredentialsGrid.setWidget(1, 1, awsSecretPasswordTextBox);
final SshKeyManagementPanel sshKeyManagementPanel = new SshKeyManagementPanel(stringMessages, userService, landscapeManagementService, tableResources, errorReporter);
final SshKeyManagementPanel sshKeyManagementPanel = new SshKeyManagementPanel(stringMessages, userService,
landscapeManagementService, tableResources, errorReporter, /* access key provider */ this);
final CaptionPanel sshKeysCaptionPanel = new CaptionPanel(stringMessages.sshKeys());
awsCredentialsAndSshKeys.add(sshKeysCaptionPanel);
sshKeysCaptionPanel.add(sshKeyManagementPanel);
@@ -214,4 +215,14 @@ public class LandscapeManagementPanel extends VerticalPanel {
RemoteServiceMappingConstants.landscapeManagementServiceRemotePath, HEADER_FORWARD_TO_MASTER);
return result;
}
@Override
public String getAwsAccessKeyId() {
return awsAccessKeyTextBox.getValue();
}
@Override
public String getAwsSecret() {
return awsSecretPasswordTextBox.getValue();
}
}
@@ -14,4 +14,6 @@ public interface LandscapeManagementWriteService extends RemoteService {
MongoEndpointDTO getMongoEndpoint(String awsAccessKey, String awsSecret, String region, String replicaSetName);
ArrayList<SSHKeyPairDTO> getSshKeys(String awsAccessKey, String awsSecret, String regionId);
void removeSshKey(String awsAccessKey, String awsSecret, SSHKeyPairDTO keyPair);
}
@@ -17,4 +17,6 @@ public interface LandscapeManagementWriteServiceAsync {
void getSshKeys(String awsAccessKey, String awsSecret, String regionId,
AsyncCallback<ArrayList<SSHKeyPairDTO>> callback);
void removeSshKey(String awsAccessKey, String awsSecret, SSHKeyPairDTO keyPair, AsyncCallback<Void> asyncCallback);
}
@@ -6,6 +6,7 @@ import java.util.Optional;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.sap.sailing.landscape.common.SecuredLandscapeTypes;
import com.sap.sailing.landscape.ui.client.i18n.StringMessages;
import com.sap.sailing.landscape.ui.shared.SSHKeyPairDTO;
import com.sap.sse.common.Util;
@@ -16,18 +17,24 @@ import com.sap.sse.gwt.client.celltable.TableWrapperWithSingleSelectionAndFilter
import com.sap.sse.gwt.client.controls.busyindicator.BusyIndicator;
import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator;
import com.sap.sse.security.ui.client.UserService;
import com.sap.sse.security.ui.client.component.AccessControlledButtonPanel;
public class SshKeyManagementPanel extends VerticalPanel {
private final LandscapeManagementWriteServiceAsync landscapeManagementService;
private final TableWrapperWithSingleSelectionAndFilter<SSHKeyPairDTO, StringMessages, AdminConsoleTableResources> sshKeyTable;
private final BusyIndicator sshKeyLoadingBusy;
private final ErrorReporter errorReporter;
public SshKeyManagementPanel(StringMessages stringMessages, UserService userService,
LandscapeManagementWriteServiceAsync landscapeManagementService, AdminConsoleTableResources tableResources,
ErrorReporter errorReporter) {
ErrorReporter errorReporter, AwsAccessKeyProvider awsAccessKeyProvider) {
this.landscapeManagementService = landscapeManagementService;
this.errorReporter = errorReporter;
final AccessControlledButtonPanel buttonPanel = new AccessControlledButtonPanel(userService, SecuredLandscapeTypes.SSH_KEY);
add(buttonPanel);
buttonPanel.addCreateAction(stringMessages.add(), ()->{
// TODO here goes the add SSH key logic: pop up a dialog for file upload, local generation, generation in AWS, and text areas for pasting keys as text
});
sshKeyTable =
new TableWrapperWithSingleSelectionAndFilter<SSHKeyPairDTO, StringMessages, AdminConsoleTableResources>(stringMessages, errorReporter, /* enablePager */ true,
Optional.of(new EntityIdentityComparator<SSHKeyPairDTO>() {
@@ -55,6 +62,18 @@ public class SshKeyManagementPanel extends VerticalPanel {
add(sshKeyTable);
sshKeyLoadingBusy = new SimpleBusyIndicator();
add(sshKeyLoadingBusy);
buttonPanel.addRemoveAction(stringMessages.remove(), sshKeyTable.getSelectionModel(), /* withConfirmation */ true, ()->{
landscapeManagementService.removeSshKey(awsAccessKeyProvider.getAwsAccessKeyId(), awsAccessKeyProvider.getAwsSecret(),
sshKeyTable.getSelectionModel().getSelectedObject(), new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(caught.getMessage());
}
@Override
public void onSuccess(Void result) {}
});
});
}
public void showKeysInRegion(String awsAccessKey, String awsSecret, String regionId) {
@@ -5,6 +5,7 @@ import java.util.Collections;
import org.apache.shiro.SecurityUtils;
import com.sap.sailing.landscape.common.SecuredLandscapeTypes;
import com.sap.sailing.landscape.ui.client.LandscapeManagementWriteService;
import com.sap.sailing.landscape.ui.shared.MongoEndpointDTO;
import com.sap.sailing.landscape.ui.shared.SSHKeyPairDTO;
@@ -19,7 +20,7 @@ import com.sap.sse.landscape.mongodb.MongoProcess;
import com.sap.sse.landscape.mongodb.MongoProcessInReplicaSet;
import com.sap.sse.landscape.mongodb.MongoReplicaSet;
import com.sap.sse.landscape.ssh.SSHKeyPair;
import com.sap.sse.security.shared.impl.SecuredSecurityTypes;
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
import software.amazon.awssdk.services.ec2.model.KeyPairInfo;
@@ -32,7 +33,7 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
@Override
public ArrayList<String> getRegions() {
SecurityUtils.getSubject().checkPermission(SecuredSecurityTypes.LANDSCAPE.getStringPermission(SecuredSecurityTypes.LandscapeActions.MANAGE));
SecurityUtils.getSubject().checkPermission(SecuredLandscapeTypes.LANDSCAPE.getStringPermission(SecuredLandscapeTypes.LandscapeActions.MANAGE));
final ArrayList<String> result = new ArrayList<>();
Util.addAll(Util.map(AwsLandscape.obtain().getRegions(), r->r.getId()), result);
return result;
@@ -40,7 +41,7 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
@Override
public ArrayList<MongoEndpointDTO> getMongoEndpoints(String awsAccessKey, String awsSecret, String region) {
SecurityUtils.getSubject().checkPermission(SecuredSecurityTypes.LANDSCAPE.getStringPermission(SecuredSecurityTypes.LandscapeActions.MANAGE));
SecurityUtils.getSubject().checkPermission(SecuredLandscapeTypes.LANDSCAPE.getStringPermission(SecuredLandscapeTypes.LandscapeActions.MANAGE));
final ArrayList<MongoEndpointDTO> result = new ArrayList<>();
for (final MongoEndpoint mongoEndpoint : AwsLandscape.obtain(awsAccessKey, awsSecret).getMongoEndpoints(new AwsRegion(region))) {
final MongoEndpointDTO dto;
@@ -78,4 +79,10 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
}
return result;
}
@Override
public void removeSshKey(String awsAccessKey, String awsSecret, SSHKeyPairDTO keyPair) {
SecurityUtils.getSubject().checkPermission(SecuredLandscapeTypes.SSH_KEY.getStringPermission(DefaultActions.DELETE));
AwsLandscape.obtain(awsAccessKey, awsSecret).deleteKeyPair(new AwsRegion(keyPair.getRegionId()), keyPair.getName());
}
}
@@ -1,23 +1,21 @@
package com.sap.sailing.landscape.ui.shared;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.sap.sailing.landscape.common.SecuredLandscapeTypes;
import com.sap.sse.common.TimePoint;
import com.sap.sse.security.shared.HasPermissions;
import com.sap.sse.security.shared.QualifiedObjectIdentifier;
import com.sap.sse.security.shared.TypeRelativeObjectIdentifier;
import com.sap.sse.security.shared.dto.NamedSecuredObjectDTO;
public class SSHKeyPairDTO implements IsSerializable {
private String regionId;
private String name;
private String creatorName;
private TimePoint creationTime;
@Deprecated
SSHKeyPairDTO() {
// for GWT RPC serialization only
}
public class SSHKeyPairDTO extends NamedSecuredObjectDTO {
private static final long serialVersionUID = -9174909996567452216L;
private final String regionId;
private final String creatorName;
private final TimePoint creationTime;
public SSHKeyPairDTO(String regionId, String name, String creatorName, TimePoint creationTime) {
super();
super(name);
this.regionId = regionId;
this.name = name;
this.creatorName = creatorName;
this.creationTime = creationTime;
}
@@ -26,10 +24,6 @@ public class SSHKeyPairDTO implements IsSerializable {
return regionId;
}
public String getName() {
return name;
}
public String getCreatorName() {
return creatorName;
}
@@ -37,4 +31,18 @@ public class SSHKeyPairDTO implements IsSerializable {
public TimePoint getCreationTime() {
return creationTime;
}
@Override
public QualifiedObjectIdentifier getIdentifier() {
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
}
public TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier() {
return new TypeRelativeObjectIdentifier(getRegionId(), getName());
}
@Override
public HasPermissions getPermissionType() {
return SecuredLandscapeTypes.SSH_KEY;
}
}
@@ -3,7 +3,6 @@ package com.sap.sse.security.shared.dto;
import com.sap.sse.security.shared.WithQualifiedObjectIdentifier;
public interface SecuredDTO extends WithQualifiedObjectIdentifier {
AccessControlListDTO getAccessControlList();
OwnershipDTO getOwnership();
@@ -108,13 +108,4 @@ public class SecuredSecurityTypes extends HasPermissionsImpl {
* type-relative identifier is the server name
*/
public static final HasPermissions SERVER = new SecuredSecurityTypes("SERVER", ServerActions.ALL_ACTIONS);
public static enum LandscapeActions implements Action {
MANAGE;
private static final Action[] ALL_ACTIONS = new Action[] { MANAGE,
DefaultActions.CHANGE_OWNERSHIP, DefaultActions.CHANGE_ACL };
}
public static final HasPermissions LANDSCAPE = new SecuredSecurityTypes("LANDSCAPE", LandscapeActions.ALL_ACTIONS);
}
@@ -141,8 +141,8 @@ public class AccessControlledButtonPanel extends Composite {
if (selectionModel == null) {
throw new IllegalArgumentException("Selection model for a remove action must not be null");
}
ClickHandler handler = wrap(removePermissionCheck, callback);
Button button = withConfirmation
final ClickHandler handler = wrap(removePermissionCheck, callback);
final Button button = withConfirmation
? new SelectedElementsCountingButton<T>(text, selectionModel, StringMessages.INSTANCE::doYouReallyWantToRemoveSelectedElements,
handler)
: new SelectedElementsCountingButton<T>(text, selectionModel, handler);
@@ -19,7 +19,6 @@ import com.sap.sse.common.Named;
* @author Dmitry Bilyk
*
*/
public class SelectedElementsCountingButton<T extends Named> extends Button {
/**
* Constructs the button without a confirmation callback installed. The {@code clickHandler}