Skip to content

Commit

Permalink
Merge pull request #15 from mkdecisiondev/pac4j
Browse files Browse the repository at this point in the history
Update the pac4j libraries, implement the DefaultLogic for logout and…
  • Loading branch information
aabiabdallah authored Jul 21, 2023
2 parents 7b216c4 + 6e1afc4 commit c7beea5
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 93 deletions.
7 changes: 4 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ dependencies {
implementation 'org.apache.commons:commons-collections4:4.4'

// pac4j
implementation 'org.pac4j:pac4j-oidc:3.0.2'
implementation 'org.pac4j:pac4j-jwt:3.0.2'
// implementation 'org.pac4j:jee-pac4j:5.0.0'
implementation 'org.pac4j:pac4j-core:5.7.1'
implementation 'org.pac4j:pac4j-javaee:5.7.1'
implementation 'org.pac4j:pac4j-oidc:5.7.1'
implementation 'org.pac4j:pac4j-oauth:5.7.1'
}

check.dependsOn.clear()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.nimbusds.jose.JOSEException
import com.nimbusds.jose.crypto.factories.DefaultJWSVerifierFactory
import com.nimbusds.jose.jwk.JWKSet
import com.nimbusds.jose.jwk.KeyConverter
import com.nimbusds.jose.proc.SimpleSecurityContext
import com.nimbusds.jwt.SignedJWT
import com.nimbusds.jwt.proc.BadJWTException
import com.nimbusds.jwt.proc.DefaultJWTClaimsVerifier
Expand Down Expand Up @@ -68,7 +69,7 @@ if (eci.user.userId) {
def set = JWKSet.load(metadata.getJWKSetURI().toURL())

// Parse the token so we can use the header
def jwt = SignedJWT.parse(context.token)
def jwt = SignedJWT.parse(ec.context.token)

// grab the sessionId
String sessionId = jwt.payload.toJSONObject().get("session_state") as String
Expand All @@ -93,7 +94,7 @@ if (eci.user.userId) {
jwt.verify(verifier)

// Verify JWT Claims (expiration/not before date)
claimsVerifier.verify(jwt.JWTClaimsSet)
claimsVerifier.verify(jwt.JWTClaimsSet, new SimpleSecurityContext())

// Log user in using the username in the token
eci.userFacade.internalLoginUser(jwt.JWTClaimsSet.getStringClaim("preferred_username"))
Expand Down
86 changes: 46 additions & 40 deletions src/main/groovy/org/mk/moqui/authentication/Login.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,25 @@ package org.mk.moqui.authentication
import groovy.transform.CompileStatic
import org.moqui.context.ExecutionContext
import org.moqui.entity.EntityFacade
import org.moqui.impl.context.UserFacadeImpl
import org.pac4j.core.client.Client
import org.pac4j.core.config.Config
import org.pac4j.core.context.DefaultAuthorizers
import org.pac4j.core.context.J2EContext
import org.pac4j.core.authorization.authorizer.DefaultAuthorizers
import org.pac4j.core.context.session.SessionStore
import org.pac4j.jee.context.JEEContext
import org.pac4j.core.profile.ProfileManager
import org.pac4j.core.context.WebContext
import org.pac4j.core.context.session.J2ESessionStore
import org.pac4j.core.engine.DefaultCallbackLogic
import org.pac4j.jee.context.session.JEESessionStore
import org.pac4j.core.engine.DefaultLogoutLogic
import org.pac4j.core.engine.DefaultSecurityLogic
import org.pac4j.core.engine.DefaultCallbackLogic
import org.pac4j.core.engine.SecurityGrantedAccessAdapter
import org.pac4j.core.http.adapter.J2ENopHttpActionAdapter
import org.pac4j.core.profile.CommonProfile
import org.pac4j.core.profile.ProfileManager
import org.pac4j.core.profile.UserProfile
import org.pac4j.jee.http.adapter.JEEHttpActionAdapter

import javax.servlet.http.HttpServletResponse
import javax.servlet.http.WebConnection
import java.util.function.Function

@CompileStatic
class Login {
static final J2ESessionStore sessionStore = new J2ESessionStore()

static Config globalConfig
static List<AuthenticationClientFactory> clientFactories = [
Expand All @@ -46,27 +44,23 @@ class Login {
.collect { entity -> entity.clientId as String }
}

static J2EContext buildContext(ExecutionContext ec) {
static JEEContext buildContext(ExecutionContext ec) {
def request = ec.getWeb().getRequest()
def response = ec.getWeb().getResponse()

return new J2EContext(request, response, sessionStore)
return new JEEContext(request, response)
}

static String getMoquiUrl(ExecutionContext ec) {
return ec.web.getWebappRootUrl(true, true)
}

static Function getProfileManagerFactory(ExecutionContext ec) {
return { WebContext ctx -> new MoquiProfileManager(ctx, ec) } as Function<WebContext, ProfileManager>
}

static login(ExecutionContext ec) {
ec.artifactExecution.disableAuthz()
def logger = ec.getLogger()
JEESessionStore sessionStore = JEESessionStore.INSTANCE

DefaultSecurityLogic logic = new DefaultSecurityLogic()
logic.setProfileManagerFactory(getProfileManagerFactory(ec))

def clients = getEnabledClients(ec.entity)
if (clients.size() < 1) {
Expand All @@ -75,15 +69,15 @@ class Login {
}

try {
def result = logic.perform(
def result = DefaultSecurityLogic.INSTANCE.perform(
buildContext(ec),
sessionStore,
getConfig(ec),
new MoquiAccessGrantedAdapter(),
J2ENopHttpActionAdapter.INSTANCE,
JEEHttpActionAdapter.INSTANCE,
clients.join(','),
DefaultAuthorizers.IS_AUTHENTICATED,
'',
false
''
)
}
catch (Exception e) {
Expand All @@ -107,20 +101,29 @@ class Login {
ec.artifactExecution.disableAuthz()
def logger = ec.getLogger()
def context = buildContext(ec)
JEESessionStore sessionStore = JEESessionStore.INSTANCE

DefaultCallbackLogic callback = new DefaultCallbackLogic()
callback.setProfileManagerFactory(getProfileManagerFactory(ec))
try {
def result = callback.perform(
context,
getConfig(ec),
J2ENopHttpActionAdapter.INSTANCE,
DefaultCallbackLogic.INSTANCE.perform(
context,
sessionStore,
getConfig(ec),
JEEHttpActionAdapter.INSTANCE,
null,
true,
false,
true,
getEnabledClients(ec.entity).join(',')
false,
null
)

// handle incoming profiles
ProfileManager profileManager = new ProfileManager(context, sessionStore)
new MoquiAccessGrantedAdapter().adapt(context, sessionStore, profileManager.getProfiles())

// login user
Optional<UserProfile> optionalProfile = profileManager.getProfile()
if (optionalProfile.isPresent()) {
UserProfile profile = optionalProfile.get()
((UserFacadeImpl)ec.user).internalLoginUser(profile.username)
}
}
catch (Exception e) {
e.printStackTrace()
Expand All @@ -131,29 +134,32 @@ class Login {

static void logout(ExecutionContext ec) {
ec.artifactExecution.disableAuthz()
DefaultLogoutLogic logout = new DefaultLogoutLogic()
logout.setProfileManagerFactory(getProfileManagerFactory(ec))

def loginUrl = "${getMoquiUrl(ec)}/Login"
JEESessionStore sessionStore = JEESessionStore.INSTANCE

try {
logout.perform(
DefaultLogoutLogic.INSTANCE.perform(
buildContext(ec),
sessionStore,
getConfig(ec),
J2ENopHttpActionAdapter.INSTANCE,
JEEHttpActionAdapter.INSTANCE,
loginUrl,
'/',
true,
true,
false,
false,
true
)

ec.user.logoutUser()
} finally {
ec.artifactExecution.enableAuthz()
}
}
}

class MoquiAccessGrantedAdapter implements SecurityGrantedAccessAdapter<Object, WebContext> {
Object adapt(WebContext context, Collection<CommonProfile> profiles, Object... parameters) {
class MoquiAccessGrantedAdapter implements SecurityGrantedAccessAdapter {
Object adapt(WebContext context, SessionStore sessionStore, Collection<UserProfile> profiles, Object... parameters) throws Exception {
return null
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package org.mk.moqui.authentication

import com.nimbusds.jose.JWSAlgorithm
import com.nimbusds.jose.Requirement
import org.moqui.entity.EntityFacade
import org.pac4j.core.client.Client
import org.pac4j.oidc.client.OidcClient
import org.pac4j.oidc.config.OidcConfiguration
import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod

class OidcClientFactory implements AuthenticationClientFactory{

Expand All @@ -18,9 +17,8 @@ class OidcClientFactory implements AuthenticationClientFactory{
config.setDiscoveryURI(entity.discoveryUri)
config.setClientId(entity.id)
config.setSecret(entity.secret)
if (entity.preferredJwsAlgorithm) {
config.setPreferredJwsAlgorithm(new JWSAlgorithm(entity.preferredJwsAlgorithm, Requirement.RECOMMENDED))
}
config.setClientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
config.setPreferredJwsAlgorithmAsString(entity.preferredJwsAlgorithm as String)
config.setUseNonce(entity.useNonce == 'Y')
def client = new OidcClient(config)
client.setName(entity.clientId)
Expand Down

0 comments on commit c7beea5

Please sign in to comment.