added unlock button per user

also renamed LockingAndBanning to TImedLock
This commit is contained in:
Sohaib Baig
2025-08-04 19:50:17 +02:00
parent b63acc0046
commit 0c8b91387f
62 changed files with 368 additions and 240 deletions
@@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO
securityManager.sessionManager.globalSessionTimeout = 31536000000
cacheManager = com.sap.sse.security.SessionCacheManager
securityManager.cacheManager = $cacheManager
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks
securityManager.authenticator.authenticationStrategy = $authenticationStrategy
# Support for anonymous user permissions
@@ -22,7 +22,7 @@ securityManager.sessionManager.sessionDAO = $sessionDAO
securityManager.sessionManager.globalSessionTimeout = 31536000000
cacheManager = com.sap.sse.security.SessionCacheManager
securityManager.cacheManager = $cacheManager
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks
securityManager.authenticator.authenticationStrategy = $authenticationStrategy
subjectDAO = com.sap.sse.security.NoSessionStorageForUnauthenticatedSessionsSessionDAO
@@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO
securityManager.sessionManager.globalSessionTimeout = 31536000000
cacheManager = com.sap.sse.security.SessionCacheManager
securityManager.cacheManager = $cacheManager
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks
securityManager.authenticator.authenticationStrategy = $authenticationStrategy
# Support for anonymous user permissions
@@ -51,7 +51,7 @@ import com.sap.sse.security.SecurityService;
import com.sap.sse.security.interfaces.UserStore;
import com.sap.sse.security.shared.UserGroupManagementException;
import com.sap.sse.security.shared.UserManagementException;
import com.sap.sse.security.shared.impl.LockingAndBanningImpl;
import com.sap.sse.security.shared.impl.TimedLockImpl;
import com.sap.sse.security.shared.impl.User;
import com.sap.sse.security.shared.impl.UserGroup;
import com.sap.sse.security.userstore.mongodb.UserStoreImpl;
@@ -111,7 +111,7 @@ public class OfflineSerializationTest extends AbstractSerializationTest {
UserStore userStore = new UserStoreImpl("defaultTenant");
userStore.clear();
UserGroup defaultTenant = userStore.createUserGroup(UUID.randomUUID(), "admin"+SecurityService.TENANT_SUFFIX);
User user = userStore.createUser("admin", "", new LockingAndBanningImpl());
User user = userStore.createUser("admin", "", new TimedLockImpl());
defaultTenant.add(user);
userStore.updateUserGroup(defaultTenant);
user.getDefaultTenantMap().put("testserver", defaultTenant);
@@ -22,7 +22,7 @@ securityManager.sessionManager.sessionDAO = $sessionDAO
securityManager.sessionManager.globalSessionTimeout = 31536000000
cacheManager = com.sap.sse.security.SessionCacheManager
securityManager.cacheManager = $cacheManager
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks
securityManager.authenticator.authenticationStrategy = $authenticationStrategy
subjectDAO = com.sap.sse.security.NoSessionStorageForUnauthenticatedSessionsSessionDAO
@@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO
securityManager.sessionManager.globalSessionTimeout = 31536000000
cacheManager = com.sap.sse.security.SessionCacheManager
securityManager.cacheManager = $cacheManager
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks
securityManager.authenticator.authenticationStrategy = $authenticationStrategy
# Support for anonymous user permissions
@@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO
securityManager.sessionManager.globalSessionTimeout = 31536000000
cacheManager = com.sap.sse.security.SessionCacheManager
securityManager.cacheManager = $cacheManager
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks
securityManager.authenticator.authenticationStrategy = $authenticationStrategy
# Support for anonymous user permissions
@@ -24,7 +24,7 @@ securityManager.sessionManager.sessionDAO = $sessionDAO
securityManager.sessionManager.globalSessionTimeout = 31536000000
cacheManager = com.sap.sse.security.SessionCacheManager
securityManager.cacheManager = $cacheManager
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks
securityManager.authenticator.authenticationStrategy = $authenticationStrategy
subjectDAO = com.sap.sse.security.NoSessionStorageForUnauthenticatedSessionsSessionDAO
@@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO
securityManager.sessionManager.globalSessionTimeout = 31536000000
cacheManager = com.sap.sse.security.SessionCacheManager
securityManager.cacheManager = $cacheManager
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks
securityManager.authenticator.authenticationStrategy = $authenticationStrategy
# Support for anonymous user permissions
@@ -32,7 +32,7 @@ securityManager.sessionManager.sessionDAO = $sessionDAO
securityManager.sessionManager.globalSessionTimeout = 31536000000
cacheManager = com.sap.sse.security.SessionCacheManager
securityManager.cacheManager = $cacheManager
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks
securityManager.authenticator.authenticationStrategy = $authenticationStrategy
# Authentication Filter Configurations
@@ -53,7 +53,7 @@ import com.sap.sse.rest.StreamingOutputUtil;
import com.sap.sse.security.SecurityService;
import com.sap.sse.security.interfaces.UserImpl;
import com.sap.sse.security.shared.Account;
import com.sap.sse.security.shared.impl.LockingAndBanningImpl;
import com.sap.sse.security.shared.impl.TimedLockImpl;
import com.sap.sse.security.shared.impl.User;
public class RegattasResourceTest extends AbstractJaxRsApiTest {
@@ -160,7 +160,7 @@ public class RegattasResourceTest extends AbstractJaxRsApiTest {
public void testCompetitorRegistrationByAdmin() throws Exception {
doReturn(securityService).when(regattasResource).getService(SecurityService.class);
doReturn(true).when(securityService).hasCurrentUserUpdatePermission(Mockito.any());
User user = new UserImpl("admin", "noreply@sapsailing.com", null, new ArrayList<Account>(0), null, new LockingAndBanningImpl());
User user = new UserImpl("admin", "noreply@sapsailing.com", null, new ArrayList<Account>(0), null, new TimedLockImpl());
setUser(user);
when(securityService.getCurrentUser()).thenReturn(user);
Response response = regattasResource.createAndAddCompetitor(closedRegattaName, boatClassName, null, "GER",
@@ -218,7 +218,7 @@ public class RegattasResourceTest extends AbstractJaxRsApiTest {
@Test
public void testCompetitorRegistrationAuthenticatedOnOpenRegatta() throws Exception {
doReturn(securityService).when(regattasResource).getService(SecurityService.class);
User user = new UserImpl("max", "noreply@sapsailing.com", null, new ArrayList<Account>(0), null, new LockingAndBanningImpl());
User user = new UserImpl("max", "noreply@sapsailing.com", null, new ArrayList<Account>(0), null, new TimedLockImpl());
setUser(user);
Regatta regatta = racingEventService.getRegattaByName(openRegattaName);
Response response = regattasResource.createAndAddCompetitor(openRegattaName, boatClassName, null, "GER", "#F00",
@@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO
securityManager.sessionManager.globalSessionTimeout = 31536000000
cacheManager = com.sap.sse.security.SessionCacheManager
securityManager.cacheManager = $cacheManager
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks
securityManager.authenticator.authenticationStrategy = $authenticationStrategy
# Support for anonymous user permissions
@@ -64,7 +64,7 @@ import com.sap.sse.replication.FullyInitializedReplicableTracker;
import com.sap.sse.security.SecurityService;
import com.sap.sse.security.interfaces.UserImpl;
import com.sap.sse.security.shared.WithQualifiedObjectIdentifier;
import com.sap.sse.security.shared.impl.LockingAndBanningImpl;
import com.sap.sse.security.shared.impl.TimedLockImpl;
import com.sap.sse.security.shared.impl.SecuredSecurityTypes.PublicReadableActions;
import com.sap.sse.security.shared.impl.SecuredSecurityTypes.ServerActions;
import com.sap.sse.security.shared.impl.User;
@@ -248,7 +248,7 @@ public class MediaReplicationTest extends AbstractServerReplicationTest {
@Test
public void testMasterDataImportForMediaTracks() throws Exception {
UserGroupImpl defaultTenant = new UserGroupImpl(new UUID(0, 1), "defaultTenant");
User currentUser = new UserImpl("test", "email@test", Collections.emptyMap(), null, new LockingAndBanningImpl());
User currentUser = new UserImpl("test", "email@test", Collections.emptyMap(), null, new TimedLockImpl());
SecurityService securityService = Mockito.mock(SecurityService.class);
Mockito.doReturn(defaultTenant).when(securityService).getServerGroup();
Mockito.doReturn(currentUser).when(securityService).getCurrentUser();
@@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO
securityManager.sessionManager.globalSessionTimeout = 31536000000
cacheManager = com.sap.sse.security.SessionCacheManager
securityManager.cacheManager = $cacheManager
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks
securityManager.authenticator.authenticationStrategy = $authenticationStrategy
# Support for anonymous user permissions
@@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO
securityManager.sessionManager.globalSessionTimeout = 31536000000
cacheManager = com.sap.sse.security.SessionCacheManager
securityManager.cacheManager = $cacheManager
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks
securityManager.authenticator.authenticationStrategy = $authenticationStrategy
# Support for anonymous user permissions
+1 -1
View File
@@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO
securityManager.sessionManager.globalSessionTimeout = 31536000000
cacheManager = com.sap.sse.security.SessionCacheManager
securityManager.cacheManager = $cacheManager
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks
securityManager.authenticator.authenticationStrategy = $authenticationStrategy
# Support for anonymous user permissions
Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

@@ -11,6 +11,9 @@ public interface IconResources extends ClientBundle {
@Source("images/change-acl.png")
ImageResource changeACLIcon();
@Source("images/unlock.png")
ImageResource resetLockIcon();
@Source("images/change-ownership.png")
ImageResource changeOwnershipIcon();
@@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO
securityManager.sessionManager.globalSessionTimeout = 31536000000
cacheManager = com.sap.sse.security.SessionCacheManager
securityManager.cacheManager = $cacheManager
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks
securityManager.authenticator.authenticationStrategy = $authenticationStrategy
# Support for anonymous user permissions
@@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO
securityManager.sessionManager.globalSessionTimeout = 31536000000
cacheManager = com.sap.sse.security.SessionCacheManager
securityManager.cacheManager = $cacheManager
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks
securityManager.authenticator.authenticationStrategy = $authenticationStrategy
# Support for anonymous user permissions
@@ -6,7 +6,7 @@ import java.util.UUID;
import com.sap.sse.common.Named;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.security.shared.impl.LockingAndBanning;
import com.sap.sse.security.shared.impl.TimedLock;
import com.sap.sse.security.shared.impl.Ownership;
import com.sap.sse.security.shared.impl.Role;
import com.sap.sse.security.shared.impl.SecuredSecurityTypes;
@@ -60,7 +60,7 @@ public interface BasicUserStore extends UserGroupProvider, Named {
User getUserByAccessToken(String accessToken);
User createUser(String name, String email, LockingAndBanning lockingAndBanning, Account... accounts)
User createUser(String name, String email, TimedLock timedLock, Account... accounts)
throws UserManagementException;
void addUser(User user) throws UserManagementException;
@@ -136,7 +136,7 @@ public interface HasPermissions extends Named {
*
*/
public enum DefaultActions implements Action {
CREATE, READ, UPDATE, DELETE, CHANGE_OWNERSHIP, CHANGE_ACL;
CREATE, READ, UPDATE, DELETE, CHANGE_OWNERSHIP, CHANGE_ACL, MANAGE_LOCK;
public static final Action[] MUTATION_ACTIONS = new Action[] { CREATE, UPDATE, DELETE, CHANGE_OWNERSHIP,
CHANGE_ACL };
@@ -10,8 +10,8 @@ import com.sap.sse.common.TimePoint;
* <p>
*
* For example, failed password authentication requests shall be logged by the realm using calls to
* {@link #failedPasswordAuthentication()}, successful ones with {@link #successfulPasswordAuthentication()}. Using the
* {@link #isAuthenticationLocked()} method, a realm can determine if the user account to which this object belongs
* {@link #extendLockDuration()}, successful ones with {@link #resetLock()}. Using the
* {@link #isLocked()} method, a realm can determine if the user account to which this object belongs
* shall currently accept password authentication.
* <p>
*
@@ -21,15 +21,15 @@ import com.sap.sse.common.TimePoint;
* @author Axel Uhl (d043530)
*
*/
public interface LockingAndBanning extends Serializable {
void failedPasswordAuthentication();
public interface TimedLock extends Serializable {
void extendLockDuration();
/**
* @return {@code true} if this locking and banning record changed due to this call
*/
boolean successfulPasswordAuthentication();
boolean resetLock();
boolean isAuthenticationLocked();
boolean isLocked();
TimePoint getLockedUntil();
}
@@ -4,31 +4,31 @@ import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
public class LockingAndBanningImpl implements LockingAndBanning {
public class TimedLockImpl implements TimedLock {
private static final long serialVersionUID = 3547356744366236677L;
public static final Duration DEFAULT_INITIAL_LOCKING_DELAY = Duration.ONE_SECOND;
/**
* An always valid time point which may be in the past. If it is in the future,
* {@link #isAuthenticationLocked()} will return {@code true}.
* {@link #isLocked()} will return {@code true}.
*/
private TimePoint lockedUntil;
/**
* An always valid, non-zero duration that indicates for how long into the future the {@link #lockedUntil} time
* point will be set in case a {@link #failedPasswordAuthentication() failed password authentication} is notified.
* point will be set in case a {@link #extendLockDuration() failed password authentication} is notified.
*/
private Duration nextLockingDelay;
/**
* Creates an instance that is unlocked and has a "last locking delay" of one second
*/
public LockingAndBanningImpl() {
public TimedLockImpl() {
this(TimePoint.BeginningOfTime, DEFAULT_INITIAL_LOCKING_DELAY);
}
public LockingAndBanningImpl(TimePoint lockedUntil, Duration nextLockingDelay) {
public TimedLockImpl(TimePoint lockedUntil, Duration nextLockingDelay) {
super();
this.lockedUntil = lockedUntil;
this.nextLockingDelay = nextLockingDelay;
@@ -38,13 +38,13 @@ public class LockingAndBanningImpl implements LockingAndBanning {
* Locks for the {@link #nextLockingDelay} and doubles the delay for the next failed attempt.
*/
@Override
public void failedPasswordAuthentication() {
public void extendLockDuration() {
lockedUntil = TimePoint.now().plus(nextLockingDelay);
nextLockingDelay = nextLockingDelay.times(2);
}
@Override
public boolean successfulPasswordAuthentication() {
public boolean resetLock() {
final Duration oldLockingDelay = nextLockingDelay;
nextLockingDelay = DEFAULT_INITIAL_LOCKING_DELAY;
final TimePoint oldLockedUntil = lockedUntil;
@@ -53,7 +53,7 @@ public class LockingAndBanningImpl implements LockingAndBanning {
}
@Override
public boolean isAuthenticationLocked() {
public boolean isLocked() {
return TimePoint.now().before(lockedUntil);
}
@@ -69,7 +69,7 @@ public class LockingAndBanningImpl implements LockingAndBanning {
@Override
public String toString() {
final StringBuilder result = new StringBuilder();
if (isAuthenticationLocked()) {
if (isLocked()) {
result.append("locked until ");
result.append(getLockedUntil());
} else {
@@ -125,5 +125,5 @@ public interface User extends SecurityUser<RoleDefinition, Role, UserGroup> {
Subscription getSubscriptionById(String subscriptionId);
LockingAndBanning getLockingAndBanning();
TimedLock getTimedLock();
}
@@ -24,7 +24,7 @@ import com.sap.sse.security.shared.Account.AccountType;
import com.sap.sse.security.shared.RoleDefinition;
import com.sap.sse.security.shared.UserGroupProvider;
import com.sap.sse.security.shared.WildcardPermission;
import com.sap.sse.security.shared.impl.LockingAndBanning;
import com.sap.sse.security.shared.impl.TimedLock;
import com.sap.sse.security.shared.impl.Ownership;
import com.sap.sse.security.shared.impl.Role;
import com.sap.sse.security.shared.impl.SecurityUserImpl;
@@ -101,25 +101,25 @@ public class UserImpl extends SecurityUserImpl<RoleDefinition, Role, UserGroup,
private Subscription[] subscriptions;
private final LockingAndBanning lockingAndBanning;
private final TimedLock timedLock;
public UserImpl(String name, String email, Map<String, UserGroup> defaultTenantForServer,
UserGroupProvider userGroupProvider, LockingAndBanning lockingAndBanning, Account... accounts) {
this(name, email, defaultTenantForServer, Arrays.asList(accounts), userGroupProvider, lockingAndBanning);
UserGroupProvider userGroupProvider, TimedLock timedLock, Account... accounts) {
this(name, email, defaultTenantForServer, Arrays.asList(accounts), userGroupProvider, timedLock);
}
public UserImpl(String name, String email, Map<String, UserGroup> defaultTenantForServer,
Collection<Account> accounts, UserGroupProvider userGroupProvider, LockingAndBanning lockingAndBanning) {
Collection<Account> accounts, UserGroupProvider userGroupProvider, TimedLock timedLock) {
this(name, email, /* fullName */ null, /* company */ null, /* locale */ null, /* is email validated */ false,
/* password reset secret */ null, /* validation secret */ null, defaultTenantForServer, accounts,
userGroupProvider, lockingAndBanning);
userGroupProvider, timedLock);
}
public UserImpl(String name, String email, String fullName, String company, Locale locale, Boolean emailValidated,
String passwordResetSecret, String validationSecret, Map<String, UserGroup> defaultTenantForServer,
Collection<Account> accounts, UserGroupProvider userGroupProvider, LockingAndBanning lockingAndBanning) {
Collection<Account> accounts, UserGroupProvider userGroupProvider, TimedLock timedLock) {
super(name);
this.lockingAndBanning = lockingAndBanning;
this.timedLock = timedLock;
this.defaultTenantForServer = defaultTenantForServer;
this.fullName = fullName;
this.company = company;
@@ -469,7 +469,7 @@ public class UserImpl extends SecurityUserImpl<RoleDefinition, Role, UserGroup,
}
@Override
public LockingAndBanning getLockingAndBanning() {
return lockingAndBanning;
public TimedLock getTimedLock() {
return timedLock;
}
}
@@ -99,7 +99,7 @@ public class SimpleSecurityReplicationTest extends AbstractSecurityReplicationTe
assertEquals(ERNIE, replicatedErnie.getName());
assertFalse(replica.checkPassword(ERNIE, BERT_MY_FRIEND));
// checking with incorrect password locks user for some time; wait long enough before retrying with correct password
final TimePoint lockedUntil = replicatedErnie.getLockingAndBanning().getLockedUntil();
final TimePoint lockedUntil = replicatedErnie.getTimedLock().getLockedUntil();
Thread.sleep(Math.max(0, TimePoint.now().until(lockedUntil).asMillis()+10));
assertTrue(replica.checkPassword(ERNIE, newPassword));
}
@@ -12,7 +12,7 @@ import org.junit.jupiter.api.Test;
import com.sap.sse.security.SecurityService;
import com.sap.sse.security.interfaces.UserImpl;
import com.sap.sse.security.shared.impl.LockingAndBanningImpl;
import com.sap.sse.security.shared.impl.TimedLockImpl;
import com.sap.sse.security.shared.impl.User;
import com.sap.sse.security.shared.impl.UserGroup;
import com.sap.sse.security.shared.impl.UserGroupImpl;
@@ -51,7 +51,7 @@ public class TestGroupIdentity {
final UUID uuid2 = UUID.randomUUID();
final String username = "user";
final UserGroup g1 = new UserGroupImpl(uuid1, username+SecurityService.TENANT_SUFFIX);
final User user = new UserImpl(username, /* email */ null, (Map<String, UserGroup>) /* defaultTenantForServer */ null, /* userGroupProvider */ null, new LockingAndBanningImpl());
final User user = new UserImpl(username, /* email */ null, (Map<String, UserGroup>) /* defaultTenantForServer */ null, /* userGroupProvider */ null, new TimedLockImpl());
g1.add(user);
final UserGroup g2 = new UserGroupImpl(uuid2, username+SecurityService.TENANT_SUFFIX);
assertFalse(SecurityStoreMerger.considerGroupsIdentical(g1, g2, Collections.emptyMap()));
@@ -63,10 +63,10 @@ public class TestGroupIdentity {
final UUID uuid2 = UUID.randomUUID();
final String username = "user";
final UserGroup g1 = new UserGroupImpl(uuid1, username+SecurityService.TENANT_SUFFIX);
final User user1 = new UserImpl(username, /* email */ null, (Map<String, UserGroup>) /* defaultTenantForServer */ null, /* userGroupProvider */ null, new LockingAndBanningImpl());
final User user1 = new UserImpl(username, /* email */ null, (Map<String, UserGroup>) /* defaultTenantForServer */ null, /* userGroupProvider */ null, new TimedLockImpl());
g1.add(user1);
final UserGroup g2 = new UserGroupImpl(uuid2, username+SecurityService.TENANT_SUFFIX);
final User user2 = new UserImpl(username, /* email */ null, (Map<String, UserGroup>) /* defaultTenantForServer */ null, /* userGroupProvider */ null, new LockingAndBanningImpl());
final User user2 = new UserImpl(username, /* email */ null, (Map<String, UserGroup>) /* defaultTenantForServer */ null, /* userGroupProvider */ null, new TimedLockImpl());
g2.add(user2);
final Map<User, User> userMap = new HashMap<>();
userMap.put(user2, user1); // user2 assumed to get merged with user1
@@ -29,7 +29,7 @@ import com.sap.sse.security.shared.TypeRelativeObjectIdentifier;
import com.sap.sse.security.shared.UserGroupManagementException;
import com.sap.sse.security.shared.UserStoreManagementException;
import com.sap.sse.security.shared.WildcardPermission;
import com.sap.sse.security.shared.impl.LockingAndBanningImpl;
import com.sap.sse.security.shared.impl.TimedLockImpl;
import com.sap.sse.security.shared.impl.QualifiedObjectIdentifierImpl;
import com.sap.sse.security.shared.impl.User;
import com.sap.sse.security.shared.impl.UserGroup;
@@ -69,7 +69,7 @@ public class AccessControlStoreTest {
Map<String, UserGroup> defaultTenantForUser = new HashMap<>();
defaultTenantForUser.put("dummyServer", adminTenant);
testOwner = new UserImpl("admin", "admin@sapsailing.com", defaultTenantForUser,
/* userGroupProvider */ null, new LockingAndBanningImpl());
/* userGroupProvider */ null, new TimedLockImpl());
}
private void newStores() {
@@ -44,7 +44,7 @@ import com.sap.sse.security.shared.UserStoreManagementException;
import com.sap.sse.security.shared.WildcardPermission;
import com.sap.sse.security.shared.WithQualifiedObjectIdentifier;
import com.sap.sse.security.shared.impl.AccessControlList;
import com.sap.sse.security.shared.impl.LockingAndBanningImpl;
import com.sap.sse.security.shared.impl.TimedLockImpl;
import com.sap.sse.security.shared.impl.Ownership;
import com.sap.sse.security.shared.impl.QualifiedObjectIdentifierImpl;
import com.sap.sse.security.shared.impl.Role;
@@ -242,7 +242,7 @@ public class LoginTest {
@Test
public void rolesTest() throws UserStoreManagementException {
userStore.createUser("me", "me@sap.com", new LockingAndBanningImpl());
userStore.createUser("me", "me@sap.com", new TimedLockImpl());
RoleDefinition testRoleDefinition = userStore.createRoleDefinition(UUID.randomUUID(), "testRole",
Collections.emptySet());
final Role testRole = new Role(testRoleDefinition, true);
@@ -254,7 +254,7 @@ public class LoginTest {
@Test
public void roleWithQualifiersTest() throws UserStoreManagementException {
UserGroupImpl userDefaultTenant = userStore.createUserGroup(UUID.randomUUID(), "me-tenant");
User meUser = userStore.createUser("me", "me@sap.com", new LockingAndBanningImpl());
User meUser = userStore.createUser("me", "me@sap.com", new TimedLockImpl());
RoleDefinition testRoleDefinition = userStore.createRoleDefinition(UUID.randomUUID(), "testRole",
Collections.emptySet());
final Role testRole = new Role(testRoleDefinition, userDefaultTenant, meUser, true);
@@ -268,7 +268,7 @@ public class LoginTest {
@Test
public void permissionsTest() throws UserStoreManagementException {
userStore.createUser("me", "me@sap.com", new LockingAndBanningImpl());
userStore.createUser("me", "me@sap.com", new TimedLockImpl());
userStore.addPermissionForUser("me", new WildcardPermission("a:b:c"));
UserStoreImpl store2 = createAndLoadUserStore();
User allUser = userStore.getUserByName(SecurityService.ALL_USERNAME);
@@ -39,7 +39,7 @@ import com.sap.sse.security.shared.UserManagementException;
import com.sap.sse.security.shared.WildcardPermission;
import com.sap.sse.security.shared.impl.AccessControlList;
import com.sap.sse.security.shared.impl.HasPermissionsImpl;
import com.sap.sse.security.shared.impl.LockingAndBanningImpl;
import com.sap.sse.security.shared.impl.TimedLockImpl;
import com.sap.sse.security.shared.impl.Ownership;
import com.sap.sse.security.shared.impl.Role;
import com.sap.sse.security.shared.impl.User;
@@ -94,7 +94,7 @@ public class PermissionCheckerTest {
userStore.deleteUser("jonas");
}
userTenant = userStore.createUserGroup(userTenantId, "jonas-tenant");
user = userStore.createUser("jonas", "jonas@dann.io", new LockingAndBanningImpl());
user = userStore.createUser("jonas", "jonas@dann.io", new TimedLockImpl());
userTenant.add(user);
userStore.updateUserGroup(userTenant);
ownership = new Ownership(user, userTenant);
@@ -25,7 +25,7 @@ import com.sap.sse.security.interfaces.UserImpl;
import com.sap.sse.security.interfaces.UserStore;
import com.sap.sse.security.shared.UserGroupManagementException;
import com.sap.sse.security.shared.UserManagementException;
import com.sap.sse.security.shared.impl.LockingAndBanningImpl;
import com.sap.sse.security.shared.impl.TimedLockImpl;
import com.sap.sse.security.shared.impl.User;
import com.sap.sse.security.shared.impl.UserGroup;
import com.sap.sse.security.userstore.mongodb.UserStoreImpl;
@@ -191,7 +191,7 @@ public class PreferenceObjectBasedNotificationSetTest {
@Test
public void userWithNonVerifiedEmailIsSkippedTest() throws UserManagementException, UserGroupManagementException {
store.createUser(user1, mail, new LockingAndBanningImpl());
store.createUser(user1, mail, new TimedLockImpl());
store.registerPreferenceConverter(prefKey, prefConverter);
store.setPreferenceObject(user1, prefKey, values1);
PreferenceObjectBasedNotificationSetImpl notificationSet = new PreferenceObjectBasedNotificationSetImpl(prefKey, store);
@@ -238,7 +238,7 @@ public class PreferenceObjectBasedNotificationSetTest {
*/
@Test
public void deleteUserWithMappingTest() throws UserManagementException, UserGroupManagementException {
store.createUser(user1, mail, new LockingAndBanningImpl());
store.createUser(user1, mail, new TimedLockImpl());
store.registerPreferenceConverter(prefKey, prefConverter);
store.setPreferenceObject(user1, prefKey, values1);
PreferenceObjectBasedNotificationSetImpl notificationSet = new PreferenceObjectBasedNotificationSetImpl(prefKey, store);
@@ -250,7 +250,7 @@ public class PreferenceObjectBasedNotificationSetTest {
@Test
public void removePreferenceConverterTest() throws UserManagementException, UserGroupManagementException {
store.createUser(user1, mail, new LockingAndBanningImpl());
store.createUser(user1, mail, new TimedLockImpl());
store.registerPreferenceConverter(prefKey, prefConverter);
store.setPreferenceObject(user1, prefKey, values1);
PreferenceObjectBasedNotificationSetImpl notificationSet = new PreferenceObjectBasedNotificationSetImpl(prefKey, store);
@@ -272,9 +272,9 @@ public class PreferenceObjectBasedNotificationSetTest {
UserGroup defaultTenantForSingleServer = store.createUserGroup(UUID.randomUUID(), username + "-tenant");
Map<String, UserGroup> defaultTenantForServer = new ConcurrentHashMap<>();
defaultTenantForServer.put(serverName, defaultTenantForSingleServer);
store.createUser(username, email, new LockingAndBanningImpl());
store.createUser(username, email, new TimedLockImpl());
store.updateUser(new UserImpl(username, email, null, null, null, true, null, null, defaultTenantForServer,
Collections.emptySet(), /* userGroupProvider */ null, new LockingAndBanningImpl()));
Collections.emptySet(), /* userGroupProvider */ null, new TimedLockImpl()));
}
private static class PreferenceObjectBasedNotificationSetImpl extends PreferenceObjectBasedNotificationSet<HashSet<String>, String> {
@@ -26,7 +26,7 @@ import com.sap.sse.security.shared.UserStoreManagementException;
import com.sap.sse.security.shared.WildcardPermission;
import com.sap.sse.security.shared.impl.AccessControlList;
import com.sap.sse.security.shared.impl.HasPermissionsImpl;
import com.sap.sse.security.shared.impl.LockingAndBanningImpl;
import com.sap.sse.security.shared.impl.TimedLockImpl;
import com.sap.sse.security.shared.impl.Ownership;
import com.sap.sse.security.shared.impl.Role;
import com.sap.sse.security.shared.impl.SecuredSecurityTypes;
@@ -65,8 +65,8 @@ public class PrivilegeEscalationTest {
PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(), TEST_DEFAULT_TENANT);
userStore.ensureDefaultRolesExist();
userStore.loadAndMigrateUsers();
user = userStore.createUser(USER_USERNAME, null, new LockingAndBanningImpl());
user2 = userStore.createUser(USER2_USERNAME, null, new LockingAndBanningImpl());
user = userStore.createUser(USER_USERNAME, null, new TimedLockImpl());
user2 = userStore.createUser(USER2_USERNAME, null, new TimedLockImpl());
userGroup = userStore.createUserGroup(USER_GROUP_UUID, USER_USERNAME+"-tenant");
userGroup.add(user);
userGroup.add(user2);
@@ -12,7 +12,7 @@ import com.sap.sse.security.interfaces.UserStore;
import com.sap.sse.security.shared.RoleDefinition;
import com.sap.sse.security.shared.UserManagementException;
import com.sap.sse.security.shared.UserStoreManagementException;
import com.sap.sse.security.shared.impl.LockingAndBanningImpl;
import com.sap.sse.security.shared.impl.TimedLockImpl;
import com.sap.sse.security.shared.impl.Role;
import com.sap.sse.security.shared.impl.User;
import com.sap.sse.security.shared.impl.UserGroup;
@@ -37,7 +37,7 @@ public class RoleDefinitionsTest {
@BeforeEach
public void doBefore() throws UserStoreManagementException {
userStore.clear();
user = userStore.createUser(username, email, new LockingAndBanningImpl());
user = userStore.createUser(username, email, new TimedLockImpl());
roleDefinition = userStore.createRoleDefinition(testRoleUUID, TEST_ROLE, Collections.emptySet());
userGroup = userStore.createUserGroup(testGroupUUID, groupName);
}
@@ -13,7 +13,7 @@ import com.sap.sse.mongodb.MongoDBConfiguration;
import com.sap.sse.mongodb.MongoDBService;
import com.sap.sse.security.shared.UserGroupManagementException;
import com.sap.sse.security.shared.UserManagementException;
import com.sap.sse.security.shared.impl.LockingAndBanningImpl;
import com.sap.sse.security.shared.impl.TimedLockImpl;
import com.sap.sse.security.userstore.mongodb.UserStoreImpl;
import com.sap.sse.security.userstore.mongodb.impl.CollectionNames;
@@ -115,7 +115,7 @@ public class UserPreferenceObjectAndConverterTest {
*/
@Test
public void deleteUserWithPreferenceObjectTest() throws UserManagementException, UserGroupManagementException {
store.createUser(user1, email, new LockingAndBanningImpl());
store.createUser(user1, email, new TimedLockImpl());
store.registerPreferenceConverter(prefKey1, prefConverter);
store.setPreferenceObject(user1, prefKey1, pref1);
store.deleteUser(user1);
@@ -124,7 +124,7 @@ public class UserPreferenceObjectAndConverterTest {
@Test
public void removeConverterTest() throws UserManagementException, UserGroupManagementException {
store.createUser(user1, email, new LockingAndBanningImpl());
store.createUser(user1, email, new TimedLockImpl());
store.registerPreferenceConverter(prefKey1, prefConverter);
store.setPreference(user1, prefKey1, serializedPref1);
store.removePreferenceConverter(prefKey1);
@@ -9,7 +9,7 @@ import org.junit.jupiter.api.Test;
import com.sap.sse.security.interfaces.UserStore;
import com.sap.sse.security.shared.UserGroupManagementException;
import com.sap.sse.security.shared.UserManagementException;
import com.sap.sse.security.shared.impl.LockingAndBanningImpl;
import com.sap.sse.security.shared.impl.TimedLockImpl;
import com.sap.sse.security.userstore.mongodb.UserStoreImpl;
public class UserStoreTest {
@@ -26,7 +26,7 @@ public class UserStoreTest {
@BeforeEach
public void setUp() throws UserManagementException, UserGroupManagementException {
userStore.createUser(username, email, new LockingAndBanningImpl());
userStore.createUser(username, email, new TimedLockImpl());
userStore.setAccessToken(username, accessToken);
userStore.setPreference(username, prefKey, prefValue);
}
@@ -34,7 +34,7 @@ import com.sap.sse.security.shared.SecurityUser;
import com.sap.sse.security.shared.UserGroupManagementException;
import com.sap.sse.security.shared.UserManagementException;
import com.sap.sse.security.shared.UserStoreManagementException;
import com.sap.sse.security.shared.impl.LockingAndBanningImpl;
import com.sap.sse.security.shared.impl.TimedLockImpl;
import com.sap.sse.security.shared.impl.Ownership;
import com.sap.sse.security.shared.impl.Role;
import com.sap.sse.security.shared.impl.User;
@@ -92,7 +92,7 @@ public class UserStoreWithPersistenceTest {
@Test
public void testCreateUser() throws UserManagementException {
store.createUser(username, email, new LockingAndBanningImpl());
store.createUser(username, email, new TimedLockImpl());
assertNotNull(store.getUserByName(username));
assertNotNull(store.getUserByEmail(email));
@@ -103,12 +103,12 @@ public class UserStoreWithPersistenceTest {
@Test
public void testMasterdataIsSaved() throws UserStoreManagementException {
store.createUser(username, email, new LockingAndBanningImpl());
store.createUser(username, email, new TimedLockImpl());
UserGroupImpl defaultTenant = createUserGroup();
HashMap<String, UserGroup> defaultTenantForServers = new HashMap<>();
defaultTenantForServers.put(serverName, defaultTenant);
store.updateUser(new UserImpl(username, email, fullName, company, Locale.GERMAN, false, null, null,
defaultTenantForServers, Collections.emptySet(), /* userGroupProvider */ null, new LockingAndBanningImpl()));
defaultTenantForServers, Collections.emptySet(), /* userGroupProvider */ null, new TimedLockImpl()));
newStore();
User savedUser = store.getUserByName(username);
assertEquals(username, savedUser.getName());
@@ -123,7 +123,7 @@ public class UserStoreWithPersistenceTest {
*/
@Test
public void testDeleteUser() throws UserManagementException {
store.createUser(username, email, new LockingAndBanningImpl());
store.createUser(username, email, new TimedLockImpl());
store.deleteUser(username);
assertNull(store.getUserByName(username));
assertNull(store.getUserByEmail(email));
@@ -135,7 +135,7 @@ public class UserStoreWithPersistenceTest {
@Test
public void testSetPreferences() throws UserManagementException {
store.createUser(username, email, new LockingAndBanningImpl());
store.createUser(username, email, new TimedLockImpl());
store.setPreference(username, prefKey, prefValue);
assertEquals(prefValue, store.getPreference(username, prefKey));
newStore();
@@ -144,7 +144,7 @@ public class UserStoreWithPersistenceTest {
@Test
public void testUnsetPreferences() throws UserManagementException {
store.createUser(username, email, new LockingAndBanningImpl());
store.createUser(username, email, new TimedLockImpl());
store.setPreference(username, prefKey, prefValue);
store.unsetPreference(username, prefKey);
assertNull(store.getPreference(username, prefKey));
@@ -157,7 +157,7 @@ public class UserStoreWithPersistenceTest {
*/
@Test
public void testDeleteUserWithPreferences() throws UserManagementException {
store.createUser(username, email, new LockingAndBanningImpl());
store.createUser(username, email, new TimedLockImpl());
store.setPreference(username, prefKey, prefValue);
store.deleteUser(username);
assertNull(store.getPreference(username, prefKey));
@@ -167,7 +167,7 @@ public class UserStoreWithPersistenceTest {
@Test
public void testCreateUserGroup() throws UserGroupManagementException, UserManagementException {
final User user = store.createUser(username, email, new LockingAndBanningImpl());
final User user = store.createUser(username, email, new TimedLockImpl());
UserGroupImpl createUserGroup = createUserGroup();
createUserGroup.add(user);
store.updateUserGroup(createUserGroup);
@@ -196,7 +196,7 @@ public class UserStoreWithPersistenceTest {
@Test
public void testTenantUsers() throws UserManagementException, UserGroupManagementException {
UserGroupImpl defaultTenant = createUserGroup();
final User user = store.createUser(username, email, new LockingAndBanningImpl());
final User user = store.createUser(username, email, new TimedLockImpl());
defaultTenant.add(user);
store.updateUserGroup(defaultTenant);
user.getDefaultTenantMap().put(serverName, defaultTenant);
@@ -218,7 +218,7 @@ public class UserStoreWithPersistenceTest {
@Test
public void testUserGroups() throws UserManagementException, UserGroupManagementException {
final User user = store.createUser(username, email, new LockingAndBanningImpl());
final User user = store.createUser(username, email, new TimedLockImpl());
final String GROUP_NAME = "group";
final UserGroupImpl group = store.createUserGroup(UUID.randomUUID(), GROUP_NAME);
group.add(user);
@@ -242,7 +242,7 @@ public class UserStoreWithPersistenceTest {
@Test
public void testGetExistingQualificationsForRoleDefinition()
throws UserManagementException, UserGroupManagementException {
User user = store.createUser("def", "d@test.de", new LockingAndBanningImpl());
User user = store.createUser("def", "d@test.de", new TimedLockImpl());
RoleDefinitionImpl roleDefinition = new RoleDefinitionImpl(UUID.randomUUID(), "My-Test-Role");
store.createRoleDefinition(roleDefinition.getId(), roleDefinition.getName(), new ArrayList<>());
UserGroupImpl userGroup = store.createUserGroup(UUID.randomUUID(), "Test-Usergroup");
@@ -23,6 +23,7 @@ import com.sap.sse.security.shared.dto.SecuredDTO;
import com.sap.sse.security.shared.dto.StrippedUserGroupDTO;
import com.sap.sse.security.shared.dto.UserDTO;
import com.sap.sse.security.shared.dto.UserGroupDTO;
import com.sap.sse.security.shared.impl.User;
import com.sap.sse.security.ui.oauth.client.CredentialDTO;
import com.sap.sse.security.ui.shared.SecurityServiceSharingDTO;
import com.sap.sse.security.ui.shared.SuccessInfo;
@@ -33,7 +34,7 @@ public interface UserManagementService extends RemoteService {
throws UnauthorizedException, org.apache.shiro.authz.UnauthorizedException;
Collection<UserGroupDTO> getUserGroups() throws org.apache.shiro.authz.UnauthorizedException;
UserGroupDTO getUserGroupByName(String userGroupName)
throws UnauthorizedException, org.apache.shiro.authz.UnauthorizedException;
@@ -21,6 +21,7 @@ import com.sap.sse.security.shared.dto.SecuredDTO;
import com.sap.sse.security.shared.dto.StrippedUserGroupDTO;
import com.sap.sse.security.shared.dto.UserDTO;
import com.sap.sse.security.shared.dto.UserGroupDTO;
import com.sap.sse.security.shared.impl.User;
import com.sap.sse.security.ui.oauth.client.CredentialDTO;
import com.sap.sse.security.ui.shared.SecurityServiceSharingDTO;
import com.sap.sse.security.ui.shared.SuccessInfo;
@@ -79,6 +79,8 @@ public interface UserManagementWriteService extends UserManagementService {
SuccessInfo deleteUser(String username) throws UnauthorizedException, org.apache.shiro.authz.UnauthorizedException;
SuccessInfo unlockUser(String username) throws UnauthorizedException, org.apache.shiro.authz.UnauthorizedException;
Set<SuccessInfo> deleteUsers(Set<String> usernames)
throws UnauthorizedException, org.apache.shiro.authz.UnauthorizedException;
@@ -61,6 +61,8 @@ public interface UserManagementWriteServiceAsync extends UserManagementServiceAs
void updateRoleDefinition(RoleDefinitionDTO roleWithNewProperties, AsyncCallback<Void> callback);
void deleteUser(String username, AsyncCallback<SuccessInfo> callback);
void unlockUser(String username, AsyncCallback<SuccessInfo> callback);
void deleteUsers(Set<String> usernames, AsyncCallback<Set<SuccessInfo>> callback);
@@ -14,6 +14,7 @@ public class DefaultActionsImagesBarCell extends ImagesBarCell {
public static final String ACTION_CHANGE_OWNERSHIP = DefaultActions.CHANGE_OWNERSHIP.name();
public static final String ACTION_MIGRATE_GROUP_OWNERSHIP_HIERARCHY = "MIGRATE_GROUP_OWNERSHIP_HIERARCHY";
public static final String ACTION_CHANGE_ACL = DefaultActions.CHANGE_ACL.name();
public static final String ACTION_MANAGE_LOCK = DefaultActions.MANAGE_LOCK.name();
protected final StringMessages stringMessages;
@@ -24,7 +25,7 @@ public class DefaultActionsImagesBarCell extends ImagesBarCell {
@Override
protected Iterable<ImageSpec> getImageSpecs() {
return Arrays.asList(getUpdateImageSpec(), getDeleteImageSpec(), getChangeOwnershipImageSpec(),
getChangeACLImageSpec());
getChangeACLImageSpec(), getResetLockImageSpec());
}
/**
@@ -65,4 +66,12 @@ public class DefaultActionsImagesBarCell extends ImagesBarCell {
IconResources.INSTANCE.changeACLIcon());
}
/**
* @return {@link ImageSpec} for {@link DefaultActions#CHANGE_ACL reset lock} action
*/
protected ImageSpec getResetLockImageSpec() {
return new ImageSpec(ACTION_MANAGE_LOCK, stringMessages.resetLock(),
IconResources.INSTANCE.resetLockIcon());
}
}
@@ -150,6 +150,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages {
String groups();
String group();
String errorDeletingUser(String username, String message);
String doYouReallyWantToUnlockUser(String name);
String doYouReallyWantToRemoveUser(String name);
String errorTryingToUpdateUser(String username, String message);
String ownership();
@@ -161,6 +162,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages {
String userNotFound(String username);
String usergroupNotFound(String userGroupName);
String actionChangeACL();
String resetLock();
String editACLForObject(String objectName);
String acl();
String errorUpdatingAcl(String name);
@@ -157,6 +157,7 @@ groups=Groups
group=Group
errorDeletingUser=Error deleting user {0}: {1}
doYouReallyWantToRemoveUser=Really remove user {0}?
doYouReallyWantToUnlockUser=Really allow locked user {0} to access SAP Sailing Analytics again?
errorTryingToUpdateUser=Error trying to update user {0}: {1}
ownership=Ownership
editObjectOwnership=Edit object ownership
@@ -167,6 +168,7 @@ pleaseWaitUntilUserGroupNameIsResolved=Please wait until user group name is reso
userNotFound=User {0} not found
usergroupNotFound=User group {0} not found
actionChangeACL=Change ACL
resetLock=Reset Lock
editACLForObject=Edit ACL for ''{0}''
acl=ACL
errorUpdatingAcl=Error updating ACL: {0}.
@@ -14,6 +14,7 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import com.google.gwt.cell.client.SafeHtmlCell;
import com.google.gwt.core.client.Callback;
@@ -45,6 +46,7 @@ import com.sap.sse.security.shared.dto.RoleWithSecurityDTO;
import com.sap.sse.security.shared.dto.StrippedUserGroupDTO;
import com.sap.sse.security.shared.dto.UserDTO;
import com.sap.sse.security.shared.impl.SecuredSecurityTypes;
import com.sap.sse.security.shared.impl.User;
import com.sap.sse.security.ui.client.EntryPointLinkFactory;
import com.sap.sse.security.ui.client.UserManagementServiceAsync;
import com.sap.sse.security.ui.client.UserManagementWriteServiceAsync;
@@ -162,42 +164,8 @@ extends TableWrapper<UserDTO, S, StringMessages, TR> {
user->user.getLockedUntil() != null && user.getLockedUntil().after(TimePoint.now()) ?
DateAndTimeFormatterUtil.dateTimeMedium.render(user.getLockedUntil().asDate()) : "",
userColumnListHandler);
final HasPermissions type = SecuredSecurityTypes.USER;
final AccessControlledActionsColumn<UserDTO, DefaultActionsImagesBarCell> userActionColumn = create(
new DefaultActionsImagesBarCell(stringMessages), userService);
userActionColumn.addAction(ACTION_UPDATE, UPDATE, user -> editUser(user));
userActionColumn.addAction(ACTION_DELETE, DELETE, user -> {
if (Window.confirm(stringMessages.doYouReallyWantToRemoveUser(user.getName()))) {
getUserManagementWriteService().deleteUser(user.getName(), new AsyncCallback<SuccessInfo>() {
@Override
public void onFailure(Throwable caught) {
deletingUserFailed(user, caught.getMessage());
}
@Override
public void onSuccess(SuccessInfo result) {
if (result.isSuccessful()) {
filterField.remove(user);
} else {
deletingUserFailed(user, result.getMessage());
}
}
private void deletingUserFailed(UserDTO user, String message) {
errorReporter.reportError(stringMessages.errorDeletingUser(user.getName(), message));
}
});
}
});
final EditOwnershipDialog.DialogConfig<UserDTO> configOwnership = EditOwnershipDialog.create(
userService.getUserManagementWriteService(), type,
user -> refreshUserList((Callback<Iterable<UserDTO>, Throwable>) null), stringMessages);
final EditACLDialog.DialogConfig<UserDTO> configACL = EditACLDialog.create(
userService.getUserManagementWriteService(), type,
user -> user.getAccessControlList(), stringMessages);
userActionColumn.addAction(ACTION_CHANGE_OWNERSHIP, CHANGE_OWNERSHIP, configOwnership::openOwnershipDialog);
userActionColumn.addAction(DefaultActionsImagesBarCell.ACTION_CHANGE_ACL, DefaultActions.CHANGE_ACL,
u -> configACL.openDialog(u));
final AccessControlledActionsColumn<UserDTO, DefaultActionsImagesBarCell> userActionColumn = composeUserActionColumn(
stringMessages, errorReporter);
filterField = new LabeledAbstractFilterablePanel<UserDTO>(new Label(stringMessages.filterUsers()),
new ArrayList<UserDTO>(), dataProvider, stringMessages) {
@Override
@@ -234,6 +202,81 @@ extends TableWrapper<UserDTO, S, StringMessages, TR> {
table.addColumn(userActionColumn, stringMessages.actions());
table.ensureDebugId("UsersTable");
}
private AccessControlledActionsColumn<UserDTO, DefaultActionsImagesBarCell> composeUserActionColumn(
StringMessages stringMessages, ErrorReporter errorReporter) {
final HasPermissions type = SecuredSecurityTypes.USER;
final AccessControlledActionsColumn<UserDTO, DefaultActionsImagesBarCell> userActionColumn = create(
new DefaultActionsImagesBarCell(stringMessages), userService);
userActionColumn.addAction(ACTION_UPDATE, UPDATE, user -> editUser(user));
userActionColumn.addAction(ACTION_DELETE, DELETE, user -> {
if (Window.confirm(stringMessages.doYouReallyWantToRemoveUser(user.getName()))) {
getUserManagementWriteService().deleteUser(user.getName(), new AsyncCallback<SuccessInfo>() {
@Override
public void onFailure(Throwable caught) {
deletingUserFailed(user, caught.getMessage());
}
@Override
public void onSuccess(SuccessInfo result) {
if (result.isSuccessful()) {
filterField.remove(user);
} else {
deletingUserFailed(user, result.getMessage());
}
}
private void deletingUserFailed(UserDTO user, String message) {
errorReporter.reportError(stringMessages.errorDeletingUser(user.getName(), message));
}
});
}
});
final EditOwnershipDialog.DialogConfig<UserDTO> configOwnership = EditOwnershipDialog.create(
userService.getUserManagementWriteService(), type,
user -> refreshUserList((Callback<Iterable<UserDTO>, Throwable>) null), stringMessages);
userActionColumn.addAction(ACTION_CHANGE_OWNERSHIP, CHANGE_OWNERSHIP, configOwnership::openOwnershipDialog);
final EditACLDialog.DialogConfig<UserDTO> configACL = EditACLDialog.create(
userService.getUserManagementWriteService(), type,
user -> user.getAccessControlList(), stringMessages);
userActionColumn.addAction(DefaultActionsImagesBarCell.ACTION_CHANGE_ACL, DefaultActions.CHANGE_ACL,
u -> configACL.openDialog(u));
userActionColumn.addAction(
DefaultActionsImagesBarCell.ACTION_MANAGE_LOCK,
DefaultActions.MANAGE_LOCK,
onManageLockPressed(stringMessages, errorReporter)
);
return userActionColumn;
}
private Consumer<UserDTO> onManageLockPressed(StringMessages stringMessages, ErrorReporter errorReporter) {
return user -> {
final boolean isLocked = user.getLockedUntil().after(TimePoint.now());
if (isLocked) {
final String userName = user.getName();
final boolean didConfirm = Window.confirm(stringMessages.doYouReallyWantToUnlockUser(userName));
if (didConfirm) {
getUserManagementWriteService().unlockUser(
userName,
new AsyncCallback<SuccessInfo>() {
@Override
public void onSuccess(SuccessInfo result) {
Window.alert("Unlock succeeded for user " + userName + ".");
}
@Override
public void onFailure(Throwable caught) {
Window.alert("Unlock failed for user " + userName + ".");
errorReporter.reportError(caught.getMessage());
}
}
);
}
} else {
Window.alert("This user is already unlocked.");
}
};
}
public Iterable<UserDTO> getAllUsers() {
return filterField.getAll();
@@ -96,7 +96,7 @@ public class SecurityDTOFactory {
/* default tenant filled in later */ null,
getSecuredPermissions(filteredPermissions, user, securityService),
createStrippedUserGroupDTOsFromUserGroups(securityService.getUserGroupsOfUser(user),
fromOriginalToStrippedDownUser, fromOriginalToStrippedDownUserGroup), user.getLockingAndBanning().getLockedUntil());
fromOriginalToStrippedDownUser, fromOriginalToStrippedDownUserGroup), user.getTimedLock().getLockedUntil());
userDTO.setDefaultTenantForCurrentServer(createStrippedUserGroupDTOFromUserGroup(
securityService.getDefaultTenantForCurrentUser(),
fromOriginalToStrippedDownUserGroup));
@@ -374,6 +374,25 @@ public class UserManagementWriteServiceImpl extends UserManagementServiceImpl im
return new SuccessInfo(false, "Could not delete user.", /* redirectURL */ null, null);
}
}
@Override
public SuccessInfo unlockUser(String username) throws UnauthorizedException {
User user = getSecurityService().getUserByName(username);
if (user != null) {
if (!getSecurityService().hasCurrentUserExplicitPermissions(user, DefaultActions.MANAGE_LOCK)) {
return new SuccessInfo(false, "You are not permitted to manage locking on user " + username,
/* redirectURL */ null, null);
}
try {
getSecurityService().resetUserTimedLock(username);
return new SuccessInfo(true, "Reset lock on user: " + username + ".", /* redirectURL */ null, null);
} catch (UserManagementException e) {
return new SuccessInfo(false, "Could not reset lock on user " + username, /* redirectURL */ null, null);
}
} else {
return new SuccessInfo(false, "Could not reset lock on user " + username, /* redirectURL */ null, null);
}
}
@Override
public Set<SuccessInfo> deleteUsers(Set<String> usernames) throws UnauthorizedException {
@@ -24,7 +24,7 @@ securityManager.sessionManager.sessionDAO = $sessionDAO
securityManager.sessionManager.globalSessionTimeout = 31536000000
cacheManager = com.sap.sse.security.SessionCacheManager
securityManager.cacheManager = $cacheManager
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks
securityManager.authenticator.authenticationStrategy = $authenticationStrategy
subjectDAO = com.sap.sse.security.NoSessionStorageForUnauthenticatedSessionsSessionDAO
@@ -34,7 +34,7 @@ import com.sap.sse.security.shared.UserManagementException;
import com.sap.sse.security.shared.UserRole;
import com.sap.sse.security.shared.UserStoreManagementException;
import com.sap.sse.security.shared.WildcardPermission;
import com.sap.sse.security.shared.impl.LockingAndBanning;
import com.sap.sse.security.shared.impl.TimedLock;
import com.sap.sse.security.shared.impl.Ownership;
import com.sap.sse.security.shared.impl.Role;
import com.sap.sse.security.shared.impl.User;
@@ -854,12 +854,12 @@ public class UserStoreImpl implements UserStore {
}
@Override
public User createUser(String name, String email, LockingAndBanning lockingAndBanning, Account... accounts)
public User createUser(String name, String email, TimedLock timedLock, Account... accounts)
throws UserManagementException {
return LockUtil.executeWithWriteLockAndResultExpectException(usersLock, () -> {
checkUsernameUniqueness(name);
final Map<String, UserGroup> tenantsForServer = new ConcurrentHashMap<>();
final User user = new UserImpl(name, email, tenantsForServer, /* user group provider */ this, lockingAndBanning, accounts);
final User user = new UserImpl(name, email, tenantsForServer, /* user group provider */ this, timedLock, accounts);
logger.info("Creating user: " + user + " with e-mail " + email);
addAndStoreUserInternal(user);
return user;
@@ -37,8 +37,8 @@ import com.sap.sse.security.shared.UserManagementException;
import com.sap.sse.security.shared.UsernamePasswordAccount;
import com.sap.sse.security.shared.WildcardPermission;
import com.sap.sse.security.shared.impl.AccessControlList;
import com.sap.sse.security.shared.impl.LockingAndBanning;
import com.sap.sse.security.shared.impl.LockingAndBanningImpl;
import com.sap.sse.security.shared.impl.TimedLock;
import com.sap.sse.security.shared.impl.TimedLockImpl;
import com.sap.sse.security.shared.impl.Ownership;
import com.sap.sse.security.shared.impl.QualifiedObjectIdentifierImpl;
import com.sap.sse.security.shared.impl.Role;
@@ -289,9 +289,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
final String validationSecret = (String) userDBObject.get(FieldNames.User.VALIDATION_SECRET.name());
final Long lockedUntilMillis = userDBObject.getLong(FieldNames.User.LOCKED_UNTIL_MILLIS.name());
final Long nextLockingDurationMillis = userDBObject.getLong(FieldNames.User.NEXT_LOCKING_DURATION_MILLIS.name());
final LockingAndBanning lockingAndBanning = new LockingAndBanningImpl(
final TimedLock timedLock = new TimedLockImpl(
lockedUntilMillis == null ? TimePoint.BeginningOfTime : TimePoint.of(lockedUntilMillis),
nextLockingDurationMillis == null ? LockingAndBanningImpl.DEFAULT_INITIAL_LOCKING_DELAY : Duration.ofMillis(nextLockingDurationMillis));
nextLockingDurationMillis == null ? TimedLockImpl.DEFAULT_INITIAL_LOCKING_DELAY : Duration.ofMillis(nextLockingDurationMillis));
final Set<Role> roles = new HashSet<>();
final Set<String> permissions = new HashSet<>();
final List<?> rolesO = (List<?>) userDBObject.get(FieldNames.User.ROLE_IDS.name());
@@ -358,7 +358,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
Map<AccountType, Account> accounts = createAccountMapFromdDBObject(accountsMap);
User result = new UserImpl(username, email, fullName, company, locale,
emailValidated == null ? false : emailValidated, passwordResetSecret, validationSecret, defaultTenant,
accounts.values(), userGroupProvider, lockingAndBanning);
accounts.values(), userGroupProvider, timedLock);
for (final Role role : roles) {
result.addRole(role);
}
@@ -27,7 +27,7 @@ import com.sap.sse.security.shared.SocialUserAccount;
import com.sap.sse.security.shared.UsernamePasswordAccount;
import com.sap.sse.security.shared.WildcardPermission;
import com.sap.sse.security.shared.impl.AccessControlList;
import com.sap.sse.security.shared.impl.LockingAndBanningImpl;
import com.sap.sse.security.shared.impl.TimedLockImpl;
import com.sap.sse.security.shared.impl.Ownership;
import com.sap.sse.security.shared.impl.Role;
import com.sap.sse.security.shared.impl.User;
@@ -216,13 +216,13 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
dbUser.put(FieldNames.User.PASSWORD_RESET_SECRET.name(), user.getPasswordResetSecret());
dbUser.put(FieldNames.User.VALIDATION_SECRET.name(), user.getValidationSecret());
dbUser.put(FieldNames.User.ACCOUNTS.name(), createAccountMapObject(user.getAllAccounts()));
if (user.getLockingAndBanning() instanceof LockingAndBanningImpl) {
final LockingAndBanningImpl lockingAndBanning = ((LockingAndBanningImpl) user.getLockingAndBanning());
dbUser.put(FieldNames.User.LOCKED_UNTIL_MILLIS.name(), lockingAndBanning.getLockedUntil().asMillis());
dbUser.put(FieldNames.User.NEXT_LOCKING_DURATION_MILLIS.name(), lockingAndBanning.getNextLockingDelay().asMillis());
if (user.getTimedLock() instanceof TimedLockImpl) {
final TimedLockImpl timedLock = ((TimedLockImpl) user.getTimedLock());
dbUser.put(FieldNames.User.LOCKED_UNTIL_MILLIS.name(), timedLock.getLockedUntil().asMillis());
dbUser.put(FieldNames.User.NEXT_LOCKING_DURATION_MILLIS.name(), timedLock.getNextLockingDelay().asMillis());
} else {
logger.warning("Expected user locking/banning to be of type "+LockingAndBanningImpl.class.getSimpleName()
+" but was of type "+user.getLockingAndBanning().getClass().getSimpleName()+"; not storing to DB");
logger.warning("Expected user locking/banning to be of type "+TimedLockImpl.class.getSimpleName()
+" but was of type "+user.getTimedLock().getClass().getSimpleName()+"; not storing to DB");
}
BasicDBList dbRoles = new BasicDBList();
for (Role role : user.getRoles()) {
@@ -10,7 +10,7 @@ import com.sap.sse.security.shared.HasPermissions;
import com.sap.sse.security.shared.QualifiedObjectIdentifier;
import com.sap.sse.security.shared.UserGroupProvider;
import com.sap.sse.security.shared.WildcardPermission;
import com.sap.sse.security.shared.impl.LockingAndBanning;
import com.sap.sse.security.shared.impl.TimedLock;
import com.sap.sse.security.shared.impl.Role;
import com.sap.sse.security.shared.impl.User;
import com.sap.sse.security.shared.impl.UserGroup;
@@ -242,7 +242,7 @@ public class UserProxy implements User {
}
@Override
public LockingAndBanning getLockingAndBanning() {
public TimedLock getTimedLock() {
throw new UnsupportedOperationException();
}
}
@@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO
securityManager.sessionManager.globalSessionTimeout = 31536000000
cacheManager = com.sap.sse.security.SessionCacheManager
securityManager.cacheManager = $cacheManager
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks
securityManager.authenticator.authenticationStrategy = $authenticationStrategy
# Support for anonymous user permissions
@@ -14,16 +14,16 @@ import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.realm.Realm;
import com.sap.sse.security.impl.Activator;
import com.sap.sse.security.shared.impl.LockingAndBanning;
import com.sap.sse.security.shared.impl.TimedLock;
import com.sap.sse.security.shared.impl.User;
import com.sap.sse.util.ServiceTrackerFactory;
public class AtLeastOneSuccessfulStrategyWithLockingAndBanning extends AtLeastOneSuccessfulStrategy {
private static final Logger logger = Logger.getLogger(AtLeastOneSuccessfulStrategyWithLockingAndBanning.class.getName());
public class AtLeastOneSuccessfulStrategyWithTimedLocks extends AtLeastOneSuccessfulStrategy {
private static final Logger logger = Logger.getLogger(AtLeastOneSuccessfulStrategyWithTimedLocks.class.getName());
private final Future<SecurityService> securityService;
public AtLeastOneSuccessfulStrategyWithLockingAndBanning() {
public AtLeastOneSuccessfulStrategyWithTimedLocks() {
if (Activator.getContext() != null) {
securityService = ServiceTrackerFactory.createServiceFuture(Activator.getContext(), SecurityService.class);
} else {
@@ -55,9 +55,9 @@ public class AtLeastOneSuccessfulStrategyWithLockingAndBanning extends AtLeastOn
logger.info("failed password authentication for user "+username);
final SecurityService mySecurityService = getSecurityService();
if (mySecurityService != null) {
final LockingAndBanning lockingAndBanning = mySecurityService.failedPasswordAuthentication(user);
if (lockingAndBanning != null) {
logger.info("User "+username+" locked for password authentication: "+lockingAndBanning);
final TimedLock timedLock = mySecurityService.failedPasswordAuthentication(user);
if (timedLock != null) {
logger.info("User "+username+" locked for password authentication: "+timedLock);
}
} else {
logger.warning("Account locking due to failed password authentication for user "+username+" not possible; security service not found");
@@ -81,9 +81,9 @@ public class AtLeastOneSuccessfulStrategyWithLockingAndBanning extends AtLeastOn
logger.info("failed bearer token authentication for client IP "+bearerToken.getClientIP()+" with user agent "+bearerToken.getUserAgent());
final SecurityService mySecurityService = getSecurityService();
if (mySecurityService != null) {
final LockingAndBanning lockingAndBanning = mySecurityService.failedBearerTokenAuthentication(bearerToken.getClientIP());
if (lockingAndBanning != null) {
logger.info("Client IP "+bearerToken.getClientIP()+" locked for bearer token authentication: "+lockingAndBanning);
final TimedLock timedLock = mySecurityService.failedBearerTokenAuthentication(bearerToken.getClientIP());
if (timedLock != null) {
logger.info("Client IP "+bearerToken.getClientIP()+" locked for bearer token authentication: "+timedLock);
}
} else {
logger.warning("Client IP locking due to failed bearer token authentication for client IP "
@@ -40,7 +40,7 @@ import com.sap.sse.security.interfaces.SocialSettingsKeys;
import com.sap.sse.security.shared.SocialUserAccount;
import com.sap.sse.security.shared.UserGroupManagementException;
import com.sap.sse.security.shared.UserManagementException;
import com.sap.sse.security.shared.impl.LockingAndBanningImpl;
import com.sap.sse.security.shared.impl.TimedLockImpl;
import com.sap.sse.security.shared.impl.User;
import com.sap.sse.security.shared.impl.UserGroup;
@@ -189,7 +189,7 @@ public class OAuthRealm extends AbstractCompositeAuthorizingRealm {
try {
UserGroup tenant = getUserStore().createUserGroup(UUID.randomUUID(), socialname + SecurityService.TENANT_SUFFIX);
getAccessControlStore().setOwnership(tenant.getIdentifier(), user, tenant, tenant.getName());
user = getUserStore().createUser(socialname, socialUser.getProperty(Social.EMAIL.name()), new LockingAndBanningImpl(), socialUser);
user = getUserStore().createUser(socialname, socialUser.getProperty(Social.EMAIL.name()), new TimedLockImpl(), socialUser);
tenant.add(user);
getUserStore().updateUserGroup(tenant);
} catch (UserManagementException | UserGroupManagementException e) {
@@ -53,7 +53,7 @@ import com.sap.sse.security.shared.UserManagementException;
import com.sap.sse.security.shared.WildcardPermission;
import com.sap.sse.security.shared.WithQualifiedObjectIdentifier;
import com.sap.sse.security.shared.impl.AccessControlList;
import com.sap.sse.security.shared.impl.LockingAndBanning;
import com.sap.sse.security.shared.impl.TimedLock;
import com.sap.sse.security.shared.impl.Ownership;
import com.sap.sse.security.shared.impl.Role;
import com.sap.sse.security.shared.impl.SecuredSecurityTypes;
@@ -222,6 +222,8 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica
void updateSimpleUserEmail(String username, String newEmail, String validationBaseURL) throws UserManagementException;
void updateUserProperties(String username, String fullName, String company, Locale locale) throws UserManagementException;
void resetUserTimedLock(String username) throws UserManagementException;
void deleteUser(String username) throws UserManagementException;
@@ -874,7 +876,7 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica
Pair<Boolean, Set<String>> getCORSFilterConfiguration(String serverName);
LockingAndBanning failedPasswordAuthentication(User user);
TimedLock failedPasswordAuthentication(User user);
void successfulPasswordAuthentication(User user);
@@ -892,7 +894,7 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica
* structures, avoiding garbage piling up.
* @return TODO
*/
LockingAndBanning failedBearerTokenAuthentication(String clientIP);
TimedLock failedBearerTokenAuthentication(String clientIP);
/**
* Call this when the combination of {@code clientIP} and {@code userAgent} was not
@@ -50,8 +50,8 @@ public class UsernamePasswordRealm extends AbstractCompositeAuthorizingRealm {
logger.warning("Rejecting authentication attempt for non-existing user "+username);
return null;
}
if (user.getLockingAndBanning().isAuthenticationLocked()) {
logger.warning("Rejected attempt to authenticate user "+username+" because it is locked: "+user.getLockingAndBanning());
if (user.getTimedLock().isLocked()) {
logger.warning("Rejected attempt to authenticate user "+username+" because it is locked: "+user.getTimedLock());
throw new LockedAccountException("Password authentication for user "+username+" is currently locked");
}
final UsernamePasswordAccount upa = (UsernamePasswordAccount) user.getAccount(AccountType.USERNAME_PASSWORD);
@@ -15,7 +15,7 @@ import com.sap.sse.security.shared.RoleDefinition;
import com.sap.sse.security.shared.UserGroupManagementException;
import com.sap.sse.security.shared.UserManagementException;
import com.sap.sse.security.shared.WildcardPermission;
import com.sap.sse.security.shared.impl.LockingAndBanning;
import com.sap.sse.security.shared.impl.TimedLock;
import com.sap.sse.security.shared.impl.Ownership;
import com.sap.sse.security.shared.impl.User;
import com.sap.sse.security.shared.subscription.Subscription;
@@ -69,6 +69,8 @@ public interface ReplicableSecurityService extends SecurityService {
Void internalUpdateUserProperties(String username, String fullName, String company, Locale locale);
Void internalResetUserTimedLock(String username);
Boolean internalValidateEmail(String username, String validationSecret);
Void internalSetPreference(String username, String key, String value);
@@ -122,13 +124,13 @@ public interface ReplicableSecurityService extends SecurityService {
Void internalSetCORSFilterConfigurationAllowedOrigins(String serverName, String... allowedOrigins);
LockingAndBanning internalFailedPasswordAuthentication(String username);
TimedLock internalFailedPasswordAuthentication(String username);
Boolean internalSuccessfulPasswordAuthentication(String username);
Boolean internalSuccessfulBearerTokenAuthentication(String clientIP);
LockingAndBanning internalFailedBearerTokenAuthentication(String clientIP);
TimedLock internalFailedBearerTokenAuthentication(String clientIP);
LockingAndBanning internalRecordUserCreationFromClientIP(String clientIP);
TimedLock internalRecordUserCreationFromClientIP(String clientIP);
}
@@ -144,6 +144,7 @@ import com.sap.sse.security.operations.RemoveRoleDefinitionFromUserGroupOperatio
import com.sap.sse.security.operations.RemoveRoleFromUserOperation;
import com.sap.sse.security.operations.RemoveUserFromUserGroupOperation;
import com.sap.sse.security.operations.ResetPasswordOperation;
import com.sap.sse.security.operations.ResetUserLockOperation;
import com.sap.sse.security.operations.SecurityOperation;
import com.sap.sse.security.operations.SetAccessTokenOperation;
import com.sap.sse.security.operations.SetDefaultTenantForServerForUserOperation;
@@ -184,8 +185,8 @@ import com.sap.sse.security.shared.UsernamePasswordAccount;
import com.sap.sse.security.shared.WildcardPermission;
import com.sap.sse.security.shared.WithQualifiedObjectIdentifier;
import com.sap.sse.security.shared.impl.AccessControlList;
import com.sap.sse.security.shared.impl.LockingAndBanning;
import com.sap.sse.security.shared.impl.LockingAndBanningImpl;
import com.sap.sse.security.shared.impl.TimedLock;
import com.sap.sse.security.shared.impl.TimedLockImpl;
import com.sap.sse.security.shared.impl.Ownership;
import com.sap.sse.security.shared.impl.PermissionAndRoleAssociation;
import com.sap.sse.security.shared.impl.Role;
@@ -277,7 +278,7 @@ implements ReplicableSecurityService, ClearStateTestSupport {
* @see #successfulBearerTokenAuthentication(String)
* @see #isClientIPLockedForBearerTokenAuthentication(String)
*/
private final ConcurrentMap<String, LockingAndBanning> clientIPBasedLockingAndBanningForBearerTokenAuthentication;
private final ConcurrentMap<String, TimedLock> clientIPBasedTimedLocksForBearerTokenAuthentication;
private final static String CLIENT_IP_NULL_ESCAPE = UUID.randomUUID().toString();
/**
@@ -289,7 +290,7 @@ implements ReplicableSecurityService, ClearStateTestSupport {
* When entering values into this map, the method entering it is responsible for also scheduling a background
* task that a while after lock expiry the record is expunged again from the map to avoid garbage piling up.
*/
private final ConcurrentMap<String, LockingAndBanning> clientIPBasedLockingAndBanningForUserCreation;
private final ConcurrentMap<String, TimedLock> clientIPBasedTimedLocksForUserCreation;
private final Zxcvbn passwordValidator;
@@ -340,8 +341,8 @@ implements ReplicableSecurityService, ClearStateTestSupport {
throw new IllegalArgumentException("No HasPermissionsProvider defined");
}
logger.info("Initializing Security Service with user store " + userStore);
this.clientIPBasedLockingAndBanningForBearerTokenAuthentication = new ConcurrentHashMap<>();
this.clientIPBasedLockingAndBanningForUserCreation = new ConcurrentHashMap<>();
this.clientIPBasedTimedLocksForBearerTokenAuthentication = new ConcurrentHashMap<>();
this.clientIPBasedTimedLocksForUserCreation = new ConcurrentHashMap<>();
this.permissionChangeListeners = new PermissionChangeListeners(this);
this.sharedAcrossSubdomainsOf = sharedAcrossSubdomainsOf;
this.subscriptionPlanProvider = subscriptionPlanProvider;
@@ -1148,22 +1149,22 @@ implements ReplicableSecurityService, ClearStateTestSupport {
// synchronize to ensure that no two threads can enter values into the map concurrently;
// still the use of a ConcurrentMap is justified because there may be concurrent write access
// through replication
synchronized (clientIPBasedLockingAndBanningForUserCreation) {
final LockingAndBanning lockingAndBanning = clientIPBasedLockingAndBanningForUserCreation.get(clientIP);
if (lockingAndBanning == null || !lockingAndBanning.isAuthenticationLocked()) {
synchronized (clientIPBasedTimedLocksForUserCreation) {
final TimedLock timedLock = clientIPBasedTimedLocksForUserCreation.get(clientIP);
if (timedLock == null || !timedLock.isLocked()) {
apply(s->s.internalRecordUserCreationFromClientIP(clientIP));
} else {
throw new UserManagementException(UserManagementException.CLIENT_CURRENTLY_LOCKED_FOR_USER_CREATION);
throw new UserManagementException("Client IP "+clientIP+" locked for user creation: "+timedLock);
}
}
}
@Override
public LockingAndBanning internalRecordUserCreationFromClientIP(String clientIP) {
final LockingAndBanning result = new LockingAndBanningImpl(TimePoint.now().plus(DEFAULT_CLIENT_IP_BASED_USER_CREATION_LOCKING_DURATION),
public TimedLock internalRecordUserCreationFromClientIP(String clientIP) {
final TimedLock result = new TimedLockImpl(TimePoint.now().plus(DEFAULT_CLIENT_IP_BASED_USER_CREATION_LOCKING_DURATION),
DEFAULT_CLIENT_IP_BASED_USER_CREATION_LOCKING_DURATION);
clientIPBasedLockingAndBanningForUserCreation.put(clientIP, result);
scheduleCleanUpTask(clientIP, result, clientIPBasedLockingAndBanningForUserCreation,
clientIPBasedTimedLocksForUserCreation.put(clientIP, result);
scheduleCleanUpTask(clientIP, result, clientIPBasedTimedLocksForUserCreation,
"client IPs locked for user creation");
return result;
}
@@ -1203,7 +1204,7 @@ implements ReplicableSecurityService, ClearStateTestSupport {
@Override
public User internalCreateUser(String username, String email, Account... accounts) throws UserManagementException {
final User result = store.createUser(username, email, new LockingAndBanningImpl(), accounts);
final User result = store.createUser(username, email, new TimedLockImpl(), accounts);
return result;
}
@@ -1262,14 +1263,31 @@ implements ReplicableSecurityService, ClearStateTestSupport {
return null;
}
@Override
public void resetUserTimedLock(String username) throws UserManagementException {
final User user = store.getUserByName(username);
if (user == null) {
throw new UserManagementException(UserManagementException.USER_DOES_NOT_EXIST);
}
apply(new ResetUserLockOperation(username, user.getTimedLock()));
}
@Override
public Void internalResetUserTimedLock(String username) {
final User user = store.getUserByName(username);
user.getTimedLock().resetLock();
store.updateUser(user);
return null;
}
@Override
public boolean checkPassword(String username, String password) throws UserManagementException {
final User user = store.getUserByName(username);
if (user == null) {
throw new UserManagementException(UserManagementException.USER_DOES_NOT_EXIST);
}
if (user.getLockingAndBanning().isAuthenticationLocked()) {
throw new UserManagementException(UserManagementException.PASSWORD_AUTHENTICATION_CURRENTLY_LOCKED_FOR_USER);
if (user.getTimedLock().isLocked()) {
throw new UserManagementException("Password authentication is locked for user "+username);
}
final UsernamePasswordAccount account = (UsernamePasswordAccount) user.getAccount(AccountType.USERNAME_PASSWORD);
String hashedOldPassword = hashPassword(password, account.getSalt());
@@ -1284,23 +1302,23 @@ implements ReplicableSecurityService, ClearStateTestSupport {
}
@Override
public LockingAndBanning failedPasswordAuthentication(User user) {
public TimedLock failedPasswordAuthentication(User user) {
return apply(s->s.internalFailedPasswordAuthentication(user.getName()));
}
@Override
public LockingAndBanning internalFailedPasswordAuthentication(String username) {
public TimedLock internalFailedPasswordAuthentication(String username) {
final User user = getUserByName(username);
final LockingAndBanning lockingAndBanning;
final TimedLock timedLock;
if (user != null) {
lockingAndBanning = user.getLockingAndBanning();
lockingAndBanning.failedPasswordAuthentication();
timedLock = user.getTimedLock();
timedLock.extendLockDuration();
store.updateUser(user);
logger.info("failed password authentication for user "+username+"; locking: "+lockingAndBanning);
logger.info("failed password authentication for user "+username+"; locking: "+timedLock);
} else {
lockingAndBanning = null;
timedLock = null;
}
return lockingAndBanning;
return timedLock;
}
@Override
@@ -1316,7 +1334,7 @@ implements ReplicableSecurityService, ClearStateTestSupport {
final boolean changed;
final User user = getUserByName(username);
if (user != null) {
changed = user.getLockingAndBanning().successfulPasswordAuthentication();
changed = user.getTimedLock().resetLock();
if (changed) {
store.updateUser(user);
}
@@ -1327,27 +1345,27 @@ implements ReplicableSecurityService, ClearStateTestSupport {
}
@Override
public LockingAndBanning failedBearerTokenAuthentication(String clientIP) {
public TimedLock failedBearerTokenAuthentication(String clientIP) {
return apply(s->s.internalFailedBearerTokenAuthentication(clientIP));
}
@Override
public LockingAndBanning internalFailedBearerTokenAuthentication(String clientIP) {
final LockingAndBanning lockingAndBanning = clientIPBasedLockingAndBanningForBearerTokenAuthentication.computeIfAbsent(escapeNullClientIP(clientIP), key->new LockingAndBanningImpl());
lockingAndBanning.failedPasswordAuthentication();
logger.info("failed bearer token authentication from client IP "+clientIP+"; locking: "+lockingAndBanning);
scheduleCleanUpTask(clientIP, lockingAndBanning, clientIPBasedLockingAndBanningForBearerTokenAuthentication,
public TimedLock internalFailedBearerTokenAuthentication(String clientIP) {
final TimedLock timedLock = clientIPBasedTimedLocksForBearerTokenAuthentication.computeIfAbsent(escapeNullClientIP(clientIP), key->new TimedLockImpl());
timedLock.extendLockDuration();
logger.info("failed bearer token authentication from client IP "+clientIP+"; locking: "+timedLock);
scheduleCleanUpTask(clientIP, timedLock, clientIPBasedTimedLocksForBearerTokenAuthentication,
"client IPs locked for bearer token authentication");
return lockingAndBanning;
return timedLock;
}
/**
* Schedule a clean-up task to avoid leaking memory for the LockingAndBanning objects; schedule it in two times the
* locking expiry of {@code lockingAndBanning}, but at least one hour, because if no authentication failure occurs
* for that IP/user agent combination, we will entirely remove the {@link LockingAndBanning} from the map,
* Schedule a clean-up task to avoid leaking memory for the TimedLock objects; schedule it in two times the
* locking expiry of {@code timedLock}, but at least one hour, because if no authentication failure occurs
* for that IP/user agent combination, we will entirely remove the {@link TimedLock} from the map,
* effectively resetting that IP to a short default locking duration again; this way, if during the double
* expiration time another failed attempt is registered, we can still grow the locking duration because we have kept
* the {@link LockingAndBanning} object available for a bit longer. Furthermore, for authentication requests, the
* the {@link TimedLock} object available for a bit longer. Furthermore, for authentication requests, the
* responsible {@link Realm} will let authentication requests get to here only if not locked, so if we were to
* expunge entries immediately as they unlock, the locking duration could never grow.<p>
*
@@ -1355,16 +1373,16 @@ implements ReplicableSecurityService, ClearStateTestSupport {
* expiry duration.
*/
private void scheduleCleanUpTask(final String clientIPOrNull,
final LockingAndBanning lockingAndBanning,
final ConcurrentMap<String, LockingAndBanning> mapToRemoveFrom,
final TimedLock timedLock,
final ConcurrentMap<String, TimedLock> mapToRemoveFrom,
final String nameOfMapForLog) {
final long millisUntilLockingExpiry = Math.max(
2*ApproximateTime.approximateNow().until(lockingAndBanning.getLockedUntil()).asMillis(),
2*ApproximateTime.approximateNow().until(timedLock.getLockedUntil()).asMillis(),
Duration.ONE_HOUR.asMillis());
ThreadPoolUtil.INSTANCE.getDefaultBackgroundTaskThreadPoolExecutor().schedule(
()->{
final LockingAndBanning lab = mapToRemoveFrom.get(escapeNullClientIP(clientIPOrNull));
if (lab != null && !lab.isAuthenticationLocked()) {
final TimedLock lab = mapToRemoveFrom.get(escapeNullClientIP(clientIPOrNull));
if (lab != null && !lab.isLocked()) {
mapToRemoveFrom.remove(escapeNullClientIP(clientIPOrNull));
logger.info("Removed "+clientIPOrNull+" from "+nameOfMapForLog+"; "
+mapToRemoveFrom.size()
@@ -1389,9 +1407,9 @@ implements ReplicableSecurityService, ClearStateTestSupport {
@Override
public Boolean internalSuccessfulBearerTokenAuthentication(String clientIP) {
final boolean changed;
final LockingAndBanning lockingAndBanning = clientIPBasedLockingAndBanningForBearerTokenAuthentication.remove(escapeNullClientIP(clientIP));
if (lockingAndBanning != null) {
logger.info("Unlocked bearer token authentication from "+clientIP+"; last locking state was "+lockingAndBanning);
final TimedLock timedLock = clientIPBasedTimedLocksForBearerTokenAuthentication.remove(escapeNullClientIP(clientIP));
if (timedLock != null) {
logger.info("Unlocked bearer token authentication from "+clientIP+"; last locking state was "+timedLock);
changed = true;
} else {
changed = false;
@@ -1401,8 +1419,8 @@ implements ReplicableSecurityService, ClearStateTestSupport {
@Override
public boolean isClientIPLockedForBearerTokenAuthentication(String clientIP) {
final LockingAndBanning lockingAndBanning = clientIPBasedLockingAndBanningForBearerTokenAuthentication.get(escapeNullClientIP(clientIP));
return lockingAndBanning != null && lockingAndBanning.isAuthenticationLocked();
final TimedLock timedLock = clientIPBasedTimedLocksForBearerTokenAuthentication.get(escapeNullClientIP(clientIP));
return timedLock != null && timedLock.isLocked();
}
@Override
@@ -2501,8 +2519,8 @@ implements ReplicableSecurityService, ClearStateTestSupport {
store.clear();
accessControlStore.clear();
corsFilterConfigurationsByReplicaSetName.clear();
clientIPBasedLockingAndBanningForBearerTokenAuthentication.clear();
clientIPBasedLockingAndBanningForUserCreation.clear();
clientIPBasedTimedLocksForBearerTokenAuthentication.clear();
clientIPBasedTimedLocksForUserCreation.clear();
}
@Override
@@ -2584,13 +2602,13 @@ implements ReplicableSecurityService, ClearStateTestSupport {
final SecurityServiceInitialLoadExtensionsDTO initialLoadExtensions = (SecurityServiceInitialLoadExtensionsDTO) is.readObject();
final ConcurrentMap<String, Pair<Boolean, Set<String>>> newCORSFilterConfigurations = initialLoadExtensions.getCorsFilterConfigurationsByReplicaSetName();
corsFilterConfigurationsByReplicaSetName.putAll(newCORSFilterConfigurations);
if (initialLoadExtensions.getClientIPBasedLockingAndBanningForBearerTokenAuthentication() != null) {
if (initialLoadExtensions.getClientIPBasedTimedLocksForBearerTokenAuthentication() != null) {
// checking for null for backward compatibility; an older primary/master may not have known this field yet
clientIPBasedLockingAndBanningForBearerTokenAuthentication.putAll(initialLoadExtensions.getClientIPBasedLockingAndBanningForBearerTokenAuthentication());
clientIPBasedTimedLocksForBearerTokenAuthentication.putAll(initialLoadExtensions.getClientIPBasedTimedLocksForBearerTokenAuthentication());
}
if (initialLoadExtensions.getClientIPBasedLockingAndBanningForUserCreation() != null) {
if (initialLoadExtensions.getClientIPBasedTimedLocksForUserCreation() != null) {
// checking for null for backward compatibility; an older primary/master may not have known this field yet
clientIPBasedLockingAndBanningForUserCreation.putAll(initialLoadExtensions.getClientIPBasedLockingAndBanningForUserCreation());
clientIPBasedTimedLocksForUserCreation.putAll(initialLoadExtensions.getClientIPBasedTimedLocksForUserCreation());
}
logger.info("Triggering SecurityInitializationCustomizers upon replication ...");
customizers.forEach(c -> c.customizeSecurityService(this));
@@ -2606,8 +2624,8 @@ implements ReplicableSecurityService, ClearStateTestSupport {
objectOutputStream.writeObject(baseUrlForCrossDomainStorage);
objectOutputStream.writeObject(new SecurityServiceInitialLoadExtensionsDTO(
corsFilterConfigurationsByReplicaSetName,
clientIPBasedLockingAndBanningForBearerTokenAuthentication,
clientIPBasedLockingAndBanningForUserCreation));
clientIPBasedTimedLocksForBearerTokenAuthentication,
clientIPBasedTimedLocksForUserCreation));
}
@Override
@@ -2914,8 +2932,8 @@ implements ReplicableSecurityService, ClearStateTestSupport {
// See com.sap.sse.security.impl.Activator.clearState(), moved due to required reinitialisation sequence for
// permission-vertical
public void clearState() throws Exception {
clientIPBasedLockingAndBanningForBearerTokenAuthentication.clear();
clientIPBasedLockingAndBanningForUserCreation.clear();
clientIPBasedTimedLocksForBearerTokenAuthentication.clear();
clientIPBasedTimedLocksForUserCreation.clear();
}
@Override
@@ -9,7 +9,7 @@ import java.util.concurrent.ConcurrentMap;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.replication.Replicable;
import com.sap.sse.security.SecurityService;
import com.sap.sse.security.shared.impl.LockingAndBanning;
import com.sap.sse.security.shared.impl.TimedLock;
/**
* Starting with the CORS filter configurations, this and future extensions of the {@link SecurityService}'s
@@ -30,28 +30,28 @@ public class SecurityServiceInitialLoadExtensionsDTO implements Serializable {
private final ConcurrentMap<String, Pair<Boolean, Set<String>>> corsFilterConfigurationsByReplicaSetName;
private final ConcurrentMap<String, LockingAndBanning> clientIPBasedLockingAndBanningForBearerTokenAuthentication;
private final ConcurrentMap<String, TimedLock> clientIPBasedTimedLocksForBearerTokenAuthentication;
private final ConcurrentMap<String, LockingAndBanning> clientIPBasedLockingAndBanningForUserCreation;
private final ConcurrentMap<String, TimedLock> clientIPBasedTimedLocksForUserCreation;
public SecurityServiceInitialLoadExtensionsDTO(ConcurrentMap<String, Pair<Boolean, Set<String>>> corsFilterConfigurationsByReplicaSetName,
ConcurrentMap<String, LockingAndBanning> clientIPBasedLockingAndBanningForBearerTokenAuthentication,
ConcurrentMap<String, LockingAndBanning> clientIPBasedLockingAndBanningForUserCreation) {
ConcurrentMap<String, TimedLock> clientIPBasedTimedLocksForBearerTokenAuthentication,
ConcurrentMap<String, TimedLock> clientIPBasedTimedLocksForUserCreation) {
super();
this.corsFilterConfigurationsByReplicaSetName = corsFilterConfigurationsByReplicaSetName;
this.clientIPBasedLockingAndBanningForBearerTokenAuthentication = clientIPBasedLockingAndBanningForBearerTokenAuthentication;
this.clientIPBasedLockingAndBanningForUserCreation = clientIPBasedLockingAndBanningForUserCreation;
this.clientIPBasedTimedLocksForBearerTokenAuthentication = clientIPBasedTimedLocksForBearerTokenAuthentication;
this.clientIPBasedTimedLocksForUserCreation = clientIPBasedTimedLocksForUserCreation;
}
ConcurrentMap<String, Pair<Boolean, Set<String>>> getCorsFilterConfigurationsByReplicaSetName() {
return corsFilterConfigurationsByReplicaSetName;
}
ConcurrentMap<String, LockingAndBanning> getClientIPBasedLockingAndBanningForBearerTokenAuthentication() {
return clientIPBasedLockingAndBanningForBearerTokenAuthentication;
ConcurrentMap<String, TimedLock> getClientIPBasedTimedLocksForBearerTokenAuthentication() {
return clientIPBasedTimedLocksForBearerTokenAuthentication;
}
ConcurrentMap<String, LockingAndBanning> getClientIPBasedLockingAndBanningForUserCreation() {
return clientIPBasedLockingAndBanningForUserCreation;
ConcurrentMap<String, TimedLock> getClientIPBasedTimedLocksForUserCreation() {
return clientIPBasedTimedLocksForUserCreation;
}
}
@@ -0,0 +1,22 @@
package com.sap.sse.security.operations;
import com.sap.sse.security.impl.ReplicableSecurityService;
import com.sap.sse.security.shared.impl.TimedLock;
public class ResetUserLockOperation implements SecurityOperation<Void> {
private static final long serialVersionUID = -6267523788529623080L;
protected final TimedLock timedLock;
protected final String username;
public ResetUserLockOperation(String username, TimedLock timedLock) {
this.username = username;
this.timedLock = timedLock;
}
@Override
public Void internalApplyTo(ReplicableSecurityService toState) throws Exception {
toState.internalResetUserTimedLock(username);
return null;
}
}
@@ -26,7 +26,7 @@ securityManager.subjectDAO = $subjectDAO
securityManager.sessionManager.globalSessionTimeout = 31536000000
cacheManager = com.sap.sse.security.SessionCacheManager
securityManager.cacheManager = $cacheManager
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithTimedLocks
securityManager.authenticator.authenticationStrategy = $authenticationStrategy
# Support for anonymous user permissions