Skip to content

Commit

Permalink
feat(agama): allow integration of Agama flows in AS consent (#10727)
Browse files Browse the repository at this point in the history
* docs: fix typo #10660

Signed-off-by: jgomer2001 <[email protected]>

* feat: add bridge for Agama consent flows #10660

Signed-off-by: jgomer2001 <[email protected]>

---------

Signed-off-by: jgomer2001 <[email protected]>
  • Loading branch information
jgomer2001 authored Jan 23, 2025
1 parent 6a955a4 commit 4ef6c9a
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 2 deletions.
145 changes: 145 additions & 0 deletions docs/script-catalog/consent_gathering/AgamaConsentGathering.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
from io.jans.agama import NativeJansFlowBridge
from io.jans.agama.engine.misc import FlowUtils
from io.jans.as.model.util import Base64Util
from io.jans.as.server.authorize.ws.rs import ConsentGatheringSessionService
from io.jans.jsf2.service import FacesService
from io.jans.model.custom.script.type.authz import ConsentGatheringType
from io.jans.service.cdi.util import CdiUtil

import java
import sys

class ConsentGathering(ConsentGatheringType):

def __init__(self, currentTimeMillis):
self.currentTimeMillis = currentTimeMillis

def init(self, customScript, configurationAttributes):
print "Agama-Consent. Initializing ..."
print "Agama-Consent. Initialized successfully"
self.enterUrl = "agama/consent.xhtml"

return True

def destroy(self, configurationAttributes):
print "Agama-Consent. Destroying ..."
print "Agama-Consent. Destroyed successfully"

return True

def getApiVersion(self):
return 11

def authorize(self, step, context):
print "Agama-Consent. Authorizing..."

if step == 1:
print "Agama-Consent. Authorize for step 1"

try:
bridge = CdiUtil.bean(NativeJansFlowBridge)
result = bridge.close()

if result == None or not result.isSuccess():
print "Agama-Consent. Flow DID NOT finished successfully"
return False
except:
print "Agama-Consent. Exception: ", sys.exc_info()[1]
return False

return True


def getNextStep(self, step, context):
return -1

def prepareForStep(self, step, context):

if not context.isAuthenticated():
print "Agama-Consent. User is not authenticated"
return False

if not CdiUtil.bean(FlowUtils).serviceEnabled():
print "Agama-Consent. Please ENABLE Agama engine in auth-server configuration"
return False

if step == 1:
print "Agama-Consent. Prepare for Step 1"

cgss = CdiUtil.bean(ConsentGatheringSessionService)
#userDn = context.getUserDn()
#session = cgss.getConsentSession(context.getHttpRequest(), context.getHttpResponse(), userDn, False)
session = cgss.getConnectSession(context.getHttpRequest())

if session == None:
print "Agama-Consent. Failed to retrieve session_id"
return False

cesar = session.getSessionAttributes()
param = cesar.get("agama_flow")

if not param:
param = self.extractAgamaFlow(cesar.get("acr_values"))

if not param:
print "Agama-Consent. Unable to determine the Agama flow to launch. Check the docs"
return False

(qn, ins) = self.extractParams(param)
if qn == None:
print "Agama-Consent. Unable to determine the Agama flow to launch. Check the docs"
return False

try:
sessionId = session.getId()
# print "==================================== %s" % sessionId

bridge = CdiUtil.bean(NativeJansFlowBridge)
running = bridge.prepareFlow(sessionId, qn, ins, False, self.enterUrl)

if running == None:
print "Agama-Consent. Flow '%s' does not exist or cannot be launched from a browser!" % qn
return False
elif running:
print "Agama-Consent. A flow is already in course"

print "Agama-Consent. Redirecting to start/resume agama flow '%s'..." % qn

CdiUtil.bean(FacesService).redirectToExternalURL(bridge.getTriggerUrl())
except:
print "Agama-Consent. An error occurred when launching flow '%s'. Check jans-auth logs" % qn
print "Agama-Consent. Exception: ", sys.exc_info()[1]
return False

return True

def getStepsCount(self, context):
return 1

def getPageForStep(self, step, context):
return "/" + self.enterUrl

# Misc routines

def extractAgamaFlow(self, acr):
prefix = "agama_"
if acr and acr.startswith(prefix):
return acr[len(prefix):]
return None

def extractParams(self, param):

# param must be of the form QN-INPUT where QN is the qualified name of the flow to launch
# INPUT is a base64URL-encoded JSON object that contains the arguments to use for the flow call.
# The keys of this object should match the already defined flow inputs. Ideally, and
# depending on the actual flow implementation, some keys may not even be required
# QN and INPUTS are separated by a hyphen
# INPUT must be properly URL-encoded when HTTP GET is used

i = param.find("-")
if i == 0:
return (None, None)
elif i == -1:
return (param, None)
else:
return (param[:i], Base64Util.base64urldecodeToString(param[i+1:]))
4 changes: 2 additions & 2 deletions docs/script-catalog/consent_gathering/consent-gathering.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,9 @@ public class ConsentGathering implements ConsentGatheringType {
if(step == 1) {
return "/authz/authorize.xhtml";
} else if (step == 2) {
return "return \"/authz/transaction.xhtml\"";
return "/authz/transaction.xhtml";
}
return "";
}
}
```
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package io.jans.as.server.util;

import io.jans.as.common.model.common.User;
import io.jans.as.common.model.registration.Client;
import io.jans.as.persistence.model.Scope;
import io.jans.as.server.authorize.ws.rs.ConsentGatheringSessionService;
import io.jans.as.server.service.*;
import io.jans.as.common.model.session.SessionId;
import io.jans.service.cdi.util.CdiUtil;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.servlet.http.HttpServletRequest;
import java.util.*;

/**
* An utilitarian class for developers writing Agama Consent flows
*/
@ApplicationScoped
public class AgamaConsentUtil {

@Inject
private ConsentGatheringSessionService cgss;

@Inject
private AuthorizeService as;

@Inject
private SessionIdService sis;

public Map<String, String> getSessionAttributes() {
return getSessionId().getSessionAttributes();
}

public Client getClient() {
return cgss.getClient(getSessionId());
}

public List<Scope> getScopes() {
String scope = getSessionAttributes().get("scope");
return Optional.ofNullable(scope).map(as::getScopes).orElse(Collections.emptyList());
}

public User getUser() {
return sis.getUser(getSessionId());
}

private SessionId getSessionId() {
return sis.getSessionId(getServletRequest());
}

private HttpServletRequest getServletRequest() {
return CdiUtil.bean(HttpServletRequest.class);
}

}
14 changes: 14 additions & 0 deletions jans-linux-setup/jans_setup/templates/scripts.ldif
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,20 @@ objectClass: jansCustomScr
jansEnabled: false
jansProgLng: python

dn: inum=BADA-DAD1,ou=scripts,o=jans
description: Agama Consent Gathering script
displayName: agama_consent
inum: BADA-DAD1
jansLevel: 10
jansModuleProperty: {"value1":"location_type","value2":"db","description":""}
jansRevision: 1
jansScr::%(consent_gathering_agamaconsentgathering)s
jansScrTyp: consent_gathering
objectClass: top
objectClass: jansCustomScr
jansEnabled: false
jansProgLng: python

dn: inum=8AF7.D82A,ou=scripts,o=jans
displayName: persistence_extension
inum: 8AF7.D82A
Expand Down

0 comments on commit 4ef6c9a

Please sign in to comment.