-
Notifications
You must be signed in to change notification settings - Fork 77
/
Copy pathAgamaBridge.py
227 lines (172 loc) · 9 KB
/
AgamaBridge.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# Janssen Project software is available under the Apache 2.0 License (2004). See http://www.apache.org/licenses/ for full text.
# Copyright (c) 2020, Janssen Project
#
from com.fasterxml.jackson.databind import ObjectMapper
from io.jans.agama import NativeJansFlowBridge
from io.jans.agama.engine.misc import FlowUtils
from io.jans.service import EncryptionService
from io.jans.as.model.util import Base64Util
from io.jans.as.server.security import Identity
from io.jans.as.server.service import AuthenticationService, UserService
from io.jans.jsf2.service import FacesService
from io.jans.jsf2.message import FacesMessages
from io.jans.model.custom.script.type.auth import PersonAuthenticationType
from io.jans.orm import PersistenceEntryManager
from io.jans.service.cdi.util import CdiUtil
from io.jans.util import StringHelper
from jakarta.faces.application import FacesMessage
from java.util import Arrays
import java
import sys
class PersonAuthentication(PersonAuthenticationType):
def __init__(self, currentTimeMillis):
self.currentTimeMillis = currentTimeMillis
def init(self, customScript, configurationAttributes):
print "Agama. Initialization"
self.resultParam = "agamaData"
prop = "finish_userid_db_attribute"
self.finish_userid_db_attr = self.configProperty(configurationAttributes, prop)
if self.finish_userid_db_attr == None:
print "Agama. Property '%s' is missing value" % prop
return False
print "Agama. DB attribute '%s' will be used to map the identity of userId passed in Finish directives (if any)" % self.finish_userid_db_attr
print "Agama. Initialized successfully"
return True
def destroy(self, configurationAttributes):
print "Agama. Destroy"
print "Agama. Destroyed successfully"
return True
def getAuthenticationMethodClaims(self, requestParameters):
return None
def getApiVersion(self):
return 11
def isValidAuthenticationMethod(self, usageType, configurationAttributes):
return True
def getAlternativeAuthenticationMethod(self, usageType, configurationAttributes):
return None
def authenticate(self, configurationAttributes, requestParameters, step):
if step == 1:
print "Agama. Authenticate for step 1"
try:
bridge = CdiUtil.bean(NativeJansFlowBridge)
result = bridge.close()
if result == None or not result.isSuccess():
print "Agama. Flow DID NOT finished successfully"
return False
else:
print "Agama. Flow finished successfully"
data = result.getData()
userId = data.get("userId") if data != None else None
if userId == None:
print "Agama. No userId provided in flow result."
self.setMessageError(FacesMessage.SEVERITY_ERROR, "Unable to determine identity of user")
return False
userService = CdiUtil.bean(UserService)
fuda = self.finish_userid_db_attr
matchingUsers = userService.getUsersByAttribute(fuda, userId, True, 2)
matches = len(matchingUsers)
if matches != 1:
if matches == 0:
print "Agama. No user matches the required condition: %s=%s" % (fuda, userId)
else:
print "Agama. Several users match the required condition: %s=%s" % (fuda, userId)
self.setMessageError(FacesMessage.SEVERITY_ERROR, "Unable to determine identity of user")
return False
inum = matchingUsers[0].getAttribute("inum")
print "Agama. Authenticating user %s..." % inum
authenticated = CdiUtil.bean(AuthenticationService).authenticateByUserInum(inum)
if not authenticated:
print "Agama. Unable to authenticate %s" % inum
return False
data.put("_encInum", CdiUtil.bean(EncryptionService).encrypt(inum))
jsonData = CdiUtil.bean(ObjectMapper).writeValueAsString(data)
CdiUtil.bean(Identity).setWorkingParameter(self.resultParam, jsonData)
except:
print "Agama. Exception: ", sys.exc_info()[1]
return False
return True
def prepareForStep(self, configurationAttributes, requestParameters, step):
if not CdiUtil.bean(FlowUtils).serviceEnabled():
print "Agama. Please ENABLE Agama engine in auth-server configuration"
return False
if step == 1:
print "Agama. Prepare for Step 1"
session = CdiUtil.bean(Identity).getSessionId()
if session == None:
print "Agama. Failed to retrieve session_id"
return False
cesar = session.getSessionAttributes()
param = cesar.get("agama_flow")
if StringHelper.isEmpty(param):
param = self.extractAgamaFlow(cesar.get("acr_values"))
if StringHelper.isEmpty(param):
print "Agama. Unable to determine the Agama flow to launch. Check the docs"
return False
(qn, ins) = self.extractParams(param)
if qn == None:
print "Agama. Unable to determine the Agama flow to launch. Check the docs"
return False
try:
bridge = CdiUtil.bean(NativeJansFlowBridge)
running = bridge.prepareFlow(session.getId(), qn, ins)
if running == None:
print "Agama. Flow '%s' does not exist or cannot be launched from a browser!" % qn
return False
elif running:
print "Agama. A flow is already in course"
print "Agama. Redirecting to start/resume agama flow '%s'..." % qn
CdiUtil.bean(FacesService).redirectToExternalURL(bridge.getTriggerUrl())
except:
print "Agama. An error occurred when launching flow '%s'. Check jans-auth logs" % qn
print "Agama. Exception: ", sys.exc_info()[1]
return False
#except java.lang.Throwable, ex:
# ex.printStackTrace()
# return False
return True
def getExtraParametersForStep(self, configurationAttributes, step):
return Arrays.asList(self.resultParam)
def getCountAuthenticationSteps(self, configurationAttributes):
return 1
def getPageForStep(self, configurationAttributes, step):
# page referenced here is only used when a flow is restarted
return "/" + CdiUtil.bean(NativeJansFlowBridge).scriptPageUrl()
def getNextStep(self, configurationAttributes, requestParameters, step):
return -1
def getLogoutExternalUrl(self, configurationAttributes, requestParameters):
return None
def logout(self, configurationAttributes, requestParameters):
return True
# Misc routines
def configProperty(self, configProperties, name):
prop = configProperties.get(name)
val = None
if prop != None:
val = prop.getValue2()
if StringHelper.isEmpty(val):
val = None
return val
def setMessageError(self, severity, msg):
facesMessages = CdiUtil.bean(FacesMessages)
facesMessages.setKeepMessages()
facesMessages.clear()
facesMessages.add(severity, msg)
def extractAgamaFlow(self, acr):
prefix = "agama_"
if StringHelper.isNotEmpty(acr) and acr.find(prefix) == 0:
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:]))