Skip to content

Commit 10ac50f

Browse files
committedMar 4, 2025
Manulally merge PR196 to add account renewal button for local accounts on Account page
git-svn-id: svn+ssh://svn.code.sf.net/p/migrid/code/trunk@6215 b75ad72c-e7d7-11dd-a971-7dbc132099af
1 parent 7138e46 commit 10ac50f

File tree

5 files changed

+498
-12
lines changed

5 files changed

+498
-12
lines changed
 

‎mig/cgi-bin/accountaction.py

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
#
4+
# --- BEGIN_HEADER ---
5+
#
6+
# accountaction - account action page front end
7+
# Copyright (C) 2003-2025 The MiG Project by the Science HPC Center at UCPH
8+
#
9+
# This file is part of MiG.
10+
#
11+
# MiG is free software: you can redistribute it and/or modify
12+
# it under the terms of the GNU General Public License as published by
13+
# the Free Software Foundation; either version 2 of the License, or
14+
# (at your option) any later version.
15+
#
16+
# MiG is distributed in the hope that it will be useful,
17+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
# GNU General Public License for more details.
20+
#
21+
# You should have received a copy of the GNU General Public License
22+
# along with this program; if not, write to the Free Software
23+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24+
#
25+
# -- END_HEADER ---
26+
#
27+
28+
from __future__ import absolute_import
29+
import cgi
30+
31+
from mig.shared.functionality.accountaction import main
32+
from mig.shared.cgiscriptstub import run_cgi_script_possibly_with_cert
33+
34+
run_cgi_script_possibly_with_cert(main)

‎mig/shared/accountreq.py

+49-5
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@
2626
#
2727

2828
"""This module contains various helper contents for the certificate and OpenID
29-
account request handlers"""
29+
account request handlers.
30+
"""
3031

3132
from __future__ import absolute_import
3233

33-
import re
3434
import os
35+
import re
3536
import time
3637

3738
# NOTE: the external iso3166 module is optional and only used if available
@@ -657,6 +658,48 @@ def account_pw_reset_template(configuration, default_values={}):
657658
return html
658659

659660

661+
def renew_account_access_template(configuration, default_values={}):
662+
"""A general form template used for renewing account access."""
663+
664+
html = """
665+
<div id='account-renew-access-grid' class='form_container'>
666+
"""
667+
668+
_logger = configuration.logger
669+
auth_map = auth_type_description(configuration)
670+
show_auth_types = [(key, auth_map[key]) for key in
671+
default_values.get('show', auth_map.keys())]
672+
filtered_auth_types = [i for i in show_auth_types if i[0] in
673+
configuration.site_signup_methods]
674+
_logger.debug("show_auth_types %s filtered_auth_types %s" %
675+
(show_auth_types, filtered_auth_types))
676+
if not filtered_auth_types:
677+
html += """<p class='warningtext'>
678+
No matching local authentication methods enabled on this site, so no account
679+
access to renew here.
680+
</p>
681+
"""
682+
else:
683+
html += """
684+
<p>
685+
Account access automatically expires after a while and needs to be actively
686+
renewed.
687+
%(peer_acceptance_notice)s
688+
</p>
689+
<!-- use post here to avoid field contents in URL -->
690+
<form method='%(form_method)s' action='%(target_op)s.py'>
691+
<input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' />
692+
<input type='hidden' name='action' value='%(account_action)s'/>
693+
<input id='submit_button' type=submit value='Renew %(auth_label)s Account Access'/>
694+
</form>
695+
"""
696+
697+
html += """
698+
</div>
699+
"""
700+
return html
701+
702+
660703
def build_accountreqitem_object(configuration, accountreq_dict):
661704
"""Build a accountreq object based on input accountreq_dict"""
662705

@@ -1250,12 +1293,13 @@ def __auto_add_user_allowed(configuration, user_dict, permit_list):
12501293
is empty.
12511294
"""
12521295

1253-
if not permit_list:
1254-
return False
1296+
match = False
1297+
if permit_list:
1298+
match = True
12551299
for (key, val) in permit_list:
12561300
if not re.match(val, user_dict.get(key, 'NO SUCH FIELD')):
12571301
return False
1258-
return True
1302+
return match
12591303

12601304

12611305
def auto_add_user_allowed_direct(configuration, user_dict):

‎mig/shared/functionality/account.py

+72-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# --- BEGIN_HEADER ---
55
#
66
# account - account page with info and account management options
7-
# Copyright (C) 2003-2024 The MiG Project by the Science HPC Center at UCPH
7+
# Copyright (C) 2003-2025 The MiG Project by the Science HPC Center at UCPH
88
#
99
# This file is part of MiG.
1010
#
@@ -34,9 +34,13 @@
3434
import os
3535

3636
from mig.shared import returnvalues
37+
from mig.shared.accountreq import renew_account_access_template
38+
from mig.shared.defaults import csrf_field, user_home_label
3739
from mig.shared.functional import validate_input_and_cert
38-
from mig.shared.init import initialize_main_variables, find_entry
39-
from mig.shared.htmlgen import html_user_messages, man_base_html, man_base_js
40+
from mig.shared.handlers import get_csrf_limit, make_csrf_token
41+
from mig.shared.htmlgen import html_user_messages, man_base_js
42+
from mig.shared.httpsclient import detect_client_auth, find_auth_type_and_label
43+
from mig.shared.init import find_entry, initialize_main_variables
4044
from mig.shared.useradm import get_full_user_map
4145

4246
_account_field_order = [('full_name', 'Full Name'),
@@ -93,7 +97,7 @@ def html_tmpl(configuration, client_id, environ, title_entry):
9397
user_token += claim_dump
9498
fill_helpers = {'short_title': configuration.short_title,
9599
'user_msg': user_msg, 'show_user_msg': show_user_msg,
96-
'user_account': user_account,
100+
'home_label': user_home_label, 'user_account': user_account,
97101
'user_token': user_token}
98102

99103
html = '''
@@ -129,7 +133,70 @@ def html_tmpl(configuration, client_id, environ, title_entry):
129133
</div>
130134
'''
131135

132-
# TODO: add account management actions
136+
# Account management like renew account access for local users
137+
# TODO: add change password and delete account support for all accounts?
138+
(auth_type_name, auth_flavor) = detect_client_auth(configuration, environ)
139+
(auth_type, auth_label) = find_auth_type_and_label(configuration,
140+
auth_type_name,
141+
auth_flavor)
142+
show_local = [i for i in configuration.site_login_methods
143+
if i.startswith('mig')]
144+
fill_helpers.update({'auth_type': auth_type,
145+
'auth_type_name': auth_type_name,
146+
'auth_flavor': auth_flavor,
147+
'auth_label': auth_label})
148+
html += '''
149+
<div id="manage-container" class="row">
150+
<div class="manage-page__header col-12">
151+
<h2>Manage Account</h2>
152+
<p class="sub-title">Depending on your %(short_title)s account
153+
type you have access to one or more account management actions
154+
below.
155+
</p>
156+
</div>
157+
''' % fill_helpers
158+
form_method = 'post'
159+
csrf_limit = get_csrf_limit(configuration)
160+
target_op = 'accountaction'
161+
csrf_token = make_csrf_token(configuration, form_method, target_op,
162+
client_id, csrf_limit)
163+
fill_helpers.update({'target_op': target_op, 'form_method':
164+
form_method, 'csrf_field': csrf_field,
165+
'csrf_token': csrf_token})
166+
# TODO: extend renew to active ext accounts?
167+
if auth_type in show_local and user_dict.get('status', 'active') == 'temporal':
168+
fill_helpers['account_action'] = "RENEW_ACCESS"
169+
fill_helpers['peer_acceptance_notice'] = ""
170+
if configuration.site_peers_mandatory:
171+
peers_full_name = user_dict.get("peers_full_name", "")
172+
peers_email = user_dict.get("peers_email", "")
173+
peers_list = user_dict.get("peers", [])
174+
if peers_list or (peers_full_name and peers_email):
175+
fill_helpers['peer_acceptance_notice'] = """
176+
Apparently %(peers_full_name)s &lt;%(peers_email)s&gt; accepted you as a peer
177+
and if that peer appointment has not yet ended you can renew your access here
178+
without further operator or peer contact involvement. Otherwise you may need to
179+
obtain or await explicit extension or peer assignment from someone else before
180+
your access renewal can proceed.
181+
""" % user_dict
182+
else:
183+
fill_helpers['peer_acceptance_notice'] = """
184+
It looks like you may need someone with authority to appoint you as their peer
185+
before your access renewal can be accepted.
186+
"""
187+
fill_helpers['renew_helper'] = renew_account_access_template(
188+
configuration, default_values=fill_helpers) % fill_helpers
189+
html += '''
190+
<div class="renew-account-access__header col-12">
191+
<h3>Renew Account Access</h3>
192+
%(renew_helper)s
193+
</div>
194+
''' % fill_helpers
195+
196+
html += '''
197+
<div class="col-lg-12 vertical-spacer"></div>
198+
</div>
199+
'''
133200

134201
return html
135202

+326
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
#
4+
# --- BEGIN_HEADER ---
5+
#
6+
# accountaction - handle account actions like change pw and renew access
7+
# Copyright (C) 2003-2025 The MiG Project by the Science HPC Center at UCPH
8+
#
9+
# This file is part of MiG.
10+
#
11+
# MiG is free software: you can redistribute it and/or modify
12+
# it under the terms of the GNU General Public License as published by
13+
# the Free Software Foundation; either version 2 of the License, or
14+
# (at your option) any later version.
15+
#
16+
# MiG is distributed in the hope that it will be useful,
17+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
# GNU General Public License for more details.
20+
#
21+
# You should have received a copy of the GNU General Public License
22+
# along with this program; if not, write to the Free Software
23+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24+
#
25+
# -- END_HEADER ---
26+
#
27+
28+
"""Account actions backend e.g. for account renewal."""
29+
30+
from __future__ import absolute_import
31+
32+
import os
33+
import time
34+
35+
from mig.shared import returnvalues
36+
from mig.shared.base import is_gdp_user
37+
from mig.shared.defaults import keyword_auto, AUTH_MIG_CERT, AUTH_MIG_OID, \
38+
AUTH_MIG_OIDC
39+
from mig.shared.functional import validate_input, REJECT_UNSET
40+
from mig.shared.gdp.all import ensure_gdp_user
41+
from mig.shared.griddaemons.https import default_user_abuse_hits, \
42+
default_proto_abuse_hits, hit_rate_limit, expire_rate_limit, \
43+
validate_auth_attempt
44+
from mig.shared.handlers import safe_handler, get_csrf_limit
45+
from mig.shared.htmlgen import themed_styles, themed_scripts
46+
from mig.shared.httpsclient import detect_client_auth, find_auth_type_and_label
47+
from mig.shared.init import initialize_main_variables
48+
from mig.shared.userdb import default_db_path
49+
from mig.shared.useradm import default_search, search_users, create_user
50+
51+
SUPPORTED_ACTIONS = ["RENEW_ACCESS", ]
52+
53+
54+
def allow_renew_access(configuration, client_id, user_dict, auth_flavor):
55+
"""Helper to check prerequisites for the RENEW_ACCESS requests."""
56+
_logger = configuration.logger
57+
allow_renew, renew_err = False, 'Not fully implemented, yet'
58+
if auth_flavor in (AUTH_MIG_CERT, AUTH_MIG_OID, AUTH_MIG_OIDC):
59+
_logger.debug("Account renew for %r is allowed to proceed" % client_id)
60+
allow_renew, renew_err = True, ""
61+
else:
62+
_logger.warning("Account renew for %r with %s auth unsupported" %
63+
(client_id, auth_flavor))
64+
renew_err = "Account access renew refused - invalid auth flavor!"
65+
return (allow_renew, renew_err)
66+
67+
68+
def renew_access(configuration, client_id, user_dict, auth_flavor):
69+
"""Helper to actually renew access for the RENEW_ACCESS requests."""
70+
_logger = configuration.logger
71+
renew_status, renew_err = False, 'Not fully implemented yet'
72+
peer_pattern = keyword_auto
73+
db_path = default_db_path(configuration)
74+
old_expire = user_dict.get('expire', -1)
75+
if auth_flavor == AUTH_MIG_CERT:
76+
extend_days = configuration.cert_valid_days
77+
elif auth_flavor == AUTH_MIG_OID:
78+
extend_days = configuration.oid_valid_days
79+
elif auth_flavor == AUTH_MIG_OIDC:
80+
extend_days = configuration.oidc_valid_days
81+
else:
82+
extend_days = configuration.generic_valid_days
83+
max_extend_secs = extend_days * 24 * 3600
84+
new_expire = max(old_expire, time.time() + max_extend_secs)
85+
user_dict['expire'] = new_expire
86+
try:
87+
_logger.info("Renew %(distinguished_name)r with expire at %(expire)d"
88+
% user_dict)
89+
updated = create_user(user_dict, configuration, db_path,
90+
ask_renew=False, default_renew=True,
91+
verify_peer=peer_pattern, auto_create_db=False)
92+
if configuration.site_enable_gdp:
93+
(gdp_success, msg) = ensure_gdp_user(configuration,
94+
"127.0.0.1",
95+
user_dict['distinguished_name'])
96+
if not gdp_success:
97+
raise Exception("Failed to renew GDP user: %s" % msg)
98+
renewed_expire = updated.get('expire', -1)
99+
_logger.info("Renewed %(distinguished_name)r to expire at %(expire)d" %
100+
updated)
101+
if renewed_expire > old_expire:
102+
renew_status, renew_err = True, ""
103+
else:
104+
renew_err = "Renew could not extend account expire value (no peer?)"
105+
except Exception as exc:
106+
_logger.warning("Error renewing user %r: %s" % (client_id, exc))
107+
108+
# TODO: send email on renew?
109+
return (renew_status, renew_err)
110+
111+
112+
def signature(configuration):
113+
"""Signature of the main function"""
114+
115+
defaults = {
116+
'action': REJECT_UNSET,
117+
}
118+
return ['text', defaults]
119+
120+
121+
def main(client_id, user_arguments_dict, environ=None):
122+
"""Main function used by front end"""
123+
124+
if environ is None:
125+
environ = os.environ
126+
(configuration, logger, output_objects, op_name) = \
127+
initialize_main_variables(client_id, op_header=False, op_title=False,
128+
op_menu=False)
129+
# IMPORTANT: no title in init above so we MUST call it immediately here
130+
# or basic styling will break on e.g. the check user result.
131+
styles = themed_styles(configuration)
132+
scripts = themed_scripts(configuration, logged_in=False)
133+
title_entry = {'object_type': 'title',
134+
'text': '%s account action' %
135+
configuration.short_title,
136+
'skipmenu': True, 'style': styles, 'script': scripts}
137+
output_objects.append(title_entry)
138+
output_objects.append({'object_type': 'header', 'text':
139+
'%s account action' %
140+
configuration.short_title
141+
})
142+
143+
defaults = signature(configuration)[1]
144+
(validate_status, accepted) = validate_input(user_arguments_dict,
145+
defaults, output_objects,
146+
allow_rejects=False)
147+
if not validate_status:
148+
# NOTE: 'accepted' is a non-sensitive error string here
149+
logger.warning('%s invalid input: %s' % (op_name, accepted))
150+
return (accepted, returnvalues.CLIENT_ERROR)
151+
152+
action = accepted['action'][-1].strip()
153+
154+
# Seconds to delay next attempt after hitting rate limit
155+
if action == "RENEW_ACCESS":
156+
delay_retry = 3600
157+
else:
158+
delay_retry = 300
159+
scripts['init'] += '''
160+
function update_reload_counter(cnt, delay) {
161+
var remain = (delay - cnt);
162+
$("#reload_counter").html(remain.toString());
163+
if (cnt >= delay) {
164+
/* Load previous page again without re-posting last attempt */
165+
location = history.back();
166+
} else {
167+
setTimeout(function() { update_reload_counter(cnt+1, delay); }, 1000);
168+
}
169+
}
170+
'''
171+
172+
if not safe_handler(configuration, 'post', op_name, client_id,
173+
get_csrf_limit(configuration), accepted):
174+
output_objects.append(
175+
{'object_type': 'error_text', 'text': '''Only accepting
176+
CSRF-filtered POST requests to prevent unintended updates'''
177+
})
178+
return (output_objects, returnvalues.CLIENT_ERROR)
179+
180+
if action not in SUPPORTED_ACTIONS:
181+
output_objects.append({'object_type': 'error_text', 'text':
182+
'Unsupported account action: %r' % action})
183+
output_objects.append(
184+
{'object_type': 'link', 'destination': 'javascript:history.back();',
185+
'class': 'genericbutton', 'text': "Try again"})
186+
return (output_objects, returnvalues.CLIENT_ERROR)
187+
188+
(auth_type_name, auth_flavor) = detect_client_auth(configuration, environ)
189+
(auth_type, auth_label) = find_auth_type_and_label(configuration,
190+
auth_type_name,
191+
auth_flavor)
192+
if auth_type not in configuration.site_login_methods:
193+
output_objects.append({'object_type': 'error_text', 'text':
194+
'Site does not support %r authentication' %
195+
auth_type_name})
196+
output_objects.append(
197+
{'object_type': 'link', 'destination': 'javascript:history.back();',
198+
'class': 'genericbutton', 'text': "Try again"})
199+
return (output_objects, returnvalues.CLIENT_ERROR)
200+
201+
logger.info('got %s account %s request from %r' % (auth_type_name, action,
202+
client_id))
203+
204+
client_addr = os.environ.get('REMOTE_ADDR', None)
205+
tcp_port = int(os.environ.get('REMOTE_PORT', '0'))
206+
207+
status = returnvalues.OK
208+
209+
# Rate account action attempts for any client_id from source addr to prevent
210+
# excessive requests spamming users or overloading server.
211+
# We do so no matter if client_id matches a valid user to prevent disclosure.
212+
# Rate limit does not affect action for another ID from same address as
213+
# that may be perfectly valid e.g. if behind a shared NAT-gateway.
214+
proto = 'https'
215+
disconnect, exceeded_rate_limit = False, False
216+
# Clean up expired entries in persistent rate limit cache
217+
expire_rate_limit(configuration, proto, fail_cache=delay_retry,
218+
expire_delay=delay_retry)
219+
if hit_rate_limit(configuration, proto, client_addr, client_id,
220+
max_user_hits=1):
221+
exceeded_rate_limit = True
222+
# Update rate limits and write to auth log
223+
(authorized, disconnect) = validate_auth_attempt(
224+
configuration,
225+
proto,
226+
"accountupdate",
227+
client_id,
228+
client_addr,
229+
tcp_port,
230+
secret='',
231+
authtype_enabled=True,
232+
modify_account=True,
233+
exceeded_rate_limit=exceeded_rate_limit,
234+
user_abuse_hits=default_user_abuse_hits,
235+
proto_abuse_hits=default_proto_abuse_hits,
236+
max_secret_hits=1,
237+
skip_notify=True,
238+
)
239+
240+
if exceeded_rate_limit or disconnect:
241+
logger.warning('Throttle %s for %s from %s - past rate limit' %
242+
(op_name, client_id, client_addr))
243+
# NOTE: we keep actual result in plain text for json extract
244+
output_objects.append({'object_type': 'html_form', 'text': '''
245+
<div class="vertical-spacer"></div>
246+
<div class="error leftpad errortext">
247+
'''})
248+
output_objects.append({'object_type': 'text', 'text': """
249+
Invalid input or rate limit exceeded - please wait %d seconds before retrying.
250+
""" % delay_retry
251+
})
252+
output_objects.append({'object_type': 'html_form', 'text': '''
253+
</div>
254+
<div class="vertical-spacer"></div>
255+
<div class="info leftpad">
256+
Origin will reload automatically in <span id="reload_counter">%d</span> seconds.
257+
</div>
258+
</div>
259+
''' % delay_retry})
260+
scripts['ready'] += '''
261+
setTimeout(function() { update_reload_counter(1, %d); }, 1000);
262+
''' % delay_retry
263+
return (output_objects, status)
264+
265+
search_filter = default_search()
266+
search_filter['distinguished_name'] = client_id
267+
(_, hits) = search_users(search_filter, configuration, keyword_auto, False)
268+
# Filter out any gdp project users
269+
hits = [i for i in hits if not is_gdp_user(configuration, i[0])]
270+
if len(hits) != 1:
271+
logger.warning("%d local users unexpectedly matched %r" % (len(hits),
272+
client_id))
273+
output_objects.append({
274+
'object_type': 'error_text', 'text':
275+
"""Account action failed with internal error. Please report to
276+
support if the problem persists.
277+
"""})
278+
status = returnvalues.SYSTEM_ERROR
279+
return (output_objects, status)
280+
281+
logger.debug('handle %s account %s request from %r' % (auth_type_name,
282+
action, client_id))
283+
(uid, user_dict) = hits[0]
284+
if action == "RENEW_ACCESS":
285+
allowed, err = allow_renew_access(configuration, client_id,
286+
user_dict, auth_flavor)
287+
if not allowed:
288+
output_objects.append(
289+
{'object_type': 'error_text', 'text':
290+
'Refused account access renew: %s' % err})
291+
output_objects.append(
292+
{'object_type': 'link', 'destination':
293+
'javascript:history.back();',
294+
'class': 'genericbutton', 'text': "Try again"})
295+
return (output_objects, returnvalues.CLIENT_ERROR)
296+
297+
renewed, err = renew_access(configuration, client_id, user_dict,
298+
auth_flavor)
299+
if not renewed:
300+
output_objects.append(
301+
{'object_type': 'error_text', 'text':
302+
'Failed to renew account access: %s' % err})
303+
output_objects.append(
304+
{'object_type': 'link', 'destination':
305+
'javascript:history.back();',
306+
'class': 'genericbutton', 'text': "Try again"})
307+
return (output_objects, returnvalues.CLIENT_ERROR)
308+
309+
output_objects.append(
310+
{'object_type': 'text', 'text':
311+
'Your account access was successfully renewed.'})
312+
else:
313+
output_objects.append(
314+
{'object_type': 'error_text', 'text':
315+
'Unsupported account action: %s' % action})
316+
output_objects.append(
317+
{'object_type': 'link', 'destination':
318+
'javascript:history.back();',
319+
'class': 'genericbutton', 'text': "Try again"})
320+
return (output_objects, returnvalues.CLIENT_ERROR)
321+
322+
logger.debug("Account %s %s completed" % (auth_type_name, action))
323+
output_objects.append(
324+
{'object_type': 'link', 'destination': 'account.py',
325+
'class': 'genericbutton', 'text': "Show Account Info"})
326+
return (output_objects, status)

‎mig/shared/httpsclient.py

+17-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# --- BEGIN_HEADER ---
55
#
66
# httpsclient - Shared functions for all HTTPS clients
7-
# Copyright (C) 2003-2024 The MiG Project lead by Brian Vinter
7+
# Copyright (C) 2003-2025 The MiG Project by the Science HPC Center at UCPH
88
#
99
# This file is part of MiG.
1010
#
@@ -33,12 +33,12 @@
3333
import os
3434
import socket
3535

36+
from mig.shared.base import is_gdp_user, get_xgi_bin, auth_type_description
3637
from mig.shared.defaults import AUTH_CERTIFICATE, AUTH_OPENID_V2, \
3738
AUTH_OPENID_CONNECT, AUTH_GENERIC, AUTH_NONE, AUTH_MIG_OID, AUTH_EXT_OID, \
3839
AUTH_MIG_OIDC, AUTH_EXT_OIDC, AUTH_MIG_CERT, AUTH_EXT_CERT, \
3940
AUTH_SID_GENERIC, AUTH_UNKNOWN, auth_openid_mig_db, auth_openid_ext_db, \
4041
keyword_all, csrf_field
41-
from mig.shared.base import is_gdp_user, get_xgi_bin
4242
from mig.shared.gdp.all import get_project_user_dn
4343
from mig.shared.handlers import get_csrf_limit
4444
from mig.shared.pwcrypto import make_csrf_token, make_csrf_trust_token
@@ -420,6 +420,21 @@ def extract_client_id(configuration, environ, lookup_dn=True):
420420
return distinguished_name
421421

422422

423+
def find_auth_type_and_label(configuration, auth_type_name, auth_flavor):
424+
"""Use contents of auth_type_description and auth_flavor to lookup actual
425+
simple auth_type (migoid, extcert, ...) and corresponding descritpive label
426+
for that auth_type.
427+
"""
428+
rev_auth_flavor_map = {AUTH_EXT_OID: 'extoid', AUTH_EXT_OIDC: 'extoidc',
429+
AUTH_MIG_OID: 'migoid', AUTH_MIG_OIDC: 'migoidc',
430+
AUTH_EXT_CERT: 'extcert', AUTH_MIG_CERT: 'migcert',
431+
AUTH_UNKNOWN: 'unknown'}
432+
auth_type = rev_auth_flavor_map[auth_flavor]
433+
auth_map = auth_type_description(configuration)
434+
auth_label = auth_map[auth_type]
435+
return (auth_type, auth_label)
436+
437+
423438
def require_twofactor_setup(configuration, script_name, client_id, environ):
424439
"""Check if site requires twofactor for this web access and if so return
425440
the corresponding functionality backend main function.

0 commit comments

Comments
 (0)
Please sign in to comment.