locked IPs turned into permission object instead of server action

Locked IPs were previously not treated as SecuredSecurityType, but
rather their actions were treated as separate ServerAction instances
with the object in question remaining the server. To maintain permission
granularity at the IP address level, Locked IP addresses have been made
HasPermissions object in SecuredSecurityType with DefaultActions READ
and DELETE serving as get and unlock method permissions respectively
that can now be treated as the object wildcard of permission triples.
This commit is contained in:
Sohaib Baig committed 2025-11-13 13:20:53 +01:00
1 parent 89edd71d65
commit 8ca8dd20eb
6 files changed
+104 -70

No files matched your search

@@ -23,11 +23,9 @@ import com.sap.sse.gwt.client.ErrorReporter;
import com.sap.sse.gwt.client.celltable.EntityIdentityComparator;
import com.sap.sse.gwt.client.celltable.RefreshableSelectionModel;
import com.sap.sse.gwt.client.panels.LabeledAbstractFilterablePanel;
import com.sap.sse.security.shared.AdminRole;
import com.sap.sse.security.shared.ServerAdminRole;
import com.sap.sse.security.shared.dto.RoleWithSecurityDTO;
import com.sap.sse.security.shared.dto.UserDTO;
import com.sap.sse.security.shared.impl.SecuredSecurityTypes.ServerActions;
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
import com.sap.sse.security.shared.WildcardPermission;
import com.sap.sse.security.shared.impl.SecuredSecurityTypes;
import com.sap.sse.security.ui.client.UserService;
import com.sap.sse.security.ui.client.component.SelectedElementsCountingButton;
@@ -35,7 +33,6 @@ abstract class IPBlocklistTableWrapper
extends TableWrapper<IpToTimedLockDTO, RefreshableSelectionModel<IpToTimedLockDTO>> {
private final UserService userService;
private final LabeledAbstractFilterablePanel<IpToTimedLockDTO> filterField;
private final ServerActions unlockAction;
private final String errorMessageOnDataFailureString;
protected abstract void fetchData(AsyncCallback<HashMap<String, TimedLock>> callback);
@@ -43,8 +40,8 @@ abstract class IPBlocklistTableWrapper
protected abstract void unlockIP(String ip, AsyncCallback<Void> asyncCallback);
public IPBlocklistTableWrapper(final SailingServiceWriteAsync sailingServiceWrite, final UserService userService,
final ServerActions unlockAction, final String errorMessageOnDataFailureString,
final StringMessages stringMessages, final ErrorReporter errorReporter) {
final String errorMessageOnDataFailureString, final StringMessages stringMessages,
final ErrorReporter errorReporter) {
super(sailingServiceWrite, stringMessages, errorReporter, true, true,
new EntityIdentityComparator<IpToTimedLockDTO>() {
@Override
@@ -57,7 +54,6 @@ abstract class IPBlocklistTableWrapper
return t.ip.hashCode();
}
});
this.unlockAction = unlockAction;
this.userService = userService;
this.errorMessageOnDataFailureString = errorMessageOnDataFailureString;
this.filterField = composeFilterField();
@@ -81,26 +77,6 @@ abstract class IPBlocklistTableWrapper
mainPanel.setSpacing(5);
}
// admin, server admin and those with the permission can all unlock
private boolean canUnlock() {
final UserDTO user = userService.getCurrentUser();
final Iterable<RoleWithSecurityDTO> roles = user.getRoles();
boolean isAdmin = false;
boolean isServerAdmin = false;
final boolean hasUnlockPermission = userService.hasServerPermission(unlockAction);
for (RoleWithSecurityDTO role : roles) {
isAdmin = role.getName().equals(AdminRole.getInstance().getName());
if (isAdmin) {
break;
}
isServerAdmin = role.getName().equals(ServerAdminRole.getInstance().getName());
if (isServerAdmin) {
break;
}
}
return isAdmin || isServerAdmin || hasUnlockPermission;
}
private Widget composeButtonPanel() {
final HorizontalPanel buttonPanel = new HorizontalPanel();
buttonPanel.setSpacing(5);
@@ -112,32 +88,42 @@ abstract class IPBlocklistTableWrapper
});
refreshButton.ensureDebugId("refreshButton");
buttonPanel.add(refreshButton);
if (canUnlock()) {
final Button unlockButton = new SelectedElementsCountingButton<IpToTimedLockDTO>(
getStringMessages().unlock(), getSelectionModel(), new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
for (IpToTimedLockDTO e : getSelectionModel().getSelectedSet()) {
unlockIP(e.ip, new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(errorMessageOnDataFailureString);
}
@Override
public void onSuccess(Void result) {
filterField.remove(e);
}
});
}
}
});
if (hasUnlockPermission()) {
final Button unlockButton = composeUnlockButton();
unlockButton.ensureDebugId("unlockButton");
buttonPanel.add(unlockButton);
}
return buttonPanel;
}
private boolean hasUnlockPermission() {
final WildcardPermission unlockIpPermission = SecuredSecurityTypes.LOCKED_IP
.getPermission(DefaultActions.DELETE);
return userService.hasPermission(unlockIpPermission, userService.getServerInfo().getOwnership());
}
private SelectedElementsCountingButton<IpToTimedLockDTO> composeUnlockButton() {
return new SelectedElementsCountingButton<IpToTimedLockDTO>(getStringMessages().unlock(), getSelectionModel(),
new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
for (IpToTimedLockDTO e : getSelectionModel().getSelectedSet()) {
unlockIP(e.ip, new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(errorMessageOnDataFailureString);
}
@Override
public void onSuccess(Void result) {
filterField.remove(e);
}
});
}
}
});
}
private void loadDataAndPopulateTable() {
final AsyncCallback<HashMap<String, TimedLock>> dataInitializationCallback = new AsyncCallback<HashMap<String, TimedLock>>() {
@Override
@@ -152,7 +152,6 @@ public class LocalServerManagementPanel extends SimplePanel {
3);
panel.ensureDebugId("bearerTokenAbusePanel");
final IPBlocklistTableWrapper table = new IPBlocklistTableWrapper(sailingService, userService,
ServerActions.UNLOCK_IPS_BLOCKED_FOR_BEARER_TOKEN_ABUSE,
stringMessages.unableToLoadIpsBlockedForBearerTokenAbuse(), stringMessages, errorReporter) {
@Override
protected void fetchData(AsyncCallback<HashMap<String, TimedLock>> callback) {
@@ -173,7 +172,6 @@ public class LocalServerManagementPanel extends SimplePanel {
3);
panel.ensureDebugId("userCreationAbusePanel");
final IPBlocklistTableWrapper table = new IPBlocklistTableWrapper(sailingService, userService,
ServerActions.UNLOCK_IPS_BLOCKED_FOR_USER_CREATION_ABUSE,
stringMessages.unableToLoadIpsBlockedForUserCreationAbuse(), stringMessages, errorReporter) {
@Override
protected void fetchData(AsyncCallback<HashMap<String, TimedLock>> callback) {
@@ -0,0 +1,28 @@
package com.sap.sse.security.shared;
import com.sap.sse.security.shared.impl.SecuredSecurityTypes;
public class IPAddress implements WithQualifiedObjectIdentifier {
private static final long serialVersionUID = 8016397230668484898L;
private final String ipAddress;
public IPAddress(final String ipAddress) {
this.ipAddress = ipAddress;
}
@Override
public String getName() {
return ipAddress;
}
@Override
public QualifiedObjectIdentifier getIdentifier() {
return getPermissionType().getQualifiedObjectIdentifier(new TypeRelativeObjectIdentifier(ipAddress));
}
@Override
public HasPermissions getPermissionType() {
return SecuredSecurityTypes.LOCKED_IP;
}
}
@@ -50,6 +50,12 @@ public class SecuredSecurityTypes extends HasPermissionsImpl {
.plus(UserActions.FORCE_OVERWRITE_PASSWORD, PublicReadableActions.READ_PUBLIC,
UserActions.ADD_SUBSCRIPTION, UserActions.BE_PREMIUM, UserActions.MANAGE_LOCK));
/**
* type-relative identifier is the {@link User#getName() ip}.
*/
public static final HasPermissions LOCKED_IP = new SecuredSecurityTypes("LOCKED_IP", DefaultActions.values());
/**
* type-relative identifier is the {@link RoleDefinition#getId() role ID's} string representation
*/
@@ -75,12 +81,6 @@ public class SecuredSecurityTypes extends HasPermissionsImpl {
CONFIGURE_LOCAL_SERVER,
CONFIGURE_REMOTE_INSTANCES,
CREATE_OBJECT,
GET_IPS_BLOCKED_FOR_USER_CREATION_ABUSE,
GET_IPS_BLOCKED_FOR_BEARER_TOKEN_ABUSE,
UNLOCK_IPS_BLOCKED_FOR_USER_CREATION_ABUSE,
UNLOCK_IPS_BLOCKED_FOR_BEARER_TOKEN_ABUSE,
/**
* This permission is used to check READ-permission on different things. For that the object type to determine
@@ -118,9 +118,7 @@ public class SecuredSecurityTypes extends HasPermissionsImpl {
CONFIGURE_CORS_FILTER
;
private static final Action[] ALL_ACTIONS = new Action[] { GET_IPS_BLOCKED_FOR_USER_CREATION_ABUSE,
GET_IPS_BLOCKED_FOR_BEARER_TOKEN_ABUSE, UNLOCK_IPS_BLOCKED_FOR_USER_CREATION_ABUSE,
UNLOCK_IPS_BLOCKED_FOR_BEARER_TOKEN_ABUSE, CONFIGURE_FILE_STORAGE, CONFIGURE_LOCAL_SERVER,
private static final Action[] ALL_ACTIONS = new Action[] { CONFIGURE_FILE_STORAGE, CONFIGURE_LOCAL_SERVER,
CONFIGURE_REMOTE_INSTANCES, CREATE_OBJECT, CAN_IMPORT_MASTERDATA, CAN_EXPORT_MASTERDATA, DATA_MINING,
REPLICATE, START_REPLICATION, READ_REPLICATOR, THREADS, CONFIGURE_AI_AGENT, CONFIGURE_CORS_FILTER,
DefaultActions.CHANGE_OWNERSHIP, DefaultActions.CHANGE_ACL, DefaultActions.CREATE,
@@ -35,6 +35,7 @@ import com.sap.sse.security.interfaces.Credential;
import com.sap.sse.security.shared.AccessControlListAnnotation;
import com.sap.sse.security.shared.HasPermissions;
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
import com.sap.sse.security.shared.IPAddress;
import com.sap.sse.security.shared.QualifiedObjectIdentifier;
import com.sap.sse.security.shared.TypeRelativeObjectIdentifier;
import com.sap.sse.security.shared.UnauthorizedException;
@@ -418,16 +419,34 @@ public class UserManagementServiceImpl extends RemoteServiceServlet implements U
@Override
public HashMap<String, TimedLock> getClientIPBasedTimedLocksForUserCreation() throws UnauthorizedException {
final SecurityService securityService = getSecurityService();
// throws UnauthorizedException if fails
securityService.checkCurrentUserServerPermission(ServerActions.GET_IPS_BLOCKED_FOR_USER_CREATION_ABUSE);
return securityService.getClientIPBasedTimedLocksForUserCreation();
final HashMap<String, TimedLock> ipToLockMap = securityService.getClientIPBasedTimedLocksForUserCreation();
// remove from Map, those where permission == FALSE
ipToLockMap.entrySet().forEach(ipToLockPair -> {
final String ip = ipToLockPair.getKey();
final WildcardPermission userReadPermissionOnIp = SecuredSecurityTypes.LOCKED_IP
.getPermissionForObject(DefaultActions.READ, new IPAddress(ip));
final boolean isPermitted = SecurityUtils.getSubject().isPermitted(userReadPermissionOnIp.toString());
if (!isPermitted) {
ipToLockMap.remove(ip);
}
});
return ipToLockMap;
}
@Override
public HashMap<String, TimedLock> getClientIPBasedTimedLocksForBearerTokenAbuse() throws UnauthorizedException {
final SecurityService securityService = getSecurityService();
// throws UnauthorizedException if fails
securityService.checkCurrentUserServerPermission(ServerActions.GET_IPS_BLOCKED_FOR_USER_CREATION_ABUSE);
return securityService.getClientIPBasedTimedLocksForBearerTokenAbuse();
final HashMap<String, TimedLock> ipToLockMap = securityService.getClientIPBasedTimedLocksForBearerTokenAbuse();
// remove from Map, those where permission == FALSE
ipToLockMap.entrySet().forEach(ipToLockPair -> {
final String ip = ipToLockPair.getKey();
final WildcardPermission userReadPermissionOnIp = SecuredSecurityTypes.LOCKED_IP
.getPermissionForObject(DefaultActions.READ, new IPAddress(ip));
final boolean isPermitted = SecurityUtils.getSubject().isPermitted(userReadPermissionOnIp.toString());
if (!isPermitted) {
ipToLockMap.remove(ip);
}
});
return ipToLockMap;
}
}
@@ -23,6 +23,7 @@ import com.sap.sse.common.media.TakedownNoticeRequestContext;
import com.sap.sse.security.Action;
import com.sap.sse.security.SecurityService;
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
import com.sap.sse.security.shared.IPAddress;
import com.sap.sse.security.shared.PermissionChecker;
import com.sap.sse.security.shared.QualifiedObjectIdentifier;
import com.sap.sse.security.shared.RoleDefinition;
@@ -745,16 +746,20 @@ public class UserManagementWriteServiceImpl extends UserManagementServiceImpl im
@Override
public void releaseUserCreationLockOnIp(String ip) throws UnauthorizedException {
final SecurityService securityService = getSecurityService();
// throws if permission not granted
securityService.checkCurrentUserServerPermission(ServerActions.UNLOCK_IPS_BLOCKED_FOR_USER_CREATION_ABUSE);
final WildcardPermission userReadPermissionOnIp = SecuredSecurityTypes.LOCKED_IP
.getPermissionForObject(DefaultActions.DELETE, new IPAddress(ip));
// throws exception if not permitted
SecurityUtils.getSubject().checkPermission(userReadPermissionOnIp.toString());
securityService.releaseUserCreationLockOnIp(ip);
}
@Override
public void releaseBearerTokenLockOnIp(String ip) throws UnauthorizedException {
final SecurityService securityService = getSecurityService();
// throws UnauthorizedException if fails
securityService.checkCurrentUserServerPermission(ServerActions.UNLOCK_IPS_BLOCKED_FOR_BEARER_TOKEN_ABUSE);
final WildcardPermission userReadPermissionOnIp = SecuredSecurityTypes.LOCKED_IP
.getPermissionForObject(DefaultActions.DELETE, new IPAddress(ip));
// throws exception if not permitted
SecurityUtils.getSubject().checkPermission(userReadPermissionOnIp.toString());
securityService.releaseBearerTokenLockOnIp(ip);
}
}