Merge branch 'master' into request-creator-name
This commit is contained in:
@@ -15,6 +15,7 @@ from atst.routes import bp
|
||||
from atst.routes.workspaces import bp as workspace_routes
|
||||
from atst.routes.requests import requests_bp
|
||||
from atst.routes.dev import bp as dev_routes
|
||||
from atst.routes.errors import make_error_pages
|
||||
from atst.domain.authnid.crl.validator import Validator
|
||||
from atst.domain.auth import apply_authentication
|
||||
|
||||
@@ -45,6 +46,7 @@ def make_app(config):
|
||||
Session(app)
|
||||
assets_environment.init_app(app)
|
||||
|
||||
make_error_pages(app)
|
||||
app.register_blueprint(bp)
|
||||
app.register_blueprint(workspace_routes)
|
||||
app.register_blueprint(requests_bp)
|
||||
|
||||
@@ -14,3 +14,17 @@ class AlreadyExistsError(Exception):
|
||||
@property
|
||||
def message(self):
|
||||
return "{} already exists".format(self.resource_name)
|
||||
|
||||
|
||||
class UnauthorizedError(Exception):
|
||||
def __init__(self, user, action):
|
||||
self.user = user
|
||||
self.action = action
|
||||
|
||||
@property
|
||||
def message(self):
|
||||
return "User {} not authorized to {}".format(self.user.id, self.action)
|
||||
|
||||
|
||||
class UnauthenticatedError(Exception):
|
||||
pass
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from sqlalchemy import exists, and_
|
||||
from sqlalchemy import exists, and_, exc
|
||||
from sqlalchemy.orm.exc import NoResultFound
|
||||
from sqlalchemy.orm.attributes import flag_modified
|
||||
|
||||
@@ -42,11 +42,14 @@ class Requests(object):
|
||||
|
||||
@classmethod
|
||||
def exists(cls, request_id, creator_id):
|
||||
return db.session.query(
|
||||
exists().where(
|
||||
and_(Request.id == request_id, Request.creator == creator_id)
|
||||
)
|
||||
).scalar()
|
||||
try:
|
||||
return db.session.query(
|
||||
exists().where(
|
||||
and_(Request.id == request_id, Request.creator == creator_id)
|
||||
)
|
||||
).scalar()
|
||||
except exc.DataError:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def get(cls, request_id):
|
||||
|
||||
@@ -5,6 +5,7 @@ import pendulum
|
||||
from atst.domain.requests import Requests
|
||||
from atst.domain.users import Users
|
||||
from atst.domain.authnid.utils import parse_sdn
|
||||
from atst.domain.exceptions import UnauthenticatedError
|
||||
|
||||
bp = Blueprint("atst", __name__)
|
||||
|
||||
@@ -29,6 +30,9 @@ def catch_all(path):
|
||||
return render_template("{}.html".format(path))
|
||||
|
||||
|
||||
# TODO: this should be partly consolidated into a domain function that takes
|
||||
# all the necessary UWSGI environment values as args and either returns a user
|
||||
# or raises the UnauthenticatedError
|
||||
@bp.route('/login-redirect')
|
||||
def login_redirect():
|
||||
if request.environ.get('HTTP_X_SSL_CLIENT_VERIFY') == 'SUCCESS' and _is_valid_certificate(request):
|
||||
@@ -39,15 +43,7 @@ def login_redirect():
|
||||
|
||||
return redirect(url_for("atst.home"))
|
||||
else:
|
||||
return redirect(url_for("atst.unauthorized"))
|
||||
|
||||
|
||||
@bp.route("/unauthorized")
|
||||
def unauthorized():
|
||||
template = render_template('unauthorized.html')
|
||||
response = app.make_response(template)
|
||||
response.status_code = 401
|
||||
return response
|
||||
raise UnauthenticatedError()
|
||||
|
||||
|
||||
def _is_valid_certificate(request):
|
||||
|
||||
19
atst/routes/errors.py
Normal file
19
atst/routes/errors.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from flask import render_template
|
||||
|
||||
import atst.domain.exceptions as exceptions
|
||||
|
||||
|
||||
def make_error_pages(app):
|
||||
@app.errorhandler(exceptions.NotFoundError)
|
||||
@app.errorhandler(exceptions.UnauthorizedError)
|
||||
# pylint: disable=unused-variable
|
||||
def not_found(e):
|
||||
return render_template("not_found.html"), 404
|
||||
|
||||
|
||||
@app.errorhandler(exceptions.UnauthenticatedError)
|
||||
# pylint: disable=unused-variable
|
||||
def unauthorized(e):
|
||||
return render_template('unauthorized.html'), 401
|
||||
|
||||
return app
|
||||
@@ -3,6 +3,8 @@ from flask import g, redirect, render_template, url_for, request as http_request
|
||||
from . import requests_bp
|
||||
from atst.domain.requests import Requests
|
||||
from atst.routes.requests.jedi_request_flow import JEDIRequestFlow
|
||||
from atst.models.permissions import Permissions
|
||||
from atst.domain.exceptions import UnauthorizedError
|
||||
|
||||
|
||||
@requests_bp.route("/requests/new/<int:screen>", methods=["GET"])
|
||||
@@ -25,6 +27,9 @@ def requests_form_new(screen):
|
||||
)
|
||||
@requests_bp.route("/requests/new/<int:screen>/<string:request_id>", methods=["GET"])
|
||||
def requests_form_update(screen=1, request_id=None):
|
||||
if request_id:
|
||||
_check_can_view_request(request_id)
|
||||
|
||||
request = Requests.get(request_id) if request_id is not None else None
|
||||
jedi_flow = JEDIRequestFlow(screen, request, request_id=request_id)
|
||||
|
||||
@@ -79,10 +84,12 @@ def requests_update(screen=1, request_id=None):
|
||||
request_id=jedi_flow.request_id,
|
||||
)
|
||||
return redirect(where)
|
||||
|
||||
else:
|
||||
return render_template(
|
||||
"requests/screen-%d.html" % int(screen), **rerender_args
|
||||
)
|
||||
|
||||
else:
|
||||
return render_template("requests/screen-%d.html" % int(screen), **rerender_args)
|
||||
|
||||
@@ -94,5 +101,18 @@ def requests_submit(request_id=None):
|
||||
|
||||
if request.status == "approved":
|
||||
return redirect("/requests?modal=True")
|
||||
|
||||
else:
|
||||
return redirect("/requests")
|
||||
|
||||
|
||||
# TODO: generalize this, along with other authorizations, into a policy-pattern
|
||||
# for authorization in the application
|
||||
def _check_can_view_request(request_id):
|
||||
if Permissions.REVIEW_AND_APPROVE_JEDI_WORKSPACE_REQUEST in g.current_user.atat_permissions:
|
||||
pass
|
||||
elif Requests.exists(request_id, g.current_user.id):
|
||||
pass
|
||||
else:
|
||||
raise UnauthorizedError(g.current_user, "view request {}".format(request_id))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user