-
Notifications
You must be signed in to change notification settings - Fork 996
Admin changes #3737
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Admin changes #3737
Changes from all commits
6597b38
3ebbd22
b467bdf
880edeb
8c35bab
c6b0098
d65595e
d9de9f5
5eae7da
c8b5853
985f1a2
fc0a25a
0bb452b
1cc5e7c
7356865
1755377
3995a94
1e7d9c4
40658f5
a892fe6
9e6b2bd
c8d3eb5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| from augur.api.routes import AUGUR_API_VERSION | ||
| from ..server import app | ||
| import sqlalchemy as s | ||
| import json | ||
| from subprocess import run, PIPE, Popen | ||
| from flask import Response, current_app, jsonify | ||
|
|
||
| from augur.application.db.lib import get_value | ||
| from augur.application.logs import AugurLogger | ||
| from augur.api.util import admin_required | ||
|
|
||
| logger = AugurLogger("augur").get_logger() | ||
|
|
||
| @app.route(f"/{AUGUR_API_VERSION}/admin/shutdown") | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Defaults to GET requests. Shouldn't this be POST only? Would prevent CSRF. |
||
| @admin_required | ||
| def shutdown_system(): | ||
| run("augur backend stop-collection-blocking".split(), stdin=PIPE, stdout=PIPE, stderr=PIPE) | ||
Check noticeCode scanning / Bandit subprocess call - check for execution of untrusted input. Note
subprocess call - check for execution of untrusted input.
|
||
| Popen("augur backend stop", shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) | ||
Check noticeCode scanning / Bandit Starting a process with a partial executable path Note
Starting a process with a partial executable path
Check noticeCode scanning / Bandit subprocess call with shell=True seems safe, but may be changed in the future, consider rewriting without shell Note
subprocess call with shell=True seems safe, but may be changed in the future, consider rewriting without shell
|
||
|
|
||
| return jsonify({"status": "shutdown"}) | ||
|
|
||
| @app.route(f"/{AUGUR_API_VERSION}/admin/restart") | ||
| @admin_required | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as |
||
| def restart_system(): | ||
| Popen("python scripts/control/restart.py", shell=True) | ||
Check noticeCode scanning / Bandit Starting a process with a partial executable path Note
Starting a process with a partial executable path
Check noticeCode scanning / Bandit subprocess call with shell=True seems safe, but may be changed in the future, consider rewriting without shell Note
subprocess call with shell=True seems safe, but may be changed in the future, consider rewriting without shell
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we use absolute path derived from
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also is this script going to work for docker installs? |
||
|
|
||
| return jsonify({"status": "restart"}) | ||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,43 +7,52 @@ | |||||||||||
| import sqlalchemy as s | ||||||||||||
|
|
||||||||||||
| # Disable the requirement for SSL by setting env["AUGUR_DEV"] = True | ||||||||||||
| from augur.application.config import get_development_flag | ||||||||||||
| from augur.api.util import ssl_required, admin_required | ||||||||||||
| from augur.application.db.lib import get_session | ||||||||||||
| from augur.application.db.models import Config | ||||||||||||
| from augur.application.config import AugurConfig | ||||||||||||
| from augur.application.db.session import DatabaseSession | ||||||||||||
| from ..server import app | ||||||||||||
|
|
||||||||||||
| logger = logging.getLogger(__name__) | ||||||||||||
| development = get_development_flag() | ||||||||||||
|
|
||||||||||||
| from augur.api.routes import AUGUR_API_VERSION | ||||||||||||
|
|
||||||||||||
| def generate_upgrade_request(): | ||||||||||||
| # https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/426 | ||||||||||||
| response = jsonify({"status": "SSL Required"}) | ||||||||||||
| response.headers["Upgrade"] = "TLS" | ||||||||||||
| response.headers["Connection"] = "Upgrade" | ||||||||||||
|
|
||||||||||||
| return response, 426 | ||||||||||||
|
|
||||||||||||
| @app.route(f"/{AUGUR_API_VERSION}/config/get", methods=['GET', 'POST']) | ||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are we allowing POST here? |
||||||||||||
| @ssl_required | ||||||||||||
| def get_config(): | ||||||||||||
| if not development and not request.is_secure: | ||||||||||||
| return generate_upgrade_request() | ||||||||||||
|
|
||||||||||||
| with DatabaseSession(logger, engine=current_app.engine) as session: | ||||||||||||
|
|
||||||||||||
| config_dict = AugurConfig(logger, session).config.load_config() | ||||||||||||
|
|
||||||||||||
| return jsonify(config_dict), 200 | ||||||||||||
|
|
||||||||||||
| @app.route(f"/{AUGUR_API_VERSION}/config/set", methods=['GET', 'POST']) | ||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to above. Config mutations via GET are a CSRF risk and leak values into server logs, browser history, and referer headers. This should be POST-only with a JSON body. |
||||||||||||
| @ssl_required | ||||||||||||
| @admin_required | ||||||||||||
| def set_config_item(): | ||||||||||||
| setting = request.args.get("setting") | ||||||||||||
| section = request.args.get("section") | ||||||||||||
| value = request.values.get("value") | ||||||||||||
|
|
||||||||||||
| result = { | ||||||||||||
| "section_name": section, | ||||||||||||
| "setting_name": setting, | ||||||||||||
| "value": value | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| if not setting or not section or not value: | ||||||||||||
| return jsonify({"status": "Missing argument"}), 400 | ||||||||||||
|
|
||||||||||||
| with get_session() as session: | ||||||||||||
| config = AugurConfig(logger, session) | ||||||||||||
| config.add_or_update_settings([result]) | ||||||||||||
|
|
||||||||||||
| return jsonify({"status": "success"}) | ||||||||||||
|
|
||||||||||||
| @app.route(f"/{AUGUR_API_VERSION}/config/update", methods=['POST']) | ||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
| @ssl_required | ||||||||||||
| def update_config(): | ||||||||||||
| if not development and not request.is_secure: | ||||||||||||
| return generate_upgrade_request() | ||||||||||||
|
|
||||||||||||
| update_dict = request.get_json() | ||||||||||||
|
|
||||||||||||
| with get_session() as session: | ||||||||||||
|
|
@@ -64,5 +73,3 @@ def update_config(): | |||||||||||
| session.commit() | ||||||||||||
|
|
||||||||||||
| return jsonify({"status": "success"}), 200 | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,14 +6,16 @@ | |||||||||||||||||||||||||||||||||||||||
| import re | ||||||||||||||||||||||||||||||||||||||||
| import beaker | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| from flask import request, jsonify, current_app | ||||||||||||||||||||||||||||||||||||||||
| from flask import request, jsonify, current_app, abort | ||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [pylint] reported by reviewdog 🐶 |
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| from augur.application.db import get_session | ||||||||||||||||||||||||||||||||||||||||
| from functools import wraps | ||||||||||||||||||||||||||||||||||||||||
| from sqlalchemy.orm.exc import NoResultFound | ||||||||||||||||||||||||||||||||||||||||
| from augur.application.config import get_development_flag | ||||||||||||||||||||||||||||||||||||||||
| from augur.application.db.models import ClientApplication | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| from flask_login import login_required, current_user | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| development = get_development_flag() | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| __ROOT = os.path.abspath(os.path.dirname(__file__)) | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -112,7 +114,6 @@ def get_client_token(): | |||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| return token | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| # usage: | ||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||
| @app.route("/path") | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -155,4 +156,22 @@ def wrapper(*args, **kwargs): | |||||||||||||||||||||||||||||||||||||||
| return generate_upgrade_request() | ||||||||||||||||||||||||||||||||||||||||
| return fun(*args, **kwargs) | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| return wrapper | ||||||||||||||||||||||||||||||||||||||||
| return wrapper | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| def admin_required(func): | ||||||||||||||||||||||||||||||||||||||||
| @login_required | ||||||||||||||||||||||||||||||||||||||||
| @wraps(func) | ||||||||||||||||||||||||||||||||||||||||
| def inner_function(*args, **kwargs): | ||||||||||||||||||||||||||||||||||||||||
| if current_user.admin: | ||||||||||||||||||||||||||||||||||||||||
| return func(*args, **kwargs) | ||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||
| abort(403) | ||||||||||||||||||||||||||||||||||||||||
| return inner_function | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+161
to
+169
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| def development_required(func): | ||||||||||||||||||||||||||||||||||||||||
| @wraps(func) | ||||||||||||||||||||||||||||||||||||||||
| def inner_function(*args, **kwargs): | ||||||||||||||||||||||||||||||||||||||||
| if not development: | ||||||||||||||||||||||||||||||||||||||||
| abort(403) | ||||||||||||||||||||||||||||||||||||||||
| return func(*args, **kwargs) | ||||||||||||||||||||||||||||||||||||||||
| return inner_function | ||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,12 @@ | ||
| from flask import render_template, redirect, url_for, session, request, jsonify | ||
| from flask_login import LoginManager | ||
| from flask_login import LoginManager, current_user, login_required | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [pylint] reported by reviewdog 🐶 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [pylint] reported by reviewdog 🐶 |
||
| from io import StringIO | ||
| from .utils import * | ||
| from .init import logger | ||
| from .url_converters import * | ||
|
|
||
| from functools import wraps | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [pylint] reported by reviewdog 🐶 |
||
|
|
||
| # from .server import User | ||
| from ..server import app, db_session | ||
| from augur.application.db.models import User, UserSessionToken | ||
|
|
@@ -38,6 +40,13 @@ def unsupported_method(error): | |
|
|
||
| return render_message("405 - Method not supported", "The resource you are trying to access does not support the request method used"), 405 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [pylint] reported by reviewdog 🐶 |
||
|
|
||
| @app.errorhandler(403) | ||
| def forbidden(error): | ||
| if AUGUR_API_VERSION in str(request.url_rule): | ||
| return jsonify({"status": "Forbidden"}), 403 | ||
|
|
||
| return render_message("403 - Forbidden", "You do not have permission to view this page"), 403 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [pylint] reported by reviewdog 🐶 |
||
|
|
||
| @app.errorhandler(500) | ||
| def internal_server_error(error): | ||
| if AUGUR_API_VERSION in str(request.path): | ||
|
|
@@ -52,8 +61,21 @@ def internal_server_error(error): | |
| errout.close() | ||
| except Exception as e: | ||
| logger.error(e) | ||
| raise e | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| return render_message("500 - Internal Server Error", "An error occurred while trying to service your request. Please try again, and if the issue persists, please file a GitHub issue with the below error message:", error=stacktrace), 500 | ||
| return render_message("500 - Internal Server Error", """An error occurred while trying to service your request. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [pylint] reported by reviewdog 🐶 |
||
| Please try again, and if the error persists, please file a GitHub issue with a description | ||
| of what you were doing before this error occurred accompanied by the below error message:""", error=stacktrace), 500 | ||
|
|
||
| @app.template_filter("escape_ID") | ||
| def escape_HTML_ID(data: str) -> str: | ||
| # Done this way in case we want to add more replacements in the future | ||
| data = data.replace(".", "\\.") | ||
| return data | ||
|
|
||
| @app.template_filter("quoted") | ||
| def quote_surrounded(data: str) -> str: | ||
| return '"' + data + '"' | ||
|
|
||
| @login_manager.unauthorized_handler | ||
| def unauthorized(): | ||
|
|
@@ -98,19 +120,16 @@ def load_user(user_id): | |
| @login_manager.request_loader | ||
| def load_user_request(request): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [pylint] reported by reviewdog 🐶 |
||
| token = get_bearer_token() | ||
|
|
||
| current_time = int(time.time()) | ||
| token = db_session.query(UserSessionToken).filter(UserSessionToken.token == token, UserSessionToken.expiration >= current_time).first() | ||
| if token: | ||
|
|
||
| print("Valid user") | ||
| token = db_session.query(UserSessionToken).filter(UserSessionToken.token == token, UserSessionToken.expiration >= current_time).first() | ||
|
|
||
| if token: | ||
| user = token.user | ||
| user._is_authenticated = True | ||
| user._is_active = True | ||
|
|
||
| return user | ||
|
|
||
| return None | ||
|
|
||
| @app.template_filter('as_datetime') | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,16 +3,23 @@ | |
| """ | ||
| import logging | ||
| import math | ||
| import os | ||
| import signal | ||
| from flask import render_template, request, redirect, url_for, session, flash | ||
| from .utils import * | ||
| from augur.api.util import admin_required, development_required | ||
| from flask_login import login_user, logout_user, current_user, login_required | ||
| from sqlalchemy.exc import OperationalError | ||
|
|
||
| from augur.application.db.models import User, Repo, ClientApplication | ||
| from .server import LoginException | ||
| from augur.application.util import * | ||
| from augur.application.db.lib import get_value | ||
| from augur.application.config import AugurConfig | ||
| from ..server import app, db_session | ||
|
|
||
| from augur.application.db.lib import get_session | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
|
|
@@ -112,6 +119,10 @@ def repo_card_view(): | |
| @app.route('/collection/status') | ||
| def status_view(): | ||
| return render_module("status", title="Status") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [pylint] reported by reviewdog 🐶 |
||
|
|
||
| @app.route('/connection_status') | ||
| def server_ping_frontend(): | ||
| return render_module("ping") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [pylint] reported by reviewdog 🐶 |
||
|
|
||
| """ ---------------------------------------------------------------- | ||
| login: | ||
|
|
@@ -318,6 +329,7 @@ def user_group_view(group = None): | |
| return render_module("user-group-repos-table", title="Repos", repos=data, query_key=query, activePage=params["page"], pages=page_count, offset=pagination_offset, PS="user_group_view", reverse = rev, sorting = params.get("sort"), group=group) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [pylint] reported by reviewdog 🐶 |
||
|
|
||
| @app.route('/error') | ||
| @development_required | ||
| def throw_exception(): | ||
| raise Exception("This Exception intentionally raised") | ||
|
|
||
|
|
@@ -326,6 +338,7 @@ def throw_exception(): | |
| View the admin dashboard. | ||
| """ | ||
| @app.route('/dashboard') | ||
| @admin_required | ||
| def dashboard_view(): | ||
| empty = [ | ||
| { "title": "Placeholder", "settings": [ | ||
|
|
@@ -337,6 +350,14 @@ def dashboard_view(): | |
| ]} | ||
| ] | ||
|
|
||
| backend_config = requestJson("config/get", False) | ||
| backend_config = AugurConfig(logger, db_session).load_config() | ||
|
|
||
| with get_session() as session: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [pylint] reported by reviewdog 🐶
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| try: | ||
| users = session.query(User).all() | ||
| except OperationalError as e: | ||
| # Instruct Gunicorn to reboot workers to resolve database connection instability | ||
| os.kill(os.getpid(), signal.SIGTERM) | ||
| return "reloading" | ||
|
|
||
| return render_template('admin-dashboard.j2', sections = empty, config = backend_config) | ||
| return render_template('admin-dashboard.j2', sections = empty, config = backend_config, users = users) | ||
Check notice
Code scanning / Bandit
Consider possible security implications associated with the subprocess module. Note