From 292d3d0fb36ca358a2741343f446bf7bcfcd66d7 Mon Sep 17 00:00:00 2001 From: Maarten Mulders Date: Tue, 2 Jul 2024 23:16:54 +0200 Subject: [PATCH] feat: Require logging in for secure area --- app/adapters/web/pom.xml | 23 ++++++++++--- .../traqqr/web/security/OidcConfig.java | 25 ++++++++++++++ .../web/security/SecurityConfiguration.java | 23 +++++++++++++ .../web/security/TraqqrIdentityStore.java | 26 ++++++++++++++ .../mulders/traqqr/web/user/UserInfoBean.java | 32 ++++++++++++++++++ .../web/src/main/webapp/META-INF/beans.xml | 5 +++ .../main/webapp/WEB-INF/layout-closed.xhtml | 24 +++++++++++++ .../{layout.xhtml => layout-open.xhtml} | 3 +- .../web/src/main/webapp/WEB-INF/web.xml | 17 ++++++++++ app/adapters/web/src/main/webapp/index.xhtml | 2 +- .../web/src/main/webapp/privacy.xhtml | 2 +- .../web/src/main/webapp/secure/index.xhtml | 16 +++++++++ .../liberty/config/google-client-keystore.p12 | Bin 0 -> 1667 bytes runtime/src/main/liberty/config/server.xml | 3 +- 14 files changed, 193 insertions(+), 8 deletions(-) create mode 100644 app/adapters/web/src/main/java/it/mulders/traqqr/web/security/OidcConfig.java create mode 100644 app/adapters/web/src/main/java/it/mulders/traqqr/web/security/SecurityConfiguration.java create mode 100644 app/adapters/web/src/main/java/it/mulders/traqqr/web/security/TraqqrIdentityStore.java create mode 100644 app/adapters/web/src/main/java/it/mulders/traqqr/web/user/UserInfoBean.java create mode 100644 app/adapters/web/src/main/webapp/META-INF/beans.xml create mode 100644 app/adapters/web/src/main/webapp/WEB-INF/layout-closed.xhtml rename app/adapters/web/src/main/webapp/WEB-INF/{layout.xhtml => layout-open.xhtml} (82%) create mode 100644 app/adapters/web/src/main/webapp/secure/index.xhtml create mode 100644 runtime/src/main/liberty/config/google-client-keystore.p12 diff --git a/app/adapters/web/pom.xml b/app/adapters/web/pom.xml index eb73c15..d33c739 100644 --- a/app/adapters/web/pom.xml +++ b/app/adapters/web/pom.xml @@ -31,13 +31,17 @@ - jakarta.faces - jakarta.faces-api + jakarta.enterprise + jakarta.enterprise.cdi-api provided - jakarta.enterprise - jakarta.enterprise.cdi-api + jakarta.el + jakarta.el-api + + + jakarta.faces + jakarta.faces-api provided @@ -45,6 +49,17 @@ jakarta.inject-api provided + + jakarta.security.enterprise + jakarta.security.enterprise-api + provided + + + jakarta.servlet + jakarta.servlet-api + provided + + org.primefaces primefaces diff --git a/app/adapters/web/src/main/java/it/mulders/traqqr/web/security/OidcConfig.java b/app/adapters/web/src/main/java/it/mulders/traqqr/web/security/OidcConfig.java new file mode 100644 index 0000000..417abfb --- /dev/null +++ b/app/adapters/web/src/main/java/it/mulders/traqqr/web/security/OidcConfig.java @@ -0,0 +1,25 @@ +package it.mulders.traqqr.web.security; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Named; + +@ApplicationScoped +@Named("oidcConfig") +public class OidcConfig { + private final String clientId; + private final String clientSecret; + + public OidcConfig() { + var environment = System.getenv(); + this.clientId = environment.get("OPENID_CLIENT_ID"); + this.clientSecret = environment.get("OPENID_CLIENT_SECRET"); + } + + public String getClientId() { + return clientId; + } + + public String getClientSecret() { + return clientSecret; + } +} diff --git a/app/adapters/web/src/main/java/it/mulders/traqqr/web/security/SecurityConfiguration.java b/app/adapters/web/src/main/java/it/mulders/traqqr/web/security/SecurityConfiguration.java new file mode 100644 index 0000000..7dd650c --- /dev/null +++ b/app/adapters/web/src/main/java/it/mulders/traqqr/web/security/SecurityConfiguration.java @@ -0,0 +1,23 @@ +package it.mulders.traqqr.web.security; + +import jakarta.security.enterprise.authentication.mechanism.http.OpenIdAuthenticationMechanismDefinition; +import jakarta.security.enterprise.authentication.mechanism.http.openid.ClaimsDefinition; +import jakarta.security.enterprise.authentication.mechanism.http.openid.LogoutDefinition; + +@OpenIdAuthenticationMechanismDefinition( + claimsDefinition = @ClaimsDefinition( + callerNameClaim = "sub" + ), + clientId = "${oidcConfig.clientId}", + clientSecret = "${oidcConfig.clientSecret}", + logout = @LogoutDefinition( + redirectURI = "${baseURL}" + ), + providerURI = "https://accounts.google.com/.well-known/openid-configuration", + redirectURI = "${baseURL}/auth/callback/google", + redirectToOriginalResource = true, + useNonce = true, + useSession = true +) +public class SecurityConfiguration { +} diff --git a/app/adapters/web/src/main/java/it/mulders/traqqr/web/security/TraqqrIdentityStore.java b/app/adapters/web/src/main/java/it/mulders/traqqr/web/security/TraqqrIdentityStore.java new file mode 100644 index 0000000..ca386bc --- /dev/null +++ b/app/adapters/web/src/main/java/it/mulders/traqqr/web/security/TraqqrIdentityStore.java @@ -0,0 +1,26 @@ +package it.mulders.traqqr.web.security; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.security.enterprise.identitystore.CredentialValidationResult; +import jakarta.security.enterprise.identitystore.IdentityStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.EnumSet; +import java.util.Set; + +@ApplicationScoped +public class TraqqrIdentityStore implements IdentityStore { + private static final Logger log = LoggerFactory.getLogger(TraqqrIdentityStore.class); + + @Override + public Set getCallerGroups(final CredentialValidationResult validationResult) { + log.info("Assigning groups; caller_unique_id={}", validationResult.getCallerUniqueId()); + return Set.of("user"); + } + + @Override + public Set validationTypes() { + return EnumSet.of(ValidationType.PROVIDE_GROUPS); + } +} diff --git a/app/adapters/web/src/main/java/it/mulders/traqqr/web/user/UserInfoBean.java b/app/adapters/web/src/main/java/it/mulders/traqqr/web/user/UserInfoBean.java new file mode 100644 index 0000000..4cd67c0 --- /dev/null +++ b/app/adapters/web/src/main/java/it/mulders/traqqr/web/user/UserInfoBean.java @@ -0,0 +1,32 @@ +package it.mulders.traqqr.web.user; + +import jakarta.enterprise.context.SessionScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import jakarta.security.enterprise.SecurityContext; +import jakarta.security.enterprise.identitystore.openid.OpenIdContext; + +import java.io.Serializable; + +@Named +@SessionScoped +public class UserInfoBean implements Serializable { + private final String displayName; + private final String profilePictureUrl; + + @Inject + public UserInfoBean(final SecurityContext securityContext, + final OpenIdContext openIdContext) { + var claims = openIdContext.getClaims(); + this.displayName = claims.getName().orElse("unknown user"); + this.profilePictureUrl = claims.getPicture().orElse(null); + } + + public String getUsername() {// name, picture, email + return displayName; + } + + public String getProfilePictureUrl() { + return profilePictureUrl; + } +} diff --git a/app/adapters/web/src/main/webapp/META-INF/beans.xml b/app/adapters/web/src/main/webapp/META-INF/beans.xml new file mode 100644 index 0000000..e7ca12d --- /dev/null +++ b/app/adapters/web/src/main/webapp/META-INF/beans.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/app/adapters/web/src/main/webapp/WEB-INF/layout-closed.xhtml b/app/adapters/web/src/main/webapp/WEB-INF/layout-closed.xhtml new file mode 100644 index 0000000..d5fa0e4 --- /dev/null +++ b/app/adapters/web/src/main/webapp/WEB-INF/layout-closed.xhtml @@ -0,0 +1,24 @@ + + + + + <ui:insert name="title">Default Title</ui:insert> | Traqqr + + + + +
+ +
+ +
+ + + Traqqr ${systemInfo.applicationVersion} (revision ${systemInfo.gitVersion}) is made with ❤️ + ☕ + Java ${systemInfo.javaVersion} + Jakarta EE 10. + Proudly running on ${systemInfo.javaRuntime}. + + + \ No newline at end of file diff --git a/app/adapters/web/src/main/webapp/WEB-INF/layout.xhtml b/app/adapters/web/src/main/webapp/WEB-INF/layout-open.xhtml similarity index 82% rename from app/adapters/web/src/main/webapp/WEB-INF/layout.xhtml rename to app/adapters/web/src/main/webapp/WEB-INF/layout-open.xhtml index 6100599..a491252 100644 --- a/app/adapters/web/src/main/webapp/WEB-INF/layout.xhtml +++ b/app/adapters/web/src/main/webapp/WEB-INF/layout-open.xhtml @@ -1,7 +1,8 @@ + xmlns:ui="http://xmlns.jcp.org/jsf/facelets" + xmlns:p="http://primefaces.org/ui"> <ui:insert name="title">Default Title</ui:insert> | Traqqr diff --git a/app/adapters/web/src/main/webapp/WEB-INF/web.xml b/app/adapters/web/src/main/webapp/WEB-INF/web.xml index d6e45fc..e58578b 100644 --- a/app/adapters/web/src/main/webapp/WEB-INF/web.xml +++ b/app/adapters/web/src/main/webapp/WEB-INF/web.xml @@ -24,6 +24,23 @@ *.xhtml + + user + + + + + Traqqr + /secure/* + + + user + + + CONFIDENTIAL + + + primefaces.CLIENT_SIDE_VALIDATION diff --git a/app/adapters/web/src/main/webapp/index.xhtml b/app/adapters/web/src/main/webapp/index.xhtml index e8c906a..04f6ec0 100644 --- a/app/adapters/web/src/main/webapp/index.xhtml +++ b/app/adapters/web/src/main/webapp/index.xhtml @@ -3,7 +3,7 @@ xmlns:f="http://xmlns.jcp.org/jsf/core" xmlns:ui="http://xmlns.jcp.org/jsf/facelets" xmlns:p="http://primefaces.org/ui" - template="/WEB-INF/layout.xhtml"> + template="/WEB-INF/layout-open.xhtml"> Start diff --git a/app/adapters/web/src/main/webapp/privacy.xhtml b/app/adapters/web/src/main/webapp/privacy.xhtml index c889c9a..d4873fc 100644 --- a/app/adapters/web/src/main/webapp/privacy.xhtml +++ b/app/adapters/web/src/main/webapp/privacy.xhtml @@ -2,7 +2,7 @@ xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://xmlns.jcp.org/jsf/facelets" xmlns:p="http://primefaces.org/ui" - template="/WEB-INF/layout.xhtml"> + template="/WEB-INF/layout-open.xhtml"> Privacy diff --git a/app/adapters/web/src/main/webapp/secure/index.xhtml b/app/adapters/web/src/main/webapp/secure/index.xhtml new file mode 100644 index 0000000..710bfe0 --- /dev/null +++ b/app/adapters/web/src/main/webapp/secure/index.xhtml @@ -0,0 +1,16 @@ + + Start + + + + Secure Dashboard + +

Hi ${userInfoBean.username}!

+
+
+
diff --git a/runtime/src/main/liberty/config/google-client-keystore.p12 b/runtime/src/main/liberty/config/google-client-keystore.p12 new file mode 100644 index 0000000000000000000000000000000000000000..1a47a4ec6c2e14c493bb9eea0a40398b4561487b GIT binary patch literal 1667 zcmV-}27LK2f(CyA0Ru3C1~~=^Duzgg_YDCD0ic2gDg=TCCNP2qA}|pK2`Yw2hW8Bt z2LYf21OhMsFoFgP1_>&LNQUFLT2$UZ#C7!CTSV;+?$DLOUbWda5n!14oXx0S6VV~V8*_F zd)CDT6XDUL`%9I*Dg-COmJ-7NrM?%lddPeRQ>P9=wrv3~T{Th%>sfbgK$MRK6D zOKt&ziOGH%3?QkUVv27IV-JRT6;I3xkr96A`gy=-Y;#D1)g zA5UmqYM=8#lzRJRDNl!Gw__z9W})iY!4g@>YCLF)@Bs~zf0r%Ij%c03pW{9&dj7xW z1xYwPWzQ>E(``mVD)Z{P52-QE6Knfi)RlUv?a#UXrWf;J zAx#D(G1s@Fx0HD}!Hz;f58#yE9GD zf0_)H;WQua(J*sl!)vTR5vV3-HBYx}?-mlTpb;T1@Hm}RJ zxqaQt>$GV383=8CyP&@1r6?i&_9xo@U0$g2J@;xn;M$I$B2x z`glfuO^^LpH{YGyYFAX$6|NkFuo;8(h`AE@1bGPWlGNvaUdk}HdWra`hwqz?uvkzk zTf~coQB4IA4y4cOQGv?rtl=84%Ekn_MwG7JCy=`DkYu>)JEg9py#X#1`P+DW4>jX` z&EGITNQn6Yh&rz6Y1ShjgQwLsGcHilD`h5r9rjC9c0?EDBzCiBrK zp1Q;V9c&0!GxknC69tavFcIg9wZO$KSAdF^^E>}~A0n~Vc#jjZ83Tr%|1T{D;a20` z?JZ&gDVsBSPen8!K4YQH52-iFrM95K^eN|(OC}%j*bhUa0dK28yrPKN3>lFWb{(&> zRhXJ=-JQSe6=O@{0hB(SJ#2r#KiDsMHGbI3naWQn>5y+eU|8U`jdTURQ__8^?i?uyHeSwDHnAtu{zO?FWQpz}$sQ=~zO5}hX`PbNRDf>4tW1SFn0&l?c4B13c0#qKO4-iB=Zvff)6+-s0ycKv4m zv->+cufwxm4zlRP&jRw{jwtc+5r7t>Mn2G6`aRlAdd(SI*R0dr-R{;tghJkY+D;Ja zJ8C<71Q=qkcIf&R`o^h7`;^bzJ1Wu^+6&fj(xedg+h=bIsn**>O<(x?ER(B%ncmRei+xdvt^+bVCDCksr9?b*POOLA&?X;a7r`sW^D)Upd|=-bq){cuLmB{=`%pz4z1l z52~1e6~uT}S%Yl|_#_p(hwWxKcz?L{n#!E6iD#<2zzl^AErSr86fZlm@^)*)j(^rWRD_|{Eu%i zJuo3K2?hl#4g&%j1povT&qcdXPx+hl;5m=tg0R=w@J>B;1QceX?f`z0`a?N-k!9G# NqtScL4zdCQ1OUS@8xH^g literal 0 HcmV?d00001 diff --git a/runtime/src/main/liberty/config/server.xml b/runtime/src/main/liberty/config/server.xml index d45b153..65adb33 100644 --- a/runtime/src/main/liberty/config/server.xml +++ b/runtime/src/main/liberty/config/server.xml @@ -1,6 +1,7 @@ + appSecurity-5.0 beanValidation-3.0 cdi-4.0 expressionLanguage-5.0 @@ -12,7 +13,7 @@ -