Skip to content
This repository has been archived by the owner on Mar 24, 2021. It is now read-only.

Commit

Permalink
Merge pull request #209 from alphagov/separate-admin-app
Browse files Browse the repository at this point in the history
Separate admin app
  • Loading branch information
jabley committed Dec 28, 2013
2 parents 191f22a + 75b05a3 commit e99bec6
Show file tree
Hide file tree
Showing 53 changed files with 7,501 additions and 214 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ install:
- pip install -q -r requirements_for_tests.txt --use-mirrors
# command to run tests
env:
- SKIP_SPLINTER_TESTS=1
- SKIP_VIRUS_SCAN=1
script:
- ./run_tests.sh
after_script:
Expand Down
Empty file added backdrop/admin/__init__.py
Empty file.
210 changes: 210 additions & 0 deletions backdrop/admin/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
from os import getenv
from functools import wraps

from flask import Flask, jsonify, url_for, request, \
session, render_template, flash, redirect

from .. import statsd
from ..core import cache_control, log_handler, database
from ..core.bucket import Bucket
from ..core.errors import ParseError, ValidationError
from ..core.log_handler \
import create_request_logger, create_response_logger
from ..core.repository \
import BucketConfigRepository, UserConfigRepository
from ..core.flaskutils import BucketConverter
from ..core.upload import create_parser
from ..write.signonotron2 import Signonotron2
from ..write.uploaded_file import UploadedFile, FileUploadError

GOVUK_ENV = getenv("GOVUK_ENV", "development")

app = Flask("backdrop.admin.app", static_url_path="/static")

# Configuration
app.config.from_object(
"backdrop.admin.config.{}".format(GOVUK_ENV))

log_handler.set_up_logging(app, GOVUK_ENV)

app.url_map.converters["bucket"] = BucketConverter

db = database.Database(
app.config['MONGO_HOST'],
app.config['MONGO_PORT'],
app.config['DATABASE_NAME']
)

bucket_repository = BucketConfigRepository(db)
user_repository = UserConfigRepository(db)


# TODO: move this out into a helper
def protected(f):
@wraps(f)
def verify_user_logged_in(*args, **kwargs):
if not "user" in session:
return redirect(
url_for('oauth_sign_in'))
return f(*args, **kwargs)
return verify_user_logged_in


@app.errorhandler(500)
@app.errorhandler(405)
@app.errorhandler(404)
def exception_handler(e):
app.logger.exception(e)

bucket_name = getattr(e, 'bucket_name', request.path)
statsd.incr("write.error", bucket=bucket_name)

code = getattr(e, 'code', 500)
name = getattr(e, 'name', 'Internal Error')

return render_template("error.html", name=name, bucket_name=bucket_name), \
code


@app.before_first_request
def setup_oauth_service():
app.oauth_service = Signonotron2(
client_id=app.config['OAUTH_CLIENT_ID'],
client_secret=app.config['OAUTH_CLIENT_SECRET'],
base_url=app.config['OAUTH_BASE_URL'],
redirect_url=app.config['BACKDROP_ADMIN_UI_HOST']
+ url_for("oauth_authorized")
)


@app.after_request
def prevent_clickjacking(response):
response.headers["X-Frame-Options"] = "SAMEORIGIN"
return response


@app.route('/', methods=['GET'])
@cache_control.set("private, must-revalidate")
def index():
"""
This representation is private to the logged-in user
(with their own buckets)
"""
user_email = session.get('user', {}).get('email')
if user_email:
user_config = user_repository.retrieve(user_email)
else:
user_config = None

return render_template("index.html", user_config=user_config)


@app.route('/_status', methods=['GET'])
@cache_control.nocache
def health_check():
return jsonify(status='ok', message='all ok')


def _create_session_user(name, email):
session.update(
{"user": {
"name": name,
"email": email
}})


if app.config.get('ALLOW_TEST_SIGNIN', True):
@app.route('/sign-in/test', methods=['GET'])
def oauth_test_signin():
_create_session_user(request.args.get('user'),
request.args.get('email'))
return "logged in as %s" % session.get('user'), 200


@app.route('/authorized', methods=['GET'])
@cache_control.nocache
def oauth_authorized():
"""
The result of this is a redirect, which shouldn't be cached in
case their permissions get changed, etc.
"""
auth_code = request.args.get('code')
if not auth_code:
abort(400)
access_token = app.oauth_service.exchange(auth_code)

user_details, can_see_backdrop = \
app.oauth_service.user_details(access_token)
if can_see_backdrop is None:
flash("Could not authenticate with single sign on.",
category="error")
return redirect(url_for("not_authorized"))
if can_see_backdrop is False:
flash("You are signed in to your GOV.UK account, "
"but you don't have permissions to use this application.")
return redirect(url_for("not_authorized"))
_create_session_user(user_details["user"]["name"],
user_details["user"]["email"])
flash("You were successfully signed in", category="success")
return redirect(url_for("user_route"))


@app.route("/sign-in")
@cache_control.nocache
def oauth_sign_in():
"""
This returns a redirect to the OAuth provider, so we shouldn't
allow this response to be cached.
"""
return redirect(app.oauth_service.authorize())


@app.route("/sign-out")
@cache_control.set("private, must-revalidate")
def oauth_sign_out():
session.clear()
flash("You have been signed out of Backdrop", category="success")
return render_template("signon/signout.html",
oauth_base_url=app.config['OAUTH_BASE_URL'])


@app.route('/<bucket:bucket_name>/upload', methods=['GET', 'POST'])
@protected
@cache_control.set("private, must-revalidate")
def upload(bucket_name):
bucket_config = bucket_repository.retrieve(bucket_name)
user_config = user_repository.retrieve(
session.get("user").get("email"))

if bucket_name not in user_config.buckets:
return abort(404)

if request.method == 'GET':
return render_template(
"upload_{}.html".format(bucket_config.upload_format),
bucket_name=bucket_name)

return _store_data(bucket_config)


def _store_data(bucket_config):
parse_file = create_parser(bucket_config)
bucket = Bucket(db, bucket_config)
expected_errors = (FileUploadError, ParseError, ValidationError)

try:
with UploadedFile(request.files['file']) as uploaded_file:
raw_data = parse_file(uploaded_file.file_stream())
bucket.parse_and_store(raw_data)
except expected_errors as e:
app.logger.error('Upload error: {}'.format(e.message))
return render_template('upload_error.html',
message=e.message,
bucket_name=bucket.name), 400

return render_template('upload_ok.html')


def start(port):
app.debug = True
app.run('0.0.0.0', port=port)
Empty file.
13 changes: 13 additions & 0 deletions backdrop/admin/config/development.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
LOG_LEVEL = "DEBUG"
BACKDROP_ADMIN_UI_HOST = "http://backdrop-admin.dev.gov.uk"
ALLOW_TEST_SIGNIN=True
SECRET_KEY = "something unique and secret"

DATABASE_NAME = "backdrop"
MONGO_HOST = 'localhost'
MONGO_PORT = 27017

try:
from development_environment import *
except ImportError:
from development_environment_sample import *
5 changes: 5 additions & 0 deletions backdrop/admin/config/development_environment_sample.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
OAUTH_CLIENT_ID = \
"1759c91cdc926eebe5d5c9fce53a58170ad17ba30a22b4b451c377a339a98844"
OAUTH_CLIENT_SECRET = \
"8f205218c0a378e33dccae5a557b4cac766f343a7dbfcb50de2286f03db4273a"
OAUTH_BASE_URL = "http://signon.dev.gov.uk"
11 changes: 11 additions & 0 deletions backdrop/admin/config/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
LOG_LEVEL = "DEBUG"
SINGLE_SIGN_ON = True
BACKDROP_ADMIN_UI_HOST = "http://backdrop-admin.dev.gov.uk"
ALLOW_TEST_SIGNIN = True
SECRET_KEY = "something unique and secret"

DATABASE_NAME = "backdrop_test"
MONGO_HOST = 'localhost'
MONGO_PORT = 27017

from test_environment import *
3 changes: 3 additions & 0 deletions backdrop/admin/config/test_environment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
OAUTH_CLIENT_ID = "it's not important here"
OAUTH_CLIENT_SECRET = "it's not important here"
OAUTH_BASE_URL = "http://signon.dev.gov.uk"
54 changes: 54 additions & 0 deletions backdrop/admin/static/backdrop.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
.background-image {
min-height: 500px;
background: url("images/performance_graphs.svg") no-repeat;
background-position: 90% 0;
background-size: auto 400px;
}

.background-image p {
width: 400px;
font-size: 1.2em;
line-height: 1.2em;
}

.background-image .btn {
margin-top: 2em;
width: 400px;
}

.masthead {
font-size: 5em;
line-height: 1em;
max-width: 400px;
}

form label {
font-size: 1.2rem;
}

form div {
padding: 16px;
}

.form-panel {
background-color: #d5e8f3;
width: 60%;
}

.warning-panel {
margin-top: 16px;
width: 60%;
background: rgb(223, 233, 235) url("images/warning.png") no-repeat 98% 1em;
font-size: 1.15em;
line-height: 1.5em;
}

.list {
margin-left: 20px;
list-style-type: disc;
}

#bucket-list {
list-style-type: none;
margin-left: 0;
}

0 comments on commit e99bec6

Please sign in to comment.