fix and add config via federation
This commit is contained in:
parent
f2dbb59e0a
commit
fd672971a3
9 changed files with 131 additions and 76 deletions
|
@ -19,23 +19,24 @@ import javax.ws.rs.client.Client;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
|
||||
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserModel;
|
||||
import sh.libre.scim.jpa.ScimResource;
|
||||
|
||||
public class ScimClient {
|
||||
|
||||
final Logger LOGGER = Logger.getLogger(ScimClient.class);
|
||||
final Client client = ResteasyClientBuilder.newClient();
|
||||
final ScimService scimService;
|
||||
final RetryRegistry registry;
|
||||
final String name;
|
||||
final String realmId;
|
||||
final EntityManager entityManager;
|
||||
final private Logger LOGGER = Logger.getLogger(ScimClient.class);
|
||||
final private Client client = ResteasyClientBuilder.newClient();
|
||||
final private ScimService scimService;
|
||||
final private RetryRegistry registry;
|
||||
final private String name;
|
||||
final private KeycloakSession session;
|
||||
|
||||
public ScimClient(String name, String url, String realmId, EntityManager entityManager) {
|
||||
public ScimClient(String name, String url, KeycloakSession session) {
|
||||
this.name = name;
|
||||
this.realmId = realmId;
|
||||
this.entityManager = entityManager;
|
||||
|
||||
this.session = session;
|
||||
var target = client.target(url);
|
||||
scimService = new ScimService(target);
|
||||
|
||||
|
@ -46,6 +47,14 @@ public class ScimClient {
|
|||
registry = RetryRegistry.of(retryConfig);
|
||||
}
|
||||
|
||||
private EntityManager getEM() {
|
||||
return session.getProvider(JpaConnectionProvider.class).getEntityManager();
|
||||
}
|
||||
|
||||
private String getRealmId() {
|
||||
return session.getContext().getRealm().getId();
|
||||
}
|
||||
|
||||
public void createUser(UserModel kcUser) {
|
||||
LOGGER.info("Create User");
|
||||
var user = toUser(kcUser);
|
||||
|
@ -58,7 +67,7 @@ public class ScimClient {
|
|||
}
|
||||
});
|
||||
var scimUser = toScimUser(spUser);
|
||||
entityManager.persist(scimUser);
|
||||
getEM().persist(scimUser);
|
||||
}
|
||||
|
||||
public void replaceUser(UserModel kcUser) {
|
||||
|
@ -80,7 +89,7 @@ public class ScimClient {
|
|||
}
|
||||
});
|
||||
} catch (NoResultException e) {
|
||||
LOGGER.warnf("Failde to replce user %s, scim mapping not found", kcUser.getId());
|
||||
LOGGER.warnf("Failde to repalce user %s, scim mapping not found", kcUser.getId());
|
||||
} catch (Exception e) {
|
||||
LOGGER.error(e);
|
||||
}
|
||||
|
@ -99,16 +108,16 @@ public class ScimClient {
|
|||
}
|
||||
return "";
|
||||
});
|
||||
entityManager.remove(resource);
|
||||
getEM().remove(resource);
|
||||
} catch (NoResultException e) {
|
||||
LOGGER.warnf("Failde to replce user %s, scim mapping not found", userId);
|
||||
LOGGER.warnf("Failde to delete user %s, scim mapping not found", userId);
|
||||
}
|
||||
}
|
||||
|
||||
private TypedQuery<ScimResource> queryUser(String query) {
|
||||
return entityManager
|
||||
return getEM()
|
||||
.createNamedQuery(query, ScimResource.class)
|
||||
.setParameter("realmId", realmId)
|
||||
.setParameter("realmId", getRealmId())
|
||||
.setParameter("type", "Users")
|
||||
.setParameter("serviceProvider", name);
|
||||
}
|
||||
|
@ -120,7 +129,7 @@ public class ScimClient {
|
|||
private ScimResource scimUser() {
|
||||
var resource = new ScimResource();
|
||||
resource.setType("Users");
|
||||
resource.setRealmId(realmId);
|
||||
resource.setRealmId(getRealmId());
|
||||
resource.setServiceProvider(name);
|
||||
return resource;
|
||||
}
|
||||
|
|
|
@ -1,58 +1,32 @@
|
|||
package sh.libre.scim.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.Consumer;
|
||||
import org.jboss.logging.Logger;
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import sh.libre.scim.storage.ScimStorageProviderFactory;
|
||||
|
||||
public class ScimDispatcher {
|
||||
final KeycloakSession session;
|
||||
final EntityManager entityManager;
|
||||
final Logger LOGGER = Logger.getLogger(ScimDispatcher.class);
|
||||
ArrayList<ScimClient> clients = new ArrayList<ScimClient>();
|
||||
final private KeycloakSession session;
|
||||
final private Logger LOGGER = Logger.getLogger(ScimDispatcher.class);
|
||||
|
||||
public ScimDispatcher(KeycloakSession session) {
|
||||
this.session = session;
|
||||
entityManager = session.getProvider(JpaConnectionProvider.class).getEntityManager();
|
||||
reloadClients();
|
||||
}
|
||||
|
||||
public void reloadClients() {
|
||||
close();
|
||||
LOGGER.info("Cleared SCIM Clients");
|
||||
var realm = session.getContext().getRealm();
|
||||
clients = new ArrayList<ScimClient>();
|
||||
var kcClients = session.clients().getClientsStream(realm);
|
||||
for (var kcClient : kcClients.toList()) {
|
||||
var endpoint = kcClient.getAttribute("scim-endpoint");
|
||||
var name = kcClient.getAttribute("scim-name");
|
||||
if (endpoint != "") {
|
||||
if (name == "") {
|
||||
name = kcClient.getName();
|
||||
}
|
||||
clients.add(new ScimClient(
|
||||
name,
|
||||
endpoint,
|
||||
realm.getId(),
|
||||
entityManager));
|
||||
LOGGER.infof("Added %s SCIM Client (%s)", name, endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void close() {
|
||||
for (var client : clients) {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void run(Consumer<ScimClient> f) {
|
||||
for (var client : clients) {
|
||||
f.accept(client);
|
||||
}
|
||||
session.getContext().getRealm().getComponentsStream()
|
||||
.filter((m) -> {
|
||||
return ScimStorageProviderFactory.ID.equals(m.getProviderId());
|
||||
})
|
||||
.forEach(m -> {
|
||||
LOGGER.infof("%s %s %s %s", m.getId(), m.getName(), m.getProviderId(), m.getProviderType());
|
||||
var client = new ScimClient(m.getName(), m.get("endpoint"), session);
|
||||
try {
|
||||
f.accept(client);
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,12 +20,10 @@ public class ScimEventListenerProvider implements EventListenerProvider {
|
|||
public ScimEventListenerProvider(KeycloakSession session) {
|
||||
this.session = session;
|
||||
dispatcher = new ScimDispatcher(session);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
dispatcher.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -45,9 +43,6 @@ public class ScimEventListenerProvider implements EventListenerProvider {
|
|||
|
||||
@Override
|
||||
public void onEvent(AdminEvent event, boolean includeRepresentation) {
|
||||
if (event.getResourceType() == ResourceType.CLIENT) {
|
||||
dispatcher.reloadClients();
|
||||
}
|
||||
if (event.getResourceType() == ResourceType.USER) {
|
||||
var userId = event.getResourcePath().replace("users/", "");
|
||||
LOGGER.infof("%s %s", userId, event.getOperationType());
|
||||
|
|
|
@ -35,16 +35,16 @@ public class ScimResource {
|
|||
@Column(name = "LOCAL_ID", nullable = false)
|
||||
private String localId;
|
||||
|
||||
public ScimResource() {
|
||||
}
|
||||
// public ScimResource() {
|
||||
// }
|
||||
|
||||
public ScimResource(String realmId, String serviceProvider, String type, String remoteId, String localId) {
|
||||
this.realmId = realmId;
|
||||
this.serviceProvider = serviceProvider;
|
||||
this.type = type;
|
||||
this.remoteId = remoteId;
|
||||
this.localId = localId;
|
||||
}
|
||||
// public ScimResource(String realmId, String serviceProvider, String type, String remoteId, String localId) {
|
||||
// this.realmId = realmId;
|
||||
// this.serviceProvider = serviceProvider;
|
||||
// this.type = type;
|
||||
// this.remoteId = remoteId;
|
||||
// this.localId = localId;
|
||||
// }
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
|
|
|
@ -4,13 +4,13 @@ import java.util.List;
|
|||
|
||||
import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
public class ScimResourceProvider implements JpaEntityProvider {
|
||||
|
||||
@Override
|
||||
public List<Class<?>> getEntities() {
|
||||
return Arrays.asList(ScimResource.class);
|
||||
return Collections.singletonList(ScimResource.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
25
src/main/java/sh/libre/scim/storage/ScimStorageProvider.java
Normal file
25
src/main/java/sh/libre/scim/storage/ScimStorageProvider.java
Normal file
|
@ -0,0 +1,25 @@
|
|||
package sh.libre.scim.storage;
|
||||
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.user.UserRegistrationProvider;
|
||||
|
||||
public class ScimStorageProvider implements UserStorageProvider, UserRegistrationProvider {
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel addUser(RealmModel realm, String username) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeUser(RealmModel realm, UserModel user) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package sh.libre.scim.storage;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||
import org.keycloak.storage.UserStorageProviderFactory;
|
||||
|
||||
public class ScimStorageProviderFactory implements UserStorageProviderFactory<ScimStorageProvider> {
|
||||
public final static String ID = "scim";
|
||||
protected static final List<ProviderConfigProperty> configMetadata;
|
||||
static {
|
||||
configMetadata = ProviderConfigurationBuilder.create()
|
||||
.property()
|
||||
.name("endpoint")
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.label("SCIM 2.0 endpoint")
|
||||
.helpText("External SCIM 2.0 base " +
|
||||
"URL (/ServiceProviderConfig /Schemas and /ResourcesTypes should be accessible)")
|
||||
.add()
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScimStorageProvider create(KeycloakSession session, ComponentModel model) {
|
||||
return new ScimStorageProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configMetadata;
|
||||
}
|
||||
|
||||
}
|
10
src/main/resources/META-INF/jboss-deployment-structure.xml
Normal file
10
src/main/resources/META-INF/jboss-deployment-structure.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<jboss-deployment-structure>
|
||||
<deployment>
|
||||
<dependencies>
|
||||
<module name="org.keycloak.keycloak-services" />
|
||||
<module name="org.keycloak.keycloak-model-jpa" />
|
||||
<module name="org.hibernate" />
|
||||
</dependencies>
|
||||
</deployment>
|
||||
</jboss-deployment-structure>
|
|
@ -0,0 +1 @@
|
|||
sh.libre.scim.storage.ScimStorageProviderFactory
|
Loading…
Reference in a new issue