Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OIDC provider #330

Merged
merged 22 commits into from
Apr 3, 2024
Merged

OIDC provider #330

merged 22 commits into from
Apr 3, 2024

Conversation

afabiani
Copy link
Member

@afabiani afabiani commented Jan 11, 2024

This Pull Request introduces the OpenID Connect Provider Filter for GeoStore
Partially Fix geosolutions-it/MapStore2#10151

@afabiani afabiani self-assigned this Jan 11, 2024
…into oidc_provider

# Conflicts:
#	src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTResourceServiceImpl.java
@tdipisa
Copy link
Member

tdipisa commented Mar 7, 2024

@afabiani can you please fix conflicts so that we can proceed finally with the review?

afabiani added 2 commits March 7, 2024 19:26
…into oidc_provider

# Conflicts:
#	src/modules/rest/impl/pom.xml
#	src/modules/rest/impl/src/main/java/it/geosolutions/geostore/services/rest/impl/RESTResourceServiceImpl.java
#	src/modules/rest/impl/src/test/java/it/geosolutions/geostore/rest/service/impl/RESTResourceServiceImplTest.java
#	src/pom.xml
@afabiani
Copy link
Member Author

afabiani commented Mar 7, 2024

@afabiani can you please fix conflicts so that we can proceed finally with the review?

@tdipisa done

Copy link
Member

@offtherailz offtherailz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the end of the review I found that only 9 files have significant changes.
Not any particular breaking change, a generic OIDC connect has been added to the current configuration.
So also this PR is safe to merge and backport. 👍 nice

It doesn't introduce the possibility of configure multiple ones (now we can have one google, one keycloak, one generic oidc. I think that anyway this is enough).

Is not clear to me how to configure this to test. Looking at various superclasses I found this list of properties:

  • IdPConfiguration
    • protected String beanName;
    • protected boolean enabled = false;
    • protected boolean autoCreateUser;
    • protected String internalRedirectUri;
    • protected String redirectUri;
    • protected Role authenticatedDefaultRole;
  • Oauth2Configuration
    • protected String clientId;
    • protected String clientSecret;
    • protected String accessTokenUri;
    • protected String authorizationUri;
    • protected String checkTokenEndpointUrl;
    • protected String logoutUri;
    • protected String scopes;
    • protected String idTokenUri;
    • protected String discoveryUrl;
    • protected String revokeEndpoint;
    • protected boolean enableRedirectEntryPoint = false;
    • protected String principalKey;
    • protected String rolesClaim;
    • protected String groupsClaim;
  • OpenIdConnectConfiguration
    • jwkURI
    • responseMode
    • postLogoutRedirectUri
    • sendClientSecret
    • allowBearerToken
    • usePKCE

Here my requests:

  • sendClientSecret, usePKCE are not clear, please document them in the JSDoc.
  • responseMode and allowBearerToken don't look to be used. Please remove them
  • jwkURI too doesn't seems to be used, only used to set the store property of GeoStoreOauthRestTemplate but never used again. Also this store is private and the PR introduces it without adding any access to the private property or propertyAccessor. If it is not needed, please remove it.
  • Please provide a sample configuration and instructions to test it.

@afabiani
Copy link
Member Author

@offtherailz please see below:

  • responseMode and allowBearerToken ok to be removed
  • sendClientSecret and usePKCE ok to be documented
  • jwkURI should contain the url for the JWT SHA keys validation. This is used by the token store automatically. The validation is optional but strongly recommended. The store will be instantiated accordingly and used to validate the JWT token.
  • All the parameters you listed above must be set in order to correctly configure the oidc connect. Most of them are tipically available through the .well_knwon endpoints

@tdipisa
Copy link
Member

tdipisa commented Mar 15, 2024

@afabiani is this ready for a new review or are you still working on it?

@afabiani
Copy link
Member Author

Nope, no other additions on my side

@tdipisa tdipisa modified the milestones: 2024.01.00, 2024.02.00 Mar 18, 2024
@offtherailz
Copy link
Member

offtherailz commented Mar 18, 2024

trying to set it up in mapstore I received the following error:

@afabiani , do you know how to fix it?

Offending resource: URL [jar:file:/home/offtherailz/work/projects/MapStore2/product/target/apache-tomcat-8.5.69/webapps/mapstore/WEB-INF/lib/geostore-rest-impl-2.2-SNAPSHOT.jar!/applicationContext.xml]; nested exception is org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Filter beans '<oidcOpenIdFilter>' and '<googleOpenIdFilter>' have the same 'order' value. When using custom filters, please make sure the positions do not conflict with default filters. Alternatively you can disable the default filters by removing the corresponding child elements from <http> and avoiding the use of <http auto-config='true'>.
Offending resource: URL [file:/home/offtherailz/work/projects/MapStore2/product/target/apache-tomcat-8.5.69/webapps/mapstore/WEB-INF/classes/geostore-spring-security.xml]
	at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:72) ~[spring-beans-5.3.18.jar:5.3.18]
	at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:119) ~[spring-beans-5.3.18.jar:5.3.18]
	at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:104) ~[spring-beans-5.3.18.jar:5.3.18]
	at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.importBeanDefinitionResource(DefaultBeanDefinitionDocumentReader.java:240) ~[spring-beans-5.3.18.jar:5.3.18]
	at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseDefaultElement(DefaultBeanDefinitionDocumentReader.java:191) ~[spring-beans-5.3.18.jar:5.3.18]
	at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:176) ~[spring-beans-5.3.18.jar:5.3.18]
	at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:149) ~[spring-beans-5.3.18.jar:5.3.18]
	at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:96) ~[spring-beans-5.3.18.jar:5.3.18]
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:511) ~[spring-beans-5.3.18.jar:5.3.18]
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:391) ~[spring-beans-5.3.18.jar:5.3.18]
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:338) ~[spring-beans-5.3.18.jar:5.3.18]
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:310) ~[spring-beans-5.3.18.jar:5.3.18]
	at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:196) ~[spring-beans-5.3.18.jar:5.3.18]
	at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:232) ~[spring-beans-5.3.18.jar:5.3.18]
	at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:203) ~[spring-beans-5.3.18.jar:5.3.18]
	at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:125) ~[spring-web-5.3.18.jar:5.3.18]
	at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:94) ~[spring-web-5.3.18.jar:5.3.18]
	at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130) ~[spring-context-5.3.18.jar:5.3.18]
	at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:671) ~[spring-context-5.3.18.jar:5.3.18]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:553) ~[spring-context-5.3.18.jar:5.3.18]
	at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:401) ~[spring-web-5.3.18.jar:5.3.18]
	at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:292) ~[spring-web-5.3.18.jar:5.3.18]
	at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:103) ~[spring-web-5.3.18.jar:5.3.18]
	at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4763) ~[catalina.jar:8.5.69]
	at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5232) ~[catalina.jar:8.5.69]
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[catalina.jar:8.5.69]
	at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:755) ~[catalina.jar:8.5.69]
	at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:729) ~[catalina.jar:8.5.69]
	at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:695) ~[catalina.jar:8.5.69]
	at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1016) ~[catalina.jar:8.5.69]
	at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1903) ~[catalina.jar:8.5.69]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[?:?]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
	at java.lang.Thread.run(Thread.java:829) ~[?:?]

@afabiani
Copy link
Member Author

@offtherailz try to update the geostore-spring-security.xml like this

<security:http auto-config="true" create-session="never" >
		<security:http-basic entry-point-ref="restAuthenticationEntryPoint"/>
		<security:csrf disabled="true"/>
		<security:custom-filter ref="authenticationTokenProcessingFilter" before="FORM_LOGIN_FILTER"/>
		<security:custom-filter ref="sessionTokenProcessingFilter" after="FORM_LOGIN_FILTER"/>
		<security:custom-filter ref="keycloakFilter" before="BASIC_AUTH_FILTER"/>
		<security:custom-filter ref="googleOpenIdFilter" after="BASIC_AUTH_FILTER"/>
		<security:custom-filter ref="oidcOpenIdFilter" before="OPENID_FILTER"/>
		<security:anonymous />
	</security:http>

@@ -27,6 +27,7 @@
<security:custom-filter ref="sessionTokenProcessingFilter" after="FORM_LOGIN_FILTER"/>
<security:custom-filter ref="keycloakFilter" before="BASIC_AUTH_FILTER"/>
<security:custom-filter ref="googleOpenIdFilter" after="BASIC_AUTH_FILTER"/>
<security:custom-filter ref="oidcOpenIdFilter" after="BASIC_AUTH_FILTER"/>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<security:custom-filter ref="oidcOpenIdFilter" after="BASIC_AUTH_FILTER"/>
<security:custom-filter ref="oidcOpenIdFilter" before="OPENID_FILTER"/>

@offtherailz
Copy link
Member

offtherailz commented Mar 19, 2024

@afabiani notes for testing

keycloak

docker pull quay.io/keycloak/keycloak:latest

docker run --name keycloak_server -p 8180:8180 \ -e KEYCLOAK_ADMIN=admin \ -e KEYCLOAK_ADMIN_PASSWORD=password \ quay.io/keycloak/keycloak:latest \ start-dev \ --http-port=8180

configure keycloak

keycloak new realm (named mapstore)

Save

Create a new client in the mapstore realm

Configure and run mapstore

  • Clone mapstore from master

Integrate new oidc

Configure MapStore front-end for Google OpenID
Add an entry for oidc in authenticationProviders inside web/client/configs/localConfig.json file.

{
    "authenticationProviders": [
      {
        "type": "openID",
        "provider": "oidc"
      },
      {
        "type": "basic",
        "provider": "geostore"
      }
    ]
}

Add to product/config/db/geostore-spring-security-db.xml the same changes for geostore-security-context.xml

<security:http auto-config="true" create-session="never">
[...]
<security:custom-filter ref="oidcOpenIdFilter" before="OPENID_FILTER"/>
[...]

 </security:http>
[...]
<bean id="oidcSecurityConfiguration" class="it.geosolutions.geostore.services.rest.security.oauth2.openid_connect.OpenIdConnectSecurityConfiguration"/>
  1. build geostore (from your PR)
mvn clean install  -Pextjs,postgres,h2 -DskipTests

(This allows to use proper jars for 2.2-SNAPSHOT from mapstore master )

  1. create a datadir folder and a file in inside the folder, with the configuration for oidc:

datadir/mapstore-ovr.properties

# Client Secret xBRWNDXiAC4fPp8l89hGCBY6QGDPDDqa

# enables the keycloak OpenID Connect filter

oidcOAuth2Config.enabled=true

# note: this is the client id you have created in Keycloak

oidcOAuth2Config.clientId=mapstore

oidcOAuth2Config.clientSecret=xBRWNDXiAC4fPp8l89hGCBY6QGDPDDqa

oidcOAuth2Config.discoveryUrl=http://localhost:8180/realms/mapstore/.well-known/openid-configuration

oidcOAuth2Config.sendClientSecret=true
 

oidcOAuth2Config.redirectUri=http://localhost:8081/rest/geostore/openid/oidc/callback

# Internal redirect URI (you can set it to relative path like this `../../..` to make this config work across domain)

oidcOAuth2Config.internalRedirectUri=https://localhost:8081/

Run and debug mapstore

edit product/pom.xml to configure data dir

# product/pom.xml
<containerId>tomcat8x</containerId>

+ <systemProperties>
+ <datadir.location>${env.DATADIR}</datadir.location>
+ </systemProperties>

open 2 consoles

  1. console 1: run mapstore front-end
# MapStore front-end
# export MAPSTORE_BACKEND_PORT=8082
npm run fe:start
  1. console 2: run backend (this will run mvn clean install and get the proper geostore.war with proper jar from previous build)
# export MAPSTORE_BACKEND_PORT=8082
## Change with path to your data dir.
export DATADIR=/home/offtherailz/work/projects/MapStore2/datadir 
# debug (opt)
# export MAVEN_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n"
# run backend.
npm run be:start

Container logs

  • Available at:

/product/target/apache-tomcat-8.5.69/logs/mapstore.logs

  • Log Level:

java/web/src/main/resources/log4j2.properties

@afabiani
Copy link
Member Author

@offtherailz see here

Copy link
Member

@offtherailz offtherailz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This integration works well and satisfies the minimum requirement to integrate login. Tested with keycloak.

@offtherailz offtherailz merged commit 7749095 into master Apr 3, 2024
2 checks passed
@afabiani afabiani deleted the oidc_provider branch April 3, 2024 15:12
@tdipisa
Copy link
Member

tdipisa commented Apr 3, 2024

@offtherailz @afabiani, once tested and accepted, this need to be backported to 2.1.x branch for MS 2024.01.01
@offtherailz I suppose there is something to be included in the MS dev guide for this. Isn't it? Please confirm.

@offtherailz
Copy link
Member

@tdipisa this was assigned to 2024.02.00 as far as I see
I also created geosolutions-it/MapStore2#10151 and geosolutions-it/MapStore2#10152 in the same milestone.
Please let us know if there is a change in plans.

@tdipisa tdipisa modified the milestones: 2024.02.00, 2024.01.01 Apr 3, 2024
@tdipisa
Copy link
Member

tdipisa commented Apr 3, 2024

@tdipisa this was assigned to 2024.02.00 as far as I see I also created geosolutions-it/MapStore2#10151 and geosolutions-it/MapStore2#10152 in the same milestone. Please let us know if there is a change in plans.

milestone changed according to #330 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

OpenID generic provider support
3 participants