Skip to content

Commit

Permalink
Merge ed7bd2b into bee5d8b
Browse files Browse the repository at this point in the history
  • Loading branch information
dhakim87 committed Jun 10, 2020
2 parents bee5d8b + ed7bd2b commit b704a90
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 102 deletions.
10 changes: 7 additions & 3 deletions microsetta_private_api/admin/admin_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,17 @@ def project_statistics_detailed(token_info, project_id):
return jsonify(summary), 200


def validate_admin_access(token_info):
def token_grants_admin_access(token_info):
with Transaction() as t:
account_repo = AccountRepo(t)
account = account_repo.find_linked_account(token_info['iss'],
token_info['sub'])
if account is None or account.account_type != 'admin':
raise Unauthorized()
return account is not None and account.account_type == 'admin'


def validate_admin_access(token_info):
if not token_grants_admin_access(token_info):
raise Unauthorized()


def create_project(body, token_info):
Expand Down
1 change: 0 additions & 1 deletion microsetta_private_api/api/microsetta_private_api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1173,7 +1173,6 @@ paths:
schema:
type: object


components:
parameters:
# path parameters
Expand Down
20 changes: 20 additions & 0 deletions microsetta_private_api/example/client.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,26 @@ paths:
text/html:
schema:
type: string

'/admin/account_search':
get:
operationId: microsetta_private_api.example.client_impl.get_interactive_account_search
tags:
- Admin
parameters:
- in: query
name: email_query
description: Email to search for
schema:
type: string
responses:
'200':
description: List of identified accounts
content:
text/html:
schema:
type: string

components:
parameters:
account_id: # Can be referenced as '#/components/parameters/account_id'
Expand Down
44 changes: 43 additions & 1 deletion microsetta_private_api/example/client_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"authrocket.pubkey")

TOKEN_KEY_NAME = 'token'
ADMIN_MODE_KEY = 'admin_mode'
WORKFLOW_URL = '/workflow'
HELP_EMAIL = "microsetta@ucsd.edu"
KIT_NAME_KEY = "kit_name"
Expand Down Expand Up @@ -112,9 +113,18 @@ def home():
acct_id = workflow_state.get("account_id", None)
has_multiple_hs_sources = workflow_needs == TOO_MANY_HUMAN_SOURCES

# Switch out home page in administrator mode
if session.get(ADMIN_MODE_KEY, False):
return render_template('admin_home.jinja2',
admin_mode=session.get(ADMIN_MODE_KEY, False),
accounts=[],
endpoint=SERVER_CONFIG["endpoint"],
authrocket_url=SERVER_CONFIG["authrocket_url"])

# Note: home.jinja2 sends the user directly to authrocket to complete the
# login if they aren't logged in yet.
return render_template('home.jinja2',
admin_mode=session.get(ADMIN_MODE_KEY, False),
user=user,
email_verified=email_verified,
acct_id=acct_id,
Expand All @@ -125,6 +135,11 @@ def home():

def authrocket_callback(token):
session[TOKEN_KEY_NAME] = token
do_return, accts_output, _ = ApiRequest.get('/accounts')
if do_return:
return accts_output

session[ADMIN_MODE_KEY] = accts_output[0]['account_type'] == 'admin'
return redirect("/home")


Expand Down Expand Up @@ -265,6 +280,7 @@ def get_workflow_create_account():

return render_template('account_details.jinja2',
CREATE_ACCT=True,
admin_mode=session.get(ADMIN_MODE_KEY, False),
authorized_email=email,
account=default_account_values)

Expand Down Expand Up @@ -302,7 +318,8 @@ def get_workflow_update_email():
if next_state != NEEDS_EMAIL_CHECK:
return redirect(WORKFLOW_URL)

return render_template("update_email.jinja2")
return render_template("update_email.jinja2",
admin_mode=session.get(ADMIN_MODE_KEY, False))


def post_workflow_update_email(body):
Expand Down Expand Up @@ -461,6 +478,7 @@ def get_workflow_fill_survey(survey_template_id):
return survey_output

return render_template("survey.jinja2",
admin_mode=session.get(ADMIN_MODE_KEY, False),
endpoint=SERVER_CONFIG["endpoint"],
survey_template_id=survey_template_id,
survey_schema=survey_output[
Expand Down Expand Up @@ -501,6 +519,8 @@ def get_account(account_id):
return sources

return render_template('account_overview.jinja2',
admin_mode=session.get(ADMIN_MODE_KEY, False),
acct_id=account_id,
account=account,
sources=sources)

Expand All @@ -516,6 +536,7 @@ def get_account_details(account_id):

return render_template('account_details.jinja2',
CREATE_ACCT=False,
admin_mode=session.get(ADMIN_MODE_KEY, False),
account=account)


Expand Down Expand Up @@ -619,6 +640,7 @@ def get_source(account_id, source_id):

is_human = source_output['source_type'] == Source.SOURCE_TYPE_HUMAN
return render_template('source.jinja2',
admin_mode=session.get(ADMIN_MODE_KEY, False),
acct_id=account_id,
source_id=source_id,
is_human=is_human,
Expand Down Expand Up @@ -653,6 +675,7 @@ def show_sample_survey(account_id, source_id, sample_id, survey_template_id):

# Handle local surveys
return render_template("survey.jinja2",
admin_mode=session.get(ADMIN_MODE_KEY, False),
survey_template_id=survey_template_id,
survey_schema=survey_output[
'survey_template_text'])
Expand Down Expand Up @@ -763,6 +786,7 @@ def get_sample(account_id, source_id, sample_id):
.build()

return render_template('sample.jinja2',
admin_mode=session.get(ADMIN_MODE_KEY, False),
acct_id=account_id,
source_id=source_id,
sample=sample_output,
Expand Down Expand Up @@ -877,6 +901,7 @@ def generate_error_page(error_msg):
HELP_EMAIL, quote("minimal interface error"), error_txt)

output = render_template('error.jinja2',
admin_mode=session.get(ADMIN_MODE_KEY, False),
mailto_url=mailto_url,
error_msg=error_msg,
endpoint=SERVER_CONFIG["endpoint"],
Expand All @@ -887,6 +912,7 @@ def generate_error_page(error_msg):

def render_faq():
output = render_template('faq.jinja2',
admin_mode=session.get(ADMIN_MODE_KEY, False),
authrocket_url=SERVER_CONFIG["authrocket_url"],
endpoint=SERVER_CONFIG["endpoint"])
return output
Expand All @@ -899,6 +925,22 @@ def render_signup_intermediate():
return output


# Administrator Mode Functionality
def get_interactive_account_search(email_query):
do_return, email_diagnostics, _ = ApiRequest.get(
'/admin/search/account/%s' % (email_query,))
if do_return:
return email_diagnostics

accounts = [{"email": acct['email'], "account_id": acct['id']}
for acct in email_diagnostics['accounts']]
return render_template('admin_home.jinja2',
admin_mode=session.get(ADMIN_MODE_KEY, False),
accounts=accounts,
endpoint=SERVER_CONFIG["endpoint"],
authrocket_url=SERVER_CONFIG["authrocket_url"])


class BearerAuth(AuthBase):
def __init__(self, token):
self.token = token
Expand Down
3 changes: 2 additions & 1 deletion microsetta_private_api/repo/account_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,8 @@ def delete_account_by_email(self, email):
def get_account_ids_by_email(self, email):
email = "%"+email+"%"
with self._transaction.cursor() as cur:
cur.execute("SELECT id FROM account WHERE email LIKE %s "
# ILIKE is case insensitive LIKE
cur.execute("SELECT id FROM account WHERE email ILIKE %s "
"ORDER BY email",
(email,))
return [x[0] for x in cur.fetchall()]
38 changes: 38 additions & 0 deletions microsetta_private_api/templates/admin_home.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{% extends "sitebase.jinja2" %}
{% set page_title = "ADMINISTRATOR MODE" %}
{% set show_breadcrumbs = False %}
{% set show_logout = True %}
{% block content %}
<h4>Search Results</h4>
<div class="container">
{% if accounts|length > 0 %}
<div class="list-group">
<div class="row">
<div class="col-sm">
<i>Email</i>
</div>
<div class="col-sm">
<i>Account ID</i>
</div>
</div>
{% for account in accounts %}
<div class="container list-group-item {{loop.cycle('odd', 'even') }}">
<div class="row">
<div class="col-sm">
<a href="/accounts/{{account.account_id |e}}">
{{ account.email |e}}
</a>
</div>
<div class="col-sm">
{{ account.account_id|e }}
</div>
</div>
</div>
{% endfor %}
</div>
<br/><br/>
{% else %}
No accounts found
{% endif %}
</div>
{% endblock %}
75 changes: 24 additions & 51 deletions microsetta_private_api/templates/sample.jinja2
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Microsetta Sample Information</title>
{% extends "sitebase.jinja2" %}
{% set page_title = "Sample Information" %}
{% set show_breadcrumbs = True %}
{% set disable_bootstrap = True %}
{% block head %}
<!-- Argh, Bootstrap 3 and 4 don't mix. So the header and footer look slightly different on this page -->
<!-- The Vue Form Generator date time picker requires bootstrap 3, can't use 4.0 -->
<link rel="stylesheet" href="/static/vendor/bootstrap-3.3.7-dist/css/bootstrap.css" />
<script type="text/javascript" src="/static/vendor/js/vue-2.5.17.min.js"></script>
Expand All @@ -14,40 +15,27 @@
<script type="text/javascript" src="/static/vendor/js/moment-2.9.0-with-locales.js"></script>
<script type="text/javascript" src="/static/vendor/bootstrap-datetimepicker-4.14.30/bootstrap-datetimepicker.js"></script>
<link rel="stylesheet" type="text/css" href="/static/vendor/bootstrap-datetimepicker-4.14.30/bootstrap-datetimepicker.css"/>

<link rel="stylesheet" href="/static/css/minimal_interface.css" />
</head>
<body>
<a href="https://microsetta.ucsd.edu" title="microsetta.ucsd.edu">
<img src="/static/img/logo-co.png" class="resize"/>
<br />
<br />
Return to microsetta.ucsd.edu
</a>
<br />
<br />

<div class="content">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/home">Home</a></li>
<li class="breadcrumb-item"><a href="/accounts/{{acct_id}}">Sources</a></li>
<li class="breadcrumb-item"><a href="/accounts/{{acct_id}}/sources/{{source_id}}">Samples</a></li>
</ol>
</nav>
<div class="container">
<div id="app">
<div class="panel panel-default">
<div class="panel-body">
<form method="post" id="sample_form">
<vue-form-generator :schema="schema" :model="model" :options="formOptions"></vue-form-generator>
</form>
</div>
</div>
{% endblock %}
{% block breadcrumb %}
<li class="breadcrumb-item"><a href="/accounts/{{acct_id}}">Account</a></li>
<li class="breadcrumb-item"><a href="/accounts/{{acct_id}}/sources/{{source_id}}">Source</a></li>
<li class="breadcrumb-item active" aria-current="page">Sample Information</li>
{% endblock %}
{% block content %}
<div class="container">
<div id="app">
<div class="panel panel-default">
<div class="panel-body">
<form method="post" id="sample_form">
<vue-form-generator :schema="schema" :model="model" :options="formOptions"></vue-form-generator>
</form>
</div>
</div>
</div>
</div>

<!-- These should really be in the head, but don't easily work there.
Probably needs a document.load handler or something? -->
<script type="text/javascript">
var survey_model={{sample|tojson}};
var survey_schema={{schema|tojson}};
Expand All @@ -63,19 +51,4 @@
});
</script>
<script type="text/javascript" src="/static/vue_survey_form.js"></script>

<div>
<br />
<p>
Questions? Check our <a href="/view_faq" target="_blank">FAQ</a>!
</p>
<p>
We are in the process of revising the site and
apologize for any inconvenience.
Please contact
<a href="mailto:microsetta@ucsd.edu">microsetta@ucsd.edu</a>
for help.
</p>
</div>
</body>
</html>
{% endblock %}

0 comments on commit b704a90

Please sign in to comment.