Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alias ID and Alias ZID for Users #2663

Merged
merged 129 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
129 commits
Select commit Hold shift + click to select a range
1c5fcd6
Checkout DB migrations for alias properties of Identity Provider table
adrianhoelzl-sap Jan 3, 2024
a2466ec
Add DB migrations for alias properties of users table
adrianhoelzl-sap Jan 3, 2024
a69eedf
Merge branch 'feature/alias-id-and-alias-zid-for-identity-providers' …
adrianhoelzl-sap Jan 3, 2024
574b205
Add alias properties to ScimUser class
adrianhoelzl-sap Jan 3, 2024
b5b1848
Add alias properties to create operations in JdbcScimUserProvisioning
adrianhoelzl-sap Jan 3, 2024
a206ef7
Add alias properties to update query in JdbcScimUserProvisioning
adrianhoelzl-sap Jan 3, 2024
dfafaa7
Add alias properties to deactivate and delete user operations in Jdbc…
adrianhoelzl-sap Jan 3, 2024
ba0f291
Add alias properties to change password query in JdbcScimUserProvisio…
adrianhoelzl-sap Jan 3, 2024
5069a5b
Add alias properties to update password change required query in Jdbc…
adrianhoelzl-sap Jan 3, 2024
867e711
Fix unit tests
adrianhoelzl-sap Jan 3, 2024
6406fb0
Add hasMirroredUser method to ScimUser
adrianhoelzl-sap Jan 3, 2024
8f72cec
Improve check for number of updated records in change password handler
adrianhoelzl-sap Jan 3, 2024
62f3d16
Add tests for alias property handling in JdbcScimUserProvisioning
adrianhoelzl-sap Jan 3, 2024
5fc1cf1
Fix unit tests
adrianhoelzl-sap Jan 3, 2024
2f59213
Add check whether the password change required flag was also updated …
adrianhoelzl-sap Jan 3, 2024
024a052
Move validity check of alias properties to separate class
adrianhoelzl-sap Jan 3, 2024
6421cef
Move mirroring handling to separate reusable class
adrianhoelzl-sap Jan 4, 2024
564ad98
Fix unit tests
adrianhoelzl-sap Jan 4, 2024
75c153a
Add javadoc to EntityMirroringHandler
adrianhoelzl-sap Jan 4, 2024
31d87a5
Add mirroring handler for ScimUser class
adrianhoelzl-sap Jan 4, 2024
dda2fb5
Add missing override annotations to JdbcScimUserProvisioning
adrianhoelzl-sap Jan 5, 2024
d0bd6b4
Fix wrong getter call in EntityMirroringHandler
adrianhoelzl-sap Jan 5, 2024
b55c124
Fix access token cache in IdentityProviderEndpointsAliasMockMvcTests
adrianhoelzl-sap Jan 5, 2024
3558354
Make access token cache in IdentityProviderEndpointsAliasMockMvcTests…
adrianhoelzl-sap Jan 5, 2024
9c7d188
Add factory method for cloning Approval
adrianhoelzl-sap Jan 5, 2024
61e2864
Add EntityMirroringResult class
adrianhoelzl-sap Jan 5, 2024
0dc23f2
Add additional validation checks to EntityMirroringHandler
adrianhoelzl-sap Jan 5, 2024
87f7277
Add mirrored entity to return value of EntityMirroringHandler.ensureC…
adrianhoelzl-sap Jan 5, 2024
a8c20aa
Add mirroring handling to ScimUser create endpoint
adrianhoelzl-sap Jan 5, 2024
3cf1bb6
Add missing constructor parameters to ScimUserEndpoints
adrianhoelzl-sap Jan 5, 2024
209de7b
Make ScimUser.getAliasZid return aliasZid instead of null
adrianhoelzl-sap Jan 8, 2024
6093128
Merge branch 'feature/alias-id-and-alias-zid-for-identity-providers' …
adrianhoelzl-sap Jan 17, 2024
2ed9aaa
Change wording from "mirrored" to "alias"
adrianhoelzl-sap Jan 17, 2024
4c56d6e
Add tests for IdentityProviderAliasHandler
adrianhoelzl-sap Jan 18, 2024
30eb9e5
Move unit tests for alias handling to separate class
adrianhoelzl-sap Jan 19, 2024
e03a727
Add further MockMvc tests for Creation/Update of IdPs with alias prop…
adrianhoelzl-sap Jan 31, 2024
55fbcaf
Add tests for read operations
adrianhoelzl-sap Jan 31, 2024
f30567a
Merge branch 'feature/alias-id-and-alias-zid-for-identity-providers-f…
adrianhoelzl-sap Feb 7, 2024
4ea9aa0
Merge branch 'develop' into feature/alias-id-and-alias-zid-for-users
adrianhoelzl-sap Feb 21, 2024
7badbac
Merge branch 'feature/move-idp-alias-handling-to-separate-class' into…
adrianhoelzl-sap Feb 22, 2024
b6ed90d
Fix IdentityProviderEndpointsTest
adrianhoelzl-sap Feb 22, 2024
5e57d02
Fix Flyway migration
adrianhoelzl-sap Feb 22, 2024
b132b19
Remove obsolete IdentityProviderEndpointsAliasTest
adrianhoelzl-sap Feb 22, 2024
4e3a20a
Fix IdentityProviderAliasHandlerEnsureConsistencyTest
adrianhoelzl-sap Feb 22, 2024
afe1673
Remove obsolete IdentityProviderAliasHandlerTest
adrianhoelzl-sap Feb 22, 2024
408158b
Add JsonIgnore annotation to EntityWithAlias#getAliasDescription
adrianhoelzl-sap Feb 22, 2024
37c160a
Fix JdbcScimUserProvisioningTests
adrianhoelzl-sap Feb 22, 2024
0fb5e3a
Refactor
adrianhoelzl-sap Feb 23, 2024
8f4233b
Remove obsolete IdentityProviderEndpointsTestBase
adrianhoelzl-sap Feb 23, 2024
0bfe054
Revert changes to JdbcScimUserProvisioning
adrianhoelzl-sap Feb 23, 2024
b7af137
Add skeleton of ScimUserEndpointsAliasMockMvcTests
adrianhoelzl-sap Feb 23, 2024
ca1b196
Introduce superclass for MockMvcTests of endpoints for entities with …
adrianhoelzl-sap Feb 23, 2024
fd16234
Add alias properties to ScimUser JSON deserialization
adrianhoelzl-sap Feb 26, 2024
ad67aa0
Add MockMvc tests for ScimUser GET with disabled alias feature
adrianhoelzl-sap Feb 26, 2024
e3d86ea
Fix JdbcScimUserProvisioningTests
adrianhoelzl-sap Feb 26, 2024
108ea27
Add MockMvc tests for SCIM user create with alias
adrianhoelzl-sap Feb 26, 2024
da0b8b3
Add more detailed comparison of original and alias user to ScimUserEn…
adrianhoelzl-sap Feb 27, 2024
50252e8
Add 'aliasEntitiesEnabled' flag to ScimUserEndpoints
adrianhoelzl-sap Feb 27, 2024
2440087
Add alias logic to deletion of SCIM users
adrianhoelzl-sap Feb 27, 2024
bb66bbe
Add MockMvc test about ignoring dangling reference during deletion
adrianhoelzl-sap Feb 27, 2024
5ac5b1e
Add MockMvc test about also deleting alias user if original is deleted
adrianhoelzl-sap Feb 27, 2024
466f17b
Add MockMvc test about breaking reference to original user in alias u…
adrianhoelzl-sap Feb 27, 2024
a65923e
Merge branch 'develop' into feature/alias-id-and-alias-zid-for-users
adrianhoelzl-sap Mar 1, 2024
e1e8536
Add MockMvc tests for SCIM user PUT with alias
adrianhoelzl-sap Mar 1, 2024
ff65904
Add MockMvc tests for SCIM User PUT with disabled alias feature
adrianhoelzl-sap Mar 1, 2024
7d5b2e5
Refactor
adrianhoelzl-sap Mar 4, 2024
799ac8a
Add MockMvc tests for SCIM user patch
adrianhoelzl-sap Mar 4, 2024
0d63a91
Fix assignment of groups and approvals of alias SCIM users
adrianhoelzl-sap Mar 5, 2024
6a9ab42
Remove obsolete Approval.clone method
adrianhoelzl-sap Mar 5, 2024
6acbaaa
Add check if alias user only has default groups of alias zone to Scim…
adrianhoelzl-sap Mar 5, 2024
d4b70b1
Revert EntityAliasResult
adrianhoelzl-sap Mar 6, 2024
a709d7d
Fix missing import
adrianhoelzl-sap Mar 6, 2024
4a6ca94
Fix unit tests
adrianhoelzl-sap Mar 6, 2024
f11fe75
Merge branch 'develop' into feature/alias-id-and-alias-zid-for-users
adrianhoelzl-sap Apr 12, 2024
c9ced59
Merge branch 'feature/alias-handler-for-scim-users' into feature/alia…
adrianhoelzl-sap Apr 12, 2024
985acb9
Revert adding retrievePasswordForUser method to ScimUserProvisioning
adrianhoelzl-sap Apr 15, 2024
5d13692
Revert making ScimUSer getters public
adrianhoelzl-sap Apr 15, 2024
4ec9fac
Remove usages of no longer accessible ScimUser getters
adrianhoelzl-sap Apr 15, 2024
11fb099
Reject user deletion if alias exists and alias feature is disabled
adrianhoelzl-sap Apr 15, 2024
a8c2d81
Adjust MockMvc tests to new deletion behavior
adrianhoelzl-sap Apr 15, 2024
db6d779
Adjust MockMvc test Update -> AliasFeatureDisabled -> ExistingAlias -…
adrianhoelzl-sap Apr 15, 2024
f70ecab
Adjust MockMvc test Update -> AliasFeatureDisabled -> ExistingAlias -…
adrianhoelzl-sap Apr 15, 2024
8337b40
Adjust MockMvc test Update -> AliasFeatureDisabled -> ExistingAlias -…
adrianhoelzl-sap Apr 15, 2024
bdbda47
Adjust MockMvc test Update -> AliasFeatureDisabled -> ExistingAlias -…
adrianhoelzl-sap Apr 15, 2024
fedcd43
Improve test case names
adrianhoelzl-sap Apr 15, 2024
77c57aa
Remove unused method
adrianhoelzl-sap Apr 15, 2024
59da5a8
Remove obsolete tests checking whether alias properties are ignored i…
adrianhoelzl-sap Apr 15, 2024
45590f9
Use 400 status code instead of 422 for rejected deletions of SCIM use…
adrianhoelzl-sap Apr 15, 2024
2163972
Adjust endpoint docs for new alias fields
adrianhoelzl-sap Apr 16, 2024
e12f969
Remove obsolete test cases for JdbcScimUserProvisioning
adrianhoelzl-sap Apr 16, 2024
ecd91bb
Refactor alias handling in ScimUser create endpoint
adrianhoelzl-sap Apr 16, 2024
9949ef4
Adjust SCIM operation counters to not count operations performed on a…
adrianhoelzl-sap Apr 16, 2024
2fa79cd
Adjust ScimUserEndpointsAliasMockMvcTests.assertIsCorrectAliasPair to…
adrianhoelzl-sap Apr 16, 2024
037f4ef
Fix endpoint docs for users delete endpoint
adrianhoelzl-sap Apr 16, 2024
9001886
Merge branch 'feature/alias-handler-for-scim-users' into feature/alia…
adrianhoelzl-sap Apr 17, 2024
5e682bc
Add unit test for ScimUserEndpoints: should throw during creation if …
adrianhoelzl-sap Apr 17, 2024
883a49a
Add unit test for ScimUserEndpoints: should throw during deletion if …
adrianhoelzl-sap Apr 17, 2024
c18257d
Fix integration tests for postgresql
adrianhoelzl-sap Apr 19, 2024
a921dfd
Fix unit tests
adrianhoelzl-sap Apr 19, 2024
e75cff9
Remove unnecessary sync of approvals and groups from update endpoint
adrianhoelzl-sap Apr 22, 2024
9c76620
Revert "Remove unnecessary sync of approvals and groups from update e…
adrianhoelzl-sap Apr 22, 2024
d198b61
Add separate class for unit tests related to user alias fields
adrianhoelzl-sap Apr 22, 2024
9e5891e
Fix unit tests
adrianhoelzl-sap Apr 22, 2024
837e891
Move ScimUserEndpointsAliasTests from uaa to server bundle
adrianhoelzl-sap Apr 22, 2024
010aa64
Add unit tests for ScimUser delete with alias
adrianhoelzl-sap Apr 23, 2024
da75626
Add unit tests for ScimUser update with alias
adrianhoelzl-sap Apr 23, 2024
93c063e
Add unit tests for ScimUser update with alias: alias handler throws e…
adrianhoelzl-sap Apr 23, 2024
b939192
Add unit tests for ScimUser delete with alias: should ignore dangling…
adrianhoelzl-sap Apr 24, 2024
eb3b1cc
Add unit tests for ScimUser update with alias: should throw ScimExcep…
adrianhoelzl-sap Apr 24, 2024
1061133
Use EntityAliasFailedException as cause if thrown by alias handler du…
adrianhoelzl-sap Apr 24, 2024
2da5c07
Adjust endpoint docs for ScimUser create/update: 422 status code if a…
adrianhoelzl-sap Apr 24, 2024
b51e355
Remove deletion of alias IdP from JdbcIdentityProviderProvisioning.de…
adrianhoelzl-sap Apr 24, 2024
5a7fd8c
Reject deletion of identity zone if an IdP with alias exists in the zone
adrianhoelzl-sap Apr 24, 2024
a6966f8
Add new status code to identity zone deletion documentation
adrianhoelzl-sap Apr 24, 2024
d612f5b
Merge branch 'feature/reject-idz-deletion-if-idp-with-alias-present' …
adrianhoelzl-sap Apr 25, 2024
410cf19
Fix ScimUser create: ensure zone ID is set before alias validity check
adrianhoelzl-sap Apr 25, 2024
ce46e69
Fix Sonar: remove unnecessary clause in if statement
adrianhoelzl-sap Apr 25, 2024
f54a141
Merge branch 'feature/reject-idz-deletion-if-idp-with-alias-present' …
adrianhoelzl-sap Apr 25, 2024
79b85b0
Merge branch 'develop' into feature/alias-id-and-alias-zid-for-users
adrianhoelzl-sap May 29, 2024
1d0e68a
Merge branch 'develop' into feature/alias-id-and-alias-zid-for-users
adrianhoelzl-sap Jun 5, 2024
c607b6b
Remove changes from PR#2850
adrianhoelzl-sap Jun 5, 2024
f600570
Replace value annotation with aliasEntitiesEnabled bean in ScimUserEn…
adrianhoelzl-sap Jun 5, 2024
30df9fc
Refactor
adrianhoelzl-sap Jun 5, 2024
ced0186
Rework: use transaction and alias handling only when alias flag is en…
adrianhoelzl-sap Jun 7, 2024
b94a96d
Rework: inject transaction template instead of creating it in the con…
adrianhoelzl-sap Jun 7, 2024
0e1683b
Fix Sonar: change collect(toList()) to toList()
adrianhoelzl-sap Jun 7, 2024
8b3c3f2
Merge remote-tracking branch 'refs/remotes/origin/develop' into featu…
strehle Jun 8, 2024
c5044f0
Import statement
strehle Jun 8, 2024
004ce43
Merge remote-tracking branch 'refs/remotes/origin/develop' into featu…
strehle Jun 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public String getId() {

@Override
public String getZoneId() {
return identityZoneId;
return getIdentityZoneId();
}

public IdentityProvider setId(String id) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,11 +353,9 @@ public int hashCode() {

private String zoneId = null;

@JsonIgnore
@Setter
private String aliasZid = null;

@JsonIgnore
@Setter
private String aliasId = null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ public ScimUser deserialize(JsonParser jp, DeserializationContext ctxt) throws I
user.setOrigin(jp.readValueAs(String.class));
} else if ("zoneId".equalsIgnoreCase(fieldName)) {
user.setZoneId(jp.readValueAs(String.class));
} else if ("aliasId".equalsIgnoreCase(fieldName)) {
user.setAliasId(jp.readValueAs(String.class));
} else if ("aliasZid".equalsIgnoreCase(fieldName)) {
user.setAliasZid(jp.readValueAs(String.class));
} else if ("salt".equalsIgnoreCase(fieldName)) {
user.setSalt(jp.readValueAs(String.class));
} else if ("passwordLastModified".equalsIgnoreCase(fieldName)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.jayway.jsonpath.JsonPathException;
import org.cloudfoundry.identity.uaa.account.UserAccountStatus;
import org.cloudfoundry.identity.uaa.account.event.UserAccountUnlockedEvent;
import org.cloudfoundry.identity.uaa.alias.EntityAliasFailedException;
import org.cloudfoundry.identity.uaa.approval.Approval;
import org.cloudfoundry.identity.uaa.approval.ApprovalStore;
import org.cloudfoundry.identity.uaa.audit.event.EntityDeletedEvent;
Expand All @@ -26,6 +27,7 @@
import org.cloudfoundry.identity.uaa.scim.ScimGroup;
import org.cloudfoundry.identity.uaa.scim.ScimGroupMembershipManager;
import org.cloudfoundry.identity.uaa.scim.ScimUser;
import org.cloudfoundry.identity.uaa.scim.ScimUserAliasHandler;
import org.cloudfoundry.identity.uaa.scim.ScimUserProvisioning;
import org.cloudfoundry.identity.uaa.scim.exception.InvalidScimResourceException;
import org.cloudfoundry.identity.uaa.scim.exception.ScimException;
Expand Down Expand Up @@ -58,9 +60,12 @@
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.jmx.support.MetricType;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
Expand All @@ -84,12 +89,14 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static org.cloudfoundry.identity.uaa.codestore.ExpiringCodeType.REGISTRATION;
import static org.springframework.util.StringUtils.hasText;
import static org.springframework.util.StringUtils.isEmpty;

import lombok.Getter;
Expand Down Expand Up @@ -122,12 +129,21 @@ public class ScimUserEndpoints implements InitializingBean, ApplicationEventPubl
private final ExpiringCodeStore codeStore;
private final ApprovalStore approvalStore;
private final ScimGroupMembershipManager membershipManager;
private final boolean aliasEntitiesEnabled;
@Getter
private final int userMaxCount;
private final HttpMessageConverter<?>[] messageConverters;
/**
* Update operations performed on alias users are not considered.
*/
private final AtomicInteger scimUpdates;
/**
* Deletion operations performed on alias users are not considered.
*/
private final AtomicInteger scimDeletes;
private final Map<String, AtomicInteger> errorCounts;
private final ScimUserAliasHandler aliasHandler;
private final TransactionTemplate transactionTemplate;

private ApplicationEventPublisher publisher;

Expand All @@ -145,7 +161,11 @@ public ScimUserEndpoints(
final ExpiringCodeStore codeStore,
final ApprovalStore approvalStore,
final ScimGroupMembershipManager membershipManager,
final @Value("${userMaxCount:500}") int userMaxCount) {
final ScimUserAliasHandler aliasHandler,
final TransactionTemplate transactionTemplate,
final @Qualifier("aliasEntitiesEnabled") boolean aliasEntitiesEnabled,
final @Value("${userMaxCount:500}") int userMaxCount
) {
if (userMaxCount <= 0) {
throw new IllegalArgumentException(
String.format("Invalid \"userMaxCount\" value (got %d). Should be positive number.", userMaxCount)
Expand All @@ -161,11 +181,14 @@ public ScimUserEndpoints(
this.passwordValidator = passwordValidator;
this.codeStore = codeStore;
this.approvalStore = approvalStore;
this.aliasEntitiesEnabled = aliasEntitiesEnabled;
this.userMaxCount = userMaxCount;
this.membershipManager = membershipManager;
this.messageConverters = new HttpMessageConverter[] {
new ExceptionReportHttpMessageConverter()
};
this.aliasHandler = aliasHandler;
this.transactionTemplate = transactionTemplate;
scimUpdates = new AtomicInteger();
scimDeletes = new AtomicInteger();
errorCounts = new ConcurrentHashMap<>();
Expand Down Expand Up @@ -224,15 +247,52 @@ public ScimUser createUser(@RequestBody ScimUser user, HttpServletRequest reques
passwordValidator.validate(user.getPassword());
}

ScimUser scimUser = scimUserProvisioning.createUser(user, user.getPassword(), identityZoneManager.getCurrentIdentityZoneId());
user.setZoneId(identityZoneManager.getCurrentIdentityZoneId());

if (!aliasHandler.aliasPropertiesAreValid(user, null)) {
throw new ScimException("Alias ID and/or alias ZID are invalid.", HttpStatus.BAD_REQUEST);
}

final ScimUser scimUser;
if (aliasEntitiesEnabled) {
// create the user and an alias for it if necessary
scimUser = createScimUserWithAliasHandling(user);
} else {
// create the user without alias handling
scimUser = scimUserProvisioning.createUser(user, user.getPassword(), identityZoneManager.getCurrentIdentityZoneId());
}

if (user.getApprovals() != null) {
for (Approval approval : user.getApprovals()) {
approval.setUserId(scimUser.getId());
approvalStore.addApproval(approval, identityZoneManager.getCurrentIdentityZoneId());
}
}
scimUser = syncApprovals(syncGroups(scimUser));
addETagHeader(response, scimUser);
final ScimUser scimUserWithApprovalsAndGroups = syncApprovals(syncGroups(scimUser));
addETagHeader(response, scimUserWithApprovalsAndGroups);
return scimUserWithApprovalsAndGroups;
}

private ScimUser createScimUserWithAliasHandling(final ScimUser user) {
final ScimUser scimUser;
try {
scimUser = transactionTemplate.execute(txStatus -> {
strehle marked this conversation as resolved.
Show resolved Hide resolved
final ScimUser originalScimUser = scimUserProvisioning.createUser(
user,
user.getPassword(),
identityZoneManager.getCurrentIdentityZoneId()
);
return aliasHandler.ensureConsistencyOfAliasEntity(
originalScimUser,
null
);
});
} catch (final EntityAliasFailedException e) {
throw new ScimException(e.getMessage(), e, HttpStatus.resolve(e.getHttpStatus()));
}
if (scimUser == null) {
throw new IllegalStateException("The persisted user is not present after handling the alias.");
}
return scimUser;
}

Expand All @@ -257,15 +317,47 @@ public ScimUser updateUser(@RequestBody ScimUser user, @PathVariable String user
int version = getVersion(userId, etag);
user.setVersion(version);

final ScimUser existingScimUser = scimUserProvisioning.retrieve(
userId,
identityZoneManager.getCurrentIdentityZoneId()
);
if (!aliasHandler.aliasPropertiesAreValid(user, existingScimUser)) {
throw new ScimException("The fields 'aliasId' and/or 'aliasZid' are invalid.", HttpStatus.BAD_REQUEST);
}

final ScimUser scimUser;
try {
ScimUser updated = scimUserProvisioning.update(userId, user, identityZoneManager.getCurrentIdentityZoneId());
scimUpdates.incrementAndGet();
ScimUser scimUser = syncApprovals(syncGroups(updated));
addETagHeader(httpServletResponse, scimUser);
return scimUser;
} catch (OptimisticLockingFailureException e) {
if (aliasEntitiesEnabled) {
// update user and create/update alias, if necessary
scimUser = updateUserWithAliasHandling(userId, user, existingScimUser);
} else {
// update user without alias handling
scimUser = scimUserProvisioning.update(userId, user, identityZoneManager.getCurrentIdentityZoneId());
}
} catch (final OptimisticLockingFailureException e) {
throw new ScimResourceConflictException(e.getMessage());
} catch (final EntityAliasFailedException e) {
throw new ScimException(e.getMessage(), e, HttpStatus.resolve(e.getHttpStatus()));
}

scimUpdates.incrementAndGet();
final ScimUser scimUserWithApprovalsAndGroups = syncApprovals(syncGroups(scimUser));
addETagHeader(httpServletResponse, scimUserWithApprovalsAndGroups);
return scimUserWithApprovalsAndGroups;
}

private ScimUser updateUserWithAliasHandling(final String userId, final ScimUser user, final ScimUser existingUser) {
return transactionTemplate.execute(txStatus -> {
final ScimUser updatedOriginalUser = scimUserProvisioning.update(
userId,
user,
identityZoneManager.getCurrentIdentityZoneId()
);
return aliasHandler.ensureConsistencyOfAliasEntity(
updatedOriginalUser,
existingUser
);
});
}

@RequestMapping(value = "/Users/{userId}", method = RequestMethod.PATCH)
Expand Down Expand Up @@ -298,13 +390,23 @@ public ScimUser patchUser(@RequestBody ScimUser patch, @PathVariable String user

@RequestMapping(value = "/Users/{userId}", method = RequestMethod.DELETE)
@ResponseBody
@Transactional
public ScimUser deleteUser(@PathVariable String userId,
@RequestHeader(value = "If-Match", required = false) String etag,
HttpServletRequest request,
HttpServletResponse httpServletResponse) {
int version = etag == null ? -1 : getVersion(userId, etag);
ScimUser user = getUser(userId, httpServletResponse);
throwWhenUserManagementIsDisallowed(user.getOrigin(), request);

final boolean userHasAlias = hasText(user.getAliasZid());
if (userHasAlias && !aliasEntitiesEnabled) {
throw new UaaException(
"Could not delete user with alias since alias entities are disabled.",
HttpStatus.BAD_REQUEST.value()
);
}

membershipManager.removeMembersByMemberId(userId, identityZoneManager.getCurrentIdentityZoneId());
scimUserProvisioning.delete(userId, version, identityZoneManager.getCurrentIdentityZoneId());
scimDeletes.incrementAndGet();
Expand All @@ -315,8 +417,35 @@ public ScimUser deleteUser(@PathVariable String userId,
SecurityContextHolder.getContext().getAuthentication(),
identityZoneManager.getCurrentIdentityZoneId())
);
logger.debug("User delete event sent[" + userId + "]");
logger.debug("User delete event sent[{}]", userId);
}

if (!userHasAlias) {
// no further action necessary
return user;
}

// also delete alias user, if present
final Optional<ScimUser> aliasUserOpt = aliasHandler.retrieveAliasEntity(user);
if (aliasUserOpt.isEmpty()) {
// ignore dangling reference to alias user
logger.warn("Attempted to delete alias of user '{}', but it was not present.", user.getId());
return user;
}
final ScimUser aliasUser = aliasUserOpt.get();
membershipManager.removeMembersByMemberId(aliasUser.getId(), aliasUser.getZoneId());
scimUserProvisioning.delete(aliasUser.getId(), aliasUser.getVersion(), aliasUser.getZoneId());
if (publisher != null) {
publisher.publishEvent(
new EntityDeletedEvent<>(
aliasUser,
SecurityContextHolder.getContext().getAuthentication(),
aliasUser.getZoneId()
)
);
logger.debug("User delete event sent[{}]", userId);
}

return user;
}

Expand Down Expand Up @@ -413,7 +542,7 @@ public SearchResults<?> findUsers(
}
} catch (IllegalArgumentException e) {
String msg = "Invalid filter expression: [" + filter + "]";
if (StringUtils.hasText(sortBy)) {
if (hasText(sortBy)) {
msg += " [" + sortBy + "]";
}
throw new ScimException(HtmlUtils.htmlEscape(msg), HttpStatus.BAD_REQUEST);
Expand Down Expand Up @@ -469,9 +598,10 @@ public UserAccountStatus updateAccountStatus(@RequestBody UserAccountStatus stat
return status;
}

private ScimUser syncGroups(ScimUser user) {
@Nullable
private ScimUser syncGroups(@Nullable ScimUser user) {
if (user == null) {
return user;
return null;
}

Set<ScimGroup> directGroups = membershipManager.getGroupsWithMember(user.getId(), false, identityZoneManager.getCurrentIdentityZoneId());
Expand All @@ -489,6 +619,9 @@ private ScimUser syncGroups(ScimUser user) {
return user;
}

/**
* Look up the approvals for the given user and keep only those that are currently active.
*/
private ScimUser syncApprovals(ScimUser user) {
if (user == null || approvalStore == null) {
return user;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
import java.sql.Types;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
Expand Down Expand Up @@ -294,12 +294,8 @@ public ScimUser create(final ScimUser user, String zoneId) {
});
} catch (DuplicateKeyException e) {
String userOrigin = hasText(user.getOrigin()) ? user.getOrigin() : OriginKeys.UAA;
ScimUser existingUser = retrieveByUsernameAndOriginAndZone(user.getUserName(), userOrigin, zoneId).get(0);
Map<String,Object> userDetails = new HashMap<>();
userDetails.put("active", existingUser.isActive());
userDetails.put("verified", existingUser.isVerified());
userDetails.put("user_id", existingUser.getId());
throw new ScimResourceAlreadyExistsException("Username already in use: " + existingUser.getUserName(), userDetails);
Map<String,Object> userDetails = Collections.singletonMap("origin", userOrigin);
throw new ScimResourceAlreadyExistsException("Username already in use: " + user.getUserName(), userDetails);
}
return retrieve(id, zoneId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,12 @@ void init() throws SQLException {
null,
null,
null,
jdbcScimGroupMembershipManager, 5);
jdbcScimGroupMembershipManager,
null,
null,
false,
5
);
IdentityZoneHolder.get().getConfig().getUserConfig().setDefaultGroups(emptyList());
}

Expand Down
Loading
Loading