Fix some identations and code conventions

This commit is contained in:
Brendan Le Ny 2024-06-12 14:47:45 +02:00
parent 000b47a581
commit 2782beafe6
10 changed files with 182 additions and 187 deletions

View file

@ -1,11 +1,9 @@
package sh.libre.scim.core; package sh.libre.scim.core;
import java.util.stream.Stream; import de.captaingoldfish.scim.sdk.common.resources.ResourceNode;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException; import jakarta.persistence.NoResultException;
import jakarta.persistence.TypedQuery; import jakarta.persistence.TypedQuery;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.connections.jpa.JpaConnectionProvider; import org.keycloak.connections.jpa.JpaConnectionProvider;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
@ -13,11 +11,11 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleMapperModel; import org.keycloak.models.RoleMapperModel;
import sh.libre.scim.jpa.ScimResource; import sh.libre.scim.jpa.ScimResource;
import de.captaingoldfish.scim.sdk.common.resources.ResourceNode; import java.util.stream.Stream;
public abstract class Adapter<M extends RoleMapperModel, S extends ResourceNode> { public abstract class Adapter<M extends RoleMapperModel, S extends ResourceNode> {
protected final Logger LOGGER; protected final Logger logger;
protected final String realmId; protected final String realmId;
protected final RealmModel realm; protected final RealmModel realm;
protected final String type; protected final String type;
@ -36,7 +34,7 @@ public abstract class Adapter<M extends RoleMapperModel, S extends ResourceNode>
this.componentId = componentId; this.componentId = componentId;
this.em = session.getProvider(JpaConnectionProvider.class).getEntityManager(); this.em = session.getProvider(JpaConnectionProvider.class).getEntityManager();
this.type = type; this.type = type;
this.LOGGER = logger; this.logger = logger;
} }
public String getId() { public String getId() {

View file

@ -1,20 +1,21 @@
package sh.libre.scim.core; 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.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.jboss.logging.Logger;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import jakarta.persistence.NoResultException;
import de.captaingoldfish.scim.sdk.common.resources.Group;
import de.captaingoldfish.scim.sdk.common.resources.multicomplex.Member;
import de.captaingoldfish.scim.sdk.common.resources.complex.Meta;
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;
public class GroupAdapter extends Adapter<GroupModel, Group> { public class GroupAdapter extends Adapter<GroupModel, Group> {
@ -64,7 +65,7 @@ public class GroupAdapter extends Adapter<GroupModel, Group> {
.getSingleResult(); .getSingleResult();
this.members.add(userMapping.getId()); this.members.add(userMapping.getId());
} catch (NoResultException e) { } catch (NoResultException e) {
LOGGER.warnf("member %s not found for scim group %s", groupMember.getValue().get(), group.getId().get()); logger.warnf("member %s not found for scim group %s", groupMember.getValue().get(), group.getId().get());
} }
} }
} }
@ -81,16 +82,16 @@ public class GroupAdapter extends Adapter<GroupModel, Group> {
var groupMember = new Member(); var groupMember = new Member();
try { try {
var userMapping = this.query("findById", member, "User").getSingleResult(); var userMapping = this.query("findById", member, "User").getSingleResult();
LOGGER.debug(userMapping.getExternalId()); logger.debug(userMapping.getExternalId());
LOGGER.debug(userMapping.getId()); logger.debug(userMapping.getId());
groupMember.setValue(userMapping.getExternalId()); groupMember.setValue(userMapping.getExternalId());
var ref = new URI(String.format("Users/%s", userMapping.getExternalId())); var ref = new URI(String.format("Users/%s", userMapping.getExternalId()));
groupMember.setRef(ref.toString()); groupMember.setRef(ref.toString());
group.addMember(groupMember); group.addMember(groupMember);
} catch (NoResultException e) { } catch (NoResultException e) {
LOGGER.warnf("member %s not found for group %s", member, id); logger.warnf("member %s not found for group %s", member, id);
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
LOGGER.warnf("bad ref uri"); logger.warnf("bad ref uri");
} }
} }
} }
@ -99,7 +100,7 @@ public class GroupAdapter extends Adapter<GroupModel, Group> {
var uri = new URI("Groups/" + externalId); var uri = new URI("Groups/" + externalId);
meta.setLocation(uri.toString()); meta.setLocation(uri.toString());
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
LOGGER.warn(e); logger.warn(e);
} }
group.setMeta(meta); group.setMeta(meta);
return group; return group;
@ -111,16 +112,13 @@ public class GroupAdapter extends Adapter<GroupModel, Group> {
return false; return false;
} }
var group = session.groups().getGroupById(realm, id); var group = session.groups().getGroupById(realm, id);
if (group != null) { return group != null;
return true;
}
return false;
} }
@Override @Override
public Boolean tryToMap() { public Boolean tryToMap() {
var group = session.groups().getGroupsStream(realm).filter( var group = session.groups().getGroupsStream(realm).filter(
x -> StringUtils.equals(x.getName(), externalId) || StringUtils.equals(x.getName(), displayName)) x -> StringUtils.equals(x.getName(), externalId) || StringUtils.equals(x.getName(), displayName))
.findFirst(); .findFirst();
if (group.isPresent()) { if (group.isPresent()) {
setId(group.get().getId()); setId(group.get().getId());
@ -141,7 +139,7 @@ public class GroupAdapter extends Adapter<GroupModel, Group> {
} }
user.joinGroup(group); user.joinGroup(group);
} catch (Exception e) { } catch (Exception e) {
LOGGER.warn(e); logger.warn(e);
} }
} }
} }

View file

@ -1,12 +1,6 @@
package sh.libre.scim.core; package sh.libre.scim.core;
import java.util.HashMap; import com.google.common.net.HttpHeaders;
import java.util.Map;
import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.ws.rs.ProcessingException;
import de.captaingoldfish.scim.sdk.client.ScimClientConfig; import de.captaingoldfish.scim.sdk.client.ScimClientConfig;
import de.captaingoldfish.scim.sdk.client.ScimRequestBuilder; import de.captaingoldfish.scim.sdk.client.ScimRequestBuilder;
import de.captaingoldfish.scim.sdk.client.http.BasicAuth; import de.captaingoldfish.scim.sdk.client.http.BasicAuth;
@ -14,7 +8,12 @@ import de.captaingoldfish.scim.sdk.client.response.ServerResponse;
import de.captaingoldfish.scim.sdk.common.exceptions.ResponseException; import de.captaingoldfish.scim.sdk.common.exceptions.ResponseException;
import de.captaingoldfish.scim.sdk.common.resources.ResourceNode; import de.captaingoldfish.scim.sdk.common.resources.ResourceNode;
import de.captaingoldfish.scim.sdk.common.response.ListResponse; import de.captaingoldfish.scim.sdk.common.response.ListResponse;
import io.github.resilience4j.core.IntervalFunction;
import io.github.resilience4j.retry.RetryConfig;
import io.github.resilience4j.retry.RetryRegistry;
import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.ws.rs.ProcessingException;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
import org.keycloak.connections.jpa.JpaConnectionProvider; import org.keycloak.connections.jpa.JpaConnectionProvider;
@ -22,23 +21,20 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RoleMapperModel; import org.keycloak.models.RoleMapperModel;
import org.keycloak.storage.user.SynchronizationResult; import org.keycloak.storage.user.SynchronizationResult;
import com.google.common.net.HttpHeaders; import java.util.HashMap;
import java.util.Map;
import io.github.resilience4j.core.IntervalFunction;
import io.github.resilience4j.retry.RetryConfig;
import io.github.resilience4j.retry.RetryRegistry;
public class ScimClient { public class ScimClient {
final protected Logger LOGGER = Logger.getLogger(ScimClient.class); protected final Logger LOGGER = Logger.getLogger(ScimClient.class);
final protected ScimRequestBuilder scimRequestBuilder; protected final ScimRequestBuilder scimRequestBuilder;
final protected RetryRegistry registry; protected final RetryRegistry registry;
final protected KeycloakSession session; protected final KeycloakSession session;
final protected String contentType; protected final String contentType;
final protected ComponentModel model; protected final ComponentModel model;
final protected String scimApplicationBaseUrl; protected final String scimApplicationBaseUrl;
final protected Map<String, String> defaultHeaders; protected final Map<String, String> defaultHeaders;
final protected Map<String, String> expectedResponseHeaders; protected final Map<String, String> expectedResponseHeaders;
public ScimClient(ComponentModel model, KeycloakSession session) { public ScimClient(ComponentModel model, KeycloakSession session) {
this.model = model; this.model = model;
@ -51,48 +47,48 @@ public class ScimClient {
switch (model.get("auth-mode")) { switch (model.get("auth-mode")) {
case "BEARER": case "BEARER":
defaultHeaders.put(HttpHeaders.AUTHORIZATION, defaultHeaders.put(HttpHeaders.AUTHORIZATION,
BearerAuthentication()); BearerAuthentication());
break; break;
case "BASIC_AUTH": case "BASIC_AUTH":
defaultHeaders.put(HttpHeaders.AUTHORIZATION, defaultHeaders.put(HttpHeaders.AUTHORIZATION,
BasicAuthentication()); BasicAuthentication());
break; break;
} }
defaultHeaders.put(HttpHeaders.CONTENT_TYPE,contentType); defaultHeaders.put(HttpHeaders.CONTENT_TYPE, contentType);
scimRequestBuilder = new ScimRequestBuilder(scimApplicationBaseUrl, genScimClientConfig()); scimRequestBuilder = new ScimRequestBuilder(scimApplicationBaseUrl, genScimClientConfig());
RetryConfig retryConfig = RetryConfig.custom() RetryConfig retryConfig = RetryConfig.custom()
.maxAttempts(10) .maxAttempts(10)
.intervalFunction(IntervalFunction.ofExponentialBackoff()) .intervalFunction(IntervalFunction.ofExponentialBackoff())
.retryExceptions(ProcessingException.class) .retryExceptions(ProcessingException.class)
.build(); .build();
registry = RetryRegistry.of(retryConfig); registry = RetryRegistry.of(retryConfig);
} }
protected String BasicAuthentication() { protected String BasicAuthentication() {
return BasicAuth.builder() return BasicAuth.builder()
.username(model.get("auth-user")) .username(model.get("auth-user"))
.password(model.get("auth-pass")) .password(model.get("auth-pass"))
.build() .build()
.getAuthorizationHeaderValue(); .getAuthorizationHeaderValue();
} }
protected ScimClientConfig genScimClientConfig() { protected ScimClientConfig genScimClientConfig() {
return ScimClientConfig.builder() return ScimClientConfig.builder()
.httpHeaders(defaultHeaders) .httpHeaders(defaultHeaders)
.connectTimeout(5) .connectTimeout(5)
.requestTimeout(5) .requestTimeout(5)
.socketTimeout(5) .socketTimeout(5)
.expectedHttpResponseHeaders(expectedResponseHeaders) .expectedHttpResponseHeaders(expectedResponseHeaders)
.hostnameVerifier((s, sslSession) -> true) .hostnameVerifier((s, sslSession) -> true)
.build(); .build();
} }
protected String BearerAuthentication() { protected String BearerAuthentication() {
return "Bearer " + model.get("auth-pass") ; return "Bearer " + model.get("auth-pass");
} }
protected EntityManager getEM() { protected EntityManager getEM() {
@ -114,7 +110,7 @@ public class ScimClient {
} }
public <M extends RoleMapperModel, S extends ResourceNode, A extends Adapter<M, S>> void create(Class<A> aClass, public <M extends RoleMapperModel, S extends ResourceNode, A extends Adapter<M, S>> void create(Class<A> aClass,
M kcModel) { M kcModel) {
var adapter = getAdapter(aClass); var adapter = getAdapter(aClass);
adapter.apply(kcModel); adapter.apply(kcModel);
if (adapter.skip) if (adapter.skip)
@ -128,25 +124,25 @@ public class ScimClient {
ServerResponse<S> response = retry.executeSupplier(() -> { ServerResponse<S> response = retry.executeSupplier(() -> {
try { try {
return scimRequestBuilder return scimRequestBuilder
.create(adapter.getResourceClass(), adapter.getSCIMEndpoint()) .create(adapter.getResourceClass(), adapter.getSCIMEndpoint())
.setResource(adapter.toSCIM()) .setResource(adapter.toSCIM())
.sendRequest(); .sendRequest();
} catch ( ResponseException e) { } catch (ResponseException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
}); });
if (!response.isSuccess()){ if (!response.isSuccess()) {
LOGGER.warn(response.getResponseBody()); LOGGER.warn(response.getResponseBody());
LOGGER.warn(response.getHttpStatus()); LOGGER.warn(response.getHttpStatus());
} }
adapter.apply(response.getResource()); adapter.apply(response.getResource());
adapter.saveMapping(); adapter.saveMapping();
}; }
public <M extends RoleMapperModel, S extends ResourceNode, A extends Adapter<M, S>> void replace(Class<A> aClass, public <M extends RoleMapperModel, S extends ResourceNode, A extends Adapter<M, S>> void replace(Class<A> aClass,
M kcModel) { M kcModel) {
var adapter = getAdapter(aClass); var adapter = getAdapter(aClass);
try { try {
adapter.apply(kcModel); adapter.apply(kcModel);
@ -158,15 +154,14 @@ public class ScimClient {
ServerResponse<S> response = retry.executeSupplier(() -> { ServerResponse<S> response = retry.executeSupplier(() -> {
try { try {
return scimRequestBuilder return scimRequestBuilder
.update(adapter.getResourceClass(), adapter.getSCIMEndpoint(), adapter.getExternalId()) .update(adapter.getResourceClass(), adapter.getSCIMEndpoint(), adapter.getExternalId())
.setResource(adapter.toSCIM()) .setResource(adapter.toSCIM())
.sendRequest() ; .sendRequest();
} catch ( ResponseException e) { } catch (ResponseException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
}); });
if (!response.isSuccess()){ if (!response.isSuccess()) {
LOGGER.warn(response.getResponseBody()); LOGGER.warn(response.getResponseBody());
LOGGER.warn(response.getHttpStatus()); LOGGER.warn(response.getHttpStatus());
} }
@ -178,7 +173,7 @@ public class ScimClient {
} }
public <M extends RoleMapperModel, S extends ResourceNode, A extends Adapter<M, S>> void delete(Class<A> aClass, public <M extends RoleMapperModel, S extends ResourceNode, A extends Adapter<M, S>> void delete(Class<A> aClass,
String id) { String id) {
var adapter = getAdapter(aClass); var adapter = getAdapter(aClass);
adapter.setId(id); adapter.setId(id);
@ -191,13 +186,13 @@ public class ScimClient {
ServerResponse<S> response = retry.executeSupplier(() -> { ServerResponse<S> response = retry.executeSupplier(() -> {
try { try {
return scimRequestBuilder.delete(adapter.getResourceClass(), adapter.getSCIMEndpoint(), adapter.getExternalId()) return scimRequestBuilder.delete(adapter.getResourceClass(), adapter.getSCIMEndpoint(), adapter.getExternalId())
.sendRequest(); .sendRequest();
} catch (ResponseException e) { } catch (ResponseException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
}); });
if (!response.isSuccess()){ if (!response.isSuccess()) {
LOGGER.warn(response.getResponseBody()); LOGGER.warn(response.getResponseBody());
LOGGER.warn(response.getHttpStatus()); LOGGER.warn(response.getHttpStatus());
} }
@ -237,7 +232,7 @@ public class ScimClient {
LOGGER.info("Import"); LOGGER.info("Import");
try { try {
var adapter = getAdapter(aClass); var adapter = getAdapter(aClass);
ServerResponse<ListResponse<S>> response = scimRequestBuilder.list(adapter.getResourceClass(), adapter.getSCIMEndpoint()).get().sendRequest(); ServerResponse<ListResponse<S>> response = scimRequestBuilder.list(adapter.getResourceClass(), adapter.getSCIMEndpoint()).get().sendRequest();
ListResponse<S> resourceTypeListResponse = response.getResource(); ListResponse<S> resourceTypeListResponse = response.getResource();
for (var resource : resourceTypeListResponse.getListedResources()) { for (var resource : resourceTypeListResponse.getListedResources()) {
@ -277,8 +272,8 @@ public class ScimClient {
case "DELETE_REMOTE": case "DELETE_REMOTE":
LOGGER.info("Delete remote resource"); LOGGER.info("Delete remote resource");
scimRequestBuilder scimRequestBuilder
.delete(adapter.getResourceClass(), adapter.getSCIMEndpoint(), resource.getId().get()) .delete(adapter.getResourceClass(), adapter.getSCIMEndpoint(), resource.getId().get())
.sendRequest(); .sendRequest();
syncRes.increaseRemoved(); syncRes.increaseRemoved();
break; break;
} }
@ -295,7 +290,7 @@ public class ScimClient {
} }
public <M extends RoleMapperModel, S extends ResourceNode, A extends Adapter<M, S>> void sync(Class<A> aClass, public <M extends RoleMapperModel, S extends ResourceNode, A extends Adapter<M, S>> void sync(Class<A> aClass,
SynchronizationResult syncRes) { SynchronizationResult syncRes) {
if (this.model.get("sync-import", false)) { if (this.model.get("sync-import", false)) {
this.importResources(aClass, syncRes); this.importResources(aClass, syncRes);
} }

View file

@ -1,20 +1,22 @@
package sh.libre.scim.core; package sh.libre.scim.core;
import java.util.function.Consumer;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import sh.libre.scim.storage.ScimStorageProviderFactory; import sh.libre.scim.storage.ScimStorageProviderFactory;
import java.util.function.Consumer;
public class ScimDispatcher { public class ScimDispatcher {
public static final String SCOPE_USER = "user"; public static final String SCOPE_USER = "user";
public static final String SCOPE_GROUP = "group"; public static final String SCOPE_GROUP = "group";
final private KeycloakSession session; private static final Logger LOGGER = Logger.getLogger(ScimDispatcher.class);
final private Logger LOGGER = Logger.getLogger(ScimDispatcher.class);
private final KeycloakSession session;
public ScimDispatcher(KeycloakSession session) { public ScimDispatcher(KeycloakSession session) {
this.session = session; this.session = session;

View file

@ -1,5 +1,16 @@
package sh.libre.scim.core; package sh.libre.scim.core;
import de.captaingoldfish.scim.sdk.common.resources.User;
import de.captaingoldfish.scim.sdk.common.resources.complex.Meta;
import de.captaingoldfish.scim.sdk.common.resources.complex.Name;
import de.captaingoldfish.scim.sdk.common.resources.multicomplex.Email;
import de.captaingoldfish.scim.sdk.common.resources.multicomplex.PersonRole;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.jboss.logging.Logger;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.UserModel;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.ArrayList; import java.util.ArrayList;
@ -9,18 +20,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Stream; import java.util.stream.Stream;
import de.captaingoldfish.scim.sdk.common.resources.User;
import de.captaingoldfish.scim.sdk.common.resources.multicomplex.Email;
import de.captaingoldfish.scim.sdk.common.resources.complex.Name;
import de.captaingoldfish.scim.sdk.common.resources.multicomplex.PersonRole;
import de.captaingoldfish.scim.sdk.common.resources.complex.Meta;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.jboss.logging.Logger;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.UserModel;
public class UserAdapter extends Adapter<UserModel, User> { public class UserAdapter extends Adapter<UserModel, User> {
private String username; private String username;
@ -167,7 +166,7 @@ public class UserAdapter extends Adapter<UserModel, User> {
var uri = new URI("Users/" + externalId); var uri = new URI("Users/" + externalId);
meta.setLocation(uri.toString()); meta.setLocation(uri.toString());
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
LOGGER.warn(e); logger.warn(e);
} }
user.setMeta(meta); user.setMeta(meta);
List<PersonRole> roles = new ArrayList<PersonRole>(); List<PersonRole> roles = new ArrayList<PersonRole>();
@ -197,10 +196,7 @@ public class UserAdapter extends Adapter<UserModel, User> {
return false; return false;
} }
var user = session.users().getUserById(realm, id); var user = session.users().getUserById(realm, id);
if (user != null) { return user != null;
return true;
}
return false;
} }
@Override @Override
@ -215,7 +211,7 @@ public class UserAdapter extends Adapter<UserModel, User> {
} }
if ((sameUsernameUser != null && sameEmailUser != null) if ((sameUsernameUser != null && sameEmailUser != null)
&& (sameUsernameUser.getId() != sameEmailUser.getId())) { && (sameUsernameUser.getId() != sameEmailUser.getId())) {
LOGGER.warnf("found 2 possible users for remote user %s %s", username, email); logger.warnf("found 2 possible users for remote user %s %s", username, email);
return false; return false;
} }
if (sameUsernameUser != null) { if (sameUsernameUser != null) {

View file

@ -1,8 +1,5 @@
package sh.libre.scim.event; package sh.libre.scim.event;
import java.util.HashMap;
import java.util.regex.Pattern;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -15,16 +12,23 @@ import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.GroupModel; import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import sh.libre.scim.core.GroupAdapter; import sh.libre.scim.core.GroupAdapter;
import sh.libre.scim.core.ScimDispatcher; import sh.libre.scim.core.ScimDispatcher;
import sh.libre.scim.core.UserAdapter; import sh.libre.scim.core.UserAdapter;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
public class ScimEventListenerProvider implements EventListenerProvider { public class ScimEventListenerProvider implements EventListenerProvider {
final Logger LOGGER = Logger.getLogger(ScimEventListenerProvider.class);
ScimDispatcher dispatcher; private static final Logger LOGGER = Logger.getLogger(ScimEventListenerProvider.class);
KeycloakSession session;
HashMap<ResourceType, Pattern> patterns = new HashMap<ResourceType, Pattern>(); private final ScimDispatcher dispatcher;
private final KeycloakSession session;
private final Map<ResourceType, Pattern> patterns = new HashMap<ResourceType, Pattern>();
public ScimEventListenerProvider(KeycloakSession session) { public ScimEventListenerProvider(KeycloakSession session) {
this.session = session; this.session = session;

View file

@ -4,75 +4,76 @@ import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.IdClass; import jakarta.persistence.IdClass;
import jakarta.persistence.NamedQuery;
import jakarta.persistence.NamedQueries; import jakarta.persistence.NamedQueries;
import jakarta.persistence.NamedQuery;
import jakarta.persistence.Table; import jakarta.persistence.Table;
@Entity @Entity
@IdClass(ScimResourceId.class) @IdClass(ScimResourceId.class)
@Table(name = "SCIM_RESOURCE") @Table(name = "SCIM_RESOURCE")
@NamedQueries({ @NamedQueries({
@NamedQuery(name = "findById", query = "from ScimResource where realmId = :realmId and componentId = :componentId and type = :type and id = :id"), @NamedQuery(name = "findById", query = "from ScimResource where realmId = :realmId and componentId = :componentId and type = :type and id = :id"),
@NamedQuery(name = "findByExternalId", query = "from ScimResource where realmId = :realmId and componentId = :componentId and type = :type and externalId = :id") }) @NamedQuery(name = "findByExternalId", query = "from ScimResource where realmId = :realmId and componentId = :componentId and type = :type and externalId = :id")
})
public class ScimResource { public class ScimResource {
@Id
@Column(name = "ID", nullable = false)
private String id;
@Id @Id
@Column(name = "REALM_ID", nullable = false) @Column(name = "ID", nullable = false)
private String realmId; private String id;
@Id @Id
@Column(name = "COMPONENT_ID", nullable = false) @Column(name = "REALM_ID", nullable = false)
private String componentId; private String realmId;
@Id @Id
@Column(name = "TYPE", nullable = false) @Column(name = "COMPONENT_ID", nullable = false)
private String type; private String componentId;
@Id @Id
@Column(name = "EXTERNAL_ID", nullable = false) @Column(name = "TYPE", nullable = false)
private String externalId; private String type;
public String getId() { @Id
return id; @Column(name = "EXTERNAL_ID", nullable = false)
} private String externalId;
public void setId(String id) { public String getId() {
this.id = id; return id;
} }
public String getRealmId() { public void setId(String id) {
return realmId; this.id = id;
} }
public void setRealmId(String realmId) { public String getRealmId() {
this.realmId = realmId; return realmId;
} }
public String getComponentId() { public void setRealmId(String realmId) {
return componentId; this.realmId = realmId;
} }
public void setComponentId(String componentId) { public String getComponentId() {
this.componentId = componentId; return componentId;
} }
public String getExternalId() { public void setComponentId(String componentId) {
return externalId; this.componentId = componentId;
} }
public void setExternalId(String externalId) { public String getExternalId() {
this.externalId = externalId; return externalId;
} }
public String getType() { public void setExternalId(String externalId) {
return type; this.externalId = externalId;
} }
public void setType(String type) { public String getType() {
this.type = type; return type;
} }
public void setType(String type) {
this.type = type;
}
} }

View file

@ -1,10 +1,9 @@
package sh.libre.scim.jpa; package sh.libre.scim.jpa;
import java.util.List;
import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider; import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
import java.util.Collections; import java.util.Collections;
import java.util.List;
public class ScimResourceProvider implements JpaEntityProvider { public class ScimResourceProvider implements JpaEntityProvider {

View file

@ -7,7 +7,9 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
public class ScimResourceProviderFactory implements JpaEntityProviderFactory { public class ScimResourceProviderFactory implements JpaEntityProviderFactory {
final static String ID ="scim-resource";
static final String ID = "scim-resource";
@Override @Override
public void close() { public void close() {
} }

View file

@ -1,12 +1,8 @@
package sh.libre.scim.storage; package sh.libre.scim.storage;
import java.util.Date; import de.captaingoldfish.scim.sdk.common.constants.HttpHeader;
import java.util.List;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
@ -20,20 +16,24 @@ import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.storage.user.ImportSynchronization; import org.keycloak.storage.user.ImportSynchronization;
import org.keycloak.storage.user.SynchronizationResult; import org.keycloak.storage.user.SynchronizationResult;
import org.keycloak.timer.TimerProvider; import org.keycloak.timer.TimerProvider;
import sh.libre.scim.core.GroupAdapter; import sh.libre.scim.core.GroupAdapter;
import sh.libre.scim.core.ScimDispatcher; import sh.libre.scim.core.ScimDispatcher;
import sh.libre.scim.core.UserAdapter; import sh.libre.scim.core.UserAdapter;
import de.captaingoldfish.scim.sdk.common.constants.HttpHeader; import java.util.Date;
import java.util.List;
public class ScimStorageProviderFactory public class ScimStorageProviderFactory
implements UserStorageProviderFactory<ScimStorageProvider>, ImportSynchronization { implements UserStorageProviderFactory<ScimStorageProvider>, ImportSynchronization {
final private Logger LOGGER = Logger.getLogger(ScimStorageProviderFactory.class);
public final static String ID = "scim"; private final Logger LOGGER = Logger.getLogger(ScimStorageProviderFactory.class);
protected static final List<ProviderConfigProperty> configMetadata;
public static final String ID = "scim";
private static final List<ProviderConfigProperty> CONFIG_METADATA;
static { static {
configMetadata = ProviderConfigurationBuilder.create() CONFIG_METADATA = ProviderConfigurationBuilder.create()
.property() .property()
.name("endpoint") .name("endpoint")
.type(ProviderConfigProperty.STRING_TYPE) .type(ProviderConfigProperty.STRING_TYPE)
@ -47,7 +47,7 @@ public class ScimStorageProviderFactory
.type(ProviderConfigProperty.LIST_TYPE) .type(ProviderConfigProperty.LIST_TYPE)
.label("Endpoint content type") .label("Endpoint content type")
.helpText("Only used when endpoint doesn't support application/scim+json") .helpText("Only used when endpoint doesn't support application/scim+json")
.options(MediaType.APPLICATION_JSON.toString(), HttpHeader.SCIM_CONTENT_TYPE) .options(MediaType.APPLICATION_JSON, HttpHeader.SCIM_CONTENT_TYPE)
.defaultValue(HttpHeader.SCIM_CONTENT_TYPE) .defaultValue(HttpHeader.SCIM_CONTENT_TYPE)
.add() .add()
.property() .property()
@ -118,12 +118,12 @@ public class ScimStorageProviderFactory
@Override @Override
public List<ProviderConfigProperty> getConfigProperties() { public List<ProviderConfigProperty> getConfigProperties() {
return configMetadata; return CONFIG_METADATA;
} }
@Override @Override
public SynchronizationResult sync(KeycloakSessionFactory sessionFactory, String realmId, public SynchronizationResult sync(KeycloakSessionFactory sessionFactory, String realmId,
UserStorageProviderModel model) { UserStorageProviderModel model) {
LOGGER.info("sync"); LOGGER.info("sync");
var result = new SynchronizationResult(); var result = new SynchronizationResult();
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() { KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
@ -149,7 +149,7 @@ public class ScimStorageProviderFactory
@Override @Override
public SynchronizationResult syncSince(Date lastSync, KeycloakSessionFactory sessionFactory, String realmId, public SynchronizationResult syncSince(Date lastSync, KeycloakSessionFactory sessionFactory, String realmId,
UserStorageProviderModel model) { UserStorageProviderModel model) {
return this.sync(sessionFactory, realmId, model); return this.sync(sessionFactory, realmId, model);
} }