1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
|
"""Entry point for flask app"""
# pylint: disable=C0413,E0611
import os
import sys
import time
import logging
import datetime
from typing import Tuple
from pathlib import Path
from urllib.parse import urljoin, urlparse
import redis
import jinja2
from flask_session import Session
from authlib.jose import JsonWebKey
from authlib.integrations.requests_client import OAuth2Session
from flask import g, Flask, flash, session, url_for, redirect, current_app
from gn2.utility import formatting
from gn3.authentication import DataRole, AdminRole
from gn2.wqflask.group_manager import group_management
from gn2.wqflask.resource_manager import resource_management
from gn2.wqflask.metadata_edits import metadata_edit
from gn2.wqflask.edit import metadata
from gn2.wqflask.api.markdown import glossary_blueprint
from gn2.wqflask.api.markdown import references_blueprint
from gn2.wqflask.api.markdown import links_blueprint
from gn2.wqflask.api.markdown import policies_blueprint
from gn2.wqflask.api.markdown import environments_blueprint
from gn2.wqflask.api.markdown import facilities_blueprint
from gn2.wqflask.api.markdown import blogs_blueprint
from gn2.wqflask.api.markdown import news_blueprint
from gn2.wqflask.api.jobs import jobs as jobs_bp
from gn2.wqflask.oauth2.routes import oauth2
from gn2.wqflask.oauth2.client import user_logged_in
from gn2.wqflask.oauth2.collections import num_collections
from gn2.wqflask.oauth2.request_utils import user_details, authserver_authorise_uri
from gn2.wqflask.jupyter_notebooks import jupyter_notebooks
from gn2.wqflask.startup import (
StartupError,
startup_errors,
check_mandatory_configs)
def numcoll():
"""Handle possible errors."""
try:
return num_collections()
except Exception as _exc:
return "ERROR"
def parse_ssl_key(app: Flask, keyconfig: str):
"""Parse key file paths into objects"""
keypath = app.config.get(keyconfig, "").strip()
if not bool(keypath):
app.logger.error("Expected configuration '%s'", keyconfig)
return
with open(keypath) as _sslkey:
app.config[keyconfig] = JsonWebKey.import_key(_sslkey.read())
def dev_loggers(appl: Flask) -> None:
"""Default development logging."""
formatter = logging.Formatter(
fmt="[%(asctime)s] %(levelname)s in %(module)s: %(message)s")
stderr_handler = logging.StreamHandler(stream=sys.stderr)
stderr_handler.setFormatter(formatter)
appl.logger.addHandler(stderr_handler)
root_logger = logging.getLogger()
root_logger.addHandler(stderr_handler)
root_logger.setLevel(appl.config.get("LOGLEVEL", "WARNING"))
def gunicorn_loggers(appl: Flask) -> None:
"""Logging with gunicorn WSGI server."""
logger = logging.getLogger("gunicorn.error")
appl.logger.handlers = logger.handlers
appl.logger.setLevel(logger.level)
def setup_logging(appl: Flask) -> None:
"""Setup appropriate logging"""
software, *_version_and_comments = os.environ.get(
"SERVER_SOFTWARE", "").split('/')
gunicorn_loggers(app) if software == "gunicorn" else dev_loggers(app)
app = Flask(__name__)
setup_logging(app)
# See http://flask.pocoo.org/docs/config/#configuring-from-files
# Note no longer use the badly named WQFLASK_OVERRIDES (nyi)
app.config.from_object('gn2.default_settings')
app.config.from_envvar('GN2_SETTINGS')
app.jinja_env.globals.update(
undefined=jinja2.StrictUndefined,
numify=formatting.numify,
logged_in=user_logged_in,
authserver_authorise_uri=authserver_authorise_uri,
user_details=user_details,
num_collections=numcoll,
datetime=datetime)
app.config["SESSION_REDIS"] = redis.from_url(app.config["REDIS_URL"])
## BEGIN: SECRETS -- Should be the last of the settings to load
secrets_file = Path(app.config.get("GN2_SECRETS", "")).absolute()
if secrets_file.exists() and secrets_file.is_file():
app.config.from_pyfile(str(secrets_file))
## END: SECRETS
# Registering blueprints
app.register_blueprint(glossary_blueprint, url_prefix="/glossary")
app.register_blueprint(references_blueprint, url_prefix="/references")
app.register_blueprint(links_blueprint, url_prefix="/links")
app.register_blueprint(policies_blueprint, url_prefix="/policies")
app.register_blueprint(environments_blueprint, url_prefix="/environments")
app.register_blueprint(facilities_blueprint, url_prefix="/facilities")
app.register_blueprint(blogs_blueprint, url_prefix="/blogs")
app.register_blueprint(news_blueprint, url_prefix="/news")
app.register_blueprint(jupyter_notebooks, url_prefix="/jupyter_notebooks")
app.register_blueprint(resource_management, url_prefix="/resource-management")
app.register_blueprint(metadata_edit, url_prefix="/datasets/")
app.register_blueprint(metadata,
url_prefix="/metadata/")
app.register_blueprint(group_management, url_prefix="/group-management")
app.register_blueprint(jobs_bp, url_prefix="/jobs")
app.register_blueprint(oauth2, url_prefix="/oauth2")
from gn2.wqflask.app_errors import register_error_handlers
register_error_handlers(app)
try:
check_mandatory_configs(app)
except StartupError as serr:
app.startup_error = serr
app.register_blueprint(startup_errors, url_prefix="/")
server_session = Session(app)
parse_ssl_key(app, "SSL_PRIVATE_KEY")
parse_ssl_key(app, "AUTH_SERVER_SSL_PUBLIC_KEY")
@app.before_request
def before_request():
g.request_start_time = time.time()
g.request_time = lambda: "%.5fs" % (time.time() - g.request_start_time)
token = session.get("oauth2_token", False)
if token and not bool(session.get("user_details", False)):
config = current_app.config
client = OAuth2Session(
config["OAUTH2_CLIENT_ID"], config["OAUTH2_CLIENT_SECRET"],
token=token)
resp = client.get(
urljoin(config["GN_SERVER_URL"], "oauth2/user"))
user_details = resp.json()
session["user_details"] = user_details
if user_details.get("error") == "invalid_token":
flash(user_details["error_description"], "alert-danger")
flash("You are now logged out.", "alert-info")
session.pop("user_details", None)
session.pop("oauth2_token", None)
@app.context_processor
def include_admin_role_class():
return {'AdminRole': AdminRole}
@app.context_processor
def include_data_role_class():
return {'DataRole': DataRole}
|