Remove all unsafe Optional.get calls
This commit is contained in:
parent
8e97335de4
commit
ad60363cbd
3 changed files with 94 additions and 101 deletions
|
@ -12,8 +12,9 @@ import sh.libre.scim.jpa.ScimResourceDao;
|
|||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class AbstractScimService<RMM extends RoleMapperModel, S extends ResourceNode> implements AutoCloseable {
|
||||
|
@ -76,52 +77,55 @@ public abstract class AbstractScimService<RMM extends RoleMapperModel, S extends
|
|||
if (isSkip(roleMapperModel))
|
||||
return;
|
||||
KeycloakId id = getId(roleMapperModel);
|
||||
ScimResource scimResource = findById(id).get();
|
||||
EntityOnRemoteScimId externalId = scimResource.getExternalIdAsEntityOnRemoteScimId();
|
||||
S scimForReplace = toScimForReplace(roleMapperModel, externalId);
|
||||
scimClient.replace(externalId, scimForReplace);
|
||||
} catch (NoSuchElementException e) {
|
||||
LOGGER.warnf("failed to replace resource %s, scim mapping not found", getId(roleMapperModel));
|
||||
Optional<EntityOnRemoteScimId> entityOnRemoteScimId = findById(id)
|
||||
.map(ScimResource::getExternalIdAsEntityOnRemoteScimId);
|
||||
entityOnRemoteScimId
|
||||
.ifPresentOrElse(
|
||||
externalId -> doReplace(roleMapperModel, externalId),
|
||||
() -> LOGGER.warnf("failed to replace resource %s, scim mapping not found", id)
|
||||
);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error(e);
|
||||
throw new ScimPropagationException("[SCIM] Error while replacing SCIM resource", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void doReplace(RMM roleMapperModel, EntityOnRemoteScimId externalId) {
|
||||
S scimForReplace = toScimForReplace(roleMapperModel, externalId);
|
||||
scimClient.replace(externalId, scimForReplace);
|
||||
}
|
||||
|
||||
protected abstract S toScimForReplace(RMM roleMapperModel, EntityOnRemoteScimId externalId);
|
||||
|
||||
public void delete(KeycloakId id) throws ScimPropagationException {
|
||||
try {
|
||||
ScimResource resource = findById(id).get();
|
||||
ScimResource resource = findById(id)
|
||||
.orElseThrow(() -> new ScimPropagationException("Failed to delete resource %s, scim mapping not found: ".formatted(id)));
|
||||
EntityOnRemoteScimId externalId = resource.getExternalIdAsEntityOnRemoteScimId();
|
||||
scimClient.delete(externalId);
|
||||
getScimResourceDao().delete(resource);
|
||||
} catch (NoSuchElementException e) {
|
||||
throw new ScimPropagationException("Failed to delete resource %s, scim mapping not found: ".formatted(id), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshResources(SynchronizationResult syncRes) throws ScimPropagationException {
|
||||
LOGGER.info("Refresh resources");
|
||||
getResourceStream().forEach(resource -> {
|
||||
try (Stream<RMM> resourcesStream = getResourceStream()) {
|
||||
Set<RMM> resources = resourcesStream.collect(Collectors.toUnmodifiableSet());
|
||||
for (RMM resource : resources) {
|
||||
KeycloakId id = getId(resource);
|
||||
LOGGER.infof("Reconciling local resource %s", id);
|
||||
if (!isSkipRefresh(resource)) {
|
||||
try {
|
||||
try {
|
||||
findById(id).get();
|
||||
if (isSkipRefresh(resource)) {
|
||||
LOGGER.infof("Skip local resource %s", id);
|
||||
continue;
|
||||
}
|
||||
if (findById(id).isPresent()) {
|
||||
LOGGER.info("Replacing it");
|
||||
replace(resource);
|
||||
} catch (NoSuchElementException e) {
|
||||
} else {
|
||||
LOGGER.info("Creating it");
|
||||
create(resource);
|
||||
}
|
||||
syncRes.increaseUpdated();
|
||||
} catch (ScimPropagationException e) {
|
||||
// TODO handle exception
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract boolean isSkipRefresh(RMM resource);
|
||||
|
@ -130,14 +134,16 @@ public abstract class AbstractScimService<RMM extends RoleMapperModel, S extends
|
|||
|
||||
public void importResources(SynchronizationResult syncRes) {
|
||||
LOGGER.info("Import");
|
||||
ScimClient<S> scimClient = this.scimClient;
|
||||
try {
|
||||
for (S resource : scimClient.listResources()) {
|
||||
try {
|
||||
LOGGER.infof("Reconciling remote resource %s", resource);
|
||||
EntityOnRemoteScimId externalId = resource.getId().map(EntityOnRemoteScimId::new).get();
|
||||
try {
|
||||
ScimResource mapping = getScimResourceDao().findByExternalId(externalId, type).get();
|
||||
EntityOnRemoteScimId externalId = resource.getId()
|
||||
.map(EntityOnRemoteScimId::new)
|
||||
.orElseThrow(() -> new ScimPropagationException("remote SCIM resource doesn't have an id"));
|
||||
Optional<ScimResource> optionalMapping = getScimResourceDao().findByExternalId(externalId, type);
|
||||
if (optionalMapping.isPresent()) {
|
||||
ScimResource mapping = optionalMapping.get();
|
||||
if (entityExists(mapping.getIdAsKeycloakId())) {
|
||||
LOGGER.info("Valid mapping found, skipping");
|
||||
continue;
|
||||
|
@ -145,8 +151,6 @@ public abstract class AbstractScimService<RMM extends RoleMapperModel, S extends
|
|||
LOGGER.info("Delete a dangling mapping");
|
||||
getScimResourceDao().delete(mapping);
|
||||
}
|
||||
} catch (NoSuchElementException e) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
Optional<KeycloakId> mapped = tryToMap(resource);
|
||||
|
@ -167,7 +171,7 @@ public abstract class AbstractScimService<RMM extends RoleMapperModel, S extends
|
|||
break;
|
||||
case DELETE_REMOTE:
|
||||
LOGGER.info("Delete remote resource");
|
||||
scimClient.delete(externalId);
|
||||
this.scimClient.delete(externalId);
|
||||
syncRes.increaseRemoved();
|
||||
break;
|
||||
case NOTHING:
|
||||
|
@ -186,9 +190,9 @@ public abstract class AbstractScimService<RMM extends RoleMapperModel, S extends
|
|||
}
|
||||
}
|
||||
|
||||
protected abstract KeycloakId createEntity(S resource);
|
||||
protected abstract KeycloakId createEntity(S resource) throws ScimPropagationException;
|
||||
|
||||
protected abstract Optional<KeycloakId> tryToMap(S resource);
|
||||
protected abstract Optional<KeycloakId> tryToMap(S resource) throws ScimPropagationException;
|
||||
|
||||
protected abstract boolean entityExists(KeycloakId keycloakId);
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@ package sh.libre.scim.core;
|
|||
import de.captaingoldfish.scim.sdk.common.resources.Group;
|
||||
import de.captaingoldfish.scim.sdk.common.resources.complex.Meta;
|
||||
import de.captaingoldfish.scim.sdk.common.resources.multicomplex.Member;
|
||||
import jakarta.persistence.NoResultException;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
@ -15,9 +15,9 @@ import sh.libre.scim.jpa.ScimResource;
|
|||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class GroupScimService extends AbstractScimService<GroupModel, Group> {
|
||||
|
@ -39,9 +39,9 @@ public class GroupScimService extends AbstractScimService<GroupModel, Group> {
|
|||
|
||||
@Override
|
||||
protected Optional<KeycloakId> tryToMap(Group resource) {
|
||||
String externalId = resource.getId().get();
|
||||
String displayName = resource.getDisplayName().get();
|
||||
Set<String> names = Set.of(externalId, displayName);
|
||||
Set<String> names = new TreeSet<>();
|
||||
resource.getId().ifPresent(names::add);
|
||||
resource.getDisplayName().ifPresent(names::add);
|
||||
Optional<GroupModel> group = getKeycloakDao().getGroupsStream()
|
||||
.filter(groupModel -> names.contains(groupModel.getName()))
|
||||
.findFirst();
|
||||
|
@ -49,28 +49,22 @@ public class GroupScimService extends AbstractScimService<GroupModel, Group> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected KeycloakId createEntity(Group resource) {
|
||||
String displayName = resource.getDisplayName().get();
|
||||
protected KeycloakId createEntity(Group resource) throws ScimPropagationException {
|
||||
String displayName = resource.getDisplayName()
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.orElseThrow(() -> new ScimPropagationException("can't create group without name: " + resource));
|
||||
GroupModel group = getKeycloakDao().createGroup(displayName);
|
||||
List<Member> groupMembers = resource.getMembers();
|
||||
if (CollectionUtils.isNotEmpty(groupMembers)) {
|
||||
for (Member groupMember : groupMembers) {
|
||||
try {
|
||||
EntityOnRemoteScimId externalId = new EntityOnRemoteScimId(groupMember.getValue().get());
|
||||
ScimResource userMapping = getScimResourceDao().findUserByExternalId(externalId).get();
|
||||
KeycloakId userId = userMapping.getIdAsKeycloakId();
|
||||
try {
|
||||
UserModel user = getKeycloakDao().getUserById(userId);
|
||||
if (user == null) {
|
||||
throw new NoResultException();
|
||||
}
|
||||
user.joinGroup(group);
|
||||
} catch (Exception e) {
|
||||
logger.warn(e);
|
||||
}
|
||||
} catch (NoSuchElementException e) {
|
||||
logger.warnf("member %s not found for scim group %s", groupMember.getValue().get(), resource.getId().get());
|
||||
}
|
||||
EntityOnRemoteScimId externalId = groupMember.getValue()
|
||||
.map(EntityOnRemoteScimId::new)
|
||||
.orElseThrow(() -> new ScimPropagationException("can't create group member for group '%s' without id: ".formatted(displayName) + resource));
|
||||
KeycloakId userId = getScimResourceDao().findUserByExternalId(externalId)
|
||||
.map(ScimResource::getIdAsKeycloakId)
|
||||
.orElseThrow(() -> new ScimPropagationException("can't find mapping for group member %s".formatted(externalId)));
|
||||
UserModel userModel = getKeycloakDao().getUserById(userId);
|
||||
userModel.joinGroup(group);
|
||||
}
|
||||
}
|
||||
return new KeycloakId(group.getId());
|
||||
|
@ -94,20 +88,23 @@ public class GroupScimService extends AbstractScimService<GroupModel, Group> {
|
|||
group.setDisplayName(groupModel.getName());
|
||||
for (KeycloakId member : members) {
|
||||
Member groupMember = new Member();
|
||||
Optional<ScimResource> optionalGroupMemberMapping = getScimResourceDao().findUserById(member);
|
||||
if (optionalGroupMemberMapping.isPresent()) {
|
||||
ScimResource groupMemberMapping = optionalGroupMemberMapping.get();
|
||||
EntityOnRemoteScimId externalIdAsEntityOnRemoteScimId = groupMemberMapping.getExternalIdAsEntityOnRemoteScimId();
|
||||
logger.debugf("found mapping for group member %s as %s", member, externalIdAsEntityOnRemoteScimId);
|
||||
groupMember.setValue(externalIdAsEntityOnRemoteScimId.asString());
|
||||
try {
|
||||
ScimResource userMapping = getScimResourceDao().findUserById(member).get();
|
||||
logger.debug(userMapping.getExternalIdAsEntityOnRemoteScimId());
|
||||
logger.debug(userMapping.getIdAsKeycloakId());
|
||||
groupMember.setValue(userMapping.getExternalIdAsEntityOnRemoteScimId().asString());
|
||||
String refString = String.format("Users/%s", userMapping.getExternalIdAsEntityOnRemoteScimId().asString());
|
||||
String refString = String.format("Users/%s", externalIdAsEntityOnRemoteScimId.asString());
|
||||
URI ref = new URI(refString);
|
||||
groupMember.setRef(ref.toString());
|
||||
group.addMember(groupMember);
|
||||
} catch (NoSuchElementException e) {
|
||||
logger.warnf("member %s not found for group %s", member, groupModel.getId());
|
||||
} catch (URISyntaxException e) {
|
||||
logger.warnf("bad ref uri for member " + member);
|
||||
}
|
||||
group.addMember(groupMember);
|
||||
} else {
|
||||
logger.warnf("member %s not found for group %s", member, groupModel.getId());
|
||||
}
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
|
|
@ -40,45 +40,37 @@ public class UserScimService extends AbstractScimService<UserModel, User> {
|
|||
|
||||
@Override
|
||||
protected Optional<KeycloakId> tryToMap(User resource) {
|
||||
String username = resource.getUserName().get();
|
||||
String email = resource.getEmails().stream()
|
||||
Optional<KeycloakId> matchedByUsername = resource.getUserName()
|
||||
.map(getKeycloakDao()::getUserByUsername)
|
||||
.map(this::getId);
|
||||
Optional<KeycloakId> matchedByEmail = resource.getEmails().stream()
|
||||
.findFirst()
|
||||
.flatMap(MultiComplexNode::getValue)
|
||||
.orElse(null);
|
||||
UserModel sameUsernameUser = null;
|
||||
UserModel sameEmailUser = null;
|
||||
if (username != null) {
|
||||
sameUsernameUser = getKeycloakDao().getUserByUsername(username);
|
||||
}
|
||||
if (email != null) {
|
||||
sameEmailUser = getKeycloakDao().getUserByEmail(email);
|
||||
}
|
||||
if ((sameUsernameUser != null && sameEmailUser != null)
|
||||
&& (!StringUtils.equals(sameUsernameUser.getId(), sameEmailUser.getId()))) {
|
||||
logger.warnf("found 2 possible users for remote user %s %s", username, email);
|
||||
.map(getKeycloakDao()::getUserByEmail)
|
||||
.map(this::getId);
|
||||
if (matchedByUsername.isPresent()
|
||||
&& matchedByEmail.isPresent()
|
||||
&& !matchedByUsername.equals(matchedByEmail)) {
|
||||
logger.warnf("found 2 possible users for remote user %s %s", matchedByUsername.get(), matchedByEmail.get());
|
||||
return Optional.empty();
|
||||
}
|
||||
if (sameUsernameUser != null) {
|
||||
return Optional.of(getId(sameUsernameUser));
|
||||
if (matchedByUsername.isPresent()) {
|
||||
return matchedByUsername;
|
||||
}
|
||||
if (sameEmailUser != null) {
|
||||
return Optional.of(getId(sameEmailUser));
|
||||
}
|
||||
return Optional.empty();
|
||||
return matchedByEmail;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected KeycloakId createEntity(User resource) {
|
||||
String username = resource.getUserName().get();
|
||||
if (StringUtils.isEmpty(username)) {
|
||||
throw new IllegalArgumentException("can't create user with empty username");
|
||||
}
|
||||
protected KeycloakId createEntity(User resource) throws ScimPropagationException {
|
||||
String username = resource.getUserName()
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.orElseThrow(() -> new ScimPropagationException("can't create user with empty username, resource id = %s".formatted(resource.getId())));
|
||||
UserModel user = getKeycloakDao().addUser(username);
|
||||
resource.getEmails().stream()
|
||||
.findFirst()
|
||||
.flatMap(MultiComplexNode::getValue)
|
||||
.ifPresent(user::setEmail);
|
||||
user.setEnabled(resource.isActive().get());
|
||||
user.setEnabled(resource.isActive().orElseThrow(() -> new ScimPropagationException("can't create user with undefined 'active', resource id = %s".formatted(resource.getId()))));
|
||||
return new KeycloakId(user.getId());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue