This is an automated email from the git hooks/post-receive script. New commit to branch keycloak in repository pollen. See https://gitlab.nuiton.org/chorem/pollen.git commit c9d9752d5c60beb3de83bf91a7925422cb157e22 Author: Sylvain Bavencoff <bavencoff@codelutin.com> Date: Thu Jul 27 15:48:28 2017 +0200 mise en place de keycloak --- pollen-rest-api/pom.xml | 5 ++ .../pollen/rest/api/PollenKeycloakFilter.java | 26 ++++++++++ .../rest/api/PollenRestApiRequestFilter.java | 45 ++++++++++++----- .../org/chorem/pollen/rest/api/v1/AuthApi.java | 4 ++ .../src/main/webapp/WEB-INF/keycloak.json | 12 +++++ pollen-rest-api/src/main/webapp/WEB-INF/web.xml | 11 +++- pollen-services/pom.xml | 12 +++++ pollen-services/src/main/config/PollenServices.ini | 22 +++++++- .../services/config/PollenServicesConfig.java | 21 ++++++++ .../service/security/KeycloakSecurityService.java | 40 +++++++++++++++ .../services/service/ssl/LazySSLSocketFactory.java | 58 ++++++++++++++++++++++ .../services/service/ssl/LazyTrustManager.java | 24 +++++++++ .../i18n/pollen-services_en_GB.properties | 4 ++ .../i18n/pollen-services_fr_FR.properties | 4 ++ pollen-ui-riot-js/package.json | 3 +- pollen-ui-riot-js/src/main/web/conf.js | 7 ++- pollen-ui-riot-js/src/main/web/js/FetchService.js | 7 ++- pollen-ui-riot-js/src/main/web/js/Session.js | 39 +++++++++++++++ pollen-ui-riot-js/src/main/web/tag/Pollen.tag.html | 15 +++++- .../src/main/web/tag/PollenHeader.tag.html | 2 +- pom.xml | 24 +++++++++ 21 files changed, 365 insertions(+), 20 deletions(-) diff --git a/pollen-rest-api/pom.xml b/pollen-rest-api/pom.xml index 1509b6fd..658cb066 100644 --- a/pollen-rest-api/pom.xml +++ b/pollen-rest-api/pom.xml @@ -225,6 +225,11 @@ </dependency> <dependency> + <groupId>org.keycloak</groupId> + <artifactId>keycloak-servlet-filter-adapter</artifactId> + </dependency> + + <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> diff --git a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/PollenKeycloakFilter.java b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/PollenKeycloakFilter.java new file mode 100644 index 00000000..8c7af8d7 --- /dev/null +++ b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/PollenKeycloakFilter.java @@ -0,0 +1,26 @@ +package org.chorem.pollen.rest.api; + +import org.keycloak.adapters.servlet.KeycloakOIDCFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class PollenKeycloakFilter extends KeycloakOIDCFilter { + + @Override + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) req; + if ("OPTIONS".equals(request.getMethod())) { + chain.doFilter(req, res); + } else { + super.doFilter(req, res, chain); + } + } +} diff --git a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/PollenRestApiRequestFilter.java b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/PollenRestApiRequestFilter.java index 5ee83aa9..66bdfef8 100644 --- a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/PollenRestApiRequestFilter.java +++ b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/PollenRestApiRequestFilter.java @@ -48,11 +48,14 @@ import org.chorem.pollen.services.service.VoteCountingTypeService; import org.chorem.pollen.services.service.VoteService; import org.chorem.pollen.services.service.VoterListService; import org.chorem.pollen.services.service.mail.EmailService; +import org.chorem.pollen.services.service.security.KeycloakSecurityService; import org.chorem.pollen.services.service.security.PollenCypherTechnicalException; import org.chorem.pollen.services.service.security.PollenInvalidSessionTokenException; import org.chorem.pollen.services.service.security.PollenSecurityContext; import org.chorem.pollen.services.service.security.SecurityService; import org.jboss.resteasy.spi.ResteasyProviderFactory; +import org.keycloak.KeycloakPrincipal; +import org.keycloak.representations.AccessToken; import javax.ws.rs.HttpMethod; import javax.ws.rs.container.ContainerRequestContext; @@ -123,7 +126,7 @@ public class PollenRestApiRequestFilter implements ContainerRequestFilter, Conta if (StringUtils.isNotBlank(requestHeaders)) { headers.add(HEADER_ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders); } else { - headers.add(HEADER_ACCESS_CONTROL_ALLOW_HEADERS, "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With, " + REQUEST_HEADER_UI_CONTEXT); + headers.add(HEADER_ACCESS_CONTROL_ALLOW_HEADERS, "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With, " + REQUEST_HEADER_UI_CONTEXT + "," + REQUEST_HEADER_SESSION_TOKEN); } } @@ -191,28 +194,46 @@ public class PollenRestApiRequestFilter implements ContainerRequestFilter, Conta private PollenSecurityContext createSecurityContext(ContainerRequestContext context, PollenRestApiApplicationContext applicationContext, PollenServiceContext serviceContext) throws PollenInvalidSessionTokenException, PollenCypherTechnicalException { + SessionToken sessionToken = null; SecurityService securityService = serviceContext.newService(SecurityService.class); - // --- get session token (from request parameters) --- // - String sessionTokenHeader = context.getHeaderString(REQUEST_HEADER_SESSION_TOKEN); + if (applicationContext.getApplicationConfig().isKeycloakEnabled()) { - if (StringUtils.isEmpty(sessionTokenHeader)) { + KeycloakPrincipal principal = (KeycloakPrincipal) context.getSecurityContext().getUserPrincipal(); - // --- get session token (from request cookies) --- // - Cookie cookie = context.getCookies().get(COOKIE_POLLEN_AUTH); - if (cookie != null) { + if (principal != null) { - if (log.isDebugEnabled()) { - log.debug("Found pollen-auth cookie:: " + cookie.getValue()); + AccessToken token = principal.getKeycloakSecurityContext().getToken(); + + KeycloakSecurityService keycloakSecurityService = serviceContext.newService(KeycloakSecurityService.class); + + sessionToken = keycloakSecurityService.getSessionToken(token); + + } + + } else { + + // --- get session token (from request parameters) --- // + String sessionTokenHeader = context.getHeaderString(REQUEST_HEADER_SESSION_TOKEN); + + if (StringUtils.isEmpty(sessionTokenHeader)) { + + // --- get session token (from request cookies) --- // + Cookie cookie = context.getCookies().get(COOKIE_POLLEN_AUTH); + if (cookie != null) { + + if (log.isDebugEnabled()) { + log.debug("Found pollen-auth cookie:: " + cookie.getValue()); + } + sessionTokenHeader = securityService.decrypt(cookie.getValue()); } - sessionTokenHeader = securityService.decrypt(cookie.getValue()); + } + sessionToken = securityService.getSessionTokenByToken(sessionTokenHeader); } - SessionToken sessionToken = securityService.getSessionTokenByToken(sessionTokenHeader); - // --- get mainPrincipal (from request parameters) --- // String permission = null; List<String> permissions = context.getUriInfo().getQueryParameters().get(REQUEST_PERMISSION_PARAMETER); diff --git a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/AuthApi.java b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/AuthApi.java index b6eb9330..fb9a1a5d 100644 --- a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/AuthApi.java +++ b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/AuthApi.java @@ -67,7 +67,11 @@ public class AuthApi { private static final Log log = LogFactory.getLog(AuthApi.class); public static final String COOKIE_POLLEN_AUTH = "pollen-auth"; + private static final String COOKIE_POLLEN_CONNECTED = "pollen-connected"; + + public static final String COOKIE_POLLEN_KEYCLOAK_CODE = "pollen-keycloak-code"; + private final static int COOKIE_MAX_AGE = 60 * 60 * 24 * 365; // 1 year public static Response.ResponseBuilder removeAuthCookie(Response.ResponseBuilder reponseBuilder) { diff --git a/pollen-rest-api/src/main/webapp/WEB-INF/keycloak.json b/pollen-rest-api/src/main/webapp/WEB-INF/keycloak.json new file mode 100644 index 00000000..faf5e37d --- /dev/null +++ b/pollen-rest-api/src/main/webapp/WEB-INF/keycloak.json @@ -0,0 +1,12 @@ +{ + "realm" : "Pollen", + "realm-public-key" : "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArN3gue13jJBaFP+oZkAAIok+ksgeB3sv5aWujSu04Y+IsT+ZF/W1obocuwkHVvxcXN/wLlTE+SgT3vo7s1st+XokI2zhjPoJM8tpUFoOiSeeaO/jgG/9B+bRiY9R8KieZe+yZsqmHuOsQb0zR2RCfw29po95FKN0uql90uZ0eSSnE8zCN7BMpj39ElC3ui/FEG8MVQKm1ECZ2RcGfbifMCIjmeHYaHjXp0pp6uz1cOH1h0pOQJ1qnAlfwRW6EwPZT5oyNPbOJSg+1PIaNw5BrrZhtxTFe2CkhEahyillsMxCHR1s7pPy7K7qpCPv+5TJgTnzkwWXcIfxK0GT/fM9HQIDAQAB", + "bearer-only" : true, + "auth-server-url" : "http://localhost:8088/auth", + "ssl-required" : "external", + "resource" : "pollen-back", + "use-resource-role-mappings" : false, + "credentials" : { + "secret" : "acadc8bb-c09c-47cb-8e96-aab3de98b6b0" + } +} diff --git a/pollen-rest-api/src/main/webapp/WEB-INF/web.xml b/pollen-rest-api/src/main/webapp/WEB-INF/web.xml index d8a94176..67f8d25d 100644 --- a/pollen-rest-api/src/main/webapp/WEB-INF/web.xml +++ b/pollen-rest-api/src/main/webapp/WEB-INF/web.xml @@ -27,7 +27,16 @@ http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name>Pollen REST Api</display-name> - + + <filter> + <filter-name>Keycloak Filter</filter-name> + <filter-class>org.chorem.pollen.rest.api.PollenKeycloakFilter</filter-class> + </filter> + <filter-mapping> + <filter-name>Keycloak Filter</filter-name> + <url-pattern>/*</url-pattern> + </filter-mapping> + <filter> <filter-name>topiaTransaction</filter-name> <filter-class> diff --git a/pollen-services/pom.xml b/pollen-services/pom.xml index a17b864f..a78d5484 100644 --- a/pollen-services/pom.xml +++ b/pollen-services/pom.xml @@ -213,6 +213,18 @@ <groupId>org.jboss.spec.javax.ws.rs</groupId> <artifactId>jboss-jaxrs-api_2.0_spec</artifactId> </dependency> + + <dependency> + <groupId>org.keycloak</groupId> + <artifactId>keycloak-authz-client</artifactId> + </dependency> + + <dependency> + <groupId>org.keycloak</groupId> + <artifactId>keycloak-core</artifactId> + </dependency> + + </dependencies> diff --git a/pollen-services/src/main/config/PollenServices.ini b/pollen-services/src/main/config/PollenServices.ini index 93c92d2c..9224f5ce 100644 --- a/pollen-services/src/main/config/PollenServices.ini +++ b/pollen-services/src/main/config/PollenServices.ini @@ -181,4 +181,24 @@ type = String description = pollen.configuration.feedback.locale key = pollen.feedback.locale type = String -defaultValue = en \ No newline at end of file +defaultValue = en + +[option keycloakServer] +description = pollen.configuration.keycloak.server +key = pollen.keycloak.server +type = String + +[option keycloakRealm] +description = pollen.configuration.keycloak.realm +key = pollen.keycloak.realm +type = String + +[option keycloakResource] +description = pollen.configuration.keycloak.resource +key = pollen.keycloak.resource +type = String + +[option keycloakSecret] +description = pollen.configuration.keycloak.secret +key = pollen.keycloak.secret +type = String \ No newline at end of file diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/config/PollenServicesConfig.java b/pollen-services/src/main/java/org/chorem/pollen/services/config/PollenServicesConfig.java index bca8228d..5839af78 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/config/PollenServicesConfig.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/config/PollenServicesConfig.java @@ -33,9 +33,11 @@ import org.chorem.pollen.persistence.entity.PollType; import org.chorem.pollen.persistence.entity.ResultVisibility; import org.chorem.pollen.persistence.entity.VoteVisibility; import org.chorem.pollen.services.PollenTechnicalException; +import org.keycloak.authorization.client.Configuration; import org.nuiton.config.ApplicationConfig; import org.nuiton.config.ArgumentsParserException; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; @@ -147,4 +149,23 @@ public class PollenServicesConfig extends GeneratedPollenServicesConfig { public Locale getFeedbackLocale() { return Locale.forLanguageTag(get().getOption(PollenServicesConfigOption.LOCALE_FEEDBACK.getKey())); } + + public boolean isKeycloakEnabled() { + return getKeycloakServer() != null; + } + + public Configuration getKeycloackConfig() { + Configuration result = null; + + if (isKeycloakEnabled()) { + result = new Configuration( + getKeycloakServer(), + getKeycloakRealm(), + getKeycloakResource(), + Collections.singletonMap("secret", getKeycloakSecret()), + null); + + } + return result; + } } diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/security/KeycloakSecurityService.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/security/KeycloakSecurityService.java new file mode 100644 index 00000000..781d4a23 --- /dev/null +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/security/KeycloakSecurityService.java @@ -0,0 +1,40 @@ +package org.chorem.pollen.services.service.security; + +import com.google.common.base.Optional; +import org.chorem.pollen.persistence.entity.PollenUser; +import org.chorem.pollen.persistence.entity.PollenUserImpl; +import org.chorem.pollen.persistence.entity.SessionToken; +import org.chorem.pollen.persistence.entity.SessionTokenImpl; +import org.chorem.pollen.services.service.PollenServiceSupport; +import org.keycloak.representations.AccessToken; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class KeycloakSecurityService extends PollenServiceSupport { + + public SessionToken getSessionToken(AccessToken accessToken) { + + SessionToken sessionToken = new SessionTokenImpl(); + + Optional<PollenUser> pollenUserOptional = getPollenUserDao().forEmailEquals(accessToken.getEmail()).tryFindUnique(); + PollenUser pollenUser = pollenUserOptional.or(() -> createFromAccessToken(accessToken)); + + sessionToken.setPollenUser(pollenUser); + return sessionToken; + } + + protected PollenUser createFromAccessToken(AccessToken accessToken) { + + PollenUser user = new PollenUserImpl(); + user.setEmail(accessToken.getEmail()); + user.setName(accessToken.getName()); + user.setAdministrator(accessToken.getRealmAccess().isUserInRole("pollen-administrator")); + user.isEmailValidated(); + user = getPollenUserDao().create(user); + commit(); + return user; + } + + +} diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/ssl/LazySSLSocketFactory.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/ssl/LazySSLSocketFactory.java new file mode 100644 index 00000000..c6561287 --- /dev/null +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/ssl/LazySSLSocketFactory.java @@ -0,0 +1,58 @@ +package org.chorem.pollen.services.service.ssl; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class LazySSLSocketFactory extends SSLSocketFactory { + private SSLSocketFactory factory; + public LazySSLSocketFactory() { + try { + SSLContext sslcontext = SSLContext.getInstance("TLS"); + sslcontext.init( + null, // No KeyManager required + new TrustManager[] { new LazyTrustManager() }, + new java.security.SecureRandom()); + factory = (SSLSocketFactory) sslcontext.getSocketFactory(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + public static SocketFactory getDefault() { + return new LazySSLSocketFactory(); + } + public Socket createSocket() throws IOException { + return factory.createSocket(); + } + public Socket createSocket(Socket socket, String s, int i, boolean flag) + throws IOException { + return factory.createSocket(socket, s, i, flag); + } + public Socket createSocket(InetAddress inaddr, int i, InetAddress inaddr1, + int j) throws IOException { + return factory.createSocket(inaddr, i, inaddr1, j); + } + public Socket createSocket(InetAddress inaddr, int i) throws IOException { + return factory.createSocket(inaddr, i); + } + public Socket createSocket(String s, int i, InetAddress inaddr, int j) + throws IOException { + return factory.createSocket(s, i, inaddr, j); + } + public Socket createSocket(String s, int i) throws IOException { + return factory.createSocket(s, i); + } + public String[] getDefaultCipherSuites() { + return factory.getSupportedCipherSuites(); + } + public String[] getSupportedCipherSuites() { + return factory.getSupportedCipherSuites(); + } +} diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/ssl/LazyTrustManager.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/ssl/LazyTrustManager.java new file mode 100644 index 00000000..da5ffc74 --- /dev/null +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/ssl/LazyTrustManager.java @@ -0,0 +1,24 @@ +package org.chorem.pollen.services.service.ssl; + +import javax.net.ssl.X509TrustManager; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * @author Sylvain Bavencoff - bavencoff@codelutin.com + */ +public class LazyTrustManager implements X509TrustManager { + public boolean isClientTrusted(X509Certificate[] cert) { + return true; + } + public boolean isServerTrusted(X509Certificate[] cert) { + return true; + } + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + public void checkClientTrusted(X509Certificate[] arg0, String arg1) + throws CertificateException {} + public void checkServerTrusted(X509Certificate[] arg0, String arg1) + throws CertificateException {} +} diff --git a/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties b/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties index 1ba0fd60..45dab200 100644 --- a/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties +++ b/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties @@ -21,6 +21,10 @@ pollen.configuration.defaultVoteVisibility=Default vote visiblity pollen.configuration.devMode=Dev mode pollen.configuration.feedback.locale=locale to send feedback pollen.configuration.feedback.mails=mails to send feedback +pollen.configuration.keycloak.realm= +pollen.configuration.keycloak.resource= +pollen.configuration.keycloak.secret= +pollen.configuration.keycloak.server= pollen.configuration.logConfigurationFile=Path to log configuration file pollen.configuration.registration.emailAddressPattern=Regular expression that the user email address must match for registration pollen.configuration.report.maxScore=Maximum score for reporting before administrators are notified diff --git a/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties b/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties index 497bc4ab..d33b8118 100644 --- a/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties +++ b/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties @@ -21,6 +21,10 @@ pollen.configuration.defaultVoteVisibility=Visibilité des votes par défaut pollen.configuration.devMode=Mode développement pollen.configuration.feedback.locale=La locale pour envoyer les retours utlisateur pollen.configuration.feedback.mails=Courriel destinataires des retours utilisateur +pollen.configuration.keycloak.realm= +pollen.configuration.keycloak.resource= +pollen.configuration.keycloak.secret= +pollen.configuration.keycloak.server= pollen.configuration.logConfigurationFile=Chemin vers le fichier de configuration des logs pollen.configuration.registration.emailAddressPattern=Expression régulière que doivent vérifier les adresses email des utilisateurs lors de l'inscription pollen.configuration.report.maxScore=Score maximum pour un signalement avant que les administrateurs soient avertis diff --git a/pollen-ui-riot-js/package.json b/pollen-ui-riot-js/package.json index 27c1099c..ac343781 100644 --- a/pollen-ui-riot-js/package.json +++ b/pollen-ui-riot-js/package.json @@ -24,7 +24,7 @@ } ], "scripts": { - "start": "webpack-dev-server --hot --inline --host 0.0.0.0 --public localhost:8080", + "start": "webpack-dev-server --hot --inline --host 0.0.0.0 --public felteu:8080", "package": "webpack --bail" }, "devDependencies": { @@ -45,6 +45,7 @@ "console.history": "^1.5.0", "font-awesome": "4.7.0", "html2canvas": "^0.5.0-beta4", + "keycloak": "^1.2.0", "moment": "^2.17.1", "nprogress": "^0.2.0", "object.values": "^1.0.4", diff --git a/pollen-ui-riot-js/src/main/web/conf.js b/pollen-ui-riot-js/src/main/web/conf.js index 5b5da95d..422b3499 100644 --- a/pollen-ui-riot-js/src/main/web/conf.js +++ b/pollen-ui-riot-js/src/main/web/conf.js @@ -3,5 +3,10 @@ window.pollenConf = { piwikUrl: "", // add the piwik url, eg: http://localhost/piwik piwikSiteId: "", // add the site id, eg: 3 defaultMessageTimeout: 15, - resourceMaxSize: 10000000 // octets => 10 Mo + resourceMaxSize: 10000000, // octets => 10 Mo + keycloak: { + url: "http://localhost:8088/auth", + realm: "Pollen", + clientId: "pollen" + } }; diff --git a/pollen-ui-riot-js/src/main/web/js/FetchService.js b/pollen-ui-riot-js/src/main/web/js/FetchService.js index 64146ff7..51b284e2 100644 --- a/pollen-ui-riot-js/src/main/web/js/FetchService.js +++ b/pollen-ui-riot-js/src/main/web/js/FetchService.js @@ -21,6 +21,7 @@ let bus = require("./PollenBus.js"); + class FetchService { constructor() { @@ -28,11 +29,15 @@ class FetchService { } fetch(url, method, headers, body) { + let session = require("./Session"); headers = headers || {}; if (!(body instanceof FormData)) { headers["Content-Type"] = "application/json;charset=UTF-8"; } - headers["X-Pollen-UI-context"] = JSON.stringify(require("./Session").pollenUIContext); + headers["X-Pollen-UI-context"] = JSON.stringify(session.pollenUIContext); + if (session.keycloak && session.keycloak.token) { + headers.Authorization = "Bearer " + session.keycloak.token; + } let loadEvent = {}; bus.trigger("loading", loadEvent); diff --git a/pollen-ui-riot-js/src/main/web/js/Session.js b/pollen-ui-riot-js/src/main/web/js/Session.js index 67fed2ef..a3991d9d 100644 --- a/pollen-ui-riot-js/src/main/web/js/Session.js +++ b/pollen-ui-riot-js/src/main/web/js/Session.js @@ -24,6 +24,7 @@ let authService = require("./AuthService"); let bus = require("./PollenBus.js"); let logger = require("./Logger"); let pageTracker = require("./PageTracker"); +let Keycloak = require("keycloak"); class Session { @@ -75,6 +76,37 @@ class Session { this.user = null; bus.trigger("user", this.user); }); + + + if (this.configuration.keycloak) { + this.keycloak = new Keycloak(this.configuration.keycloak); + this.keycloak.init({checkLoginIframe: false}).success(authenticated => { + if (authenticated) { + this.keycloak.loadUserProfile().success(user => { + this.user = { + name: user.username, + administrator: this.keycloak.hasRealmRole("pollen-administrator"), + email: user.email, + isDisabled: !user.enabled, + isBanned: false, + emailIsValidate: user.emailVerified, + language: user.toLocaleString() + }; + document.cookie = "pollen-connected=true"; + bus.trigger("user", this.user); + logger.info(user); + }).error(error => { + logger.error("failed to load user", error); + }); + } else { + this.user = null; + bus.trigger("user", this.user); + } + logger.info(authenticated ? "authenticated" : "not authenticated"); + }).error(() => { + logger.error("failed to initialize"); + }); + } } start() { @@ -147,6 +179,9 @@ class Session { } signOut() { + if (this.configuration.keycloak) { + this.keycloak.logout({redirectUri: window.location.href}); + } return authService.signOut().then(() => { this.user = null; bus.trigger("user", this.user); @@ -154,6 +189,10 @@ class Session { }); } + keycloakLogin() { + this.keycloak.login({redirectUri: window.location.href}); + } + } module.exports = singleton(Session); diff --git a/pollen-ui-riot-js/src/main/web/tag/Pollen.tag.html b/pollen-ui-riot-js/src/main/web/tag/Pollen.tag.html index 8825148a..a2baf8af 100644 --- a/pollen-ui-riot-js/src/main/web/tag/Pollen.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/Pollen.tag.html @@ -73,7 +73,13 @@ require("./popup/InformationPopup.tag.html"); this.on("mount", () => { this.listen("locale", this.onLocaleChange); this.listen("unauthorize", this.refs.signIn.open); - this.listen("signIn", this.refs.signIn.open); + this.listen("signIn", () => { + if (session.configuration.keycloak) { + session.keycloakLogin(); + } else { + this.refs.signIn.open(); + } + }); this.listen("closeSignIn", this.refs.signIn.close); }); @@ -190,7 +196,7 @@ require("./popup/InformationPopup.tag.html"); riot.mount(this.refs.content, "editpoll", {pollId: pollId, permission: permission, clone: true}); }); route("/poll/*/summary/*", (pollId, permission) => { - riot.mount(this.refs.content, "editpoll", {pollId: pollId, permission: permission, showSummary:true}); + riot.mount(this.refs.content, "editpoll", {pollId: pollId, permission: permission, showSummary: true}); }); route("/poll/*", (pollId) => { riot.mount(this.refs.content, "poll", {pollId: pollId}); @@ -217,6 +223,11 @@ require("./popup/InformationPopup.tag.html"); riot.mount(this.refs.content, "favoritelist", {favoriteListId: favoriteListId}); }); + route("code=*", code => { + session.keycloakLogin(code); + route(""); + }); + route(() => { this.bus.trigger("pageChanged", "home"); riot.mount(this.refs.content, "home"); diff --git a/pollen-ui-riot-js/src/main/web/tag/PollenHeader.tag.html b/pollen-ui-riot-js/src/main/web/tag/PollenHeader.tag.html index 14e3003e..58058ae2 100644 --- a/pollen-ui-riot-js/src/main/web/tag/PollenHeader.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/PollenHeader.tag.html @@ -52,7 +52,7 @@ require("./popup/FeedbackModal.tag.html"); </div> <div if="{!user}"> - <a class="header-link" onclick="{signIn}"> + <a class="header-link" onclick={signIn}> <i class="fa fa-sign-in" aria-hidden="true" /><span class="action-label">{__.signin}</span> </a> </div> diff --git a/pom.xml b/pom.xml index bcc9711a..013fd7e5 100644 --- a/pom.xml +++ b/pom.xml @@ -197,6 +197,8 @@ <seleniumVersion>2.33.0</seleniumVersion> <httpCommonsHttpclientVersion>4.5.2</httpCommonsHttpclientVersion> + <keycloakVersion>3.2.0.Final</keycloakVersion> + <pollenI18nBundle>pollen-i18n</pollenI18nBundle> <!-- license to use --> <license.licenseName>agpl_v3</license.licenseName> @@ -723,6 +725,28 @@ <scope>test</scope> </dependency> + <dependency> + <groupId>org.keycloak</groupId> + <artifactId>keycloak-authz-client</artifactId> + <version>${keycloakVersion}</version> + </dependency> + + <dependency> + <groupId>org.keycloak</groupId> + <artifactId>keycloak-core</artifactId> + <version>${keycloakVersion}</version> + </dependency> + + <dependency> + <groupId>org.keycloak</groupId> + <artifactId>keycloak-servlet-filter-adapter</artifactId> + <version>${keycloakVersion}</version> + </dependency> + + + + + </dependencies> </dependencyManagement> -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.