gluu
公開メンバ関数 | 公開変数類 | 全メンバ一覧
casa.PersonAuthentication クラス
casa.PersonAuthentication の継承関係図
Inheritance graph
casa.PersonAuthentication 連携図
Collaboration graph

公開メンバ関数

def __init__ (self, currentTimeMillis)
 
def init (self, configurationAttributes)
 
def destroy (self, configurationAttributes)
 
def getApiVersion (self)
 
def isValidAuthenticationMethod (self, usageType, configurationAttributes)
 
def getAlternativeAuthenticationMethod (self, usageType, configurationAttributes)
 
def authenticate (self, configurationAttributes, requestParameters, step)
 
def prepareForStep (self, configurationAttributes, requestParameters, step)
 
def getExtraParametersForStep (self, configurationAttributes, step)
 
def getCountAuthenticationSteps (self, configurationAttributes)
 
def getPageForStep (self, configurationAttributes, step)
 
def getNextStep (self, configurationAttributes, requestParameters, step)
 
def logout (self, configurationAttributes, requestParameters)
 
def getLocalPrimaryKey (self)
 
def computeMethods (self, scriptList)
 
def getConfigurationAttributes (self, acr, scriptsList)
 
def getAvailMethodsUser (self, user, skip)
 
def simulateFirstStep (self, requestParameters, acr)
 
def determineSkip2FA (self, userService, identity, foundUser, platform)
 
def process2FAPolicy (self, identity, foundUser, deviceInf, locationCriterion, deviceCriterion)
 
def getGeolocation (self, identity)
 

公開変数類

 currentTimeMillis
 
 ACR_SG
 
 ACR_SMS
 
 ACR_OTP
 
 ACR_U2F
 
 modulePrefix
 
 authenticators
 
 configFileLocation
 
 uid_attr
 

詳解

構築子と解体子

◆ __init__()

def casa.PersonAuthentication.__init__ (   self,
  currentTimeMillis 
)
31  def __init__(self, currentTimeMillis):
32  self.currentTimeMillis = currentTimeMillis
33  self.ACR_SG = "super_gluu"
34  self.ACR_SMS = "twilio_sms"
35  self.ACR_OTP = "otp"
36  self.ACR_U2F = "u2f"
37 
38  self.modulePrefix = "casa-external_"
39 

関数詳解

◆ authenticate()

def casa.PersonAuthentication.authenticate (   self,
  configurationAttributes,
  requestParameters,
  step 
)
101  def authenticate(self, configurationAttributes, requestParameters, step):
102  print "Casa. authenticate %s" % str(step)
103 
104  userService = CdiUtil.bean(UserService)
105  authenticationService = CdiUtil.bean(AuthenticationService)
106  identity = CdiUtil.bean(Identity)
107 
108  if step == 1:
109  credentials = identity.getCredentials()
110  user_name = credentials.getUsername()
111  user_password = credentials.getPassword()
112 
113  if StringHelper.isNotEmptyString(user_name) and StringHelper.isNotEmptyString(user_password):
114 
115  foundUser = userService.getUserByAttribute(self.uid_attr, user_name)
116  #foundUser = userService.getUser(user_name)
117  if foundUser == None:
118  print "Casa. authenticate for step 1. Unknown username"
119  else:
120  acr = foundUser.getAttribute("oxPreferredMethod")
121  logged_in = False
122 
123  if acr == None:
124  logged_in = authenticationService.authenticate(user_name, user_password)
125  elif acr in self.authenticators:
126  module = self.authenticators[acr]
127  logged_in = module.authenticate(module.configAttrs, requestParameters, step)
128 
129  if logged_in:
130  foundUser = authenticationService.getAuthenticatedUser()
131 
132  if foundUser == None:
133  print "Casa. authenticate for step 1. Cannot retrieve logged user"
134  else:
135  if acr == None:
136  identity.setWorkingParameter("skip2FA", True)
137  else:
138  #Determine whether to skip 2FA based on policy defined (global or user custom)
139  skip2FA = self.determineSkip2FA(userService, identity, foundUser, ServerUtil.getFirstValue(requestParameters, "loginForm:platform"))
140  identity.setWorkingParameter("skip2FA", skip2FA)
141  identity.setWorkingParameter("ACR", acr)
142 
143  return True
144 
145  else:
146  print "Casa. authenticate for step 1 was not successful"
147  return False
148 
149  else:
150  user = authenticationService.getAuthenticatedUser()
151  if user == None:
152  print "Casa. authenticate for step 2. Cannot retrieve logged user"
153  return False
154 
155  #see casa.xhtml
156  alter = ServerUtil.getFirstValue(requestParameters, "alternativeMethod")
157  if alter != None:
158  #bypass the rest of this step if an alternative method was provided. Current step will be retried (see getNextStep)
159  self.simulateFirstStep(requestParameters, alter)
160  return True
161 
162  session_attributes = identity.getSessionId().getSessionAttributes()
163  acr = session_attributes.get("ACR")
164  #this working parameter is used in casa.xhtml
165  identity.setWorkingParameter("methods", self.getAvailMethodsUser(user, acr))
166 
167  success = False
168  if acr in self.authenticators:
169  module = self.authenticators[acr]
170  success = module.authenticate(module.configAttrs, requestParameters, step)
171 
172  #Update the list of trusted devices if 2fa passed
173  if success:
174  print "Casa. authenticate. 2FA authentication was successful"
175  tdi = session_attributes.get("trustedDevicesInfo")
176  if tdi == None:
177  print "Casa. authenticate. List of user's trusted devices was not updated"
178  else:
179  user.setAttribute("oxTrustedDevicesInfo", tdi)
180  userService.updateUser(user)
181  else:
182  print "Casa. authenticate. 2FA authentication failed"
183 
184  return success
185 
186  return False
187 
188 

◆ computeMethods()

def casa.PersonAuthentication.computeMethods (   self,
  scriptList 
)
297  def computeMethods(self, scriptList):
298 
299  methods = []
300  try:
301  f = open(self.configFileLocation, 'r')
302  cmConfigs = json.loads(f.read())
303  if 'acr_plugin_mapping' in cmConfigs:
304  mapping = cmConfigs['acr_plugin_mapping']
305  else:
306  mapping = {}
307 
308  for m in mapping:
309  for customScript in scriptList:
310  if customScript.getName() == m and customScript.isEnabled():
311  methods.append(m)
312  except:
313  print "Casa. computeMethods. Failed to read configuration file"
314  finally:
315  f.close()
316  return methods
317 
318 

◆ destroy()

def casa.PersonAuthentication.destroy (   self,
  configurationAttributes 
)
83  def destroy(self, configurationAttributes):
84  print "Casa. Destroyed called"
85  return True
86 
87 

◆ determineSkip2FA()

def casa.PersonAuthentication.determineSkip2FA (   self,
  userService,
  identity,
  foundUser,
  platform 
)
368  def determineSkip2FA(self, userService, identity, foundUser, platform):
369 
370  f = open(self.configFileLocation, 'r')
371  try:
372  cmConfigs = json.loads(f.read())
373  if 'policy_2fa' in cmConfigs:
374  policy2FA = ','.join(cmConfigs['policy_2fa'])
375  else:
376  policy2FA = 'EVERY_LOGIN'
377  except:
378  print "Casa. determineSkip2FA. Failed to read policy_2fa from configuration file"
379  return False
380  finally:
381  f.close()
382 
383  print "Casa. determineSkip2FA with general policy %s" % policy2FA
384  policy2FA += ','
385  skip2FA = False
386 
387  if 'CUSTOM,' in policy2FA:
388  #read setting from user profile
389  policy = foundUser.getAttribute("oxStrongAuthPolicy")
390  if policy == None:
391  policy = 'EVERY_LOGIN,'
392  else:
393  policy = policy.upper() + ','
394  print "Casa. determineSkip2FA. Using user's enforcement policy %s" % policy
395 
396  else:
397  #If it's not custom, then apply the global setting admin defined
398  policy = policy2FA
399 
400  if not 'EVERY_LOGIN,' in policy:
401  locationCriterion = 'LOCATION_UNKNOWN,' in policy
402  deviceCriterion = 'DEVICE_UNKNOWN,' in policy
403 
404  if locationCriterion or deviceCriterion:
405  try:
406  #Find device info passed in HTTP request params (see index.xhtml)
407  deviceInf = json.loads(platform)
408  skip2FA = self.process2FAPolicy(identity, foundUser, deviceInf, locationCriterion, deviceCriterion)
409 
410  if skip2FA:
411  print "Casa. determineSkip2FA. Second factor is skipped"
412  #Update attribute if authentication will not have second step
413  devInf = identity.getWorkingParameter("trustedDevicesInfo")
414  if devInf != None:
415  foundUser.setAttribute("oxTrustedDevicesInfo", devInf)
416  userService.updateUser(foundUser)
417  except:
418  print "Casa. determineSkip2FA. Error parsing current user device data. Forcing 2FA to take place..."
419 
420  else:
421  print "Casa. determineSkip2FA. Unknown %s policy: cannot skip 2FA" % policy
422 
423  return skip2FA
424 
425 

◆ getAlternativeAuthenticationMethod()

def casa.PersonAuthentication.getAlternativeAuthenticationMethod (   self,
  usageType,
  configurationAttributes 
)
97  def getAlternativeAuthenticationMethod(self, usageType, configurationAttributes):
98  return None
99 
100 

◆ getApiVersion()

def casa.PersonAuthentication.getApiVersion (   self)
88  def getApiVersion(self):
89  return 2
90 
91 

◆ getAvailMethodsUser()

def casa.PersonAuthentication.getAvailMethodsUser (   self,
  user,
  skip 
)
331  def getAvailMethodsUser(self, user, skip):
332  methods = ArrayList()
333 
334  for method in self.authenticators:
335  try:
336  module = self.authenticators[method]
337  if module.hasEnrollments(module.configAttrs, user):
338  methods.add(method)
339  except:
340  print "Casa. getAvailMethodsUser. hasEnrollments call could not be issued for %s module" % method
341 
342  try:
343  #skip is guaranteed to be a member of methods (if hasEnrollments routines are properly implemented).
344  #A call to remove strangely crashes if skip is absent
345  methods.remove(skip)
346  except:
347  print "Casa. getAvailMethodsUser. methods list does not contain %s" % skip
348 
349  print "Casa. getAvailMethodsUser %s" % methods.toString()
350  return methods
351 
352 

◆ getConfigurationAttributes()

def casa.PersonAuthentication.getConfigurationAttributes (   self,
  acr,
  scriptsList 
)
319  def getConfigurationAttributes(self, acr, scriptsList):
320 
321  configMap = HashMap()
322  for customScript in scriptsList:
323  if customScript.getName() == acr and customScript.isEnabled():
324  for prop in customScript.getConfigurationProperties():
325  configMap.put(prop.getValue1(), SimpleCustomProperty(prop.getValue1(), prop.getValue2()))
326 
327  print "Casa. getConfigurationAttributes. %d configuration properties were found for %s" % (configMap.size(), acr)
328  return configMap
329 
330 

◆ getCountAuthenticationSteps()

def casa.PersonAuthentication.getCountAuthenticationSteps (   self,
  configurationAttributes 
)
235  def getCountAuthenticationSteps(self, configurationAttributes):
236  print "Casa. getCountAuthenticationSteps called"
237 
238  if CdiUtil.bean(Identity).getWorkingParameter("skip2FA"):
239  return 1
240 
241  acr = CdiUtil.bean(Identity).getWorkingParameter("ACR")
242  if acr in self.authenticators:
243  module = self.authenticators[acr]
244  return module.getCountAuthenticationSteps(module.configAttrs)
245  else:
246  return 2
247 
248  print "Casa. getCountAuthenticationSteps. Could not determine the step count for acr %s" % acr
249 
250 

◆ getExtraParametersForStep()

def casa.PersonAuthentication.getExtraParametersForStep (   self,
  configurationAttributes,
  step 
)
215  def getExtraParametersForStep(self, configurationAttributes, step):
216  print "Casa. getExtraParametersForStep %s" % str(step)
217 
218  if step > 1:
219  list = ArrayList()
220  acr = CdiUtil.bean(Identity).getWorkingParameter("ACR")
221 
222  if acr in self.authenticators:
223  module = self.authenticators[acr]
224  params = module.getExtraParametersForStep(module.configAttrs, step)
225  if params != None:
226  list.addAll(params)
227 
228  list.addAll(Arrays.asList("ACR", "methods", "trustedDevicesInfo"))
229  print "extras are %s" % list
230  return list
231 
232  return None
233 
234 

◆ getGeolocation()

def casa.PersonAuthentication.getGeolocation (   self,
  identity 
)
520  def getGeolocation(self, identity):
521 
522  session_attributes = identity.getSessionId().getSessionAttributes()
523  if session_attributes.containsKey("remote_ip"):
524  remote_ip = session_attributes.get("remote_ip")
525  if StringHelper.isNotEmpty(remote_ip):
526 
527  httpService = CdiUtil.bean(HttpService)
528 
529  http_client = httpService.getHttpsClient()
530  http_client_params = http_client.getParams()
531  http_client_params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 4 * 1000)
532 
533  geolocation_service_url = "http://ip-api.com/json/%s?fields=country,city,status,message" % remote_ip
534  geolocation_service_headers = { "Accept" : "application/json" }
535 
536  try:
537  http_service_response = httpService.executeGet(http_client, geolocation_service_url, geolocation_service_headers)
538  http_response = http_service_response.getHttpResponse()
539  except:
540  print "Casa. Determine remote location. Exception: ", sys.exc_info()[1]
541  return None
542 
543  try:
544  if not httpService.isResponseStastusCodeOk(http_response):
545  print "Casa. Determine remote location. Get non 200 OK response from server:", str(http_response.getStatusLine().getStatusCode())
546  httpService.consume(http_response)
547  return None
548 
549  response_bytes = httpService.getResponseContent(http_response)
550  response_string = httpService.convertEntityToString(response_bytes, Charset.forName("UTF-8"))
551  httpService.consume(http_response)
552  finally:
553  http_service_response.closeConnection()
554 
555  if response_string == None:
556  print "Casa. Determine remote location. Get empty response from location server"
557  return None
558 
559  response = json.loads(response_string)
560 
561  if not StringHelper.equalsIgnoreCase(response['status'], "success"):
562  print "Casa. Determine remote location. Get response with status: '%s'" % response['status']
563  return None
564 
565  return response
566 
567  return None
568 

◆ getLocalPrimaryKey()

def casa.PersonAuthentication.getLocalPrimaryKey (   self)
287  def getLocalPrimaryKey(self):
288  #Pick (one) attribute where user id is stored (e.g. uid/mail)
289  oxAuthInitializer = CdiUtil.bean(AppInitializer)
290  #This call does not create anything, it's like a getter (see oxAuth's AppInitializer)
291  ldapAuthConfigs = oxAuthInitializer.createLdapAuthConfigs()
292  uid_attr = ldapAuthConfigs.get(0).getLocalPrimaryKey()
293  print "Casa. init. uid attribute is '%s'" % uid_attr
294  return uid_attr
295 
296 

◆ getNextStep()

def casa.PersonAuthentication.getNextStep (   self,
  configurationAttributes,
  requestParameters,
  step 
)
267  def getNextStep(self, configurationAttributes, requestParameters, step):
268 
269  print "Casa. getNextStep called %s" % str(step)
270  if step > 1:
271  acr = ServerUtil.getFirstValue(requestParameters, "alternativeMethod")
272  if acr != None:
273  print "Casa. getNextStep. Use alternative method %s" % acr
274  CdiUtil.bean(Identity).setWorkingParameter("ACR", acr)
275  #retry step with different acr
276  return 2
277 
278  return -1
279 
280 

◆ getPageForStep()

def casa.PersonAuthentication.getPageForStep (   self,
  configurationAttributes,
  step 
)
251  def getPageForStep(self, configurationAttributes, step):
252  print "Casa. getPageForStep called %s" % str(step)
253 
254  if step > 1:
255  acr = CdiUtil.bean(Identity).getWorkingParameter("ACR")
256  if acr in self.authenticators:
257  module = self.authenticators[acr]
258  page = module.getPageForStep(module.configAttrs, step)
259  else:
260  page=None
261 
262  return page
263 
264  return ""
265 
266 

◆ init()

def casa.PersonAuthentication.init (   self,
  configurationAttributes 
)
40  def init(self, configurationAttributes):
41 
42  print "Casa. init called"
43  self.authenticators = {}
44  self.configFileLocation = "/etc/gluu/conf/casa.json"
45  self.uid_attr = self.getLocalPrimaryKey()
46 
47  custScriptService = CdiUtil.bean(CustomScriptService)
48  scriptsList = custScriptService.findCustomScripts(Collections.singletonList(CustomScriptType.PERSON_AUTHENTICATION), "oxConfigurationProperty", "displayName", "gluuStatus")
49  dynamicMethods = self.computeMethods(scriptsList)
50 
51  if len(dynamicMethods) > 0:
52  print "Casa. init. Loading scripts for dynamic modules: %s" % dynamicMethods
53 
54  for acr in dynamicMethods:
55  moduleName = self.modulePrefix + acr
56  try:
57  external = __import__(moduleName, globals(), locals(), ["PersonAuthentication"], -1)
58  module = external.PersonAuthentication(self.currentTimeMillis)
59 
60  print "Casa. init. Got dynamic module for acr %s" % acr
61  configAttrs = self.getConfigurationAttributes(acr, scriptsList)
62 
63  if acr == self.ACR_U2F:
64  u2f_application_id = configurationAttributes.get("u2f_app_id").getValue2()
65  configAttrs.put("u2f_application_id", SimpleCustomProperty("u2f_application_id", u2f_application_id))
66  elif acr == self.ACR_SG:
67  client_redirect_uri = configurationAttributes.get("supergluu_app_id").getValue2()
68  configAttrs.put("client_redirect_uri", SimpleCustomProperty("client_redirect_uri", client_redirect_uri))
69 
70  if module.init(configAttrs):
71  module.configAttrs = configAttrs
72  self.authenticators[acr] = module
73  else:
74  print "Casa. init. Call to init in module '%s' returned False" % moduleName
75  except:
76  print "Casa. init. Failed to load module %s" % moduleName
77  print "Exception: ", sys.exc_info()[1]
78 
79  print "Casa. init. Initialized successfully"
80  return True
81 
82 

◆ isValidAuthenticationMethod()

def casa.PersonAuthentication.isValidAuthenticationMethod (   self,
  usageType,
  configurationAttributes 
)
92  def isValidAuthenticationMethod(self, usageType, configurationAttributes):
93  print "Casa. isValidAuthenticationMethod called"
94  return True
95 
96 

◆ logout()

def casa.PersonAuthentication.logout (   self,
  configurationAttributes,
  requestParameters 
)
281  def logout(self, configurationAttributes, requestParameters):
282  print "Casa. logout called"
283  return True
284 
285 # Miscelaneous
286 

◆ prepareForStep()

def casa.PersonAuthentication.prepareForStep (   self,
  configurationAttributes,
  requestParameters,
  step 
)
189  def prepareForStep(self, configurationAttributes, requestParameters, step):
190  print "Casa. prepareForStep %s" % str(step)
191  if step == 1:
192  return True
193  else:
194  identity = CdiUtil.bean(Identity)
195  session_attributes = identity.getSessionId().getSessionAttributes()
196 
197  authenticationService = CdiUtil.bean(AuthenticationService)
198  user = authenticationService.getAuthenticatedUser()
199 
200  if user == None:
201  print "Casa. prepareForStep. Cannot retrieve logged user"
202  return False
203 
204  acr = session_attributes.get("ACR")
205  print "Casa. prepareForStep. ACR = %s" % acr
206  identity.setWorkingParameter("methods", self.getAvailMethodsUser(user, acr))
207 
208  if acr in self.authenticators:
209  module = self.authenticators[acr]
210  return module.prepareForStep(module.configAttrs, requestParameters, step)
211  else:
212  return False
213 
214 

◆ process2FAPolicy()

def casa.PersonAuthentication.process2FAPolicy (   self,
  identity,
  foundUser,
  deviceInf,
  locationCriterion,
  deviceCriterion 
)
426  def process2FAPolicy(self, identity, foundUser, deviceInf, locationCriterion, deviceCriterion):
427 
428  skip2FA = False
429  #Retrieve user's devices info
430  devicesInfo = foundUser.getAttribute("oxTrustedDevicesInfo")
431 
432  #do geolocation
433  geodata = self.getGeolocation(identity)
434  if geodata == None:
435  print "Casa. process2FAPolicy: Geolocation data not obtained. 2FA skipping based on location cannot take place"
436 
437  try:
438  encService = CdiUtil.bean(EncryptionService)
439 
440  if devicesInfo == None:
441  print "Casa. process2FAPolicy: There are no trusted devices for user yet"
442  #Simulate empty list
443  devicesInfo = "[]"
444  else:
445  devicesInfo = encService.decrypt(devicesInfo)
446 
447  devicesInfo = json.loads(devicesInfo)
448 
449  partialMatch = False
450  idx = 0
451  #Try to find a match for device only
452  for device in devicesInfo:
453  partialMatch = device['browser']['name']==deviceInf['name'] and device['os']['version']==deviceInf['os']['version'] and device['os']['family']==deviceInf['os']['family']
454  if partialMatch:
455  break
456  idx+=1
457 
458  matchFound = False
459 
460  #At least one of locationCriterion or deviceCriterion is True
461  if locationCriterion and not deviceCriterion:
462  #this check makes sense if there is city data only
463  if geodata!=None:
464  for device in devicesInfo:
465  #Search all registered cities that are found in trusted devices
466  for origin in device['origins']:
467  matchFound = matchFound or origin['city']==geodata['city']
468 
469  elif partialMatch:
470  #In this branch deviceCriterion is True
471  if not locationCriterion:
472  matchFound = True
473  elif geodata!=None:
474  for origin in devicesInfo[idx]['origins']:
475  matchFound = matchFound or origin['city']==geodata['city']
476 
477  skip2FA = matchFound
478  now = Date().getTime()
479 
480  #Update attribute oxTrustedDevicesInfo accordingly
481  if partialMatch:
482  #Update an existing record (update timestamp in city, or else add it)
483  if geodata != None:
484  partialMatch = False
485  idxCity = 0
486 
487  for origin in devicesInfo[idx]['origins']:
488  partialMatch = origin['city']==geodata['city']
489  if partialMatch:
490  break;
491  idxCity+=1
492 
493  if partialMatch:
494  devicesInfo[idx]['origins'][idxCity]['timestamp'] = now
495  else:
496  devicesInfo[idx]['origins'].append({"city": geodata['city'], "country": geodata['country'], "timestamp": now})
497  else:
498  #Create a new entry
499  browser = {"name": deviceInf['name'], "version": deviceInf['version']}
500  os = {"family": deviceInf['os']['family'], "version": deviceInf['os']['version']}
501 
502  if geodata == None:
503  origins = []
504  else:
505  origins = [{"city": geodata['city'], "country": geodata['country'], "timestamp": now}]
506 
507  obj = {"browser": browser, "os": os, "addedOn": now, "origins": origins}
508  devicesInfo.append(obj)
509 
510  enc = json.dumps(devicesInfo, separators=(',',':'))
511  enc = encService.encrypt(enc)
512  identity.setWorkingParameter("trustedDevicesInfo", enc)
513 
514  except:
515  print "Casa. process2FAPolicy. Error!", sys.exc_info()[1]
516 
517  return skip2FA
518 
519 

◆ simulateFirstStep()

def casa.PersonAuthentication.simulateFirstStep (   self,
  requestParameters,
  acr 
)
353  def simulateFirstStep(self, requestParameters, acr):
354  #To simulate 1st step, there is no need to call:
355  # getPageforstep (no need as user/pwd won't be shown again)
356  # isValidAuthenticationMethod (by restriction, it returns True)
357  # prepareForStep (by restriction, it returns True)
358  # getExtraParametersForStep (by restriction, it returns None)
359  print "Casa. simulateFirstStep. Calling authenticate (step 1) for %s module" % acr
360  if acr in self.authenticators:
361  module = self.authenticators[acr]
362  auth = module.authenticate(module.configAttrs, requestParameters, 1)
363  print "Casa. simulateFirstStep. returned value was %s" % auth
364 
365 
366 # 2FA policy enforcement
367 

メンバ詳解

◆ ACR_OTP

casa.PersonAuthentication.ACR_OTP

◆ ACR_SG

casa.PersonAuthentication.ACR_SG

◆ ACR_SMS

casa.PersonAuthentication.ACR_SMS

◆ ACR_U2F

casa.PersonAuthentication.ACR_U2F

◆ authenticators

casa.PersonAuthentication.authenticators

◆ configFileLocation

casa.PersonAuthentication.configFileLocation

◆ currentTimeMillis

casa.PersonAuthentication.currentTimeMillis

◆ modulePrefix

casa.PersonAuthentication.modulePrefix

◆ uid_attr

casa.PersonAuthentication.uid_attr

このクラス詳解は次のファイルから抽出されました: