Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
19f531c
added qrcode support
Aug 5, 2021
4d1e677
added profile dropdown for MFA
Aug 5, 2021
842f964
qrcode support
Aug 5, 2021
6e16049
Merge branch 'test' of https://github.com/maniaclab/ciconnect-portal …
Sep 2, 2021
c041174
Added test portal
Sep 2, 2021
d983dac
Fixed the notice about usernames hopefully
Sep 2, 2021
3f9e0f5
removed the egregiously mispelled word that has been there forever
Sep 2, 2021
3bc2a9a
attempt to request a TOTP secret
Sep 2, 2021
160b8e8
Just return an error message but not internal server error when we're…
LincolnBryant Sep 2, 2021
7734177
Merge branch 'test' of https://github.com/maniaclab/ciconnect-portal …
LincolnBryant Sep 2, 2021
7d92aad
fixed tabbing issue
LincolnBryant Sep 2, 2021
721b562
added MFA QR code generation
Sep 2, 2021
7fed0f4
added button to regenerate MFA
Sep 10, 2021
38fefec
Merge branch 'test' of https://github.com/maniaclab/ciconnect-portal …
Sep 10, 2021
7920241
update the string
Sep 10, 2021
3e1cc88
Merge branch 'test' of https://github.com/maniaclab/ciconnect-portal …
Sep 10, 2021
2b1e874
add a button for creating TOTP secret generation requests, move the T…
Sep 28, 2021
a17e7b7
added the TOTP information box incl. QR code on the profile page
Sep 28, 2021
1563b4c
minor formatting changes
Sep 28, 2021
c5ebedf
minor formatting pass, and also added a button for generating MFA tokens
Sep 28, 2021
ba4e10a
black formatter pass, such that we can appease PEP8 and safely edit t…
Sep 28, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions portal/connect_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Binary file added portal/static/img/www-test-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion portal/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<meta http-equiv="Content-Language" content="en">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="CI Connect Portal, Effiently connect your science to cycles and data">
<meta name="description" content="CI Connect Portal">
<meta name="author" content="Jeremy Van">

{# Favicons for Connect #}
Expand Down
2 changes: 2 additions & 0 deletions portal/templates/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ <h5 class="section-description" id="{{collab['name']}}"></h5>
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')){
Expand Down
47 changes: 46 additions & 1 deletion portal/templates/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,53 @@ <h1>Profile</h1>
</div>

</div>
</div>
</div> <!-- inner row -->
</div> <!-- row -->
{% if profile['totp_secret'] %}
<br>
<div class="row"> <!-- MFA card begin -->
<div class="col-lg-12 mx-auto">
<div class="row">
<div class="col-sm-12 col-md-6">
<div class="card h-100 btn-box-shadow ">
<div class="card-header">
<b>Multi-Factor Authentication</b> <i class="fas fa-user-secret float-right"></i>
<div id="accordionTwo" role="tablist" aria-multiselectable="true">
</div>
<div class="card panel panel-info resource-profile" style="margin: 1em;">
<div class="card-header panel-title" role="tab" id="heading">
<h6 class="mb-0">
<a class="collapsed" data-toggle="collapse" data-parent="#accordionTwo" href="#collapseFour" aria-expanded="true" aria-controls="collapse">
Setup Information<i class="fa fa-info-circle pull-right" aria-hidden="true"></i>
</a>
</h6>
</div>
<div id="collapseFour" class="collapse" role="tabpanel" aria-labelledby="heading">
<!-- TODO: Put information here -->
<div class="card-block panel-body disabled" style="font-size: 14px">
<ol>
<li>Download the Google Authenticator application:
<ul>
<li> <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2"> Android </a> </li>
<li> <a href="https://apps.apple.com/us/app/google-authenticator/id388497605"> iOS </a> </li>
</ul>
</li>
<li> Click the "+" icon in the lower righthand corner, and click "Scan a QR Code" </li>
<li> Scan the following QR code: </li>
<div> <img src="{{ qrcode(authenticator_string) }}"> </div>
<li> 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 </li>
</ol>
</div>
</div>
</div>
</div><!-- /.accordionTwo-->
</div>
</div>
</div>
</div>
</div>
</div><!-- MFA card end -->
{% endif %}
</div>
</section>

Expand Down
9 changes: 5 additions & 4 deletions portal/templates/profile_create.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<div class="row">
<div class="col-lg-6 mx-auto">
<h2>Create Profile</h2>
<h4>Globus ID: {{session['primary_identity']}}</h4>
<small id="globusID" class="text-muted">Globus ID: {{session['primary_identity']}}</small>
<p class="asterik">* = required field</p>
<hr/>
<form role="form" action="{{url_for('create_profile')}}" method="POST">
Expand All @@ -38,8 +38,7 @@ <h4>Globus ID: {{session['primary_identity']}}</h4>
</div>

<div class="form-group">
<label for="unix_name">Unix Username <span class="asterik">*</span></label>
<p class="text-muted">Please use POSIX unix convention <i class="fas fa-question-circle" data-container="body" data-toggle="popover" data-trigger="hover" data-placement="right" data-content="Usernames must start with a lowercase letter. All other characters may be lowercase letters, numbers, or hyphens."></i></p>
<label for="unix_name">UNIX Username <span class="asterik">*</span></label>
<input
type="text"
class="form-control"
Expand All @@ -52,6 +51,7 @@ <h4>Globus ID: {{session['primary_identity']}}</h4>
maxlength="32"
pattern="^[a-z][-a-z0-9]*$"
>
<small id="unixHelp" class="form-text text-muted">Usernames must start with a lowercase letter. All other characters may be lowercase letters, numbers, or hyphens.</small>
</div>

<div class="form-group">
Expand Down Expand Up @@ -85,7 +85,7 @@ <h4>Globus ID: {{session['primary_identity']}}</h4>
</div>

<div class="form-group">
<label for="email">Email <span class="asterik">*</span> (please use institutional email)</label>
<label for="email">Email <span class="asterik">*</span></label>
<input
type="email"
id="email"
Expand All @@ -96,6 +96,7 @@ <h4>Globus ID: {{session['primary_identity']}}</h4>
required="required"
tabindex="5"
>
<small id="emailHelp" class="form-text text-muted"> Please use an institutional email address </small>
</div>

<div class="form-group">
Expand Down
18 changes: 14 additions & 4 deletions portal/templates/profile_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
<div class="row">
<div class="col-lg-6 mx-auto">
<h2>Edit Profile</h2>
<h6>Unix Username: {{session['unix_name']}}</h6>
<h6>User Globus ID: {{session['primary_identity']}}</h6>
<small id="unixName" class="text-muted">UNIX Username: {{session['unix_name']}}</small>
<br/><small id="globusID" class="text-muted">Globus ID: {{session['primary_identity']}}</small>
<p class="asterik">* = required field</p>
<hr/>
<form role="form" action="{{url_for('edit_profile', unix_name=unix_name)}}" method="POST">
Expand Down Expand Up @@ -66,7 +66,7 @@ <h6>User Globus ID: {{session['primary_identity']}}</h6>
</div>

<div class="form-group">
<label for="email">Email <span class="asterik">*</span> (please use institutional email)</label>
<label for="email">Email <span class="asterik">*</span></label>
<input
type="email"
id="email"
Expand All @@ -76,6 +76,7 @@ <h6>User Globus ID: {{session['primary_identity']}}</h6>
required="required"
tabindex="4"
>
<small id="emailHelp" class="form-text text-muted"> Please use an institutional email address </small>
</div>

<div class="form-group">
Expand All @@ -91,6 +92,16 @@ <h6>User Globus ID: {{session['primary_identity']}}</h6>
>
</div>

<div class="form-check">
<input type="checkbox" class="form-check-input" id="totpsecret" name="totpsecret">
<label class="form-check-label" for="totpsecret">Set up Multi-Factor Authentication</label>
{% if profile['totp_secret'] %}
<small id="mfaHelp" class="form-text text-muted">This will <b>delete</b> your current MFA secret and generate a new one </small>
{% else %}
<small id="mfaHelp" class="form-text text-muted">Enabling MFA will add an additional layer of security for SSH connections </small>
{% endif %}
</div></br>

<div class="form-group">
<label for="sshpubstring">SSH Public Key</label>
<textarea
Expand Down Expand Up @@ -178,7 +189,6 @@ <h6 class="mb-0">
</div>
</div>
</div>

</div><!-- /.accordion-->
</div><!-- /.col-md-6 -->
</div> <!-- row -->
Expand Down
11 changes: 10 additions & 1 deletion portal/templates/scripts.html
Original file line number Diff line number Diff line change
Expand Up @@ -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:[email protected]";
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 || [];
Expand Down
53 changes: 46 additions & 7 deletions portal/views.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from flask import flash, redirect, render_template, request, session, url_for, jsonify
from flask_qrcode import QRcode
import requests
import json

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
Expand All @@ -29,6 +30,9 @@
get_user_group_status,
)

# enable QR code support
QRcode(app)

# Use these four lines on container
import sys
import subprocess
Expand Down Expand Up @@ -143,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(
Expand All @@ -154,9 +164,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"])
Expand Down Expand Up @@ -750,8 +764,9 @@ 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

# Schema and query for adding users to CI Connect DB
if public_key:
Expand All @@ -767,6 +782,7 @@ def create_profile():
"unix_name": unix_name,
"superuser": superuser,
"service_account": service_account,
"create_totp_secret": create_totp_secret,
},
}
else:
Expand All @@ -781,6 +797,7 @@ def create_profile():
"unix_name": unix_name,
"superuser": superuser,
"service_account": service_account,
"create_totp_secret": create_totp_secret,
},
}
r = requests.post(
Expand Down Expand Up @@ -861,6 +878,10 @@ def edit_profile(unix_name):
public_key = request.form["sshpubstring"]
globus_id = session["primary_identity"]
x509dn = request.form["x509dn"]
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
Expand All @@ -874,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:
Expand All @@ -885,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
Expand Down Expand Up @@ -925,8 +948,23 @@ 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)
Expand Down Expand Up @@ -963,6 +1001,7 @@ def profile():
user_status=user_status,
group_memberships=group_memberships,
group_unix_name_description=group_unix_name_description,
authenticator_string=authenticator_string,
)


Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
pyyaml