diff --git a/jans-keycloak-integration/authenticator/installation.md b/jans-keycloak-integration/authenticator/installation.md deleted file mode 100644 index 7726c66a146..00000000000 --- a/jans-keycloak-integration/authenticator/installation.md +++ /dev/null @@ -1,62 +0,0 @@ -## Keycloak Installation configuration for use with Janssen Auth - -### 1- Brief - - This guide contains instructions on how to install keycloak for use with keycloak -and run it in a production setting alongside Janssen. - - -### 2- Keycloak and Plugins Installation - - We will be using the quarkus distribution of keycloak which can be found -[here](https://github.com/keycloak/keycloak/releases/download/22.0.3/keycloak-22.0.3.zip). -directory. -After downloading the binaries , it's suggested to unzip it in the `/opt/keycloak` directory. - -#### 2.1 - Keycloak Authentication Plugin Installation - -Installing the authentication plugin is straightforward. -It resides at the url -https://jenkins.jans.io/maven/io/jans/jans-authenticator// -Binaries of interest have to be copied to the -`/opt/keycloak/providers/` directory. They are: -- `kc-jans-authn-plugin-.jar` -- `kc-jans-authn-plugin--deps.zip`. It's contents have to -be unzipped into the directory. These are the plugin's dependencies. - -No further action is needed after copying these files. - - -### 3 - Running Keycloak - - The following assumptions will be made -- Keycloak has been installed under the directory `/opt/keycloak/` -- The Janssen Server's hostname is `janssen-with-kc.local` -- Keycloak will run behind a reverse proxy/ load balancer (e.g. apache ) - and will be listening only on the local interface on port 8092 - -From the terminal, run the following command -``` -/opt/keycloak/bin/kc.sh --log "console,file" --http-host=127.0.0.1 --http-port=8092 \ ---hostname-url=https://janssen-with-kc.local --spi-connections-http-client-default-disable-trust-manager=true \ ---proxy edge -``` - -#### 3.1 - Database Setup - By default , in a non-production environment , keycloak relies on the embedded H2 database for operation. -In a production setting, a more appropriate database needs to be deployed. -You can find a list of supported databases [here](https://www.keycloak.org/server/db). -Additional database configuration will need to be done. - - - -#### 3.2 - Reverse Proxy -As keycloak will run behind a proxy, there are a couple paths that need to be exposed (or not), with the full list -found [here](https://www.keycloak.org/server/reverseproxy). - - -### 5 - Configuration changes in Keycloak and Janssen-Auth -TBD - -### 6 - Clustering -TBD \ No newline at end of file diff --git a/jans-keycloak-integration/authenticator/pom.xml b/jans-keycloak-integration/authenticator/pom.xml deleted file mode 100644 index da1a2a3234c..00000000000 --- a/jans-keycloak-integration/authenticator/pom.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - 4.0.0 - io.jans - kc-jans-authn-plugin - kc-jans-authn-plugin - jar - - - io.jans - jans-kc-parent - 1.1.3-SNAPSHOT - - - - ${maven.min-version} - - - - - - - org.keycloak - keycloak-core - - - - org.keycloak - keycloak-server-spi - - - - org.keycloak - keycloak-server-spi-private - - - - org.keycloak - keycloak-services - - - - - - com.nimbusds - oauth2-oidc-sdk - - - - - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - - - - org.apache.maven.plugins - maven-assembly-plugin - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - - - diff --git a/jans-keycloak-integration/authenticator/src/assembly/dependencies.xml b/jans-keycloak-integration/authenticator/src/assembly/dependencies.xml deleted file mode 100644 index 392faa3826e..00000000000 --- a/jans-keycloak-integration/authenticator/src/assembly/dependencies.xml +++ /dev/null @@ -1,17 +0,0 @@ - - deps - - zip - - false - - - target/deps/ - . - - *.jar - - - - \ No newline at end of file diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/ProviderIDs.java b/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/ProviderIDs.java deleted file mode 100644 index 6b63e573d94..00000000000 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/ProviderIDs.java +++ /dev/null @@ -1,6 +0,0 @@ -package io.jans.kc.spi; - -public class ProviderIDs { - public static final String JANS_AUTHENTICATOR_PROVIDER = "kc-jans-authn"; - public static final String JANS_AUTH_RESPONSE_REST_PROVIDER = "kc-jans-authn-rest-bridge"; -} diff --git a/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/api/admin/client/model/ManagedSamlClient.java b/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/api/admin/client/model/ManagedSamlClient.java index e1ff0b209b6..0de4826711d 100644 --- a/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/api/admin/client/model/ManagedSamlClient.java +++ b/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/api/admin/client/model/ManagedSamlClient.java @@ -152,8 +152,8 @@ private void initClientRepresentation() { clientRepresentation.setAuthenticationFlowBindingOverrides(authnFlowBindingOverrides); //set default saml attributes - samlShoulDocumentsBeSigned(false); - samlSignAssertions(false); + samlShoulDocumentsBeSigned(true); + samlSignAssertions(true); samlForcePostBinding(false); samlEncryptAssertions(false); samlForceArtifactBinding(false); diff --git a/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/api/admin/client/model/ProtocolMapper.java b/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/api/admin/client/model/ProtocolMapper.java index e42c9e4c16b..f96d4a6213b 100644 --- a/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/api/admin/client/model/ProtocolMapper.java +++ b/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/api/admin/client/model/ProtocolMapper.java @@ -125,6 +125,12 @@ public SamlUserAttributeMapperBuilder attributeNameFormatUnspecified() { return this; } + public SamlUserAttributeMapperBuilder jansAttributeName(final String attributename) { + + config.put("jans.attribute.name",attributename); + return this; + } + public ProtocolMapper build() { return this.mapper; diff --git a/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/scheduler/App.java b/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/scheduler/App.java index d2d9a207547..ad6edd2bec2 100644 --- a/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/scheduler/App.java +++ b/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/scheduler/App.java @@ -87,7 +87,7 @@ public static void main(String[] args) throws InterruptedException, ParserCreate Thread.sleep(1000); } } - log.info("Application shutthing down"); + log.info("Application shutting down"); }catch(StartupError e) { log.error("Application startup failed",e); if(jobScheduler != null) { @@ -95,6 +95,15 @@ public static void main(String[] args) throws InterruptedException, ParserCreate } System.exit(-1); return; + }catch(InterruptedException e) { + log.error("Application interrupted",e); + Thread.currentThread().interrupt(); + }catch(Exception e) { + log.error("Fatal error starting application",e); + if(jobScheduler != null ) { + jobScheduler.stop(); + } + System.exit(-1); } } @@ -163,10 +172,8 @@ private static final JobScheduler createQuartzJobSchedulerFromConfiguration(AppC private static final void runCronJobs() { - log.debug("Running trust relationship sync cron job"); TrustRelationshipSyncJob trsyncjob = new TrustRelationshipSyncJob(); trsyncjob.run(null); - log.debug("Trust relationship sync cron job complete"); } private static final void performPostStartupOperations() { @@ -271,7 +278,7 @@ public static class ShutdownHook extends Thread { public void run() { try { - log.debug("Shutting down application"); + log.info("Shutting down application"); if (jobScheduler != null) { jobScheduler.stop(); } diff --git a/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/scheduler/TrustRelationshipSyncJob.java b/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/scheduler/TrustRelationshipSyncJob.java index a3890009c67..1e01ea212e9 100644 --- a/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/scheduler/TrustRelationshipSyncJob.java +++ b/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/scheduler/TrustRelationshipSyncJob.java @@ -39,12 +39,7 @@ public TrustRelationshipSyncJob() { this.keycloakApi = App.keycloakApi(); this.realm = App.configuration().keycloakResourcesRealm(); this.samlUserAttributeMapperId = App.configuration().keycloakResourcesSamlUserAttributeMapper(); - try { - this.authnBrowserFlow = keycloakApi.getAuthenticationFlowFromAlias(realm,App.configuration().keycloakResourcesBrowserFlowAlias()); - }catch(Exception e) { - log.warn("Could not properly initialize sync job",e); - this.authnBrowserFlow = null; - } + this.authnBrowserFlow = keycloakApi.getAuthenticationFlowFromAlias(realm,App.configuration().keycloakResourcesBrowserFlowAlias()); } @Override @@ -213,13 +208,10 @@ private void addReleasedAttributesToManagedSamlClient(ManagedSamlClient client, List protmappers = releasedattributes.stream().map((r)-> { log.debug("Preparing to add released attribute {} to managed saml client with clientId {}",r.getName(),client.clientId()); return ProtocolMapper - .samlUserAttributeMapper(samlUserAttributeMapperId) - .name(generateKeycloakUniqueProtocolMapperName(r)) - .userAttribute(r.getName()) - .friendlyName(r.getDisplayName()!=null?r.getDisplayName():r.getName()) - .attributeName(r.getSaml2Uri()) - .attributeNameFormatUriReference() - .build(); + .samlUserAttributeMapper(samlUserAttributeMapperId) + .name(generateKeycloakUniqueProtocolMapperName(r)) + .jansAttributeName(r.getName()) + .build(); }).toList(); keycloakApi.addProtocolMappersToManagedSamlClient(realm, client, protmappers); @@ -230,10 +222,7 @@ private void updateManagedSamlClientProtocolMapper(ManagedSamlClient client, Pro log.debug("Updating managed client released attribute. Client id: {} / Attribute name: {}",client.clientId(),releasedattribute.getName()); ProtocolMapper newmapper = ProtocolMapper .samlUserAttributeMapper(mapper) - .userAttribute(releasedattribute.getName()) - .friendlyName(releasedattribute.getDisplayName()!=null?releasedattribute.getDisplayName():releasedattribute.getName()) - .attributeName(releasedattribute.getSaml2Uri()) - .attributeNameFormatUriReference() + .jansAttributeName(releasedattribute.getName()) .build(); keycloakApi.updateManagedSamlClientProtocolMapper(realm, client,newmapper); } diff --git a/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/scheduler/job/impl/QuartzJobWrapper.java b/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/scheduler/job/impl/QuartzJobWrapper.java index 2d14fc4db51..cf177c1484f 100644 --- a/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/scheduler/job/impl/QuartzJobWrapper.java +++ b/jans-keycloak-integration/job-scheduler/src/main/java/io/jans/kc/scheduler/job/impl/QuartzJobWrapper.java @@ -38,8 +38,7 @@ public void execute(JobExecutionContext context) throws JobExecutionException { io.jans.kc.scheduler.job.Job job = (io.jans.kc.scheduler.job.Job) constructor.newInstance(); ExecutionContext effectivecontext = new QuartzExecutionContext(context.getMergedJobDataMap()); job.run(effectivecontext); - } catch(ReflectiveOperationException e) { - e.printStackTrace(); + }catch(Exception e) { throw new JobExecutionException("Failed to run job " + jobname,e); } } diff --git a/jans-keycloak-integration/job-scheduler/src/main/resources/config.properties.sample b/jans-keycloak-integration/job-scheduler/src/main/resources/config.properties.sample index 2f8ab122fc7..08bf2b4e404 100644 --- a/jans-keycloak-integration/job-scheduler/src/main/resources/config.properties.sample +++ b/jans-keycloak-integration/job-scheduler/src/main/resources/config.properties.sample @@ -28,4 +28,4 @@ app.job.trustrelationship-sync.schedule-interval=PT10M # keycloak resources configuration app.keycloak.resources.realm=jans app.keycloak.resources.authn.browser.flow-alias=janssen login -app.keycloak.resources.saml.user-attribute-mapper=saml-user-attribute-mapper +app.keycloak.resources.saml.user-attribute-mapper=kc-jans-saml-user-attribute-mapper diff --git a/jans-keycloak-integration/job-scheduler/src/main/resources/logback.xml.sample b/jans-keycloak-integration/job-scheduler/src/main/resources/logback.xml.sample index 6a62328f644..9d0fc7b0cf3 100644 --- a/jans-keycloak-integration/job-scheduler/src/main/resources/logback.xml.sample +++ b/jans-keycloak-integration/job-scheduler/src/main/resources/logback.xml.sample @@ -25,7 +25,7 @@ ${app.logdir}/scheduler.log true - ${app.logdir}/scheduler-%d{yyyy-mm-dd}.log.gz + ${app.logdir}/scheduler-%d{yyyy-MM-dd}.log.gz ${app.logging.loghistory:-180} diff --git a/jans-keycloak-integration/pom.xml b/jans-keycloak-integration/pom.xml index 3f62d9d3b31..703a6aa857e 100644 --- a/jans-keycloak-integration/pom.xml +++ b/jans-keycloak-integration/pom.xml @@ -57,9 +57,8 @@ - authenticator - storage-spi job-scheduler + spi @@ -144,14 +143,6 @@ - - - io.jans - jans-scim-model - ${jans.version} - - - jakarta.ws.rs @@ -282,7 +273,55 @@ io.jans jans-core-saml - ${project.version} + ${jans.version} + + + + io.jans + jans-scim-model + ${jans.version} + + + + io.jans + jans-core-standalone + ${jans.version} + + + + io.jans + jans-orm-standalone + ${jans.version} + + + + io.jans + jans-orm-couchbase + ${jans.version} + + + + io.jans + jans-orm-hybrid + ${jans.version} + + + + io.jans + jans-orm-ldap + ${jans.version} + + + + io.jans + jans-orm-sql + ${jans.version} + + + + io.jans + jans-core-service + ${jans.version} @@ -335,14 +374,15 @@ com.fasterxml.jackson.core, commons-codec, commons-lang3, - commons-lang, commons-collections4, commons-io, commons-logging, commons-text, - org.apache.commons, - commons-configuration + jakarta.persistence + + + diff --git a/jans-keycloak-integration/spi/pom.xml b/jans-keycloak-integration/spi/pom.xml new file mode 100644 index 00000000000..a95d5b6f3c4 --- /dev/null +++ b/jans-keycloak-integration/spi/pom.xml @@ -0,0 +1,131 @@ + + + 4.0.0 + io.jans + kc-jans-spi + kc-jans-spi + jar + + + io.jans + jans-kc-parent + 1.1.3-SNAPSHOT + + + + ${maven.min-version} + + + + + + + org.keycloak + keycloak-core + + + + org.keycloak + keycloak-server-spi + + + + org.keycloak + keycloak-server-spi-private + + + + org.keycloak + keycloak-services + + + + org.keycloak + keycloak-saml-core-public + + + + + + io.jans + jans-core-standalone + + + + io.jans + jans-core-service + + + + io.jans + jans-orm-standalone + + + + io.jans + jans-orm-couchbase + + + + io.jans + jans-orm-hybrid + + + + io.jans + jans-orm-ldap + + + + io.jans + jans-orm-sql + + + + + + org.jboss.slf4j + slf4j-jboss-logmanager + 2.0.1.Final + + + + + org.apache.commons + commons-dbcp2 + 2.12.0 + + + + + com.nimbusds + oauth2-oidc-sdk + + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + + diff --git a/jans-keycloak-integration/storage-spi/src/assembly/dependencies.xml b/jans-keycloak-integration/spi/src/assembly/dependencies.xml similarity index 73% rename from jans-keycloak-integration/storage-spi/src/assembly/dependencies.xml rename to jans-keycloak-integration/spi/src/assembly/dependencies.xml index 392faa3826e..373f6f1d362 100644 --- a/jans-keycloak-integration/storage-spi/src/assembly/dependencies.xml +++ b/jans-keycloak-integration/spi/src/assembly/dependencies.xml @@ -13,5 +13,12 @@ *.jar + + src/main/resources/assembly + . + + *.DONOTDELETE + + \ No newline at end of file diff --git a/jans-keycloak-integration/spi/src/main/java/io/jans/kc/model/JansUserAttributeModel.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/model/JansUserAttributeModel.java new file mode 100644 index 00000000000..43d4296e6a4 --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/model/JansUserAttributeModel.java @@ -0,0 +1,55 @@ +package io.jans.kc.model; + +import io.jans.kc.model.internal.JansPerson; +import io.jans.model.GluuStatus; +import io.jans.model.JansAttribute; + +import java.util.List; + +import org.apache.commons.lang3.StringUtils; + +import org.keycloak.dom.saml.v2.assertion.AttributeType; + +import org.keycloak.saml.common.constants.JBossSAMLURIConstants; + +public class JansUserAttributeModel { + + private final JansAttribute jansAttribute; + private final JansPerson jansPerson; + + public JansUserAttributeModel(final JansAttribute jansAttribute, final JansPerson jansPerson) { + + this.jansAttribute = jansAttribute; + this.jansPerson = jansPerson; + } + + public boolean isActive() { + + return jansAttribute.getStatus() == GluuStatus.ACTIVE; + } + + public AttributeType asSamlKeycloakAttribute() { + + List values = jansPerson.customAttributeValues(jansAttribute.getName()); + if(values == null) { + + return null; + } + String samlAttributeName = jansAttribute.getSaml2Uri(); + String samlAttributeNameFormat = JBossSAMLURIConstants.ATTRIBUTE_FORMAT_URI.get(); + if(StringUtils.isEmpty(samlAttributeName)) { + samlAttributeName = jansAttribute.getName(); + samlAttributeNameFormat = JBossSAMLURIConstants.ATTRIBUTE_FORMAT_BASIC.get(); + } + AttributeType ret = new AttributeType(samlAttributeName); + ret.setNameFormat(samlAttributeNameFormat); + if(!StringUtils.isEmpty(jansAttribute.getDisplayName())) { + ret.setFriendlyName(jansAttribute.getDisplayName()); + }else { + ret.setFriendlyName(jansAttribute.getName()); + } + + values.forEach(ret::addAttributeValue); + return ret; + } +} diff --git a/jans-keycloak-integration/spi/src/main/java/io/jans/kc/model/JansUserModel.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/model/JansUserModel.java new file mode 100644 index 00000000000..e53bb9544ba --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/model/JansUserModel.java @@ -0,0 +1,327 @@ +package io.jans.kc.model; + +import io.jans.kc.model.internal.JansPerson; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.keycloak.component.ComponentModel; + +import org.keycloak.models.ClientModel; +import org.keycloak.models.GroupModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.SubjectCredentialManager; +import org.keycloak.storage.ReadOnlyException; +import org.keycloak.storage.StorageId; + +public class JansUserModel implements UserModel { + + private static final String INUM_ATTR_NAME = "inum"; + private static final String UID_ATTR_NAME = "uid"; + private static final String JANS_CREATION_TIMESTAMP_ATTR_NAME = "jansCreationTimestamp"; + private static final String JANS_STATUS_ATTR_NAME = "jansStatus"; + private static final String GIVEN_NAME_ATTR_NAME = "givenName"; + private static final String MAIL_ATTR_NAME = "mail"; + private static final String EMAIL_VERIFIED_ATTR_NAME = "emailVerified"; + private static final String USER_READ_ONLY_EXCEPTION_MSG = "User is read-only for this update"; + + private final JansPerson jansPerson; + private final StorageId storageId; + + public JansUserModel(ComponentModel storageProviderModel, JansPerson jansPerson) { + + this.jansPerson = jansPerson; + String userId = jansPerson.customAttributeValue(INUM_ATTR_NAME); + this.storageId = new StorageId(storageProviderModel.getId(),userId); + } + + @Override + public String getId() { + + return storageId.getId(); + } + + @Override + public String getUsername() { + + return jansPerson.customAttributeValue(UID_ATTR_NAME); + } + + @Override + public void setUsername(String username) { + + throw new ReadOnlyException(USER_READ_ONLY_EXCEPTION_MSG); + } + + @Override + public Long getCreatedTimestamp() { + + try { + final String createdStr = jansPerson.customAttributeValue(JANS_CREATION_TIMESTAMP_ATTR_NAME); + if(createdStr == null) { + return null; + } + return Long.parseLong(createdStr); + }catch(NumberFormatException e) { + return null; + } + } + + @Override + public void setCreatedTimestamp(Long timestamp) { + + throw new ReadOnlyException(USER_READ_ONLY_EXCEPTION_MSG); + } + + @Override + public boolean isEnabled() { + + final String enabledStr = jansPerson.customAttributeValue(JANS_STATUS_ATTR_NAME); + if(enabledStr == null) { + return false; + } + return "active".equals(enabledStr); + } + + @Override + public void setEnabled(boolean enabled) { + throw new ReadOnlyException(USER_READ_ONLY_EXCEPTION_MSG); + } + + @Override + public void setSingleAttribute(String name, String value) { + throw new ReadOnlyException(USER_READ_ONLY_EXCEPTION_MSG); + } + + @Override + public void setAttribute(String name, List value) { + + throw new ReadOnlyException(USER_READ_ONLY_EXCEPTION_MSG); + } + + @Override + public void removeAttribute(String name) { + throw new ReadOnlyException(USER_READ_ONLY_EXCEPTION_MSG); + } + + + @Override + public String getFirstAttribute(String name) { + + if(USERNAME.equals(name)) { + return getUsername(); + }else if(FIRST_NAME.equals(name)) { + return getFirstName(); + }else if(EMAIL.equals(name)) { + return getEmail(); + }else { + return jansPerson.customAttributeValue(name); + } + } + + @Override + public Stream getAttributeStream(final String name) { + + List ret = new ArrayList<>(); + + if(USERNAME.equals(name)) { + ret.add(getUsername()); + }else if(FIRST_NAME.equals(name)) { + ret.add(getFirstName()); + }else if(EMAIL.equals(name)) { + ret.add(getEmail()); + }else { + return jansPerson.customAttributeValues(name).stream(); + } + return ret.stream(); + } + + @Override + public Map> getAttributes() { + + Map> ret = new HashMap<>(); + for(String attrName : jansPerson.customAttributeNames()) { + ret.put(attrName,jansPerson.customAttributeValues(attrName)); + } + return ret; + } + + @Override + public Stream getRequiredActionsStream() { + + return new ArrayList().stream(); + } + + @Override + public void addRequiredAction(String action) { + throw new ReadOnlyException(USER_READ_ONLY_EXCEPTION_MSG); + } + + @Override + public void removeRequiredAction(String action) { + throw new ReadOnlyException(USER_READ_ONLY_EXCEPTION_MSG); + } + + @Override + public String getFirstName() { + + return jansPerson.customAttributeValue(GIVEN_NAME_ATTR_NAME); + } + + @Override + public void setFirstName(String firstName) { + + throw new ReadOnlyException(USER_READ_ONLY_EXCEPTION_MSG); + } + + @Override + public String getLastName() { + + return null; + } + + @Override + public void setLastName(String lastName) { + + throw new ReadOnlyException(USER_READ_ONLY_EXCEPTION_MSG); + } + + @Override + public String getEmail() { + + return jansPerson.customAttributeValue(MAIL_ATTR_NAME); + } + + @Override + public void setEmail(final String email) { + + throw new ReadOnlyException(USER_READ_ONLY_EXCEPTION_MSG); + } + + @Override + public boolean isEmailVerified() { + + try { + final String attr = jansPerson.customAttributeValue(EMAIL_VERIFIED_ATTR_NAME); + if(attr == null) { + return false; + } + return Boolean.parseBoolean(attr); + }catch(NumberFormatException e) { + return false; + } + } + + @Override + public void setEmailVerified(boolean verified) { + + throw new ReadOnlyException(USER_READ_ONLY_EXCEPTION_MSG); + } + + @Override + public Stream getGroupsStream() { + + return new ArrayList().stream(); + } + + @Override + public long getGroupsCount() { + + return 0; + } + + @Override + public long getGroupsCountByNameContaining(String search) { + + return 0; + } + + @Override + public void joinGroup(GroupModel group) { + + throw new ReadOnlyException(USER_READ_ONLY_EXCEPTION_MSG); + } + + @Override + public void leaveGroup(GroupModel group) { + + throw new ReadOnlyException(USER_READ_ONLY_EXCEPTION_MSG); + } + + @Override + public boolean isMemberOf(GroupModel group) { + + return false; + } + + @Override + public String getFederationLink() { + + return null; + } + + @Override + public void setFederationLink(String link) { + + throw new ReadOnlyException(USER_READ_ONLY_EXCEPTION_MSG); + } + + @Override + public String getServiceAccountClientLink() { + + return null; + } + + @Override + public void setServiceAccountClientLink(String clientInternalId) { + + throw new ReadOnlyException(USER_READ_ONLY_EXCEPTION_MSG); + } + + @Override + public SubjectCredentialManager credentialManager() { + + return null; + } + + @Override + public Stream getRealmRoleMappingsStream() { + + return new ArrayList().stream(); + } + + @Override + public Stream getClientRoleMappingsStream(ClientModel app) { + + return new ArrayList().stream(); + } + + @Override + public boolean hasRole(RoleModel role) { + + return false; + } + + @Override + public void grantRole(RoleModel role) { + + throw new ReadOnlyException(USER_READ_ONLY_EXCEPTION_MSG); + } + + @Override + public Stream getRoleMappingsStream() { + + return new ArrayList().stream(); + } + + @Override + public void deleteRoleMapping(RoleModel role) { + + throw new ReadOnlyException(USER_READ_ONLY_EXCEPTION_MSG); + } + +} diff --git a/jans-keycloak-integration/spi/src/main/java/io/jans/kc/model/internal/JansPerson.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/model/internal/JansPerson.java new file mode 100644 index 00000000000..936ed16b191 --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/model/internal/JansPerson.java @@ -0,0 +1,118 @@ +package io.jans.kc.model.internal; + +import java.io.Serializable; + +import java.util.ArrayList; +import java.util.List; + +import io.jans.orm.annotation.*; +import io.jans.orm.model.base.CustomObjectAttribute; + +@DataEntry +@ObjectClass(value="jansPerson") +public class JansPerson implements Serializable { + + private static final long serialVersionUID = -1L; + + @DN + private String dn; + + @AttributesList(name="name",value="values",multiValued="multiValued") + private List customAttributes = new ArrayList<>(); + + public String getDn() { + + return this.dn; + } + + public void setDn(final String dn) { + + this.dn = dn; + } + + public void setCustomAttributes(List customAttributes) { + + this.customAttributes = customAttributes; + } + + public List getCustomAttributes() { + + return this.customAttributes; + } + + public boolean hasCustomAttributes() { + + return (this.customAttributes != null && !this.customAttributes.isEmpty()); + } + + public boolean isMultiValuedCustomAttributes() { + + return hasCustomAttributes() && this.customAttributes.size() > 1; + } + + public CustomObjectAttribute getCustomObjectAttribute(final String name) { + + for(CustomObjectAttribute customAttribute : customAttributes) { + if(customAttribute.getName().equals(name)) { + return customAttribute; + } + } + return null; + } + + public List customAttributeValues(final String name) { + + + for(CustomObjectAttribute customAttribute : customAttributes) { + if(customAttribute.getName().equals(name)) { + List values = customAttribute.getValues(); + if(values == null || values.isEmpty()) { + return new ArrayList<>(); + } + return convertToString(values); + } + } + return new ArrayList<>(); + } + + public List customAttributeNames() { + + List ret = new ArrayList<>(); + for(CustomObjectAttribute customAttribute : customAttributes) { + ret.add(customAttribute.getName()); + } + return ret; + } + + public String customAttributeValue(final String attributeName) { + + for(CustomObjectAttribute customAttribute : customAttributes) { + if(customAttribute.getName().equals(attributeName)) { + List values = customAttribute.getValues(); + if(values == null || values.isEmpty()) { + return null; + } + List ret = convertToString(values); + if(ret.isEmpty()) { + return null; + } + return ret.get(0); + } + } + + return null; + } + + private List convertToString(List values) { + + List ret = new ArrayList<>(); + for(Object val : values) { + if(val instanceof String strval) { + ret.add(strval); + }else { + ret.add(val.toString()); + } + } + return ret; + } +} \ No newline at end of file diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCAccessToken.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCAccessToken.java similarity index 55% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCAccessToken.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCAccessToken.java index d3585611adc..4d2646ca476 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCAccessToken.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCAccessToken.java @@ -1,4 +1,4 @@ -package io.jans.kc.spi.auth.oidc; +package io.jans.kc.oidc; public interface OIDCAccessToken { diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCAuthRequest.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCAuthRequest.java similarity index 91% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCAuthRequest.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCAuthRequest.java index d362488ac4a..489e7fa5a0f 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCAuthRequest.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCAuthRequest.java @@ -1,4 +1,4 @@ -package io.jans.kc.spi.auth.oidc; +package io.jans.kc.oidc; import java.util.List; import java.util.ArrayList; @@ -17,8 +17,8 @@ public OIDCAuthRequest() { this.clientId = null; this.state = null; this.nonce = null; - this.scopes = new ArrayList(); - this.responseTypes = new ArrayList(); + this.scopes = new ArrayList<>(); + this.responseTypes = new ArrayList<>(); this.redirectUri = null; } diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCMetaCache.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCMetaCache.java similarity index 81% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCMetaCache.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCMetaCache.java index 5db2d94c2be..01d209f1e0a 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCMetaCache.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCMetaCache.java @@ -1,4 +1,4 @@ -package io.jans.kc.spi.auth.oidc; +package io.jans.kc.oidc; public interface OIDCMetaCache { public void put(String issuer, String key , Object value); diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCMetaCacheKeys.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCMetaCacheKeys.java similarity index 71% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCMetaCacheKeys.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCMetaCacheKeys.java index 76671559992..f5b69c39de0 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCMetaCacheKeys.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCMetaCacheKeys.java @@ -1,7 +1,11 @@ -package io.jans.kc.spi.auth.oidc; +package io.jans.kc.oidc; public class OIDCMetaCacheKeys { public static final String AUTHORIZATION_URL = "oidc.authorization.url"; public static final String TOKEN_URL = "oidc.token.url"; public static final String USERINFO_URL = "oidc.userinfo.url"; + + private OIDCMetaCacheKeys() { + //private constructor + } } diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCMetaError.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCMetaError.java similarity index 86% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCMetaError.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCMetaError.java index 1f6cc47323a..1c7de99302d 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCMetaError.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCMetaError.java @@ -1,4 +1,4 @@ -package io.jans.kc.spi.auth.oidc; +package io.jans.kc.oidc; public class OIDCMetaError extends Exception { diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCRefreshToken.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCRefreshToken.java similarity index 56% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCRefreshToken.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCRefreshToken.java index 7aa7a2d5d23..740d2c602b4 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCRefreshToken.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCRefreshToken.java @@ -1,4 +1,4 @@ -package io.jans.kc.spi.auth.oidc; +package io.jans.kc.oidc; public interface OIDCRefreshToken { diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCService.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCService.java similarity index 94% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCService.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCService.java index b30effb139c..1eefabddd03 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCService.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCService.java @@ -1,4 +1,4 @@ -package io.jans.kc.spi.auth.oidc; +package io.jans.kc.oidc; import java.net.URI; diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCTokenError.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCTokenError.java similarity index 94% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCTokenError.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCTokenError.java index d203619aab2..491b28918df 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCTokenError.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCTokenError.java @@ -1,4 +1,4 @@ -package io.jans.kc.spi.auth.oidc; +package io.jans.kc.oidc; public class OIDCTokenError { diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCTokenRequest.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCTokenRequest.java similarity index 96% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCTokenRequest.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCTokenRequest.java index 99836cfe377..15ee313053c 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCTokenRequest.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCTokenRequest.java @@ -1,4 +1,4 @@ -package io.jans.kc.spi.auth.oidc; +package io.jans.kc.oidc; import java.net.URI; diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCTokenRequestError.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCTokenRequestError.java similarity index 87% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCTokenRequestError.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCTokenRequestError.java index 05a96e56896..836ff580eed 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCTokenRequestError.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCTokenRequestError.java @@ -1,4 +1,4 @@ -package io.jans.kc.spi.auth.oidc; +package io.jans.kc.oidc; public class OIDCTokenRequestError extends Exception { diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCTokenResponse.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCTokenResponse.java similarity index 85% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCTokenResponse.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCTokenResponse.java index 98991558aa5..d71d97d86e2 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCTokenResponse.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCTokenResponse.java @@ -1,4 +1,4 @@ -package io.jans.kc.spi.auth.oidc; +package io.jans.kc.oidc; public interface OIDCTokenResponse { diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCUserInfoError.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCUserInfoError.java similarity index 94% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCUserInfoError.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCUserInfoError.java index f5c25d79223..f6afbc738dd 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCUserInfoError.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCUserInfoError.java @@ -1,4 +1,4 @@ -package io.jans.kc.spi.auth.oidc; +package io.jans.kc.oidc; public class OIDCUserInfoError { diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCUserInfoRequestError.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCUserInfoRequestError.java similarity index 87% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCUserInfoRequestError.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCUserInfoRequestError.java index 13d4e9110ef..e234cb6723a 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCUserInfoRequestError.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCUserInfoRequestError.java @@ -1,4 +1,4 @@ -package io.jans.kc.spi.auth.oidc; +package io.jans.kc.oidc; public class OIDCUserInfoRequestError extends Exception { diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCUserInfoResponse.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCUserInfoResponse.java similarity index 84% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCUserInfoResponse.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCUserInfoResponse.java index 7787cd06727..f8778b8e637 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/OIDCUserInfoResponse.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/OIDCUserInfoResponse.java @@ -1,4 +1,4 @@ -package io.jans.kc.spi.auth.oidc; +package io.jans.kc.oidc; public interface OIDCUserInfoResponse { diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/impl/HashBasedOIDCMetaCache.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/impl/HashBasedOIDCMetaCache.java similarity index 69% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/impl/HashBasedOIDCMetaCache.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/impl/HashBasedOIDCMetaCache.java index a04b212d53e..9c1847c16b3 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/impl/HashBasedOIDCMetaCache.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/impl/HashBasedOIDCMetaCache.java @@ -1,17 +1,14 @@ -package io.jans.kc.spi.auth.oidc.impl; +package io.jans.kc.oidc.impl; import java.util.Map; -import org.jboss.logging.Logger; - -import io.jans.kc.spi.auth.oidc.OIDCMetaCache; +import io.jans.kc.oidc.OIDCMetaCache; import java.util.HashMap; public class HashBasedOIDCMetaCache implements OIDCMetaCache{ - private static final Logger log = Logger.getLogger(HashBasedOIDCMetaCache.class); - private static final long DEFAULT_CACHE_TTL = 20*60; // 20 seconds + private static final long DEFAULT_CACHE_TTL = 20*60l; // 20 seconds private long cacheEntryTtl; @@ -28,7 +25,7 @@ public HashBasedOIDCMetaCache(long cacheEntryTtl) { this.cacheEntryTtl = DEFAULT_CACHE_TTL; } this.cacheEntryTtl = this.cacheEntryTtl * 1000; // convert to milliseconds - this.cacheEntries = new HashMap>(); + this.cacheEntries = new HashMap<>(); } @Override @@ -63,42 +60,41 @@ private boolean issuerCacheEntryIsMissing(String issuer) { private Object getIssuerCacheEntryValue(String issuer, String key) { - Map issuer_cache = cacheEntries.get(issuer); - return issuer_cache.get(key).getValue(); + Map issuerCache = cacheEntries.get(issuer); + return issuerCache.get(key).getValue(); } private void createIfNotExistIssuerCacheEntry(String issuer) { - if(!cacheEntries.containsKey(issuer)) { - cacheEntries.put(issuer,new HashMap()); - } + cacheEntries.computeIfAbsent(issuer, k-> new HashMap<>()); } private void addIssuerCacheEntry(String issuer,String key, Object value) { Map issuerCache = cacheEntries.get(issuer); - for(String existingkey : issuerCache.keySet()) { - if(existingkey.equalsIgnoreCase(key)) { + if(issuerCache == null) { + return; + } + + for(Map.Entry entry : issuerCache.entrySet()) { + if(entry.getKey().equalsIgnoreCase(key)) { //update cache entry - CacheEntry cache_entry = issuerCache.get(existingkey); - cache_entry.updateValue(value); + entry.getValue().updateValue(value); return; } } - issuerCache.put(key,new CacheEntry(cacheEntryTtl, value)); } private void performHouseCleaning() { - for(String issuer: cacheEntries.keySet()) { - Map issuer_cache = cacheEntries.get(issuer); - for(String key :issuer_cache.keySet()) { - CacheEntry cache_entry = issuer_cache.get(key); - if(cache_entry.isExpired()) { - issuer_cache.remove(key); - } - } + for(Map.Entry> cacheEntry: cacheEntries.entrySet()) { + Map issuerCache = cacheEntries.get(cacheEntry.getKey()); + for(Map.Entry issuerEntry : issuerCache.entrySet()) { + if(issuerEntry.getValue().isExpired()) { + issuerCache.remove(issuerEntry.getKey()); + } + } } } diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/impl/NimbusOIDCAccessToken.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/impl/NimbusOIDCAccessToken.java similarity index 83% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/impl/NimbusOIDCAccessToken.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/impl/NimbusOIDCAccessToken.java index da2c7bc9a88..4192bf4cfb8 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/impl/NimbusOIDCAccessToken.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/impl/NimbusOIDCAccessToken.java @@ -1,9 +1,9 @@ -package io.jans.kc.spi.auth.oidc.impl; +package io.jans.kc.oidc.impl; import com.nimbusds.oauth2.sdk.token.AccessToken; import com.nimbusds.oauth2.sdk.token.BearerAccessToken; -import io.jans.kc.spi.auth.oidc.OIDCAccessToken; +import io.jans.kc.oidc.OIDCAccessToken; public class NimbusOIDCAccessToken implements OIDCAccessToken { diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/impl/NimbusOIDCRefreshToken.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/impl/NimbusOIDCRefreshToken.java similarity index 63% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/impl/NimbusOIDCRefreshToken.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/impl/NimbusOIDCRefreshToken.java index 7a500071d9e..76ea7434acc 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/impl/NimbusOIDCRefreshToken.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/impl/NimbusOIDCRefreshToken.java @@ -1,8 +1,8 @@ -package io.jans.kc.spi.auth.oidc.impl; +package io.jans.kc.oidc.impl; import com.nimbusds.oauth2.sdk.token.RefreshToken; -import io.jans.kc.spi.auth.oidc.OIDCRefreshToken; +import io.jans.kc.oidc.OIDCRefreshToken; public class NimbusOIDCRefreshToken implements OIDCRefreshToken{ @@ -12,4 +12,8 @@ public NimbusOIDCRefreshToken(RefreshToken refreshToken) { this.refreshToken = refreshToken; } + private RefreshToken refreshTokenRef() { + + return this.refreshToken; + } } diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/impl/NimbusOIDCService.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/impl/NimbusOIDCService.java similarity index 88% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/impl/NimbusOIDCService.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/impl/NimbusOIDCService.java index 10d9e64b1ce..030f611d5e1 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/impl/NimbusOIDCService.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/impl/NimbusOIDCService.java @@ -1,4 +1,4 @@ -package io.jans.kc.spi.auth.oidc.impl; +package io.jans.kc.oidc.impl; import java.io.IOException; @@ -25,28 +25,26 @@ import com.nimbusds.openid.connect.sdk.UserInfoResponse; import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata; -import io.jans.kc.spi.auth.oidc.OIDCAccessToken; -import io.jans.kc.spi.auth.oidc.OIDCAuthRequest; -import io.jans.kc.spi.auth.oidc.OIDCMetaCache; -import io.jans.kc.spi.auth.oidc.OIDCMetaCacheKeys; -import io.jans.kc.spi.auth.oidc.OIDCMetaError; -import io.jans.kc.spi.auth.oidc.OIDCService; -import io.jans.kc.spi.auth.oidc.OIDCTokenRequest; -import io.jans.kc.spi.auth.oidc.OIDCTokenRequestError; -import io.jans.kc.spi.auth.oidc.OIDCTokenResponse; -import io.jans.kc.spi.auth.oidc.OIDCUserInfoRequestError; -import io.jans.kc.spi.auth.oidc.OIDCUserInfoResponse; +import io.jans.kc.oidc.OIDCAccessToken; +import io.jans.kc.oidc.OIDCAuthRequest; +import io.jans.kc.oidc.OIDCMetaCache; +import io.jans.kc.oidc.OIDCMetaCacheKeys; +import io.jans.kc.oidc.OIDCMetaError; +import io.jans.kc.oidc.OIDCService; +import io.jans.kc.oidc.OIDCTokenRequest; +import io.jans.kc.oidc.OIDCTokenRequestError; +import io.jans.kc.oidc.OIDCTokenResponse; +import io.jans.kc.oidc.OIDCUserInfoRequestError; +import io.jans.kc.oidc.OIDCUserInfoResponse; import java.net.URI; import java.net.URISyntaxException; import java.util.List; -import org.jboss.logging.Logger; public class NimbusOIDCService implements OIDCService { - private static final Logger log = Logger.getLogger(NimbusOIDCService.class); - + private OIDCMetaCache metaCache; public NimbusOIDCService(OIDCMetaCache metaCache) { @@ -134,8 +132,8 @@ public OIDCUserInfoResponse requestUserInfo(String issuerUrl, OIDCAccessToken ac BearerAccessToken bearertoken = ((NimbusOIDCAccessToken) accesstoken).asBearerToken(); try { - HTTPResponse http_response = new UserInfoRequest(getUserInfoEndpoint(issuerUrl),bearertoken).toHTTPRequest().send(); - UserInfoResponse userinforesponse = UserInfoResponse.parse(http_response); + HTTPResponse httpResponse = new UserInfoRequest(getUserInfoEndpoint(issuerUrl),bearertoken).toHTTPRequest().send(); + UserInfoResponse userinforesponse = UserInfoResponse.parse(httpResponse); return new NimbusOIDCUserInfoResponse(userinforesponse); } catch (IOException e) { throw new OIDCUserInfoRequestError("I/O error trying to obtain user info",e); @@ -205,9 +203,7 @@ private OIDCProviderMetadata obtainMetadataFromServer(String issuerUrl) throws O try { Issuer issuer = new Issuer(issuerUrl); return OIDCProviderMetadata.resolve(issuer); - }catch(GeneralException e) { - throw new OIDCMetaError("Could not obtain metadata from server",e); - }catch(IOException e) { + }catch(GeneralException | IOException e) { throw new OIDCMetaError("Could not obtain metadata from server",e); } } diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/impl/NimbusOIDCTokenResponse.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/impl/NimbusOIDCTokenResponse.java similarity index 83% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/impl/NimbusOIDCTokenResponse.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/impl/NimbusOIDCTokenResponse.java index 569a6683800..6f9ddd249b9 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/impl/NimbusOIDCTokenResponse.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/impl/NimbusOIDCTokenResponse.java @@ -1,13 +1,13 @@ -package io.jans.kc.spi.auth.oidc.impl; +package io.jans.kc.oidc.impl; import com.nimbusds.oauth2.sdk.AccessTokenResponse; import com.nimbusds.oauth2.sdk.TokenResponse; import com.nimbusds.oauth2.sdk.token.Tokens; -import io.jans.kc.spi.auth.oidc.OIDCAccessToken; -import io.jans.kc.spi.auth.oidc.OIDCRefreshToken; -import io.jans.kc.spi.auth.oidc.OIDCTokenError; -import io.jans.kc.spi.auth.oidc.OIDCTokenResponse; +import io.jans.kc.oidc.OIDCAccessToken; +import io.jans.kc.oidc.OIDCRefreshToken; +import io.jans.kc.oidc.OIDCTokenError; +import io.jans.kc.oidc.OIDCTokenResponse; public class NimbusOIDCTokenResponse implements OIDCTokenResponse { @@ -25,8 +25,6 @@ public NimbusOIDCTokenResponse(TokenResponse tokenResponse) { Tokens tokens = atresponse.getTokens(); this.accessToken = new NimbusOIDCAccessToken(tokens.getAccessToken()); this.refreshToken = new NimbusOIDCRefreshToken(tokens.getRefreshToken()); - }else { - } } diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/impl/NimbusOIDCUserInfoResponse.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/impl/NimbusOIDCUserInfoResponse.java similarity index 89% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/impl/NimbusOIDCUserInfoResponse.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/impl/NimbusOIDCUserInfoResponse.java index db78425dd9d..ca8ae2b0afa 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/oidc/impl/NimbusOIDCUserInfoResponse.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/oidc/impl/NimbusOIDCUserInfoResponse.java @@ -1,9 +1,9 @@ -package io.jans.kc.spi.auth.oidc.impl; +package io.jans.kc.oidc.impl; import com.nimbusds.openid.connect.sdk.UserInfoResponse; -import io.jans.kc.spi.auth.oidc.OIDCUserInfoError; -import io.jans.kc.spi.auth.oidc.OIDCUserInfoResponse; +import io.jans.kc.oidc.OIDCUserInfoError; +import io.jans.kc.oidc.OIDCUserInfoResponse; public class NimbusOIDCUserInfoResponse implements OIDCUserInfoResponse { diff --git a/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/JansSpiInitException.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/JansSpiInitException.java new file mode 100644 index 00000000000..36ff90e4489 --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/JansSpiInitException.java @@ -0,0 +1,12 @@ +package io.jans.kc.spi; + +public class JansSpiInitException extends RuntimeException { + + public JansSpiInitException(final String msg) { + super(msg); + } + + public JansSpiInitException(final String msg, Throwable cause) { + super(msg,cause); + } +} diff --git a/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/ProviderIDs.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/ProviderIDs.java new file mode 100644 index 00000000000..e349ecd6808 --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/ProviderIDs.java @@ -0,0 +1,13 @@ +package io.jans.kc.spi; + +public class ProviderIDs { + public static final String JANS_AUTHENTICATOR_PROVIDER = "kc-jans-authn"; + public static final String JANS_AUTH_RESPONSE_REST_PROVIDER = "kc-jans-authn-rest-bridge"; + public static final String JANS_SAML_USER_ATTRIBUTE_MAPPER_PROVIDER = "kc-jans-saml-user-attribute-mapper"; + public static final String JANS_DEFAULT_THIN_BRIDGE_PROVIDER = "kc-jans-thin-bridge-default"; + public static final String JANS_USER_STORAGE_PROVIDER = "kc-jans-user-storage"; + + private ProviderIDs() { + //private constructor + } +} \ No newline at end of file diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/JansAuthenticator.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/auth/JansAuthenticator.java similarity index 85% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/JansAuthenticator.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/auth/JansAuthenticator.java index 1b583fc0ba3..c59bbe8665e 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/JansAuthenticator.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/auth/JansAuthenticator.java @@ -2,15 +2,12 @@ import java.io.UnsupportedEncodingException; import java.net.URI; -import java.net.URISyntaxException; import java.net.URLDecoder; - -import java.text.MessageFormat; +import java.security.SecureRandom; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Random; import java.util.ArrayList; import jakarta.ws.rs.core.Response; @@ -29,16 +26,16 @@ import org.keycloak.models.utils.KeycloakModelUtils; import io.jans.kc.spi.ProviderIDs; -import io.jans.kc.spi.auth.oidc.OIDCAuthRequest; -import io.jans.kc.spi.auth.oidc.OIDCMetaError; -import io.jans.kc.spi.auth.oidc.OIDCService; -import io.jans.kc.spi.auth.oidc.OIDCTokenError; -import io.jans.kc.spi.auth.oidc.OIDCTokenRequest; -import io.jans.kc.spi.auth.oidc.OIDCTokenRequestError; -import io.jans.kc.spi.auth.oidc.OIDCTokenResponse; -import io.jans.kc.spi.auth.oidc.OIDCUserInfoError; -import io.jans.kc.spi.auth.oidc.OIDCUserInfoRequestError; -import io.jans.kc.spi.auth.oidc.OIDCUserInfoResponse; +import io.jans.kc.oidc.OIDCAuthRequest; +import io.jans.kc.oidc.OIDCMetaError; +import io.jans.kc.oidc.OIDCService; +import io.jans.kc.oidc.OIDCTokenError; +import io.jans.kc.oidc.OIDCTokenRequest; +import io.jans.kc.oidc.OIDCTokenRequestError; +import io.jans.kc.oidc.OIDCTokenResponse; +import io.jans.kc.oidc.OIDCUserInfoError; +import io.jans.kc.oidc.OIDCUserInfoRequestError; +import io.jans.kc.oidc.OIDCUserInfoResponse; public class JansAuthenticator implements Authenticator { @@ -73,8 +70,16 @@ public void authenticate(AuthenticationFlowContext context) { return; } + Response response = null; try { URI redirecturi = createRedirectUri(context); + + if(redirecturi == null) { + log.error("Invalid redirect URI"); + response = context.form().createForm(JANS_AUTH_ERROR_FTL); + context.failure(AuthenticationFlowError.INTERNAL_ERROR,response); + return; + } URI actionuri = createActionUrl(context); String state = generateOIDCState(); @@ -85,7 +90,7 @@ public void authenticate(AuthenticationFlowContext context) { URI loginurl = oidcService.createAuthorizationUrl(config.normalizedIssuerUrl(), oidcauthrequest); URI loginurlnoparams = UriBuilder.fromUri(loginurl.toString()).replaceQuery(null).build(); - Response response = context + response = context .form() .setActionUri(actionuri) .setAttribute(JANS_LOGIN_URL_ATTRIBUTE,loginurlnoparams.toString()) @@ -99,7 +104,7 @@ public void authenticate(AuthenticationFlowContext context) { context.challenge(response); }catch(OIDCMetaError e) { log.errorv(e,"OIDC Error obtaining the authorization url"); - Response response = context.form().createForm(JANS_AUTH_ERROR_FTL); + response = context.form().createForm(JANS_AUTH_ERROR_FTL); context.failure(AuthenticationFlowError.INTERNAL_ERROR,response); } } @@ -113,14 +118,14 @@ public void action(AuthenticationFlowContext context) { return; } - String openid_code = getOpenIdCode(context); - if(openid_code == null) { + String openidCode = getOpenIdCode(context); + if(openidCode == null) { log.errorv("Missing authentication code during response processing"); context.failure(AuthenticationFlowError.INTERNAL_ERROR,onMissingAuthenticationCode(context)); return; } - OIDCTokenRequest tokenrequest = createTokenRequest(config, openid_code, createRedirectUri(context)); + OIDCTokenRequest tokenrequest = createTokenRequest(config, openidCode, createRedirectUri(context)); try { OIDCTokenResponse tokenresponse = oidcService.requestTokens(config.normalizedIssuerUrl(), tokenrequest); if(!tokenresponse.indicatesSuccess()) { @@ -170,20 +175,19 @@ public boolean configuredFor(KeycloakSession session, RealmModel realm, UserMode @Override public void setRequiredActions(KeycloakSession session, RealmModel model, UserModel user) { - - return; + //for now no required actions to specify } @Override public List getRequiredActions(KeycloakSession session) { - return null; + return new ArrayList<>(); } @Override public void close() { - return; + // nothing to do for now when then authenticator is shutdown } private Configuration extractAndValidateConfiguration(AuthenticationFlowContext context) { @@ -230,7 +234,7 @@ private UserModel findUserByNameOrEmail(AuthenticationFlowContext context, Strin private Map parseQueryParameters(String params) { - Map ret = new HashMap(); + Map ret = new HashMap<>(); if(params == null) { return ret; } @@ -260,20 +264,20 @@ private Configuration pluginConfigurationFromContext(AuthenticationFlowContext c return null; } - String server_url = config.getConfig().get(JansAuthenticatorConfigProp.SERVER_URL.getName()); - String client_id = config.getConfig().get(JansAuthenticatorConfigProp.CLIENT_ID.getName()); - String client_secret = config.getConfig().get(JansAuthenticatorConfigProp.CLIENT_SECRET.getName()); + String serverUrl = config.getConfig().get(JansAuthenticatorConfigProp.SERVER_URL.getName()); + String clientId = config.getConfig().get(JansAuthenticatorConfigProp.CLIENT_ID.getName()); + String clientSecret = config.getConfig().get(JansAuthenticatorConfigProp.CLIENT_SECRET.getName()); String issuer = config.getConfig().get(JansAuthenticatorConfigProp.ISSUER.getName()); - String extra_scopes = config.getConfig().get(JansAuthenticatorConfigProp.EXTRA_SCOPES.getName()); - List parsed_extra_scopes = new ArrayList<>(); - if(extra_scopes != null) { - String [] tokens = extra_scopes.split("\\s*,\\s*"); + String extraScopes = config.getConfig().get(JansAuthenticatorConfigProp.EXTRA_SCOPES.getName()); + List parsedExtraScopes = new ArrayList<>(); + if(extraScopes != null) { + String [] tokens = extraScopes.split(","); for(String token : tokens) { - parsed_extra_scopes.add(token); + parsedExtraScopes.add(token.trim()); } } - return new Configuration(server_url,client_id,client_secret,issuer,parsed_extra_scopes); + return new Configuration(serverUrl,clientId,clientSecret,issuer,parsedExtraScopes); } private final String generateOIDCState() { @@ -302,7 +306,7 @@ private String generateRandomString(int length) { int leftlimit = 48; int rightlimit = 122; - return new Random().ints(leftlimit,rightlimit+1) + return new SecureRandom().ints(leftlimit,rightlimit+1) .filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97)) .limit(length) .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) @@ -364,7 +368,7 @@ public static class ValidationResult { public void addError(String error) { if(errors == null) { - this.errors = new ArrayList(); + this.errors = new ArrayList<>(); } this.errors.add(error); } @@ -418,18 +422,18 @@ public ValidationResult validate() { public String normalizedIssuerUrl() { - String effective_url = issuerUrl; - if(effective_url == null) { - effective_url = serverUrl; + String effectiveUrl = issuerUrl; + if(effectiveUrl == null) { + effectiveUrl = serverUrl; } - if(effective_url == null) { + if(effectiveUrl == null) { return null; } - if(effective_url.charAt(effective_url.length() -1) == '/') { - return effective_url.substring(0, effective_url.length() -1); + if(effectiveUrl.charAt(effectiveUrl.length() -1) == '/') { + return effectiveUrl.substring(0, effectiveUrl.length() -1); } - return effective_url; + return effectiveUrl; } } diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/JansAuthenticatorConfigProp.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/auth/JansAuthenticatorConfigProp.java similarity index 100% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/JansAuthenticatorConfigProp.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/auth/JansAuthenticatorConfigProp.java diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/JansAuthenticatorFactory.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/auth/JansAuthenticatorFactory.java similarity index 89% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/JansAuthenticatorFactory.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/auth/JansAuthenticatorFactory.java index 6a49c9cba92..eca25248403 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/JansAuthenticatorFactory.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/auth/JansAuthenticatorFactory.java @@ -16,10 +16,10 @@ import org.keycloak.provider.ProviderConfigProperty; import io.jans.kc.spi.ProviderIDs; -import io.jans.kc.spi.auth.oidc.OIDCMetaCache; -import io.jans.kc.spi.auth.oidc.OIDCService; -import io.jans.kc.spi.auth.oidc.impl.HashBasedOIDCMetaCache; -import io.jans.kc.spi.auth.oidc.impl.NimbusOIDCService; +import io.jans.kc.oidc.OIDCMetaCache; +import io.jans.kc.oidc.OIDCService; +import io.jans.kc.oidc.impl.HashBasedOIDCMetaCache; +import io.jans.kc.oidc.impl.NimbusOIDCService; public class JansAuthenticatorFactory implements AuthenticatorFactory { @@ -59,19 +59,19 @@ public Authenticator create(KeycloakSession session) { @Override public void init(Config.Scope config) { - return; + //nothing to do for now during initialization } @Override public void close() { - return; + //nothing to do for now during shutdown } @Override public void postInit(KeycloakSessionFactory factory) { - return; + //nothing to do postInit } @Override diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/SessionAttributes.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/auth/SessionAttributes.java similarity index 85% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/SessionAttributes.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/auth/SessionAttributes.java index e7b0331939f..0b6c6a1dd05 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/auth/SessionAttributes.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/auth/SessionAttributes.java @@ -7,4 +7,8 @@ public class SessionAttributes { public static final String KC_ACTION_URI = "kc.action-uri"; public static final String JANS_OIDC_CODE = "jans.oidc.code"; public static final String JANS_SESSION_STATE = "jans.session.state"; + + private SessionAttributes() { + //private constructor + } } diff --git a/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/JansThinBridgeInitException.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/JansThinBridgeInitException.java new file mode 100644 index 00000000000..b533077c411 --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/JansThinBridgeInitException.java @@ -0,0 +1,12 @@ +package io.jans.kc.spi.custom; + +public class JansThinBridgeInitException extends RuntimeException { + + public JansThinBridgeInitException(final String msg) { + super(msg); + } + + public JansThinBridgeInitException(final String msg, Throwable cause) { + super(msg,cause); + } +} diff --git a/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/JansThinBridgeOperationException.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/JansThinBridgeOperationException.java new file mode 100644 index 00000000000..7a550dc68b5 --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/JansThinBridgeOperationException.java @@ -0,0 +1,13 @@ +package io.jans.kc.spi.custom; + + +public class JansThinBridgeOperationException extends RuntimeException { + + public JansThinBridgeOperationException(final String msg) { + super(msg); + } + + public JansThinBridgeOperationException(final String msg, Throwable cause) { + super(msg,cause); + } +} diff --git a/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/JansThinBridgeProvider.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/JansThinBridgeProvider.java new file mode 100644 index 00000000000..c84d11d7c09 --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/JansThinBridgeProvider.java @@ -0,0 +1,15 @@ +package io.jans.kc.spi.custom; + +import org.keycloak.provider.*; + +import io.jans.kc.model.JansUserAttributeModel; +import io.jans.kc.model.internal.JansPerson; + + +public interface JansThinBridgeProvider extends Provider { + + JansUserAttributeModel getUserAttribute(final String kcLoginUsername, final String attributeName); + JansPerson getJansUserByUsername(final String username); + JansPerson getJansUserByEmail(final String email); + JansPerson getJansUserByInum(final String inum); +} diff --git a/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/JansThinBridgeProviderFactory.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/JansThinBridgeProviderFactory.java new file mode 100644 index 00000000000..accf3b20de2 --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/JansThinBridgeProviderFactory.java @@ -0,0 +1,8 @@ +package io.jans.kc.spi.custom; + +import org.keycloak.provider.*; + +public interface JansThinBridgeProviderFactory extends ProviderFactory { + + +} diff --git a/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/JansThinBridgeSpi.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/JansThinBridgeSpi.java new file mode 100644 index 00000000000..67b3a471ffa --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/JansThinBridgeSpi.java @@ -0,0 +1,31 @@ +package io.jans.kc.spi.custom; + +import org.keycloak.provider.*; + +public class JansThinBridgeSpi implements Spi { + + private static final String SPI_NAME = "kc-jans-thin-bridge"; + @Override + public boolean isInternal() { + + return false; + } + + @Override + public String getName() { + + return SPI_NAME; + } + + @Override + public Class getProviderClass() { + + return JansThinBridgeProvider.class; + } + + @Override + public Class getProviderFactoryClass() { + + return JansThinBridgeProviderFactory.class; + } +} diff --git a/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/impl/DefaultJansThinBridgeProvider.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/impl/DefaultJansThinBridgeProvider.java new file mode 100644 index 00000000000..40087ffd5a7 --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/impl/DefaultJansThinBridgeProvider.java @@ -0,0 +1,146 @@ +package io.jans.kc.spi.custom.impl; + +import java.util.List; + +import io.jans.kc.model.JansUserAttributeModel; +import io.jans.kc.model.internal.JansPerson; +import io.jans.kc.spi.custom.JansThinBridgeProvider; +import io.jans.kc.spi.custom.JansThinBridgeOperationException; + +import io.jans.model.JansAttribute; +import io.jans.orm.PersistenceEntryManager; +import io.jans.orm.search.filter.Filter; + +import org.jboss.logging.Logger; + + +public class DefaultJansThinBridgeProvider implements JansThinBridgeProvider { + + private static final String JANS_ATTRIBUTES_ROOT_DN = "ou=attributes,o=jans"; + private static final String JANS_PEOPLE_ROOT_DN = "ou=people,o=jans"; + private static final String UID_ATTR_NAME = "uid"; + private static final String MAIL_ATTR_NAME = "mail"; + private static final String INUM_ATTR_NAME = "inum"; + private static final Logger log = Logger.getLogger(DefaultJansThinBridgeProvider.class); + private static final String [] defaultUserReturnAttributes = new String [] { + "uid","mail","displayName","givenName","inum","sn", "cn", + "jansCreationTimestamp", "jansLastLogonTime","updatedAt", "jansStatus" + }; + + private final PersistenceEntryManager persistenceEntryManager; + + public DefaultJansThinBridgeProvider(final PersistenceEntryManager persistenceEntryManager) { + + this.persistenceEntryManager = persistenceEntryManager; + } + + @Override + public void close() { + //for now , nothing to do during the close of the provider + } + + @Override + public JansUserAttributeModel getUserAttribute(final String kcLoginUsername, final String attributeName) { + + try { + + String [] jansAttrReturnAttributes = new String [] { + "displayName","jansAttrTyp","jansClaimName", + "jansSAML1URI","jansSAML2URI","jansStatus", "jansAttrName" + }; + + final JansAttribute jansAttr = findAttributeByName(attributeName,jansAttrReturnAttributes); + if(jansAttr == null) { + return null; + } + + String [] jansPersonReturnAttributes = new String [] { + attributeName + }; + final JansPerson jansPerson = findPersonByKcLoginUsername(kcLoginUsername, jansPersonReturnAttributes); + if(jansPerson == null) { + return null; + } + + return new JansUserAttributeModel(jansAttr,jansPerson); + + }catch(Exception e) { + throw new JansThinBridgeOperationException("Could not get attributes for user " + kcLoginUsername,e); + } + } + + @Override + public JansPerson getJansUserByUsername(final String username) { + + try { + final Filter uidSearchFilter = Filter.createEqualityFilter(UID_ATTR_NAME,username); + final JansPerson person = findPerson(uidSearchFilter,defaultUserReturnAttributes); + if(person == null) { + log.debugv("User with uid {0} not found in janssen",username); + return null; + } + log.debugv("User with uid {0} was found in janssen",username); + return person; + }catch(Exception e) { + throw new JansThinBridgeOperationException("Error fetching jans user with username " + username,e); + } + } + + @Override + public JansPerson getJansUserByEmail(final String email) { + + try { + final Filter mailSearchFilter = Filter.createEqualityFilter(MAIL_ATTR_NAME, email); + final JansPerson person = findPerson(mailSearchFilter,defaultUserReturnAttributes); + if(person == null) { + log.debugv("User with email {0} not found in janssen",email); + return null; + } + log.debugv("User with email {0} was found in janssen",email); + return person; + }catch(Exception e) { + throw new JansThinBridgeOperationException("Error fetching jans user with email " + email ,e); + } + } + + @Override + public JansPerson getJansUserByInum(final String inum) { + + try { + final Filter inumSearchFilter = Filter.createEqualityFilter(INUM_ATTR_NAME,inum); + final JansPerson person = findPerson(inumSearchFilter,defaultUserReturnAttributes); + if(person == null) { + log.debugv("User with inum not found in janssen",inum); + return null; + } + log.debugv("User with inum {0} found in janssen",inum); + return person; + }catch(Exception e) { + throw new JansThinBridgeOperationException("Error fetching jans user with inum "+inum,e); + } + } + + private JansAttribute findAttributeByName(final String attributeName, final String [] returnAttributes) { + + final Filter searchFilter = Filter.createEqualityFilter("jansAttrName", attributeName); + List searchresult = persistenceEntryManager.findEntries(JANS_ATTRIBUTES_ROOT_DN, + JansAttribute.class,searchFilter,returnAttributes); + + return (searchresult.isEmpty() ? null: searchresult.get(0)); + } + + private JansPerson findPersonByKcLoginUsername(final String kcLoginUsername,final String [] returnAttributes) { + + final Filter uidSearchFilter = Filter.createEqualityFilter(UID_ATTR_NAME,kcLoginUsername); + final Filter mailSearchFilter = Filter.createEqualityFilter(MAIL_ATTR_NAME,kcLoginUsername); + final Filter searchFilter = Filter.createORFilter(uidSearchFilter,mailSearchFilter); + + return findPerson(searchFilter,returnAttributes); + } + + private JansPerson findPerson(final Filter searchFilter, final String [] returnAttributes) { + + List searchresult = persistenceEntryManager.findEntries(JANS_PEOPLE_ROOT_DN,JansPerson.class,searchFilter,returnAttributes); + return (searchresult.isEmpty() ? null: searchresult.get(0)); + } +} diff --git a/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/impl/DefaultJansThinBridgeProviderFactory.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/impl/DefaultJansThinBridgeProviderFactory.java new file mode 100644 index 00000000000..3565a88a24f --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/custom/impl/DefaultJansThinBridgeProviderFactory.java @@ -0,0 +1,132 @@ +package io.jans.kc.spi.custom.impl; + + + +import io.jans.kc.spi.custom.JansThinBridgeProvider; +import io.jans.kc.spi.custom.JansThinBridgeProviderFactory; +import io.jans.kc.spi.ProviderIDs; +import io.jans.kc.spi.custom.JansThinBridgeInitException; +import io.jans.orm.model.PersistenceConfiguration; +import io.jans.orm.PersistenceEntryManager; +import io.jans.orm.PersistenceEntryManagerFactory; +import io.jans.orm.service.PersistanceFactoryService; +import io.jans.orm.service.StandalonePersistanceFactoryService; +import io.jans.orm.util.properties.FileConfiguration; +import io.jans.util.security.PropertiesDecrypter; +import io.jans.util.security.StringEncrypter; + +import java.io.File; +import java.util.Properties; + +import org.apache.commons.lang.StringUtils; + +import org.jboss.logging.Logger; + +import org.keycloak.Config; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; + + + +public class DefaultJansThinBridgeProviderFactory implements JansThinBridgeProviderFactory { + + + private static final Logger log = Logger.getLogger(DefaultJansThinBridgeProviderFactory.class); + private static final String DEFAULT_CONFIG_FILENAME = "jans.properties"; + private static final String SALT_FILENAME = "salt"; + private static final String PROVIDER_ID = ProviderIDs.JANS_DEFAULT_THIN_BRIDGE_PROVIDER; + + private final PersistanceFactoryService persistenceFactoryService; + private final PersistenceConfiguration persistenceConfiguration; + private final PersistenceEntryManager persistenceEntryManager; + + + + public DefaultJansThinBridgeProviderFactory() { + + log.info("Establishing connection with janssen database"); + persistenceFactoryService = new StandalonePersistanceFactoryService(); + persistenceConfiguration = persistenceFactoryService.loadPersistenceConfiguration(DEFAULT_CONFIG_FILENAME); + if(persistenceConfiguration == null) { + throw new JansThinBridgeInitException("Failed to load persistence configuration from file. " + + "\n+ Jans configuration base directory: " + getJansConfigurationBaseDir() + + "\n+ Jans configuration default file: " + DEFAULT_CONFIG_FILENAME); + } + + String confdir = persistenceConfiguration.getConfiguration().getString("confDir"); + if(!StringUtils.isNotBlank(confdir)) { + confdir = getJansConfigurationBaseDir() + File.separator + "conf" + File.separator; + } + final String salt = cryptographicSaltFromFile(confdir + SALT_FILENAME); + if(!StringUtils.isNotBlank(salt)) { + throw new JansThinBridgeInitException("Failed to load cryptographic material from configuration"); + } + persistenceEntryManager = createPersistenceEntryManager(persistenceConfiguration, persistenceFactoryService, salt); + log.info("Connection established to janssen database"); + } + + @Override + public JansThinBridgeProvider create(KeycloakSession session) { + + return new DefaultJansThinBridgeProvider(persistenceEntryManager); + } + + + @Override + public void init(Config.Scope config) { + //nothing to do during init for now + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + //nothing to do during postInit for now + } + + @Override + public void close() { + //nothing to do during cost for now + } + + @Override + public String getId() { + + return PROVIDER_ID; + } + + private final String cryptographicSaltFromFile(final String path) { + + FileConfiguration cryptoconfig = new FileConfiguration(path); + return cryptoconfig.getString("encodeSalt"); + } + + private final Properties preparePersistenceProperties(final PersistenceConfiguration persistenceConfiguration, final String salt) { + + try { + FileConfiguration config = persistenceConfiguration.getConfiguration(); + Properties connprops = config.getProperties(); + return PropertiesDecrypter.decryptAllProperties(StringEncrypter.defaultInstance(),connprops,salt); + }catch(StringEncrypter.EncryptionException e) { + throw new JansThinBridgeInitException("Failed to decrypt persistence connection parameters",e); + } + } + + private final PersistenceEntryManager createPersistenceEntryManager(final PersistenceConfiguration config, + final PersistanceFactoryService persistenceFactoryService, final String salt) { + + Properties persistconnprops = preparePersistenceProperties(config,salt); + PersistenceEntryManagerFactory peManagerFactory = persistenceFactoryService.getPersistenceEntryManagerFactory(config); + return peManagerFactory.createEntryManager(persistconnprops); + } + + private final String getJansConfigurationBaseDir() { + + if( System.getProperty("jans.base") !=null ) { + return System.getProperty("jans.base"); + }else if( (System.getProperty("catalina.base") != null) && (System.getProperty("catalina.base.ignore") == null) ) { + return System.getProperty("catalina.base"); + }else if( System.getProperty("catalina.home") != null ) { + return System.getProperty("catalina.home"); + } + return null; + } +} diff --git a/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/protocol/mapper/saml/JansSamlUserAttributeMapper.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/protocol/mapper/saml/JansSamlUserAttributeMapper.java new file mode 100644 index 00000000000..936c6c25b5f --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/protocol/mapper/saml/JansSamlUserAttributeMapper.java @@ -0,0 +1,133 @@ +package io.jans.kc.spi.protocol.mapper.saml; + +import io.jans.kc.model.JansUserAttributeModel; +import io.jans.kc.spi.ProviderIDs; +import io.jans.kc.spi.custom.JansThinBridgeOperationException; +import io.jans.kc.spi.custom.JansThinBridgeProvider; + +import java.util.List; + +import org.jboss.logging.Logger; + +import org.keycloak.Config; + +import org.keycloak.models.AuthenticatedClientSessionModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.ProtocolMapperModel; +import org.keycloak.models.UserSessionModel; + +import org.keycloak.dom.saml.v2.assertion.AttributeStatementType; +import org.keycloak.dom.saml.v2.assertion.AttributeType; + +import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.provider.ProviderConfigurationBuilder; + +import org.keycloak.protocol.saml.mappers.AttributeStatementHelper; +import org.keycloak.protocol.saml.mappers.AbstractSAMLProtocolMapper; +import org.keycloak.protocol.saml.mappers.SAMLAttributeStatementMapper; + +public class JansSamlUserAttributeMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper { + + private static final String DISPLAY_TYPE = "Janssen User Attribute"; + private static final String DISPLAY_CATEGORY = AttributeStatementHelper.ATTRIBUTE_STATEMENT_CATEGORY; + private static final String HELP_TEXT = "Maps a Janssen User's Attribute to a SAML Attribute"; + + private static final String PROVIDER_ID = ProviderIDs.JANS_SAML_USER_ATTRIBUTE_MAPPER_PROVIDER; + //properties + private static final String JANS_ATTR_NAME_PROP_NAME = "jans.attribute.name"; + private static final String JANS_ATTR_NAME_PROP_LABEL = "Jans Attribute"; + private static final String JANS_ATTR_NAME_PROP_HELPTEXT = "Name of the Attribute in Janssen Auth Server"; + private static final List configProperties; + + private static final Logger log = Logger.getLogger(JansSamlUserAttributeMapper.class); + + static { + configProperties = ProviderConfigurationBuilder.create() + .property() + .name(JANS_ATTR_NAME_PROP_NAME) + .label(JANS_ATTR_NAME_PROP_LABEL) + .helpText(JANS_ATTR_NAME_PROP_HELPTEXT) + .type(ProviderConfigProperty.STRING_TYPE) + .defaultValue(null) + .required(true) + .add() + .build(); + } + + @Override + public void init(Config.Scope scope) { + //nothing for now to do in init + } + + @Override + public void close() { + //nothing for now to do in close + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + //nothing to do for now in postInit + } + + @Override + public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, + UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) { + + try { + final JansThinBridgeProvider jansThinBridge = session.getProvider(JansThinBridgeProvider.class); + final String attributeName = mappingModel.getConfig().get(JANS_ATTR_NAME_PROP_NAME); + final String loginUsername = userSession.getLoginUsername(); + final JansUserAttributeModel userAttribute = jansThinBridge.getUserAttribute(loginUsername,attributeName); + if(userAttribute == null) { + log.info("Could not find jans attribute information for user " + loginUsername); + return; + } + if(!userAttribute.isActive()) { + log.info("Jans attribute " + attributeName + " is not active"); + return; + } + AttributeType keycloakAttribute = userAttribute.asSamlKeycloakAttribute(); + if(keycloakAttribute == null) { + log.info("Could not convert jans attribute " + attributeName + " into a keycloak attribute"); + return; + } + attributeStatement.addAttribute(new AttributeStatementType.ASTChoiceType(keycloakAttribute)); + }catch(JansThinBridgeOperationException e) { + log.error("Error mapping saml attribute from jans",e); + } + + } + + @Override + public List getConfigProperties() { + + return configProperties; + } + + @Override + public String getId() { + + return PROVIDER_ID; + } + + @Override + public String getDisplayType() { + + return DISPLAY_TYPE; + } + + @Override + public String getDisplayCategory() { + + return DISPLAY_CATEGORY; + } + + @Override + public String getHelpText() { + + return HELP_TEXT; + } + + +} diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProvider.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProvider.java similarity index 91% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProvider.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProvider.java index 478a4d59fa6..8e6bde77b78 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProvider.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProvider.java @@ -48,7 +48,7 @@ public Object getResource() { @Override public void close() { - + //nothing to do for now } @GET @@ -91,10 +91,10 @@ private final boolean realmHasActionUri(RealmModel realm) { return (actionuri != null); } - private final void saveAuthResultInRealm(RealmModel realm, String code, String session_state) { + private final void saveAuthResultInRealm(RealmModel realm, String code, String sessionState) { realm.setAttribute(SessionAttributes.JANS_OIDC_CODE,code); - realm.setAttribute(SessionAttributes.JANS_SESSION_STATE,session_state); + realm.setAttribute(SessionAttributes.JANS_SESSION_STATE,sessionState); } private final Response createResponseWithForm(String formtemplate,Map attributes) { @@ -102,8 +102,8 @@ private final Response createResponseWithForm(String formtemplate,Map attrEntry: attributes.entrySet()) { + lfp.setAttribute(attrEntry.getKey(),attrEntry.getValue()); } } return lfp.createForm(formtemplate); @@ -111,14 +111,14 @@ private final Response createResponseWithForm(String formtemplate,Map attributes = new HashMap(); + Map attributes = new HashMap<>(); attributes.put(ERR_MSG_TPL_PARAM,errmsgid); return createResponseWithForm(JANS_AUTH_RESPONSE_ERR_FTL,attributes); } private final Response createFinalizeAuthResponse(String actionuri) { - Map attributes = new HashMap(); + Map attributes = new HashMap<>(); attributes.put(ACTION_URI_TPL_PARAM,actionuri); return createResponseWithForm(JANS_AUTH_RESPONSE_COMPLETE_FTL, attributes); } diff --git a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProviderFactory.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProviderFactory.java similarity index 92% rename from jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProviderFactory.java rename to jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProviderFactory.java index 8384f1bf782..6c93fc96f04 100644 --- a/jans-keycloak-integration/authenticator/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProviderFactory.java +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/rest/JansAuthResponseResourceProviderFactory.java @@ -32,11 +32,11 @@ public void init(Scope config) { @Override public void postInit(KeycloakSessionFactory factory) { - + //nothing to do here post init } @Override public void close() { - + //nothing to do here on close } } \ No newline at end of file diff --git a/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/storage/JansUserStorageProvider.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/storage/JansUserStorageProvider.java new file mode 100644 index 00000000000..4b065273030 --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/storage/JansUserStorageProvider.java @@ -0,0 +1,91 @@ +package io.jans.kc.spi.storage; + +import org.keycloak.component.ComponentModel; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; + +import org.keycloak.storage.StorageId; +import org.keycloak.storage.UserStorageProvider; +import org.keycloak.storage.user.UserLookupProvider; + +import io.jans.kc.model.JansUserModel; +import io.jans.kc.model.internal.JansPerson; +import io.jans.kc.spi.custom.JansThinBridgeOperationException; +import io.jans.kc.spi.custom.JansThinBridgeProvider; + +import org.jboss.logging.Logger; + +public class JansUserStorageProvider implements UserStorageProvider, UserLookupProvider { + + private static final Logger log = Logger.getLogger(JansUserStorageProvider.class); + + private final JansThinBridgeProvider jansThinBridge; + private final ComponentModel model; + private final KeycloakSession session; + + public JansUserStorageProvider(KeycloakSession session,ComponentModel model, JansThinBridgeProvider jansThinBridge) { + + this.session = session; + this.model = model; + this.jansThinBridge = jansThinBridge; + } + + @Override + public void close() { + + } + + @Override + public UserModel getUserByUsername(RealmModel realm, String username) { + + try { + log.infov("getUserByUsername(). Username: {0}",username); + JansPerson person = jansThinBridge.getJansUserByUsername(username); + if(person != null) { + return new JansUserModel(model,person); + } + return null; + }catch(JansThinBridgeOperationException e) { + log.errorv(e,"Error fetching jans user with username " + username); + return null; + } + } + + @Override + public UserModel getUserByEmail(RealmModel realm, String email) { + + try { + log.infov("getUserByEmail(). Email : {0}",email); + JansPerson person = jansThinBridge.getJansUserByEmail(email); + if(person != null) { + return new JansUserModel(model,person); + } + return null; + }catch(JansThinBridgeOperationException e) { + log.errorv(e,"Error fetching jans user with email " + email); + return null; + } + } + + @Override + public UserModel getUserById(RealmModel realm, String id) { + + try { + log.infov("getUserById(). Id: {0}",id); + StorageId storageId = new StorageId(id); + final String inum = storageId.getExternalId(); + JansPerson person = jansThinBridge.getJansUserByInum(inum); + if(person != null) { + return new JansUserModel(model,person); + } + return null; + }catch(JansThinBridgeOperationException e) { + log.errorv(e,"Error fetching jans user with id " + id); + return null; + } + } + + +} diff --git a/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/storage/JansUserStorageProviderFactory.java b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/storage/JansUserStorageProviderFactory.java new file mode 100644 index 00000000000..76f42c0cdf1 --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/java/io/jans/kc/spi/storage/JansUserStorageProviderFactory.java @@ -0,0 +1,34 @@ +package io.jans.kc.spi.storage; + +import org.keycloak.component.ComponentModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.storage.UserStorageProviderFactory; + +import io.jans.kc.spi.ProviderIDs; +import io.jans.kc.spi.JansSpiInitException; +import io.jans.kc.spi.custom.JansThinBridgeProvider; + +import org.jboss.logging.Logger; + +public class JansUserStorageProviderFactory implements UserStorageProviderFactory { + + + private static final String PROVIDER_ID = ProviderIDs.JANS_USER_STORAGE_PROVIDER; + private static final Logger log = Logger.getLogger(JansUserStorageProviderFactory.class); + + @Override + public String getId() { + + return PROVIDER_ID; + } + + @Override + public JansUserStorageProvider create(KeycloakSession session, ComponentModel model) { + + JansThinBridgeProvider jansThinBridgeProvider = session.getProvider(JansThinBridgeProvider.class); + if(jansThinBridgeProvider == null) { + throw new JansSpiInitException("Could not obtain reference to thin bridge provider"); + } + return new JansUserStorageProvider(session,model,jansThinBridgeProvider); + } +} diff --git a/jans-keycloak-integration/spi/src/main/resources/META-INF/services/io.jans.kc.spi.custom.JansThinBridgeProviderFactory b/jans-keycloak-integration/spi/src/main/resources/META-INF/services/io.jans.kc.spi.custom.JansThinBridgeProviderFactory new file mode 100644 index 00000000000..edd9fed9b5e --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/resources/META-INF/services/io.jans.kc.spi.custom.JansThinBridgeProviderFactory @@ -0,0 +1 @@ +io.jans.kc.spi.custom.impl.DefaultJansThinBridgeProviderFactory \ No newline at end of file diff --git a/jans-keycloak-integration/authenticator/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory b/jans-keycloak-integration/spi/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory similarity index 100% rename from jans-keycloak-integration/authenticator/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory rename to jans-keycloak-integration/spi/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory diff --git a/jans-keycloak-integration/spi/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper b/jans-keycloak-integration/spi/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper new file mode 100644 index 00000000000..4a8751a217e --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper @@ -0,0 +1 @@ +io.jans.kc.spi.protocol.mapper.saml.JansSamlUserAttributeMapper \ No newline at end of file diff --git a/jans-keycloak-integration/spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/jans-keycloak-integration/spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi new file mode 100644 index 00000000000..86e73a0b055 --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -0,0 +1 @@ +io.jans.kc.spi.custom.JansThinBridgeSpi \ No newline at end of file diff --git a/jans-keycloak-integration/authenticator/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory b/jans-keycloak-integration/spi/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory similarity index 100% rename from jans-keycloak-integration/authenticator/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory rename to jans-keycloak-integration/spi/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory diff --git a/jans-keycloak-integration/spi/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory b/jans-keycloak-integration/spi/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory new file mode 100644 index 00000000000..df4a036ae57 --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory @@ -0,0 +1 @@ +io.jans.kc.spi.storage.JansUserStorageProviderFactory \ No newline at end of file diff --git a/jans-keycloak-integration/spi/src/main/resources/assembly/.DONOTDELETE b/jans-keycloak-integration/spi/src/main/resources/assembly/.DONOTDELETE new file mode 100644 index 00000000000..c7c1c13b13c --- /dev/null +++ b/jans-keycloak-integration/spi/src/main/resources/assembly/.DONOTDELETE @@ -0,0 +1 @@ +This file is used to prevent build errors during the run of the maven assembly plugin \ No newline at end of file diff --git a/jans-keycloak-integration/authenticator/src/main/resources/theme-resources/messages/messages_en.properties b/jans-keycloak-integration/spi/src/main/resources/theme-resources/messages/messages_en.properties similarity index 100% rename from jans-keycloak-integration/authenticator/src/main/resources/theme-resources/messages/messages_en.properties rename to jans-keycloak-integration/spi/src/main/resources/theme-resources/messages/messages_en.properties diff --git a/jans-keycloak-integration/authenticator/src/main/resources/theme-resources/resources/img/janssen project favicon transparent 50px 50px.png b/jans-keycloak-integration/spi/src/main/resources/theme-resources/resources/img/janssen project favicon transparent 50px 50px.png similarity index 100% rename from jans-keycloak-integration/authenticator/src/main/resources/theme-resources/resources/img/janssen project favicon transparent 50px 50px.png rename to jans-keycloak-integration/spi/src/main/resources/theme-resources/resources/img/janssen project favicon transparent 50px 50px.png diff --git a/jans-keycloak-integration/authenticator/src/main/resources/theme-resources/resources/img/janssen-project.jpg b/jans-keycloak-integration/spi/src/main/resources/theme-resources/resources/img/janssen-project.jpg similarity index 100% rename from jans-keycloak-integration/authenticator/src/main/resources/theme-resources/resources/img/janssen-project.jpg rename to jans-keycloak-integration/spi/src/main/resources/theme-resources/resources/img/janssen-project.jpg diff --git a/jans-keycloak-integration/authenticator/src/main/resources/theme-resources/resources/img/janssen_dove_icon.jpg b/jans-keycloak-integration/spi/src/main/resources/theme-resources/resources/img/janssen_dove_icon.jpg similarity index 100% rename from jans-keycloak-integration/authenticator/src/main/resources/theme-resources/resources/img/janssen_dove_icon.jpg rename to jans-keycloak-integration/spi/src/main/resources/theme-resources/resources/img/janssen_dove_icon.jpg diff --git a/jans-keycloak-integration/authenticator/src/main/resources/theme-resources/resources/js/jans-auth.js b/jans-keycloak-integration/spi/src/main/resources/theme-resources/resources/js/jans-auth.js similarity index 100% rename from jans-keycloak-integration/authenticator/src/main/resources/theme-resources/resources/js/jans-auth.js rename to jans-keycloak-integration/spi/src/main/resources/theme-resources/resources/js/jans-auth.js diff --git a/jans-keycloak-integration/authenticator/src/main/resources/theme-resources/templates/jans-auth-error.ftl b/jans-keycloak-integration/spi/src/main/resources/theme-resources/templates/jans-auth-error.ftl similarity index 100% rename from jans-keycloak-integration/authenticator/src/main/resources/theme-resources/templates/jans-auth-error.ftl rename to jans-keycloak-integration/spi/src/main/resources/theme-resources/templates/jans-auth-error.ftl diff --git a/jans-keycloak-integration/authenticator/src/main/resources/theme-resources/templates/jans-auth-redirect.ftl b/jans-keycloak-integration/spi/src/main/resources/theme-resources/templates/jans-auth-redirect.ftl similarity index 100% rename from jans-keycloak-integration/authenticator/src/main/resources/theme-resources/templates/jans-auth-redirect.ftl rename to jans-keycloak-integration/spi/src/main/resources/theme-resources/templates/jans-auth-redirect.ftl diff --git a/jans-keycloak-integration/authenticator/src/main/resources/theme-resources/templates/jans-auth-response-complete.ftl b/jans-keycloak-integration/spi/src/main/resources/theme-resources/templates/jans-auth-response-complete.ftl similarity index 100% rename from jans-keycloak-integration/authenticator/src/main/resources/theme-resources/templates/jans-auth-response-complete.ftl rename to jans-keycloak-integration/spi/src/main/resources/theme-resources/templates/jans-auth-response-complete.ftl diff --git a/jans-keycloak-integration/authenticator/src/main/resources/theme-resources/templates/jans-auth-response-error.ftl b/jans-keycloak-integration/spi/src/main/resources/theme-resources/templates/jans-auth-response-error.ftl similarity index 100% rename from jans-keycloak-integration/authenticator/src/main/resources/theme-resources/templates/jans-auth-response-error.ftl rename to jans-keycloak-integration/spi/src/main/resources/theme-resources/templates/jans-auth-response-error.ftl diff --git a/jans-keycloak-integration/storage-spi/.gitignore b/jans-keycloak-integration/storage-spi/.gitignore deleted file mode 100644 index c53194b720c..00000000000 --- a/jans-keycloak-integration/storage-spi/.gitignore +++ /dev/null @@ -1,36 +0,0 @@ -# Eclipse -.project -jans-config-api-access*.log -.classpath -.settings/ -bin/ - -# IntelliJ -.idea -*.ipr -*.iml -*.iws - -# NetBeans -nb-configuration.xml - -# Visual Studio Code -.vscode - -# OSX -.DS_Store - -# Vim -*.swp -*.swo - -# patch -*.orig -*.rej - -# Maven -target/ -pom.xml.tag -pom.xml.releaseBackup -pom.xml.versionsBackup -release.properties diff --git a/jans-keycloak-integration/storage-spi/pom.xml b/jans-keycloak-integration/storage-spi/pom.xml deleted file mode 100644 index 5add606ecf0..00000000000 --- a/jans-keycloak-integration/storage-spi/pom.xml +++ /dev/null @@ -1,97 +0,0 @@ - - - - 4.0.0 - kc-jans-storage-plugin - kc-jans-storage-plugin - jar - - - io.jans - jans-kc-parent - 1.1.3-SNAPSHOT - - - - - - - io.jans - jans-scim-model - - - - - jakarta.ws.rs - jakarta.ws.rs-api - - - jakarta.servlet - jakarta.servlet-api - - - - - org.keycloak - keycloak-core - - - org.keycloak - keycloak-server-spi - - - org.keycloak - keycloak-model-legacy - - - - - org.apache.commons - commons-collections4 - - - org.apache.commons - commons-lang3 - - - - - com.fasterxml.jackson.jaxrs - jackson-jaxrs-base - - - com.fasterxml.jackson.jaxrs - jackson-jaxrs-json-provider - - - - - - io.swagger.core.v3 - swagger-core-jakarta - - - - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - org.apache.maven.plugins - maven-assembly-plugin - - - - - - diff --git a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/config/PluginConfiguration.java b/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/config/PluginConfiguration.java deleted file mode 100644 index 77950fe6c11..00000000000 --- a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/config/PluginConfiguration.java +++ /dev/null @@ -1,84 +0,0 @@ -package io.jans.kc.spi.storage.config; - -import java.util.Arrays; -import java.util.ArrayList; -import java.util.List; -import org.keycloak.Config; - -public class PluginConfiguration { - - private static final String AUTH_TOKEN_ENDPOINT_KEY = "auth-token-endpoint"; - private static final String SCIM_USER_ENDPOINT_KEY = "scim-user-endpoint"; - private static final String SCIM_USER_SEARCH_ENDPOINT_KEY = "scim-user-search-endpoint"; - private static final String SCIM_OAUTH_SCOPES_KEY = "scim-oauth-scopes"; - private static final String SCIM_CLIENT_ID_KEY = "scim-client-id"; - private static final String SCIM_CLIENT_SECRET = "scim-client-secret"; - - private String authTokenEndpoint; - private String scimUserEndpoint; - private String scimUserSearchEndpoint; - private List scimOauthScopes; - private String scimClientId; - private String scimClientSecret; - - private PluginConfiguration() { - - } - - public static PluginConfiguration fromKeycloakConfiguration(Config.Scope config) { - - PluginConfiguration ret = new PluginConfiguration(); - ret.authTokenEndpoint = config.get(AUTH_TOKEN_ENDPOINT_KEY); - ret.scimUserEndpoint = config.get(SCIM_USER_ENDPOINT_KEY); - ret.scimUserSearchEndpoint = config.get(SCIM_USER_SEARCH_ENDPOINT_KEY); - ret.scimOauthScopes = new ArrayList<>(); - String tmpscopes = config.get(SCIM_OAUTH_SCOPES_KEY); - if(tmpscopes != null) { - ret.scimOauthScopes = Arrays.asList(tmpscopes.split(",")); - } - ret.scimClientId = config.get(SCIM_CLIENT_ID_KEY); - ret.scimClientSecret = config.get(SCIM_CLIENT_SECRET); - return ret; - } - - public String getAuthTokenEndpoint() { - - return authTokenEndpoint; - } - - public String getScimUserEndpoint() { - - return scimUserEndpoint; - } - - - public String getScimUserSearchEndpoint() { - - return scimUserSearchEndpoint; - } - - public List getScimOauthScopes() { - - return scimOauthScopes; - } - - public String getScimClientId() { - - return scimClientId; - } - - public String getScimClientSecret() { - - return scimClientSecret; - } - - public boolean isValid() { - - return authTokenEndpoint != null - && scimUserEndpoint != null - && scimUserSearchEndpoint != null - && scimOauthScopes != null - && scimClientId != null - && scimClientSecret != null; - } -} diff --git a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/exception/JansConfigurationException.java b/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/exception/JansConfigurationException.java deleted file mode 100644 index e4461547a96..00000000000 --- a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/exception/JansConfigurationException.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. - * - * Copyright (c) 2020, Janssen Project - */ - -package io.jans.kc.spi.storage.exception; - -public class JansConfigurationException extends RuntimeException { - - - public JansConfigurationException() { - } - - public JansConfigurationException(String message) { - super(message); - } - - public JansConfigurationException(Throwable cause) { - super(cause); - } - - public JansConfigurationException(String message, Throwable cause) { - super(message, cause); - } - -} diff --git a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/service/CredentialAuthenticatingService.java b/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/service/CredentialAuthenticatingService.java deleted file mode 100644 index 14fc98309c3..00000000000 --- a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/service/CredentialAuthenticatingService.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.jans.kc.spi.storage.service; - -import io.jans.kc.spi.storage.util.Constants; -import io.jans.kc.spi.storage.util.JansUtil; - -import jakarta.ws.rs.core.MediaType; - -import org.apache.commons.lang.StringUtils; - -import org.jboss.logging.Logger; - -public class CredentialAuthenticatingService { - - private static Logger log = Logger.getLogger(CredentialAuthenticatingService.class); - - private JansUtil jansUtil; - - public CredentialAuthenticatingService(JansUtil jansUtil) { - this.jansUtil = jansUtil; - } - - public boolean authenticateUser(final String username, final String password) { - log.debugv("CredentialAuthenticatingService::authenticateUser() - username:{0}, password:{1} ", username, - password); - boolean isValid = false; - try { - - String token = jansUtil.requestUserToken(jansUtil.getTokenEndpoint(), username, password, null, - Constants.RESOURCE_OWNER_PASSWORD_CREDENTIALS, null, MediaType.APPLICATION_FORM_URLENCODED); - - log.debugv("CredentialAuthenticatingService::authenticateUser() - Final token token - {0}", token); - - if (StringUtils.isNotBlank(token)) { - isValid = true; - } - } catch (Exception ex) { - log.debug("CredentialAuthenticatingService::authenticateUser() - Error while authenticating", ex); - } - return isValid; - } - -} diff --git a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/service/RemoteUserStorageProvider.java b/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/service/RemoteUserStorageProvider.java deleted file mode 100644 index 3a70142e225..00000000000 --- a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/service/RemoteUserStorageProvider.java +++ /dev/null @@ -1,179 +0,0 @@ -package io.jans.kc.spi.storage.service; - -import io.jans.kc.spi.storage.config.PluginConfiguration; -import io.jans.kc.spi.storage.util.JansUtil; -import io.jans.scim.model.scim2.user.UserResource; - -import org.keycloak.component.ComponentModel; -import org.keycloak.credential.CredentialInput; -import org.keycloak.credential.CredentialInputValidator; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserModel; -import org.keycloak.models.credential.PasswordCredentialModel; -import org.keycloak.storage.StorageId; -import org.keycloak.storage.UserStorageProvider; -import org.keycloak.storage.user.UserLookupProvider; - -import org.jboss.logging.Logger; - - -public class RemoteUserStorageProvider implements CredentialInputValidator, UserLookupProvider, UserStorageProvider { - - private static Logger log = Logger.getLogger(RemoteUserStorageProvider.class); - - private KeycloakSession session; - private ComponentModel model; - private UsersApiLegacyService usersService; - private CredentialAuthenticatingService credentialAuthenticatingService; - - public RemoteUserStorageProvider(KeycloakSession session, ComponentModel model, PluginConfiguration pluginConfiguration) { - log.debugv("RemoteUserStorageProvider() - session:{0}, model:{1}", session, model); - JansUtil jansUtil = new JansUtil(pluginConfiguration); - this.session = session; - this.model = model; - this.usersService = new UsersApiLegacyService(session, model,new ScimService(jansUtil)); - this.credentialAuthenticatingService = new CredentialAuthenticatingService(jansUtil); - } - - @Override - public boolean supportsCredentialType(String credentialType) { - log.debugv("RemoteUserStorageProvider::supportsCredentialType() - credentialType:{0}", credentialType); - return PasswordCredentialModel.TYPE.equals(credentialType); - } - - @Override - public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) { - log.debugv("RemoteUserStorageProvider::isConfiguredFor() - realm:{0}, user:{1}, credentialType:{2} ", realm, user, - credentialType); - return user.credentialManager().isConfiguredFor(credentialType); - } - - @Override - public boolean isValid(RealmModel realm, UserModel user, CredentialInput credentialInput) { - log.debugv( - "RemoteUserStorageProvider::isValid() - realm:{0}, user:{1}, credentialInput:{2}, user.getUsername():{2}, credentialInput.getChallengeResponse():{}", - realm, user, credentialInput, user.getUsername(), credentialInput.getChallengeResponse()); - - boolean valid = credentialAuthenticatingService.authenticateUser(user.getUsername(), - credentialInput.getChallengeResponse()); - - log.debugv("RemoteUserStorageProvider::isValid() - valid:{0}", valid); - - return valid; - - } - - /** - * Get user based on id - */ - public UserModel getUserById(RealmModel paramRealmModel, String id) { - log.debugv("RemoteUserStorageProvider::getUserById() - paramRealmModel:{0}, id:{1}", paramRealmModel, id); - - UserModel userModel = null; - try { - UserResource user = usersService.getUserById(StorageId.externalId(id)); - log.debugv("RemoteUserStorageProvider::getUserById() - user fetched based on id:{0} is user:{1}", id, user); - if (user != null) { - userModel = createUserModel(paramRealmModel, user); - log.debugv(" RemoteUserStorageProvider::getUserById() - userModel:{0}", userModel); - - if (userModel != null) { - log.debugv( - "RemoteUserStorageProvider::getUserById() - Final userModel fetched with id:{0}, userModel:{1}, userModel.getAttributes(:{2})", - id, userModel, userModel.getAttributes()); - } - } - - log.debugv( - "RemoteUserStorageProvider::getUserById() - User fetched with id:{0} from external service is:{1}", - id, user); - - } catch (Exception ex) { - log.errorv(ex, - "RemoteUserStorageProvider::getUserById() - Error fetching user id:{0} from external service", - id); - - } - - return userModel; - } - - /** - * Get user based on name - */ - public UserModel getUserByUsername(RealmModel paramRealmModel, String name) { - log.debugv("RemoteUserStorageProvider::getUserByUsername() - paramRealmModel:{0}, name:{1}", paramRealmModel, - name); - - UserModel userModel = null; - try { - UserResource user = usersService.getUserByName(name); - log.debugv( - "RemoteUserStorageProvider::getUserByUsername() - User fetched with name:{0} from external service is:{1}", - name, user); - - if (user != null) { - userModel = createUserModel(paramRealmModel, user); - log.debugv("RemoteUserStorageProvider::getUserByUsername() - userModel:{0}", userModel); - } - if (userModel != null) { - log.debugv( - "RemoteUserStorageProvider::getUserByUsername() - Final User fetched with name:{0}, userModel:{1}, userModel.getAttributes():{2}", - name, userModel, userModel.getAttributes()); - } - - } catch (Exception ex) { - log.errorv(ex, - "\n RemoteUserStorageProvider::getUserByUsername() - Error fetching user name:{0}", - name); - - } - return userModel; - } - - public UserModel getUserByEmail(RealmModel paramRealmModel, String email) { - log.debugv("RemoteUserStorageProvider::getUserByEmail() - paramRealmModel:{0}, email:{1}", paramRealmModel, - email); - - UserModel userModel = null; - try { - UserResource user = usersService.getUserByEmail(email); - log.debugv( - "RemoteUserStorageProvider::getUserByEmail() - User fetched with email:{0} from external service is:{1}", - email, user); - - if (user != null) { - userModel = createUserModel(paramRealmModel, user); - log.debugv("RemoteUserStorageProvider::getUserByEmail() - userModel:{0}", userModel); - } - - if (userModel != null) { - log.debugv( - "RemoteUserStorageProvider::getUserByEmail() - Final User fetched with email:{0}, userModel:{1}, userModel.getAttributes(:{2})", - email, userModel, userModel.getAttributes()); - } - - } catch (Exception ex) { - log.errorv(ex, - "RemoteUserStorageProvider::getUserByEmail() - Error fetching user email:{0}", - email); - - } - return userModel; - } - - public void close() { - log.debug("RemoteUserStorageProvider::close()"); - - } - - private UserModel createUserModel(RealmModel realm, UserResource user) { - log.debugv("RemoteUserStorageProvider::createUserModel() - realm:{0} , user:{1}", realm, user); - - UserModel userModel = new UserAdapter(session, realm, model, user); - log.debugv("Final RemoteUserStorageProvider::createUserModel() - userModel:{0}", userModel); - - return userModel; - } -} diff --git a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/service/RemoteUserStorageProviderFactory.java b/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/service/RemoteUserStorageProviderFactory.java deleted file mode 100644 index 77a1c8fbeae..00000000000 --- a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/service/RemoteUserStorageProviderFactory.java +++ /dev/null @@ -1,57 +0,0 @@ -package io.jans.kc.spi.storage.service; - -import io.jans.kc.spi.storage.config.PluginConfiguration; -import io.jans.kc.spi.storage.util.Constants; - -import org.jboss.logging.Logger; - -import org.keycloak.Config; -import org.keycloak.component.ComponentModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.storage.UserStorageProviderFactory; - - - - -public class RemoteUserStorageProviderFactory implements UserStorageProviderFactory { - - private static Logger log = Logger.getLogger(RemoteUserStorageProviderFactory.class); - - public static final String PROVIDER_NAME = "jans-keycloak-storage-api"; - private PluginConfiguration pluginConfiguration; - - @Override - public RemoteUserStorageProvider create(KeycloakSession session, ComponentModel model) { - log.debugv("RemoteUserStorageProviderFactory::create() - session:{}, model:{}", session, model); - return new RemoteUserStorageProvider(session, model,pluginConfiguration); - } - - @Override - public String getId() { - - return Constants.PROVIDER_ID; - } - - @Override - public String getHelpText() { - return "Janssen User Storage Provider"; - } - - @Override - public void init(Config.Scope config) { - - this.pluginConfiguration = PluginConfiguration.fromKeycloakConfiguration(config); - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - log.debug("RemoteUserStorageProviderFactory::postInit()"); - } - - @Override - public void close() { - log.debug("RemoteUserStorageProviderFactory::close() - Exit"); - } - -} diff --git a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/service/ScimService.java b/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/service/ScimService.java deleted file mode 100644 index 13002f1de2f..00000000000 --- a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/service/ScimService.java +++ /dev/null @@ -1,187 +0,0 @@ -package io.jans.kc.spi.storage.service; - -import com.fasterxml.jackson.databind.JsonNode; - -import io.jans.kc.spi.storage.util.JansUtil; -import io.jans.scim.model.scim2.SearchRequest; -import io.jans.scim.model.scim2.user.UserResource; - -import org.apache.http.client.HttpClient; -import org.apache.http.impl.client.HttpClientBuilder; - -import org.jboss.logging.Logger; - -import org.keycloak.broker.provider.util.SimpleHttp; -import org.keycloak.util.JsonSerialization; - - -public class ScimService { - - private static Logger log = Logger.getLogger(ScimService.class); - - private JansUtil jansUtil; - - public ScimService(JansUtil jansUtil) { - this.jansUtil = jansUtil; - } - - private String getScimUserEndpoint() { - String scimUserEndpoint = jansUtil.getScimUserEndpoint(); - log.debugv("ScimService::getScimUserEndpoint() - scimUserEndpoint:{0}", scimUserEndpoint); - return scimUserEndpoint; - } - - private String getScimUserSearchEndpoint() { - String scimUserSearchEndpoint = jansUtil.getScimUserSearchEndpoint(); - log.debugv("ScimService::getScimUserSearchEndpoint() - scimUserSearchEndpoint:{0}", scimUserSearchEndpoint); - return scimUserSearchEndpoint; - } - - private String requestAccessToken() { - log.debug("ScimService::requestAccessToken()"); - String token = null; - - try { - token = jansUtil.requestScimAccessToken(); - log.debugv("ScimService::requestAccessToken() - token:{}", token); - } catch (Exception ex) { - log.errorv(ex,"ScimService::requestAccessToken() - Error while generating access token for SCIM"); - throw new RuntimeException( - "ScimService::requestAccessToken() - Error while generating access token for SCIM endpoint",ex); - } - return token; - } - - public UserResource getUserById(String inum) { - log.infov(" ScimService::getUserById() - inum:{0}", inum); - try { - return getData(getScimUserEndpoint() + "/" + inum, this.requestAccessToken()); - } catch (Exception ex) { - log.errorv(ex, - "ScimService::getUserById() - Error fetching user based on inum:{0} from external service", - inum); - } - return null; - } - - public UserResource getUserByName(String username) { - log.infov("ScimService::getUserByName() - username:{0}", username); - try { - - String filter = "userName eq \"" + username + "\""; - return postData(this.getScimUserSearchEndpoint(), this.requestAccessToken(), filter); - } catch (Exception ex) { - log.errorv(ex, - "ScimService::getUserByName() - Error fetching user based on username:{0} from external service", - username); - } - return null; - } - - public UserResource getUserByEmail(String email) { - log.debugv(" ScimService::getUserByEmail() - email:{}", email); - try { - - String filter = "emails[value eq \"" + email + "\"]"; - log.debugv(" ScimService::getUserByEmail() - filter:{}", filter); - return postData(this.getScimUserSearchEndpoint(), this.requestAccessToken(), filter); - } catch (Exception ex) { - log.errorv(ex, - " ScimService::getUserByEmail() - Error fetching user based on email:{0}", - email); - - } - return null; - } - - public UserResource postData(String uri, String accessToken, String filter) { - UserResource user = null; - log.debugv("ScimService::postData() - uri:{0}, accessToken:{1}, filter:{2}", uri, accessToken, filter); - try { - HttpClient client = HttpClientBuilder.create().build(); - - SearchRequest searchRequest = createSearchRequest(filter); - log.debugv("ScimService::postData() - client:{0}, searchRequest:{1}, accessToken:{2}", client, searchRequest.toString(), - accessToken); - - JsonNode jsonNode = SimpleHttp.doPost(uri, client).auth(accessToken).json(searchRequest).asJson(); - - log.debugv("\n\n ScimService::postData() - jsonNode:{0}", jsonNode); - - user = getUserResourceFromList(jsonNode); - - log.debugv("ScimService::postData() - user:{0}", user); - - } catch (Exception ex) { - log.errorv(ex,"ScimService::postData() - Error while fetching data"); - } - return user; - } - - public UserResource getData(String uri, String accessToken) { - UserResource user = null; - log.debugv("ScimService::getData() - uri:{0}, accessToken:{1}", uri, accessToken); - try { - HttpClient client = HttpClientBuilder.create().build(); - - JsonNode jsonNode = SimpleHttp.doGet(uri, client).auth(accessToken).asJson(); - - log.debugv("\n\n ScimService::getData() - jsonNode:{0}", jsonNode); - - user = getUserResource(jsonNode); - - log.debugv("ScimService::getData() - user:{}", user); - - } catch (Exception ex) { - log.errorv(ex,"\n\n ScimService::getData() - Error while fetching data"); - } - return user; - } - - private SearchRequest createSearchRequest(String filter) { - log.debugv("ScimService::createSearchRequest() - createSearchRequest() - filter:{0}", filter); - SearchRequest searchRequest = new SearchRequest(); - searchRequest.setFilter(filter); - - log.debugv(" ScimService::createSearchRequest() - searchRequest:{0}", searchRequest); - - return searchRequest; - } - - private UserResource getUserResourceFromList(JsonNode jsonNode) { - log.debugv(" \n\n ScimService::getUserResourceFromList() - jsonNode:{0}", jsonNode); - - UserResource user = null; - try { - if (jsonNode != null) { - if (jsonNode.get("Resources") != null) { - JsonNode value = jsonNode.get("Resources").get(0); - log.debugv("*** ScimService::getUserResourceFromList() - value:{0}, value.getClass():{1}", value, - value.getClass()); - user = JsonSerialization.readValue(JsonSerialization.writeValueAsBytes(value), UserResource.class); - log.debugv(" ScimService::getUserResourceFromList() - user:{0}, user.getClass():{1}", user, - user.getClass()); - } - } - } catch (Exception ex) { - log.errorv(ex,"\n\n ScimService::getUserResourceFromList() - Error while fetching data"); - } - return user; - } - - private UserResource getUserResource(JsonNode jsonNode) { - log.debugv("ScimService::getUserResource() - jsonNode:{0}", jsonNode); - - UserResource user = null; - try { - if (jsonNode != null) { - user = JsonSerialization.readValue(JsonSerialization.writeValueAsBytes(jsonNode), UserResource.class); - log.debugv(" ScimService::getUserResource() - user:{0}, user.getClass():{1}", user, user.getClass()); - } - } catch (Exception ex) { - log.errorv(ex,"\n\n ScimService::getUserResource() - Error while fetching data is ex:{}"); - } - return user; - } - -} diff --git a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/service/UserAdapter.java b/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/service/UserAdapter.java deleted file mode 100644 index 8e52331fb93..00000000000 --- a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/service/UserAdapter.java +++ /dev/null @@ -1,141 +0,0 @@ -package io.jans.kc.spi.storage.service; - -import io.jans.kc.spi.storage.util.JansDataUtil; -import io.jans.scim.model.scim2.user.UserResource; -import io.jans.scim.model.scim2.util.DateUtil; - -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Stream; - -import org.apache.commons.lang3.StringUtils; -import org.keycloak.common.util.MultivaluedHashMap; -import org.keycloak.component.ComponentModel; -import org.keycloak.credential.LegacyUserCredentialManager; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleModel; -import org.keycloak.models.SubjectCredentialManager; -import org.keycloak.models.UserModel; -import org.keycloak.storage.StorageId; -import org.keycloak.storage.adapter.AbstractUserAdapter; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class UserAdapter extends AbstractUserAdapter { - private static Logger logger = LoggerFactory.getLogger(UserAdapter.class); - private final UserResource user; - - public UserAdapter(KeycloakSession session, RealmModel realm, ComponentModel model, UserResource user) { - - super(session, realm, model); - logger.debug( - " UserAdapter() - model:{}, user:{}, storageProviderModel:{}, storageProviderModel.getId():{}, user.getId():{}", - model, user, storageProviderModel, storageProviderModel.getId(), user.getId()); - this.storageId = new StorageId(storageProviderModel.getId(), user.getId()); - this.user = user; - - logger.debug("UserAdapter() - All User Resource field():{}", printUserResourceField()); - } - - @Override - public String getUsername() { - return user.getUserName(); - } - - @Override - public String getFirstName() { - return user.getDisplayName(); - } - - @Override - public String getLastName() { - return user.getNickName(); - } - - @Override - public String getEmail() { - return ((user.getEmails() != null && user.getEmails().get(0) != null) ? user.getEmails().get(0).getValue() - : null); - } - - @Override - public SubjectCredentialManager credentialManager() { - return new LegacyUserCredentialManager(session, realm, this); - } - - public Map getCustomAttributes() { - printUserCustomAttributes(); - return user.getCustomAttributes(); - } - - @Override - public boolean isEnabled() { - boolean enabled = false; - if (user != null) { - enabled = user.getActive(); - } - return enabled; - } - - @Override - public Long getCreatedTimestamp() { - Long createdDate = null; - if (user.getMeta().getCreated() != null) { - String created = user.getMeta().getCreated(); - if (created != null && StringUtils.isNotBlank(created)) { - createdDate = DateUtil.ISOToMillis(created); - } - } - return createdDate; - } - - @Override - public Map> getAttributes() { - MultivaluedHashMap attributes = new MultivaluedHashMap<>(); - attributes.add(UserModel.USERNAME, getUsername()); - attributes.add(UserModel.EMAIL, getEmail()); - attributes.add(UserModel.FIRST_NAME, getFirstName()); - attributes.add(UserModel.LAST_NAME, getLastName()); - return attributes; - } - - @Override - public Stream getAttributeStream(String name) { - if (name.equals(UserModel.USERNAME)) { - return Stream.of(getUsername()); - } - return Stream.empty(); - } - - @Override - protected Set getRoleMappingsInternal() { - return Set.of(); - } - - private void printUserCustomAttributes() { - logger.info(" UserAdapter::printUserCustomAttributes() - user:{}", user); - if (user == null || user.getCustomAttributes() == null || user.getCustomAttributes().isEmpty()) { - return; - } - - user.getCustomAttributes().keySet().stream() - .forEach(key -> logger.info("key:{} , value:{}", key, user.getCustomAttributes().get(key))); - - } - - private Map printUserResourceField() { - logger.info(" UserAdapter::printUserResourceField() - user:{}", user); - Map propertyTypeMap = null; - if (user == null) { - return propertyTypeMap; - } - - propertyTypeMap = JansDataUtil.getFieldTypeMap(user.getClass()); - logger.info("UserAdapter::printUserResourceField() - all fields of user:{}", propertyTypeMap); - return propertyTypeMap; - } - -} diff --git a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/service/UsersApiLegacyService.java b/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/service/UsersApiLegacyService.java deleted file mode 100644 index 4bb8d75b2a6..00000000000 --- a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/service/UsersApiLegacyService.java +++ /dev/null @@ -1,66 +0,0 @@ -package io.jans.kc.spi.storage.service; - -import io.jans.scim.model.scim2.user.UserResource; - -import java.util.Properties; - -import org.jboss.logging.Logger; - -import org.keycloak.component.ComponentModel; -import org.keycloak.models.KeycloakSession; - -public class UsersApiLegacyService { - - private static Logger log = Logger.getLogger(UsersApiLegacyService.class); - - private ScimService scimService; - protected Properties jansProperties = new Properties(); - - public UsersApiLegacyService(KeycloakSession session, ComponentModel model, ScimService scimService) { - - log.debugv(" UsersApiLegacyService() - session:{0}, model:{1}", session, model); - this.scimService = scimService; - } - - public UserResource getUserById(String inum) { - log.debugv("UsersApiLegacyService::getUserById() - inum:{0}", inum); - try { - return scimService.getUserById(inum); - } catch (Exception ex) { - log.errorv(ex, - "UsersApiLegacyService::getUserById() - Error fetching user based on inum:{0} from external service", - inum); - - } - return null; - } - - public UserResource getUserByName(String username) { - log.debugv(" UsersApiLegacyService::getUserByName() - username:{0}", username); - try { - - return scimService.getUserByName(username); - } catch (Exception ex) { - log.errorv(ex, - "UsersApiLegacyService::getUserByName() - Error fetching user based on username:{0} from external service", - username); - - } - return null; - } - - public UserResource getUserByEmail(String email) { - log.infov(" UsersApiLegacyService::getUserByEmail() - email:{0}", email); - try { - - return scimService.getUserByEmail(email); - } catch (Exception ex) { - log.errorv( - " UsersApiLegacyService::getUserByEmail() - Error fetching user based on email:{0}", - email); - - } - return null; - } - -} diff --git a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/util/Constants.java b/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/util/Constants.java deleted file mode 100644 index 4740e3c734f..00000000000 --- a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/util/Constants.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. - * - * Copyright (c) 2020, Janssen Project - */ - -package io.jans.kc.spi.storage.util; - -public class Constants { - - private Constants() {} - - public static final String PROVIDER_ID = "kc-jans-storage"; - public static final String JANS_CONFIG_PROP_PATH = "jans.config.prop.path"; - public static final String KEYCLOAK_USER = "/keycloak-user"; - public static final String BASE_URL = "https://localhost"; - - public static final String SCOPE_TYPE_OPENID = "openid"; - public static final String UTF8_STRING_ENCODING = "UTF-8"; - public static final String CLIENT_SECRET_BASIC = "client_secret_basic"; - public static final String CLIENT_CREDENTIALS = "client_credentials"; - public static final String RESOURCE_OWNER_PASSWORD_CREDENTIALS = "password"; - - //properties - public static final String KEYCLOAK_SERVER_URL = "keycloak.server.url"; - public static final String AUTH_TOKEN_ENDPOINT = "auth.token.endpoint"; - public static final String SCIM_USER_ENDPOINT = "scim.user.endpoint"; - public static final String SCIM_USER_SEARCH_ENDPOINT = "scim.user.search.endpoint"; - public static final String SCIM_OAUTH_SCOPE = "scim.oauth.scope"; - public static final String KEYCLOAK_SCIM_CLIENT_ID = "keycloak.scim.client.id"; - public static final String KEYCLOAK_SCIM_CLIENT_PASSWORD = "keycloak.scim.client.password"; - - -} \ No newline at end of file diff --git a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/util/JansDataUtil.java b/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/util/JansDataUtil.java deleted file mode 100644 index 179dd9bebdd..00000000000 --- a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/util/JansDataUtil.java +++ /dev/null @@ -1,107 +0,0 @@ -package io.jans.kc.spi.storage.util; - -import java.beans.IntrospectionException; -import java.beans.PropertyDescriptor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.jboss.logging.Logger; - -public class JansDataUtil { - - private static final Logger log = Logger.getLogger(JansDataUtil.class); - - public static Object invokeMethod(Class clazz, String methodName, Class... parameterTypes) - throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - log.debugv("JansDataUtil::invokeMethod() - Invoke clazz:{0} on methodName:{1} with name:{2} ", clazz, methodName, - parameterTypes); - Object obj = null; - if (clazz == null || methodName == null || parameterTypes == null) { - return obj; - } - Method m = clazz.getDeclaredMethod(methodName, parameterTypes); - obj = m.invoke(null, parameterTypes); - - log.debugv("JansDataUtil::invokeMethod() - methodName:{0} returned obj:{1} ", methodName, obj); - return obj; - } - - public Object invokeReflectionGetter(Object obj, String variableName) { - log.debugv("JansDataUtil::invokeMethod() - Invoke obj:{0}, variableName:{1}", obj, variableName); - try { - if (obj == null) { - return obj; - } - PropertyDescriptor pd = new PropertyDescriptor(variableName, obj.getClass()); - Method getter = pd.getReadMethod(); - log.debugv("JansDataUtil::invokeMethod() - Invoke getter:{0}", getter); - if (getter != null) { - return getter.invoke(obj); - } else { - log.errorv( - "JansDataUtil::invokeReflectionGetter() - Getter Method not found for class:{0} property:{1}", - obj.getClass().getName(), variableName); - } - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException - | IntrospectionException e) { - log.errorv(e,"JansDataUtil::invokeReflectionGetter() - Getter Method ERROR for class: {0} property: {1}", - obj.getClass().getName(), variableName); - } - return obj; - } - - public static List getAllFields(Class type) { - log.debugv("JansDataUtil::getAllFields() - type:{0} ", type); - List allFields = new ArrayList<>(); - if (type == null) { - return allFields; - } - getAllFields(allFields, type); - log.debugv("JansDataUtil::getAllFields() - Fields:{0} of type:{1} ", allFields, type); - return allFields; - } - - public static List getAllFields(List fields, Class type) { - log.debugv("JansDataUtil::getAllFields() - fields:{0} , type:{1} ", fields, type); - if (fields == null || type == null) { - return fields; - } - fields.addAll(Arrays.asList(type.getDeclaredFields())); - - if (type.getSuperclass() != null) { - getAllFields(fields, type.getSuperclass()); - } - log.debugv("JansDataUtil::getAllFields() - Final fields:{0} of type:{1} ", fields, type); - return fields; - } - - public static Map getFieldTypeMap(Class clazz) { - log.debugv("JansDataUtil::getFieldTypeMap() - clazz:{0} ", clazz); - Map propertyTypeMap = new HashMap<>(); - - if (clazz == null) { - return propertyTypeMap; - } - - List fields = getAllFields(clazz); - log.debugv("JansDataUtil::getFieldTypeMap() - all-fields:{0} ", fields); - - for (Field field : fields) { - log.debugv( - "JansDataUtil::getFieldTypeMap() - field:{0} , field.getAnnotatedType():{1}, field.getAnnotations():{2} , field.getType().getAnnotations():{3}, field.getType().getCanonicalName():{4} , field.getType().getClass():{5} , field.getType().getClasses():{6} , field.getType().getComponentType():{7}", - field, field.getAnnotatedType(), field.getAnnotations(), field.getType().getAnnotations(), - field.getType().getCanonicalName(), field.getType().getClass(), field.getType().getClasses(), - field.getType().getComponentType()); - propertyTypeMap.put(field.getName(), field.getType().getSimpleName()); - } - log.debugv("JansDataUtil::getFieldTypeMap() - Final propertyTypeMap{0} ", propertyTypeMap); - return propertyTypeMap; - } - -} diff --git a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/util/JansUtil.java b/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/util/JansUtil.java deleted file mode 100644 index ff33cf8f614..00000000000 --- a/jans-keycloak-integration/storage-spi/src/main/java/io/jans/kc/spi/storage/util/JansUtil.java +++ /dev/null @@ -1,266 +0,0 @@ -package io.jans.kc.spi.storage.util; - -import com.fasterxml.jackson.databind.JsonNode; - -import io.jans.kc.spi.storage.config.PluginConfiguration; - -import jakarta.ws.rs.core.MediaType; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.stream.*; - -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.lang3.StringUtils; -import org.apache.http.client.HttpClient; -import org.apache.http.impl.client.HttpClientBuilder; - -import org.jboss.logging.Logger; - -import org.keycloak.broker.provider.util.SimpleHttp; - -public class JansUtil { - - private static Logger log = Logger.getLogger(JansUtil.class); - private PluginConfiguration pluginConfiguration; - - public JansUtil(PluginConfiguration pluginConfiguration) { - - this.pluginConfiguration = pluginConfiguration; - if (this.pluginConfiguration == null || !this.pluginConfiguration.isValid()) { - throw new RuntimeException("Plugin configuration missing or invalid"); - } - } - - public String getTokenEndpoint() { - log.debugv("JansUtil::getTokenEndpoint() - {0}", - pluginConfiguration.getAuthTokenEndpoint()); - - return pluginConfiguration.getAuthTokenEndpoint(); - } - - public String getScimUserEndpoint() { - log.debugv("JansUtil::getScimUserEndpoint() - {0}", - pluginConfiguration.getScimUserEndpoint()); - - return pluginConfiguration.getScimUserEndpoint(); - } - - public String getScimUserSearchEndpoint() { - log.debugv( - "JansUtil::getScimUserSearchEndpoint() - {0}", - pluginConfiguration.getScimUserSearchEndpoint()); - return pluginConfiguration.getScimUserSearchEndpoint(); - } - - public String getScimClientId() { - log.debugv("JansUtil::getScimClientId() - {0}", - pluginConfiguration.getScimClientId()); - return pluginConfiguration.getScimClientId(); - } - - public String getScimClientSecret() { - log.debugv("JansUtil::getClientPassword() - {0}", - pluginConfiguration.getScimClientSecret()); - return pluginConfiguration.getScimClientSecret(); - } - - public List getScimOauthScopes() { - log.debugv("JansUtil::getScimOauthScope() - {0}", - pluginConfiguration.getScimOauthScopes()); - return pluginConfiguration.getScimOauthScopes(); - } - - public String requestScimAccessToken() throws IOException { - log.debug("JansUtil::requestScimAccessToken() "); - List scopes = getScimOauthScopes(); - String token = requestAccessToken(getScimClientId(), scopes); - log.debugv("JansUtil::requestScimAccessToken() - token:{0} ", token); - return token; - } - - public String requestAccessToken(final String clientId, final List scope) throws IOException { - log.debugv("JansUtil::requestAccessToken() - Request for AccessToken - clientId:{0}, scope:{1} ", clientId, - scope); - - String tokenUrl = getTokenEndpoint(); - String token = getAccessToken(tokenUrl, clientId, scope); - log.debugv("JansUtil::requestAccessToken() - oAuth AccessToken response - token:{0}", token); - - return token; - } - - public String getAccessToken(final String tokenUrl, final String clientId, final List scopes) - throws IOException { - log.debugv("JansUtil::getAccessToken() - Access Token Request - tokenUrl:{0}, clientId:{1}, scopes:{2}", tokenUrl, - clientId, scopes); - - // Get clientSecret - String clientSecret = getScimClientSecret(); - log.debugv("JansUtil::getAccessToken() - Access Token Request - clientId:{0}, clientSecret:{1}", clientId, - clientSecret); - - // distinct scopes - Set scopesSet = new HashSet<>(scopes); - StringBuilder scope = new StringBuilder(Constants.SCOPE_TYPE_OPENID); - for (String s : scopesSet) { - scope.append(" ").append(s); - } - - log.debugv("JansUtil::getAccessToken() - Scope required - {0}", scope); - - String token = requestAccessToken(tokenUrl, clientId, clientSecret, scope.toString(), - Constants.CLIENT_CREDENTIALS, Constants.CLIENT_SECRET_BASIC, MediaType.APPLICATION_FORM_URLENCODED); - log.debugv("JansUtil::getAccessToken() - Final token token - {0}", token); - return token; - } - - public String requestAccessToken(final String tokenUrl, final String clientId, final String clientSecret, - final String scope, String grantType, String authenticationMethod, String mediaType) throws IOException { - log.debugv( - "JansUtil::requestAccessToken() - Request for Access Token - tokenUrl:{0}, clientId:{1}, clientSecret:{2}, scope:{3}, grantType:{4}, authenticationMethod:{5}, mediaType:{6}", - tokenUrl, clientId, clientSecret, scope, grantType, authenticationMethod, mediaType); - String token = null; - try { - - log.debugv("JansUtil::requestAccessToken() - this.getEncodedCredentials():{0}", - this.getEncodedCredentials(clientId, clientSecret)); - HttpClient client = HttpClientBuilder.create().build(); - JsonNode jsonNode = SimpleHttp.doPost(tokenUrl, client) - .header("Authorization", "Basic " + this.getEncodedCredentials(clientId, clientSecret)) - .header("Content-Type", mediaType).param("grant_type", "client_credentials") - .param("username", clientId + ":" + clientSecret).param("scope", scope).param("client_id", clientId) - .param("client_secret", clientSecret).param("authorization_method", "client_secret_basic").asJson(); - log.debugv("JansUtil::requestAccessToken() - POST Request for Access Token - jsonNode:{0} ", jsonNode); - - token = this.getToken(jsonNode); - - log.debugv("\n JansUtil::requestAccessToken() - After Post request for Access Token - token:{0} ", token); - - } catch (Exception ex) { - log.error("JansUtil::requestAccessToken() - Post error is ",ex); - } - return token; - } - - public String requestUserToken(final String tokenUrl, final String username, final String password, - final String scope, String grantType, String authenticationMethod, String mediaType) throws IOException { - log.debugv( - "JansUtil::requestUserToken() - Request for Access Token - tokenUrl:{0}, username:{1}, password:{2}, scope:{3}, grantType:{4}, authenticationMethod:{5}, mediaType:{6}", - tokenUrl, username, password, scope, grantType, authenticationMethod, mediaType); - String token = null; - try { - String clientId = this.getScimClientId(); - String clientSecret = this.getScimClientSecret(); - - log.debugv( - " JansUtil::requestUserToken() - clientId:{0} , clientSecret:{1}, this.getEncodedCredentials():{2}", - clientId, clientSecret, this.getEncodedCredentials(clientId, clientSecret)); - HttpClient client = HttpClientBuilder.create().build(); - JsonNode jsonNode = SimpleHttp.doPost(tokenUrl, client) - .header("Authorization", "Basic " + this.getEncodedCredentials(clientId, clientSecret)) - .header("Content-Type", mediaType).param("grant_type", grantType).param("username", username) - .param("password", password).asJson(); - - log.debugv("JansUtil::requestUserToken() - After invoking post request for user token - jsonNode:{0}", - jsonNode); - - token = this.getToken(jsonNode); - - log.debugv("\n JansUtil::requestUserToken() -POST Request for Access Token - token:{0} ", token); - - } catch (Exception ex) { - log.errorv("\n JansUtil::requestUserToken() - Error getting user token", ex); - } - return token; - } - - private boolean validateTokenScope(JsonNode jsonNode, String scope) { - - log.debugv("JansUtil::validateTokenScope() - jsonNode:{0}, scope:{1}", jsonNode, scope); - boolean validScope = false; - try { - - List scopeList = Stream.of(scope.split(" ", -1)).collect(Collectors.toList()); - - if (jsonNode != null && jsonNode.get("scope") != null) { - JsonNode value = jsonNode.get("scope"); - log.debugv("JansUtil::validateTokenScope() - value:{0}", value); - - if (value != null) { - String responseScope = value.toString(); - log.debugv( - "JansUtil::validateTokenScope() - scope:{0}, responseScope:{1}, responseScope.contains(scope):{2}", - scope, responseScope, responseScope.contains(scope)); - if (scopeList.contains(responseScope)) { - validScope = true; - } - } - - } - log.debugv("JansUtil::validateTokenScope() - validScope:{0}", validScope); - - } catch (Exception ex) { - log.error("JansUtil::validateTokenScope() - Error while validating token scope from response is ", - ex); - } - return validScope; - - } - - private String getToken(JsonNode jsonNode) { - log.debugv("JansUtil::getToken() - jsonNode:{0}", jsonNode); - - String token = null; - try { - - if (jsonNode != null && jsonNode.get("access_token") != null) { - JsonNode value = jsonNode.get("access_token"); - log.debugv("JansUtil::getToken() - value:{0}", value); - - if (value != null) { - token = value.asText(); - } - log.debugv("getToken() - token:{0}", token); - } - } catch (Exception ex) { - log.errorv("Error while getting token from response", ex); - } - return token; - } - - private boolean hasCredentials(String authUsername, String authPassword) { - return (StringUtils.isNotBlank(authUsername) && StringUtils.isNotBlank(authPassword)); - } - - /** - * Returns the client credentials (URL encoded). - * - * @return The client credentials. - */ - private String getCredentials(String authUsername, String authPassword) throws UnsupportedEncodingException { - log.debugv("getCredentials() - authUsername:{0}, authPassword:{1}", authUsername, authPassword); - return URLEncoder.encode(authUsername, Constants.UTF8_STRING_ENCODING) + ":" - + URLEncoder.encode(authPassword, Constants.UTF8_STRING_ENCODING); - } - - private String getEncodedCredentials(String authUsername, String authPassword) { - log.debugv("getEncodedCredentials() - authUsername:{0}, authPassword:{1}", authUsername, authPassword); - try { - if (hasCredentials(authUsername, authPassword)) { - return Base64.encodeBase64String(getBytes(getCredentials(authUsername, authPassword))); - } - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - - return null; - } - - private static byte[] getBytes(String str) { - return str.getBytes(StandardCharsets.UTF_8); - } - -} diff --git a/jans-keycloak-integration/storage-spi/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory b/jans-keycloak-integration/storage-spi/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory deleted file mode 100644 index 2691b848dd3..00000000000 --- a/jans-keycloak-integration/storage-spi/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory +++ /dev/null @@ -1 +0,0 @@ -io.jans.kc.spi.storage.service.RemoteUserStorageProviderFactory \ No newline at end of file