bug6099: interims commit with a first implementation of a user account locking/banning mechanism;

yet missing is writing to persistence and replication
This commit is contained in:
Axel Uhl
2025-03-17 16:30:57 +01:00
parent c542e0d85d
commit 2416b49f89
44 changed files with 278 additions and 63 deletions
@@ -26,6 +26,8 @@ securityManager.subjectDAO = $subjectDAO
securityManager.sessionManager.globalSessionTimeout = 31536000000
cacheManager = com.sap.sse.security.SessionCacheManager
securityManager.cacheManager = $cacheManager
authenticationStrategy = com.sap.sse.security.AtLeastOneSuccessfulStrategyWithLockingAndBanning
securityManager.authenticator.authenticationStrategy = $authenticationStrategy
# Support for anonymous user permissions
webSubjectFactoryWithAnonymousPrincipalSupport=com.sap.sse.security.WebSubjectFactoryWithAnonymousPrincipalSupport
@@ -0,0 +1,39 @@
package com.sap.sse.security;
import java.util.logging.Logger;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.realm.Realm;
import com.sap.sse.security.shared.impl.User;
public class AtLeastOneSuccessfulStrategyWithLockingAndBanning extends AtLeastOneSuccessfulStrategy {
private static final Logger logger = Logger
.getLogger(AtLeastOneSuccessfulStrategyWithLockingAndBanning.class.getName());
@Override
public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo,
AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException {
if (token != null && token.getPrincipal() != null && realm instanceof UsernamePasswordRealm) {
final UsernamePasswordRealm upRealm = (UsernamePasswordRealm) realm;
final String username = token.getPrincipal().toString();
final User user = upRealm.getUserStore().getUserByName(username);
if (user != null) {
if (t != null) {
if (t instanceof IncorrectCredentialsException) {
logger.info("failed password authentication for user "+username);
user.getLockingAndBanning().failedPasswordAuthentication();
}
} else {
// no exception, so the authentication must have been successful
user.getLockingAndBanning().successfulPasswordAuthentication();
}
}
}
return super.afterAttempt(realm, token, singleRealmInfo, aggregateInfo, t);
}
}
@@ -40,6 +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.User;
import com.sap.sse.security.shared.impl.UserGroup;
@@ -188,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()), socialUser);
user = getUserStore().createUser(socialname, socialUser.getProperty(Social.EMAIL.name()), new LockingAndBanningImpl(), socialUser);
tenant.add(user);
getUserStore().updateUserGroup(tenant);
} catch (UserManagementException | UserGroupManagementException e) {
@@ -3,6 +3,7 @@ package com.sap.sse.security;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SaltedAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
@@ -12,7 +13,6 @@ import com.sap.sse.security.interfaces.SimpleSaltedAuthenticationInfo;
import com.sap.sse.security.shared.UsernamePasswordAccount;
public class UsernamePasswordRealm extends AbstractCompositeAuthorizingRealm {
public UsernamePasswordRealm() {
super();
setAuthenticationTokenClass(UsernamePasswordToken.class);
@@ -45,6 +45,9 @@ public class UsernamePasswordRealm extends AbstractCompositeAuthorizingRealm {
if (user == null) {
return null;
}
if (user.getLockingAndBanning().isPasswordAuthenticationLocked()) {
throw new LockedAccountException("Password authentication for user "+username+" is currently locked");
}
UsernamePasswordAccount upa = (UsernamePasswordAccount) user.getAccount(AccountType.USERNAME_PASSWORD);
if (upa == null){
return null;
@@ -61,6 +64,4 @@ public class UsernamePasswordRealm extends AbstractCompositeAuthorizingRealm {
SaltedAuthenticationInfo sai = new SimpleSaltedAuthenticationInfo(username, saltedPassword, salt);
return sai;
}
}
@@ -171,6 +171,7 @@ 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.LockingAndBanningImpl;
import com.sap.sse.security.shared.impl.Ownership;
import com.sap.sse.security.shared.impl.PermissionAndRoleAssociation;
import com.sap.sse.security.shared.impl.Role;
@@ -1097,7 +1098,7 @@ implements ReplicableSecurityService, ClearStateTestSupport {
@Override
public User internalCreateUser(String username, String email, Account... accounts) throws UserManagementException {
final User result = store.createUser(username, email, accounts); // TODO: get the principal as owner
final User result = store.createUser(username, email, new LockingAndBanningImpl(), accounts); // TODO: get the principal as owner
return result;
}
@@ -1161,11 +1162,17 @@ implements ReplicableSecurityService, ClearStateTestSupport {
if (user == null) {
throw new UserManagementException(UserManagementException.USER_DOES_NOT_EXIST);
}
if (user.getLockingAndBanning().isPasswordAuthenticationLocked()) {
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());
final boolean result = Util.equalsWithNull(hashedOldPassword, account.getSaltedPassword());
if (!result) {
logger.info("Failed password check for user "+username);
user.getLockingAndBanning().failedPasswordAuthentication();
} else {
user.getLockingAndBanning().successfulPasswordAuthentication();
}
return result;
}