From 19f531c09a91ae3a49d46e5a0e874e09bf6d1afc Mon Sep 17 00:00:00 2001 From: Lincoln Bryant Date: Thu, 5 Aug 2021 12:54:35 -0500 Subject: [PATCH 01/17] added qrcode support --- portal/views.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/portal/views.py b/portal/views.py index 4e2a3d0..d8dae9f 100644 --- a/portal/views.py +++ b/portal/views.py @@ -1,4 +1,5 @@ -from flask import flash, redirect, render_template, request, session, url_for, jsonify +from flask import flash, redirect, render_template, request, session, url_for, jsonify +from flask_qrcode import QRcode import requests import json @@ -29,6 +30,9 @@ get_user_group_status, ) +# enable QR code support +QRcode(app) + # Use these four lines on container import sys import subprocess @@ -795,6 +799,7 @@ def create_profile(): session["phone"] = r["phone"] session["institution"] = r["institution"] session["unix_name"] = r["unix_name"] + #session["totp_secret"] = r["totp_secret"] # Auto generate group membership into connect group # put_query = {"apiVersion": 'v1alpha1', From 4d1e677792e548e86dd02ad3b2cc364b8b360341 Mon Sep 17 00:00:00 2001 From: Lincoln Bryant Date: Thu, 5 Aug 2021 12:54:49 -0500 Subject: [PATCH 02/17] added profile dropdown for MFA --- portal/templates/profile_edit.html | 35 +++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/portal/templates/profile_edit.html b/portal/templates/profile_edit.html index a27fa78..239dc73 100644 --- a/portal/templates/profile_edit.html +++ b/portal/templates/profile_edit.html @@ -178,8 +178,41 @@
- + +

+ Multi-Factor Authentication +

+
+
+
+
+ +
+ +
+
    +
  1. Download the Google Authenticator application: + +
  2. +
  3. Click the "+" icon in the lower righthand corner, and click "Scan a QR Code"
  4. +
  5. Scan the following QR code:
  6. +
    +
  7. Once the QR code has been scanned, the app will start to generate six digit security tokens. You will need to enter this token when logging in
  8. +
+
+
+
+ From 842f9642eecda93d0c9b19723f592caab8263643 Mon Sep 17 00:00:00 2001 From: Lincoln Bryant Date: Thu, 5 Aug 2021 12:54:56 -0500 Subject: [PATCH 03/17] qrcode support --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 25459d1..7ca8e24 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,7 @@ Flask==1.0.2 Flask-Markdown Flask-Misaka flask_wtf +flask-qrcode pygal==2.1.1 globus-sdk==1.7.1 -pyyaml \ No newline at end of file +pyyaml From c0411748808170e38101eff39ed8afac1de0f866 Mon Sep 17 00:00:00 2001 From: Lincoln Bryant Date: Thu, 2 Sep 2021 13:55:25 -0500 Subject: [PATCH 04/17] Added test portal --- portal/connect_api.py | 2 ++ portal/static/img/www-test-logo.png | Bin 0 -> 4597 bytes portal/templates/home.html | 2 ++ portal/templates/scripts.html | 11 ++++++++++- portal/views.py | 6 ++++++ 5 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 portal/static/img/www-test-logo.png diff --git a/portal/connect_api.py b/portal/connect_api.py index 4b73075..cf66f66 100644 --- a/portal/connect_api.py +++ b/portal/connect_api.py @@ -338,6 +338,8 @@ def domain_name_edgecase(): domain_name = "cms.ci-connect.net" elif "af" in domain_name: domain_name = "af.uchicago.edu" + elif "test" in domain_name: + domain_name = "www-test.ci-connect.net" elif "uchicago" in domain_name: domain_name = "psdconnect.uchicago.edu" elif "snowmass21" in domain_name: diff --git a/portal/static/img/www-test-logo.png b/portal/static/img/www-test-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a07bda1d07ee751340645264b6577e9cabbd8ddc GIT binary patch literal 4597 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 document.getElementById("home-brand").src="{{url_for('static', filename='img/spt-logo.jpg')}}"; } else if(String(hostname).includes('af.uchicago')){ document.getElementById("home-brand").src="{{url_for('static', filename='img/atlas-af-logo.png')}}"; +} else if(String(hostname).includes('test')){ + document.getElementById("home-brand").src="{{url_for('static', filename='img/www-test-logo.png')}}"; } else if(String(hostname).includes('snowmass21')){ document.getElementById("home-brand").src="{{url_for('static', filename='img/snowmass-connect-logo.png')}}"; } else if(String(hostname).includes('psdconnect') || String(hostname).includes('uchicago')){ diff --git a/portal/templates/scripts.html b/portal/templates/scripts.html index bb6ec67..ae0fe43 100644 --- a/portal/templates/scripts.html +++ b/portal/templates/scripts.html @@ -228,7 +228,16 @@ // Set the href property var href = 'https://gracc.opensciencegrid.org/dashboard/db/osg-connect-summary-uchicago-ci?orgId=1'; addDropdownItem(title, href); - + } else if(String(hostname).includes('test')) { + connect_brand.src="{{url_for('static', filename='img/www-test-logo.png')}}"; + nav_overview.href="https://www-test.ci-connect.net/"; + nav_emailto.href="mailto:support@ci-connect.uchicago.edu"; + document.getElementById("login-node-nav").style.display = "inline"; + // Set the title + var title = "CI Connect Test Branch" + // Set the href property + var href = 'https://gracc.opensciencegrid.org/dashboard/db/osg-connect-summary-uchicago-ci?orgId=1'; + addDropdownItem(title, href); } else { // Global site tag (gtag.js) - Google Analytics window.dataLayer = window.dataLayer || []; diff --git a/portal/views.py b/portal/views.py index d8dae9f..eb2ca3b 100644 --- a/portal/views.py +++ b/portal/views.py @@ -147,6 +147,12 @@ def home(): "img": "img/snowmass-connect-logo.png", "description": get_about_markdown("snowmass21.ci-connect.net"), }, + { + "name": "Test", + "href": "https://www-test.ci-connect.net", + "img": "img/www-test-logo.png", + "description": get_about_markdown("www-test.ci-connect.net"), + }, ] return render_template( From d983daca623ce1e44986f6ccafec1052925cfb9f Mon Sep 17 00:00:00 2001 From: Lincoln Bryant Date: Thu, 2 Sep 2021 14:12:39 -0500 Subject: [PATCH 05/17] Fixed the notice about usernames hopefully --- portal/templates/profile_create.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/portal/templates/profile_create.html b/portal/templates/profile_create.html index 16ddf6a..e5f274c 100644 --- a/portal/templates/profile_create.html +++ b/portal/templates/profile_create.html @@ -39,7 +39,7 @@

Globus ID: {{session['primary_identity']}}

-

Please use POSIX unix convention

+

Usernames must start with a lowercase letter. All other characters may be lowercase letters, numbers, or hyphens.

Date: Thu, 2 Sep 2021 14:16:20 -0500 Subject: [PATCH 06/17] removed the egregiously mispelled word that has been there forever --- portal/templates/base.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/portal/templates/base.html b/portal/templates/base.html index ab25e5a..faad480 100644 --- a/portal/templates/base.html +++ b/portal/templates/base.html @@ -7,7 +7,7 @@ - + {# Favicons for Connect #} From 3bc2a9a2e9d4b6494eba7c906239e8803b2921de Mon Sep 17 00:00:00 2001 From: Lincoln Bryant Date: Thu, 2 Sep 2021 14:36:40 -0500 Subject: [PATCH 07/17] attempt to request a TOTP secret --- portal/views.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/portal/views.py b/portal/views.py index eb2ca3b..bdbd8b2 100644 --- a/portal/views.py +++ b/portal/views.py @@ -762,6 +762,7 @@ def create_profile(): globus_id = session["primary_identity"] superuser = False service_account = False + create_totp_secret = True # Schema and query for adding users to CI Connect DB if public_key: @@ -777,6 +778,7 @@ def create_profile(): "unix_name": unix_name, "superuser": superuser, "service_account": service_account, + "create_totp_secret": create_totp_secret, }, } else: @@ -791,6 +793,7 @@ def create_profile(): "unix_name": unix_name, "superuser": superuser, "service_account": service_account, + "create_totp_secret": create_totp_secret, }, } r = requests.post( @@ -805,7 +808,7 @@ def create_profile(): session["phone"] = r["phone"] session["institution"] = r["institution"] session["unix_name"] = r["unix_name"] - #session["totp_secret"] = r["totp_secret"] + session["totp_secret"] = r["totp_secret"] # Auto generate group membership into connect group # put_query = {"apiVersion": 'v1alpha1', From 160b8e82c2d31c78bf574a4d4fa3406072a146bd Mon Sep 17 00:00:00 2001 From: Lincoln Bryant Date: Thu, 2 Sep 2021 14:44:08 -0500 Subject: [PATCH 08/17] Just return an error message but not internal server error when we're missing a file --- portal/views.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/portal/views.py b/portal/views.py index d8dae9f..c00a42a 100644 --- a/portal/views.py +++ b/portal/views.py @@ -158,9 +158,13 @@ def home(): def get_about_markdown(domain_name): - with open(brand_dir + "/" + domain_name + "/about/about.md", "r") as file: - about = file.read() - return about + try: + with open(brand_dir + "/" + domain_name + "/about/about.md", "r") as file: + about = file.read() + return about + except EnvironmentError as e: + print("Could not open markdown directories") + return "Empty or missing about.md - did you create the portal markdowns?" @app.route("/groups/new", methods=["GET", "POST"]) From 7d92aad815c7603211652e23806637d61c4bdbea Mon Sep 17 00:00:00 2001 From: Lincoln Bryant Date: Thu, 2 Sep 2021 14:45:53 -0500 Subject: [PATCH 09/17] fixed tabbing issue --- portal/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/portal/views.py b/portal/views.py index 441356e..6ffabca 100644 --- a/portal/views.py +++ b/portal/views.py @@ -812,7 +812,7 @@ def create_profile(): session["phone"] = r["phone"] session["institution"] = r["institution"] session["unix_name"] = r["unix_name"] - session["totp_secret"] = r["totp_secret"] + session["totp_secret"] = r["totp_secret"] # Auto generate group membership into connect group # put_query = {"apiVersion": 'v1alpha1', From 721b562107d3a882ff9d726bedb440beac434165 Mon Sep 17 00:00:00 2001 From: Lincoln Bryant Date: Thu, 2 Sep 2021 16:20:46 -0500 Subject: [PATCH 10/17] added MFA QR code generation --- portal/templates/profile_edit.html | 5 ++++- portal/views.py | 9 +++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/portal/templates/profile_edit.html b/portal/templates/profile_edit.html index 239dc73..10d4a80 100644 --- a/portal/templates/profile_edit.html +++ b/portal/templates/profile_edit.html @@ -180,6 +180,7 @@
+ {% if profile['totp_secret'] %}

Multi-Factor Authentication

@@ -206,13 +207,15 @@
  • Click the "+" icon in the lower righthand corner, and click "Scan a QR Code"
  • Scan the following QR code:
  • -
    +
  • Once the QR code has been scanned, the app will start to generate six digit security tokens. You will need to enter this token when logging in
  • + {% endif %} + diff --git a/portal/views.py b/portal/views.py index 6ffabca..41dc9b4 100644 --- a/portal/views.py +++ b/portal/views.py @@ -812,7 +812,6 @@ def create_profile(): session["phone"] = r["phone"] session["institution"] = r["institution"] session["unix_name"] = r["unix_name"] - session["totp_secret"] = r["totp_secret"] # Auto generate group membership into connect group # put_query = {"apiVersion": 'v1alpha1', @@ -866,9 +865,15 @@ def edit_profile(unix_name): # Get user info, pass through as args, convert to json and load input fields profile = get_user_profile(unix_name) profile = profile["metadata"] + + # The auth string should never get used if the totp_secret key doesn't exist anyhow. + try: + authenticator_string = "otpauth://totp/" + unix_name + "@ci-connect?secret=" + profile["totp_secret"] + "&issuer=CI%20Connect" + except KeyError: + authenticator_string = None return render_template( - "profile_edit.html", profile=profile, unix_name=unix_name + "profile_edit.html", profile=profile, unix_name=unix_name, authenticator_string=authenticator_string ) elif request.method == "POST": From 7fed0f4b4001b179b21da6a1e40f6bdf7e30b359 Mon Sep 17 00:00:00 2001 From: Lincoln Bryant Date: Fri, 10 Sep 2021 12:45:19 -0500 Subject: [PATCH 11/17] added button to regenerate MFA --- portal/templates/profile_edit.html | 10 ++++++++++ portal/views.py | 1 + 2 files changed, 11 insertions(+) diff --git a/portal/templates/profile_edit.html b/portal/templates/profile_edit.html index 239dc73..21f2b4e 100644 --- a/portal/templates/profile_edit.html +++ b/portal/templates/profile_edit.html @@ -102,6 +102,16 @@
    User Globus ID: {{session['primary_identity']}}
    > +
    + + +
    +
    diff --git a/portal/views.py b/portal/views.py index bdbd8b2..b54860c 100644 --- a/portal/views.py +++ b/portal/views.py @@ -875,6 +875,7 @@ def edit_profile(unix_name): public_key = request.form["sshpubstring"] globus_id = session["primary_identity"] x509dn = request.form["x509dn"] + create_totp_secret = request.form["create_totp_secret"] access_token = get_user_access_token(session) query = {"token": access_token, "globus_id": identity_id} # Schema and query for adding users to CI Connect DB From 79202417189c9c55781e8cb4a41c856b6d214fd4 Mon Sep 17 00:00:00 2001 From: Lincoln Bryant Date: Fri, 10 Sep 2021 12:46:52 -0500 Subject: [PATCH 12/17] update the string --- portal/views.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/portal/views.py b/portal/views.py index 41dc9b4..dd0a52c 100644 --- a/portal/views.py +++ b/portal/views.py @@ -5,7 +5,7 @@ try: # Python 2 - from urllib.parse import urlparse, urlencode, parse_qs + from urllib.parse import urlparse, urlencode, parse_qs, quote except ImportError: # Python 3 from urlparse import urlparse, parse_qs @@ -764,7 +764,7 @@ def create_profile(): institution = request.form["institution"] public_key = request.form["sshpubstring"] globus_id = session["primary_identity"] - superuser = False + superuser = False # is this safe? TODO flagging this one service_account = False create_totp_secret = True @@ -865,10 +865,13 @@ def edit_profile(unix_name): # Get user info, pass through as args, convert to json and load input fields profile = get_user_profile(unix_name) profile = profile["metadata"] + + print("SESSION IS: " + str(session)) # The auth string should never get used if the totp_secret key doesn't exist anyhow. try: - authenticator_string = "otpauth://totp/" + unix_name + "@ci-connect?secret=" + profile["totp_secret"] + "&issuer=CI%20Connect" + issuer = quote(session["url_host"]["display_name"]) + authenticator_string = "otpauth://totp/" + unix_name + "?secret=" + profile["totp_secret"] + "&issuer=" + issuer except KeyError: authenticator_string = None From 2b1e874efe2336f47ebb590808aa7d6441a44914 Mon Sep 17 00:00:00 2001 From: Lincoln Bryant Date: Tue, 28 Sep 2021 10:48:28 -0500 Subject: [PATCH 13/17] add a button for creating TOTP secret generation requests, move the TOTP information to the profile page rather than profile edit --- portal/views.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/portal/views.py b/portal/views.py index d429ca1..aed7fdd 100644 --- a/portal/views.py +++ b/portal/views.py @@ -866,17 +866,8 @@ def edit_profile(unix_name): profile = get_user_profile(unix_name) profile = profile["metadata"] - print("SESSION IS: " + str(session)) - - # The auth string should never get used if the totp_secret key doesn't exist anyhow. - try: - issuer = quote(session["url_host"]["display_name"]) - authenticator_string = "otpauth://totp/" + unix_name + "?secret=" + profile["totp_secret"] + "&issuer=" + issuer - except KeyError: - authenticator_string = None - return render_template( - "profile_edit.html", profile=profile, unix_name=unix_name, authenticator_string=authenticator_string + "profile_edit.html", profile=profile, unix_name=unix_name ) elif request.method == "POST": @@ -887,7 +878,10 @@ def edit_profile(unix_name): public_key = request.form["sshpubstring"] globus_id = session["primary_identity"] x509dn = request.form["x509dn"] - create_totp_secret = request.form["create_totp_secret"] + if request.form.get("totpsecret") is not None: + create_totp_secret = True + else: + create_totp_secret = False access_token = get_user_access_token(session) query = {"token": access_token, "globus_id": identity_id} # Schema and query for adding users to CI Connect DB @@ -901,6 +895,7 @@ def edit_profile(unix_name): "institution": institution, "public_key": public_key, "X.509_DN": x509dn, + "create_totp_secret": create_totp_secret, }, } else: @@ -912,6 +907,7 @@ def edit_profile(unix_name): "phone": phone, "institution": institution, "X.509_DN": x509dn, + "totp_secret": create_totp_secret, }, } # PUT request to update user information @@ -952,8 +948,14 @@ def profile(): profile = None if profile: - print("Found profile: {}".format(profile)) - profile = profile["metadata"] + profile = profile["metadata"] # let's fix this, sometime. it's kinda unsavory. + # The auth string should never get used if the totp_secret key doesn't exist anyhow. + try: + issuer = quote(session["url_host"]["display_name"]) + authenticator_string = "otpauth://totp/" + unix_name + "?secret=" + profile["totp_secret"] + "&issuer=" + issuer + except KeyError as e: + print("Couldn't find a totp_secret in the profile for ",unix_name) + authenticator_string = None unix_name = profile["unix_name"] group_name = session["url_host"]["unix_name"] user_status = get_user_group_status(unix_name, group_name, session) @@ -990,6 +992,7 @@ def profile(): user_status=user_status, group_memberships=group_memberships, group_unix_name_description=group_unix_name_description, + authenticator_string=authenticator_string ) From a17e7b7fdde916b5363392a9992825ce5f38c475 Mon Sep 17 00:00:00 2001 From: Lincoln Bryant Date: Tue, 28 Sep 2021 10:48:44 -0500 Subject: [PATCH 14/17] added the TOTP information box incl. QR code on the profile page --- portal/templates/profile.html | 47 ++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/portal/templates/profile.html b/portal/templates/profile.html index f5161ba..81761c1 100644 --- a/portal/templates/profile.html +++ b/portal/templates/profile.html @@ -105,8 +105,53 @@

    Profile

    - + + {% if profile['totp_secret'] %} +
    +
    +
    +
    +
    +
    +
    + Multi-Factor Authentication +
    +
    +
    + +
    + +
    +
      +
    1. Download the Google Authenticator application: + +
    2. +
    3. Click the "+" icon in the lower righthand corner, and click "Scan a QR Code"
    4. +
    5. Scan the following QR code:
    6. +
      +
    7. Once the QR code has been scanned, the app will start to generate six digit security tokens. You will need to enter this token when logging in
    8. +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + {% endif %} From 1563b4c8fafa299b2f2bafcc696b00ed23832a87 Mon Sep 17 00:00:00 2001 From: Lincoln Bryant Date: Tue, 28 Sep 2021 10:50:08 -0500 Subject: [PATCH 15/17] minor formatting changes --- portal/templates/profile_create.html | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/portal/templates/profile_create.html b/portal/templates/profile_create.html index e5f274c..47273df 100644 --- a/portal/templates/profile_create.html +++ b/portal/templates/profile_create.html @@ -15,7 +15,7 @@

    Create Profile

    -

    Globus ID: {{session['primary_identity']}}

    + Globus ID: {{session['primary_identity']}}

    * = required field


    @@ -38,8 +38,7 @@

    Globus ID: {{session['primary_identity']}}

    - -

    Usernames must start with a lowercase letter. All other characters may be lowercase letters, numbers, or hyphens.

    + Globus ID: {{session['primary_identity']}}
    maxlength="32" pattern="^[a-z][-a-z0-9]*$" > + Usernames must start with a lowercase letter. All other characters may be lowercase letters, numbers, or hyphens.
    @@ -85,7 +85,7 @@

    Globus ID: {{session['primary_identity']}}

    - + Globus ID: {{session['primary_identity']}} required="required" tabindex="5" > + Please use an institutional email address
    From c5ebedfe2668e05703a56a0a24ba5198498c9765 Mon Sep 17 00:00:00 2001 From: Lincoln Bryant Date: Tue, 28 Sep 2021 10:51:18 -0500 Subject: [PATCH 16/17] minor formatting pass, and also added a button for generating MFA tokens --- portal/templates/profile_edit.html | 64 +++++++----------------------- 1 file changed, 14 insertions(+), 50 deletions(-) diff --git a/portal/templates/profile_edit.html b/portal/templates/profile_edit.html index 85830ac..d8b6a6b 100644 --- a/portal/templates/profile_edit.html +++ b/portal/templates/profile_edit.html @@ -15,8 +15,8 @@

    Edit Profile

    -
    Unix Username: {{session['unix_name']}}
    -
    User Globus ID: {{session['primary_identity']}}
    + UNIX Username: {{session['unix_name']}} +
    Globus ID: {{session['primary_identity']}}

    * = required field


    @@ -66,7 +66,7 @@
    User Globus ID: {{session['primary_identity']}}
    - + User Globus ID: {{session['primary_identity']}} required="required" tabindex="4" > + Please use an institutional email address
    @@ -91,6 +92,16 @@
    User Globus ID: {{session['primary_identity']}}
    >
    +
    + + + {% if profile['totp_secret'] %} + This will delete your current MFA secret and generate a new one + {% else %} + Enabling MFA will add an additional layer of security for SSH connections + {% endif %} +

    +
    -
    - - -
    -
    @@ -189,43 +190,6 @@
    - - {% if profile['totp_secret'] %} -

    - Multi-Factor Authentication -

    -
    -
    -
    -
    - -
    - -
    -
      -
    1. Download the Google Authenticator application: - -
    2. -
    3. Click the "+" icon in the lower righthand corner, and click "Scan a QR Code"
    4. -
    5. Scan the following QR code:
    6. -
      -
    7. Once the QR code has been scanned, the app will start to generate six digit security tokens. You will need to enter this token when logging in
    8. -
    -
    -
    -
    - - {% endif %} - From ba4e10aa8d903c0a16eac3698ee16529a09695d6 Mon Sep 17 00:00:00 2001 From: Lincoln Bryant Date: Tue, 28 Sep 2021 10:58:10 -0500 Subject: [PATCH 17/17] black formatter pass, such that we can appease PEP8 and safely edit this code on VT100s --- portal/views.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/portal/views.py b/portal/views.py index aed7fdd..03dc916 100644 --- a/portal/views.py +++ b/portal/views.py @@ -1,4 +1,4 @@ -from flask import flash, redirect, render_template, request, session, url_for, jsonify +from flask import flash, redirect, render_template, request, session, url_for, jsonify from flask_qrcode import QRcode import requests import json @@ -764,7 +764,7 @@ def create_profile(): institution = request.form["institution"] public_key = request.form["sshpubstring"] globus_id = session["primary_identity"] - superuser = False # is this safe? TODO flagging this one + superuser = False # is this safe? TODO flagging this one service_account = False create_totp_secret = True @@ -879,9 +879,9 @@ def edit_profile(unix_name): globus_id = session["primary_identity"] x509dn = request.form["x509dn"] if request.form.get("totpsecret") is not None: - create_totp_secret = True + create_totp_secret = True else: - create_totp_secret = False + create_totp_secret = False access_token = get_user_access_token(session) query = {"token": access_token, "globus_id": identity_id} # Schema and query for adding users to CI Connect DB @@ -948,13 +948,22 @@ def profile(): profile = None if profile: - profile = profile["metadata"] # let's fix this, sometime. it's kinda unsavory. + profile = profile[ + "metadata" + ] # let's fix this, sometime. it's kinda unsavory. # The auth string should never get used if the totp_secret key doesn't exist anyhow. try: issuer = quote(session["url_host"]["display_name"]) - authenticator_string = "otpauth://totp/" + unix_name + "?secret=" + profile["totp_secret"] + "&issuer=" + issuer + authenticator_string = ( + "otpauth://totp/" + + unix_name + + "?secret=" + + profile["totp_secret"] + + "&issuer=" + + issuer + ) except KeyError as e: - print("Couldn't find a totp_secret in the profile for ",unix_name) + print("Couldn't find a totp_secret in the profile for ", unix_name) authenticator_string = None unix_name = profile["unix_name"] group_name = session["url_host"]["unix_name"] @@ -992,7 +1001,7 @@ def profile(): user_status=user_status, group_memberships=group_memberships, group_unix_name_description=group_unix_name_description, - authenticator_string=authenticator_string + authenticator_string=authenticator_string, )