chore: switch scim sdk
This commit is contained in:
parent
19e23a44f4
commit
7163d2a0f0
10 changed files with 178 additions and 149 deletions
4
auto.sh
Executable file
4
auto.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
gradle jar shadowjar
|
||||
scp build/libs/keycloak-scim-1.0-SNAPSHOT-all.jar root@192.168.130.252:/var/www/html/keycloak-scim-1.0-SNAPSHOT-all.jar
|
||||
scp build/libs/keycloak-scim-1.0-SNAPSHOT-all.jar root@192.168.130.252:/var/www/html/keycloak-scim-aws-1.0-SNAPSHOT-all.jar
|
||||
k delete pod keycloak-keycloakx-0 -n keycloak
|
14
build.gradle
14
build.gradle
|
@ -21,20 +21,14 @@ dependencies {
|
|||
compileOnly 'org.keycloak:keycloak-services:18.0.0'
|
||||
compileOnly 'org.keycloak:keycloak-model-jpa:18.0.0'
|
||||
implementation 'io.github.resilience4j:resilience4j-retry:1.7.1'
|
||||
implementation('com.unboundid.product.scim2:scim2-sdk-client:2.3.7') {
|
||||
transitive false
|
||||
}
|
||||
implementation('com.unboundid.product.scim2:scim2-sdk-common:2.3.7') {
|
||||
transitive false
|
||||
}
|
||||
implementation 'de.captaingoldfish:scim-sdk-common:1.15.3'
|
||||
|
||||
implementation files('/home/marcportabella/Documents/totmicro/Repos/keycloak-scim-aws/scim-sdk-client-1.15.4-SNAPSHOT.jar')
|
||||
//implementation 'de.captaingoldfish:scim-sdk-client:1.15.3'
|
||||
implementation('org.wildfly.client:wildfly-client-config:1.0.1.Final') {
|
||||
transitive false
|
||||
}
|
||||
implementation('org.jboss.resteasy:resteasy-client:4.7.6.Final') {
|
||||
transitive false
|
||||
}
|
||||
implementation('org.jboss.resteasy:resteasy-client-api:4.7.6.Final') {
|
||||
transitive false
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,12 +21,7 @@ dependencies {
|
|||
compileOnly 'org.keycloak:keycloak-services:18.0.0'
|
||||
compileOnly 'org.keycloak:keycloak-model-jpa:18.0.0'
|
||||
implementation 'io.github.resilience4j:resilience4j-retry:1.7.1'
|
||||
implementation('com.unboundid.product.scim2:scim2-sdk-client:2.3.7') {
|
||||
transitive false
|
||||
}
|
||||
implementation('com.unboundid.product.scim2:scim2-sdk-common:2.3.7') {
|
||||
transitive false
|
||||
}
|
||||
|
||||
compileOnly 'org.wildfly.client:wildfly-client-config:1.0.1.Final'
|
||||
compileOnly 'org.jboss.resteasy:resteasy-client:4.7.6.Final'
|
||||
compileOnly 'org.jboss.resteasy:resteasy-client-api:4.7.6.Final'
|
||||
|
|
|
@ -14,7 +14,9 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleMapperModel;
|
||||
import sh.libre.scim.jpa.ScimResource;
|
||||
|
||||
public abstract class Adapter<M extends RoleMapperModel, S extends com.unboundid.scim2.common.ScimResource> {
|
||||
import de.captaingoldfish.scim.sdk.common.resources.ResourceNode;
|
||||
|
||||
public abstract class Adapter<M extends RoleMapperModel, S extends ResourceNode> {
|
||||
|
||||
protected final Logger LOGGER;
|
||||
protected final String realmId;
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
package sh.libre.scim.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
|
||||
import javax.ws.rs.client.ClientRequestContext;
|
||||
import javax.ws.rs.client.ClientRequestFilter;
|
||||
|
||||
public class BasicAuthentication implements ClientRequestFilter {
|
||||
private final String user;
|
||||
private final String password;
|
||||
|
||||
BasicAuthentication(String user, String password) {
|
||||
this.user = user;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void filter(ClientRequestContext requestContext) throws IOException {
|
||||
var token = Base64.getEncoder().encodeToString((user + ":" + password).getBytes());
|
||||
requestContext.getHeaders().add("Authorization", "Basic " + token);
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package sh.libre.scim.core;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.ws.rs.client.ClientRequestContext;
|
||||
import javax.ws.rs.client.ClientRequestFilter;
|
||||
|
||||
public class BearerAuthentication implements ClientRequestFilter {
|
||||
private final String token;
|
||||
|
||||
BearerAuthentication(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void filter(ClientRequestContext requestContext) throws IOException {
|
||||
requestContext.getHeaders().add("Authorization", "Bearer " + this.token);
|
||||
|
||||
}
|
||||
}
|
|
@ -7,19 +7,16 @@ import java.util.HashSet;
|
|||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.persistence.NoResultException;
|
||||
|
||||
import com.unboundid.scim2.common.types.GroupResource;
|
||||
import com.unboundid.scim2.common.types.Member;
|
||||
import com.unboundid.scim2.common.types.Meta;
|
||||
|
||||
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.lang.StringUtils;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
public class GroupAdapter extends Adapter<GroupModel, GroupResource> {
|
||||
public class GroupAdapter extends Adapter<GroupModel, Group> {
|
||||
|
||||
private String displayName;
|
||||
private Set<String> members = new HashSet<String>();
|
||||
|
@ -39,8 +36,8 @@ public class GroupAdapter extends Adapter<GroupModel, GroupResource> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Class<GroupResource> getResourceClass() {
|
||||
return GroupResource.class;
|
||||
public Class<Group> getResourceClass() {
|
||||
return Group.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -55,14 +52,14 @@ public class GroupAdapter extends Adapter<GroupModel, GroupResource> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void apply(GroupResource group) {
|
||||
setExternalId(group.getId());
|
||||
setDisplayName(group.getDisplayName());
|
||||
public void apply(Group group) {
|
||||
setExternalId(group.getId().get());
|
||||
setDisplayName(group.getDisplayName().get());
|
||||
var groupMembers = group.getMembers();
|
||||
if (groupMembers != null && groupMembers.size() > 0) {
|
||||
this.members = new HashSet<String>();
|
||||
for (var groupMember : groupMembers) {
|
||||
var userMapping = this.query("findByExternalId", groupMember.getValue(), "User")
|
||||
var userMapping = this.query("findByExternalId", groupMember.getValue().get(), "User")
|
||||
.getSingleResult();
|
||||
this.members.add(userMapping.getId());
|
||||
}
|
||||
|
@ -70,8 +67,8 @@ public class GroupAdapter extends Adapter<GroupModel, GroupResource> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public GroupResource toSCIM(Boolean addMeta) {
|
||||
var group = new GroupResource();
|
||||
public Group toSCIM(Boolean addMeta) {
|
||||
var group = new Group();
|
||||
group.setId(externalId);
|
||||
group.setExternalId(id);
|
||||
group.setDisplayName(displayName);
|
||||
|
@ -83,7 +80,7 @@ public class GroupAdapter extends Adapter<GroupModel, GroupResource> {
|
|||
var userMapping = this.query("findById", member, "User").getSingleResult();
|
||||
groupMember.setValue(userMapping.getExternalId());
|
||||
var ref = new URI(String.format("Users/%s", userMapping.getExternalId()));
|
||||
groupMember.setRef(ref);
|
||||
groupMember.setRef(ref.toString());
|
||||
groupMembers.add(groupMember);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error(e);
|
||||
|
@ -95,7 +92,7 @@ public class GroupAdapter extends Adapter<GroupModel, GroupResource> {
|
|||
var meta = new Meta();
|
||||
try {
|
||||
var uri = new URI("Groups/" + externalId);
|
||||
meta.setLocation(uri);
|
||||
meta.setLocation(uri.toString());
|
||||
} catch (URISyntaxException e) {
|
||||
}
|
||||
group.setMeta(meta);
|
||||
|
|
|
@ -1,61 +1,108 @@
|
|||
package sh.libre.scim.core;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.ws.rs.ProcessingException;
|
||||
import javax.ws.rs.client.Client;
|
||||
|
||||
import com.unboundid.scim2.client.ScimService;
|
||||
import com.unboundid.scim2.common.ScimResource;
|
||||
import com.unboundid.scim2.common.exceptions.ScimException;
|
||||
import de.captaingoldfish.scim.sdk.client.ScimClientConfig;
|
||||
import de.captaingoldfish.scim.sdk.client.ScimRequestBuilder;
|
||||
import de.captaingoldfish.scim.sdk.client.http.BasicAuth;
|
||||
import de.captaingoldfish.scim.sdk.client.response.ServerResponse;
|
||||
import de.captaingoldfish.scim.sdk.common.exceptions.ResponseException;
|
||||
import de.captaingoldfish.scim.sdk.common.resources.ResourceNode;
|
||||
import de.captaingoldfish.scim.sdk.common.response.ListResponse;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RoleMapperModel;
|
||||
import org.keycloak.storage.user.SynchronizationResult;
|
||||
|
||||
import com.google.common.net.HttpHeaders;
|
||||
|
||||
import io.github.resilience4j.core.IntervalFunction;
|
||||
import io.github.resilience4j.retry.RetryConfig;
|
||||
import io.github.resilience4j.retry.RetryRegistry;
|
||||
|
||||
|
||||
public class ScimClient {
|
||||
final protected Logger LOGGER = Logger.getLogger(ScimClient.class);
|
||||
final protected Client client = ResteasyClientBuilder.newClient();
|
||||
final protected ScimService scimService;
|
||||
final protected ScimRequestBuilder scimRequestBuilder;
|
||||
final protected RetryRegistry registry;
|
||||
final protected KeycloakSession session;
|
||||
final protected String contentType;
|
||||
final protected ComponentModel model;
|
||||
final protected String scimApplicationBaseUrl;
|
||||
final protected Map<String, String> defaultHeaders;
|
||||
final protected Map<String, String> expectedResponseHeaders;
|
||||
|
||||
public ScimClient(ComponentModel model, KeycloakSession session) {
|
||||
this.model = model;
|
||||
this.contentType = model.get("content-type");
|
||||
|
||||
this.session = session;
|
||||
var target = client.target(model.get("endpoint"));
|
||||
this.scimApplicationBaseUrl = model.get("endpoint");
|
||||
this.defaultHeaders = new HashMap<>();
|
||||
this.expectedResponseHeaders = new HashMap<>();
|
||||
|
||||
switch (model.get("auth-mode")) {
|
||||
case "BEARER":
|
||||
target = target.register(new BearerAuthentication(model.get("auth-pass")));
|
||||
defaultHeaders.put(HttpHeaders.AUTHORIZATION,
|
||||
BearerAuthentication(model.get("auth-pass")));
|
||||
break;
|
||||
case "BASIC_AUTH":
|
||||
target = target.register(new BasicAuthentication(
|
||||
model.get("auth-user"),
|
||||
model.get("auth-pass")));
|
||||
defaultHeaders.put(HttpHeaders.AUTHORIZATION,
|
||||
BasicAuthentication(model.get("auth-user"),
|
||||
model.get("auth-pass")));
|
||||
break;
|
||||
}
|
||||
|
||||
scimService = new ScimService(target);
|
||||
defaultHeaders.put(HttpHeaders.CONTENT_TYPE,contentType);
|
||||
|
||||
scimRequestBuilder = new ScimRequestBuilder(scimApplicationBaseUrl, genScimClientConfig());
|
||||
|
||||
RetryConfig retryConfig = RetryConfig.custom()
|
||||
.maxAttempts(10)
|
||||
.intervalFunction(IntervalFunction.ofExponentialBackoff())
|
||||
.retryExceptions(ProcessingException.class)
|
||||
.build();
|
||||
.maxAttempts(10)
|
||||
.intervalFunction(IntervalFunction.ofExponentialBackoff())
|
||||
.retryExceptions(ProcessingException.class)
|
||||
.build();
|
||||
|
||||
registry = RetryRegistry.of(retryConfig);
|
||||
}
|
||||
|
||||
protected String BasicAuthentication(String username ,String password) {
|
||||
return BasicAuth.builder()
|
||||
.username(model.get(username))
|
||||
.password(model.get(password))
|
||||
.build()
|
||||
.getAuthorizationHeaderValue();
|
||||
}
|
||||
|
||||
protected ScimClientConfig genScimClientConfig() {
|
||||
return ScimClientConfig.builder()
|
||||
.httpHeaders(defaultHeaders)
|
||||
.connectTimeout(5)
|
||||
.requestTimeout(5)
|
||||
.socketTimeout(5)
|
||||
.expectedHttpResponseHeaders(expectedResponseHeaders)
|
||||
.hostnameVerifier((s, sslSession) -> true)
|
||||
.build();
|
||||
}
|
||||
|
||||
protected String BearerAuthentication(String token) {
|
||||
return "Bearer " + token ;
|
||||
}
|
||||
|
||||
protected String genScimUrl(String scimEndpoint,String resourcePath) {
|
||||
return String.format("%s%s/%s", scimApplicationBaseUrl ,
|
||||
scimEndpoint,
|
||||
resourcePath);
|
||||
}
|
||||
|
||||
|
||||
protected EntityManager getEM() {
|
||||
return session.getProvider(JpaConnectionProvider.class).getEntityManager();
|
||||
}
|
||||
|
@ -64,7 +111,7 @@ public class ScimClient {
|
|||
return session.getContext().getRealm().getId();
|
||||
}
|
||||
|
||||
protected <M extends RoleMapperModel, S extends ScimResource, A extends Adapter<M, S>> A getAdapter(
|
||||
protected <M extends RoleMapperModel, S extends ResourceNode, A extends Adapter<M, S>> A getAdapter(
|
||||
Class<A> aClass) {
|
||||
try {
|
||||
return aClass.getDeclaredConstructor(KeycloakSession.class, String.class)
|
||||
|
@ -74,7 +121,7 @@ public class ScimClient {
|
|||
}
|
||||
}
|
||||
|
||||
public <M extends RoleMapperModel, S extends ScimResource, 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) {
|
||||
var adapter = getAdapter(aClass);
|
||||
adapter.apply(kcModel);
|
||||
|
@ -85,21 +132,28 @@ public class ScimClient {
|
|||
return;
|
||||
}
|
||||
var retry = registry.retry("create-" + adapter.getId());
|
||||
var resource = retry.executeSupplier(() -> {
|
||||
|
||||
ServerResponse<S> response = retry.executeSupplier(() -> {
|
||||
try {
|
||||
return scimService.createRequest(adapter.getSCIMEndpoint(),
|
||||
adapter.toSCIM(false))
|
||||
.contentType(contentType).invoke();
|
||||
} catch (ScimException e) {
|
||||
return scimRequestBuilder
|
||||
.create(adapter.getResourceClass(), String.format("/" + adapter.getSCIMEndpoint()))
|
||||
.setResource(adapter.toSCIM(false))
|
||||
.sendRequest();
|
||||
} catch ( ResponseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
adapter.apply(resource);
|
||||
adapter.saveMapping();
|
||||
|
||||
if (!response.isSuccess()){
|
||||
LOGGER.warn(response.getResponseBody());
|
||||
LOGGER.warn(response.getHttpStatus());
|
||||
}
|
||||
|
||||
adapter.apply(response.getResource());
|
||||
adapter.saveMapping();
|
||||
};
|
||||
|
||||
public <M extends RoleMapperModel, S extends ScimResource, 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) {
|
||||
var adapter = getAdapter(aClass);
|
||||
try {
|
||||
|
@ -109,13 +163,22 @@ public class ScimClient {
|
|||
var resource = adapter.query("findById", adapter.getId()).getSingleResult();
|
||||
adapter.apply(resource);
|
||||
var retry = registry.retry("replace-" + adapter.getId());
|
||||
retry.executeSupplier(() -> {
|
||||
ServerResponse<S> response = retry.executeSupplier(() -> {
|
||||
try {
|
||||
return scimService.replaceRequest(adapter.toSCIM(true)).contentType(contentType).invoke();
|
||||
} catch (ScimException e) {
|
||||
return scimRequestBuilder
|
||||
.update(genScimUrl(adapter.getSCIMEndpoint(), adapter.getExternalId()),
|
||||
adapter.getResourceClass())
|
||||
.setResource(adapter.toSCIM(false))
|
||||
.sendRequest() ;
|
||||
} catch ( ResponseException e) {
|
||||
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
if (!response.isSuccess()){
|
||||
LOGGER.warn(response.getResponseBody());
|
||||
LOGGER.warn(response.getHttpStatus());
|
||||
}
|
||||
} catch (NoResultException e) {
|
||||
LOGGER.warnf("failed to replace resource %s, scim mapping not found", adapter.getId());
|
||||
} catch (Exception e) {
|
||||
|
@ -123,30 +186,40 @@ public class ScimClient {
|
|||
}
|
||||
}
|
||||
|
||||
public <M extends RoleMapperModel, S extends ScimResource, 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) {
|
||||
var adapter = getAdapter(aClass);
|
||||
adapter.setId(id);
|
||||
|
||||
try {
|
||||
var resource = adapter.query("findById", adapter.getId()).getSingleResult();
|
||||
adapter.apply(resource);
|
||||
|
||||
var retry = registry.retry("delete-" + id);
|
||||
retry.executeSupplier(() -> {
|
||||
|
||||
ServerResponse<S> response = retry.executeSupplier(() -> {
|
||||
try {
|
||||
scimService.deleteRequest(adapter.getSCIMEndpoint(), resource.getExternalId())
|
||||
.contentType(contentType).invoke();
|
||||
} catch (ScimException e) {
|
||||
return scimRequestBuilder.delete(genScimUrl(adapter.getSCIMEndpoint(), adapter.getExternalId()),
|
||||
adapter.getResourceClass())
|
||||
.sendRequest();
|
||||
} catch (ResponseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return "";
|
||||
});
|
||||
|
||||
if (!response.isSuccess()){
|
||||
LOGGER.warn(response.getResponseBody());
|
||||
LOGGER.warn(response.getHttpStatus());
|
||||
}
|
||||
|
||||
getEM().remove(resource);
|
||||
|
||||
} catch (NoResultException e) {
|
||||
LOGGER.warnf("Failed to delete resource %s, scim mapping not found", id);
|
||||
}
|
||||
}
|
||||
|
||||
public <M extends RoleMapperModel, S extends ScimResource, A extends Adapter<M, S>> void refreshResources(
|
||||
public <M extends RoleMapperModel, S extends ResourceNode, A extends Adapter<M, S>> void refreshResources(
|
||||
Class<A> aClass,
|
||||
SynchronizationResult syncRes) {
|
||||
LOGGER.info("Refresh resources");
|
||||
|
@ -169,16 +242,17 @@ public class ScimClient {
|
|||
|
||||
}
|
||||
|
||||
public <M extends RoleMapperModel, S extends ScimResource, A extends Adapter<M, S>> void importResources(
|
||||
public <M extends RoleMapperModel, S extends ResourceNode, A extends Adapter<M, S>> void importResources(
|
||||
Class<A> aClass, SynchronizationResult syncRes) {
|
||||
LOGGER.info("Import");
|
||||
try {
|
||||
var adapter = getAdapter(aClass);
|
||||
var resources = scimService.searchRequest(adapter.getSCIMEndpoint()).contentType(contentType)
|
||||
.invoke(adapter.getResourceClass());
|
||||
for (var resource : resources) {
|
||||
ServerResponse<ListResponse<S>> response = scimRequestBuilder.list("url",adapter.getResourceClass()).get().sendRequest();
|
||||
ListResponse<S> resourceTypeListResponse = response.getResource();
|
||||
|
||||
for (var resource : resourceTypeListResponse.getListedResources()) {
|
||||
try {
|
||||
LOGGER.infof("Reconciling remote resource %s", resource.getId());
|
||||
LOGGER.infof("Reconciling remote resource %s", resource);
|
||||
adapter = getAdapter(aClass);
|
||||
adapter.apply(resource);
|
||||
|
||||
|
@ -212,9 +286,11 @@ public class ScimClient {
|
|||
break;
|
||||
case "DELETE_REMOTE":
|
||||
LOGGER.info("Delete remote resource");
|
||||
scimService.deleteRequest(adapter.getSCIMEndpoint(), resource.getId())
|
||||
.contentType(contentType)
|
||||
.invoke();
|
||||
scimRequestBuilder
|
||||
.delete(genScimUrl(adapter.getSCIMEndpoint(),
|
||||
resource.getId().get()),
|
||||
adapter.getResourceClass())
|
||||
.sendRequest();
|
||||
syncRes.increaseRemoved();
|
||||
break;
|
||||
}
|
||||
|
@ -225,12 +301,12 @@ public class ScimClient {
|
|||
syncRes.increaseFailed();
|
||||
}
|
||||
}
|
||||
} catch (ScimException e) {
|
||||
} catch (ResponseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public <M extends RoleMapperModel, S extends ScimResource, 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) {
|
||||
if (this.model.get("sync-import", false)) {
|
||||
this.importResources(aClass, syncRes);
|
||||
|
@ -241,6 +317,6 @@ public class ScimClient {
|
|||
}
|
||||
|
||||
public void close() {
|
||||
client.close();
|
||||
scimRequestBuilder.close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,17 +7,19 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.unboundid.scim2.common.types.Email;
|
||||
import com.unboundid.scim2.common.types.Meta;
|
||||
import com.unboundid.scim2.common.types.Role;
|
||||
import com.unboundid.scim2.common.types.UserResource;
|
||||
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.lang.StringUtils;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
||||
public class UserAdapter extends Adapter<UserModel, UserResource> {
|
||||
public class UserAdapter extends Adapter<UserModel, User> {
|
||||
|
||||
private String username;
|
||||
private String displayName;
|
||||
|
@ -78,8 +80,8 @@ public class UserAdapter extends Adapter<UserModel, UserResource> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Class<UserResource> getResourceClass() {
|
||||
return UserResource.class;
|
||||
public Class<User> getResourceClass() {
|
||||
return User.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -114,27 +116,29 @@ public class UserAdapter extends Adapter<UserModel, UserResource> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void apply(UserResource user) {
|
||||
setExternalId(user.getId());
|
||||
setUsername(user.getUserName());
|
||||
setDisplayName(user.getDisplayName());
|
||||
setActive(user.getActive());
|
||||
public void apply(User user) {
|
||||
setExternalId(user.getId().get());
|
||||
setUsername(user.getUserName().get());
|
||||
setDisplayName(user.getDisplayName().get());
|
||||
setActive(user.isActive().get());
|
||||
if (user.getEmails().size() > 0) {
|
||||
setEmail(user.getEmails().get(0).getValue());
|
||||
setEmail(user.getEmails().get(0).getValue().get());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserResource toSCIM(Boolean addMeta) {
|
||||
var user = new UserResource();
|
||||
public User toSCIM(Boolean addMeta) {
|
||||
var user = new User();
|
||||
user.setExternalId(id);
|
||||
user.setUserName(username);
|
||||
user.setId(externalId);
|
||||
user.setDisplayName(displayName);
|
||||
Name name = new Name();
|
||||
user.setName(name);
|
||||
var emails = new ArrayList<Email>();
|
||||
if (email != null) {
|
||||
emails.add(
|
||||
new Email().setPrimary(true).setValue(email));
|
||||
Email.builder().value(getEmail()).build());
|
||||
}
|
||||
user.setEmails(emails);
|
||||
user.setActive(active);
|
||||
|
@ -142,14 +146,14 @@ public class UserAdapter extends Adapter<UserModel, UserResource> {
|
|||
var meta = new Meta();
|
||||
try {
|
||||
var uri = new URI("Users/" + externalId);
|
||||
meta.setLocation(uri);
|
||||
meta.setLocation(uri.toString());
|
||||
} catch (URISyntaxException e) {
|
||||
}
|
||||
user.setMeta(meta);
|
||||
}
|
||||
List<Role> roles = new ArrayList<Role>();
|
||||
List<PersonRole> roles = new ArrayList<PersonRole>();
|
||||
for (var r : this.roles) {
|
||||
var role = new Role();
|
||||
var role = new PersonRole();
|
||||
role.setValue(r);
|
||||
roles.add(role);
|
||||
}
|
||||
|
|
|
@ -5,8 +5,6 @@ import java.util.List;
|
|||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import com.unboundid.scim2.client.ScimService;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
@ -24,6 +22,8 @@ import sh.libre.scim.core.GroupAdapter;
|
|||
import sh.libre.scim.core.ScimDispatcher;
|
||||
import sh.libre.scim.core.UserAdapter;
|
||||
|
||||
import de.captaingoldfish.scim.sdk.common.constants.HttpHeader;
|
||||
|
||||
public class ScimStorageProviderFactory
|
||||
implements UserStorageProviderFactory<ScimStorageProvider>, ImportSynchronization {
|
||||
final private Logger LOGGER = Logger.getLogger(ScimStorageProviderFactory.class);
|
||||
|
@ -43,8 +43,8 @@ public class ScimStorageProviderFactory
|
|||
.type(ProviderConfigProperty.LIST_TYPE)
|
||||
.label("Endpoint content type")
|
||||
.helpText("Only used when endpoint doesn't support application/scim+json")
|
||||
.options(MediaType.APPLICATION_JSON.toString(), ScimService.MEDIA_TYPE_SCIM_TYPE.toString())
|
||||
.defaultValue(ScimService.MEDIA_TYPE_SCIM_TYPE.toString())
|
||||
.options(MediaType.APPLICATION_JSON.toString(), HttpHeader.SCIM_CONTENT_TYPE)
|
||||
.defaultValue(HttpHeader.SCIM_CONTENT_TYPE)
|
||||
.add()
|
||||
.property()
|
||||
.name("auth-mode")
|
||||
|
|
Loading…
Reference in a new issue