def test_exchange_when_code_is_rejected(self): oauth_service = Signonotron2(None, None, None, "") oauth_service.signon = Mock() response = Response() response.status_code = 401 oauth_service.signon.get_raw_access_token.return_value = response assert_that(oauth_service.exchange("code to reject"), is_(None))
def test_authorize_returns_a_redirect_to_signon_service(self): oauth_service = Signonotron2(None, None, None, "") oauth_service.signon = Mock() oauth_service.signon.get_authorize_url.return_value = "" response = oauth_service.authorize() assert_that(response, has_status(302))
def test_exchange_when_code_is_accepted(self, process_token_request): oauth_service = Signonotron2(None, None, None, "") oauth_service.signon = Mock() response = Response() response.status_code = 200 oauth_service.signon.get_raw_access_token.return_value = response process_token_request.return_value = tuple(["access toucan"]) assert_that(oauth_service.exchange("code to accept"), is_("access toucan"))
def test_user_details_if_access_token_is_rejected(self): oauth_service = Signonotron2(None, None, None, "") oauth_service.signon = Mock() session_object = Mock() response = Response() response._content = "" response.status_code = 401 session_object.get.return_value = response oauth_service.signon.get_session.return_value = session_object user_details = oauth_service.user_details("token is rejected") assert_that(user_details, is_((None, None)))
def test_returns_none_and_logs_error_if_cannot_parse_token(self): mock_logger = Mock() signonotron2.log = mock_logger oauth_service = Signonotron2(None, None, None, "") oauth_service.signon = Mock() response = Response() response._content = '{"foo":"bar"}' response.status_code = 200 oauth_service.signon.get_raw_access_token.return_value = response access_token = oauth_service.exchange("top secret code") assert_that(access_token, is_(None)) assert_that(mock_logger.warn.call_args[0][0], starts_with('Could not parse token from response'))
def test_user_details_if_access_token_is_accepted(self): oauth_service = Signonotron2(None, None, None, "") oauth_service.signon = Mock() session_object = Mock() response = Response() user_details_json = \ { "user": {"name": "Gareth The Wizard", "permissions": "signin"}} response._content = json.dumps(user_details_json) response.status_code = 200 session_object.get.return_value = response oauth_service.signon.get_session.return_value = session_object user_details = oauth_service.user_details("token is accepted") assert_that(user_details, is_((user_details_json, True)))
def setup(app, db): USER_SCOPE = app.config['USER_SCOPE'] ADMIN_UI_HOST = app.config["BACKDROP_ADMIN_UI_HOST"] MAX_UPLOAD_SIZE = 1000000 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'], backdrop_admin_ui_host=ADMIN_UI_HOST) @app.after_request def prevent_clickjacking(response): response.headers["X-Frame-Options"] = "SAMEORIGIN" return response def protected(f): @wraps(f) def verify_user_logged_in(*args, **kwargs): if not "user" in session: return redirect(url_for(ADMIN_UI_HOST, 'oauth_sign_in')) return f(*args, **kwargs) return verify_user_logged_in @app.route(USER_SCOPE) @cache_control.set("private, must-revalidate") def user_route(): """ This representation is private to the logged-in user (with their own buckets) """ if use_single_sign_on(app): buckets_available = app.permissions.buckets_in_session(session) return render_template("index.html", buckets_available=buckets_available) else: return "Backdrop is running." @app.route(USER_SCOPE + "/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 app.oauth_service.authorize() @app.route(USER_SCOPE + "/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(USER_SCOPE + "/authorized") @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(ADMIN_UI_HOST, "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(ADMIN_UI_HOST, "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(ADMIN_UI_HOST, "user_route")) @app.route(USER_SCOPE + "/not_authorized") @cache_control.nocache def not_authorized(): return render_template("signon/not_authorized.html") @app.route(USER_SCOPE + "/protected", methods=['GET']) @protected def upload_buckets(): return "hello" if allow_test_signin(app): @app.route(USER_SCOPE + "/sign_in/test", methods=['GET']) def test_signin(): _create_session_user(request.args.get('user'), request.args.get('email')) return "logged in as %s" % session.get('user'), 200 def _create_session_user(name, email): session.update({"user": {"name": name, "email": email}}) @app.route('/<bucket:bucket_name>/upload', methods=['GET', 'POST']) @protected @cache_control.set("private, must-revalidate") def upload(bucket_name): current_user_email = session.get("user").get("email") if not app.permissions.allowed(current_user_email, bucket_name): return abort(404) upload_format = _upload_format_for(bucket_name) upload_filters = _upload_filters_for(bucket_name) if request.method == 'GET': return render_template("upload_%s.html" % upload_format, bucket_name=bucket_name) parser = create_parser(upload_format, upload_filters) return _store_data(bucket_name, parser) def _store_data(bucket_name, parser): file_stream = request.files["file"].stream if not request.files["file"].filename: return _invalid_upload("file is required") try: if request.content_length > MAX_UPLOAD_SIZE: return _invalid_upload("file too large") try: data = parser(file_stream) auto_id_keys = _auto_id_keys_for(bucket_name) bucket = Bucket(db, bucket_name, generate_id_from=auto_id_keys) bucket.parse_and_store(data) return render_template("upload_ok.html") except (ParseError, ValidationError) as e: return _invalid_upload(e.message) finally: file_stream.close() def _upload_format_for(bucket_name): return app.config.get("BUCKET_UPLOAD_FORMAT", {})\ .get(bucket_name, "csv") def _upload_filters_for(bucket_name): return app.config.get("BUCKET_UPLOAD_FILTERS", {})\ .get(bucket_name, [first_sheet_filter]) def _auto_id_keys_for(bucket_name): return app.config.get("BUCKET_AUTO_ID_KEYS", {}).get(bucket_name) def _invalid_upload(msg): app.logger.error("Upload error: %s" % msg) return render_template("upload_error.html", message=msg), 400
def setup(app, db): USER_SCOPE = app.config['USER_SCOPE'] ADMIN_UI_HOST = app.config["BACKDROP_ADMIN_UI_HOST"] MAX_UPLOAD_SIZE = 100000 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'], backdrop_admin_ui_host=ADMIN_UI_HOST) def protected(f): @wraps(f) def verify_user_logged_in(*args, **kwargs): if not "user" in session: return redirect(url_for(ADMIN_UI_HOST, 'oauth_sign_in')) return f(*args, **kwargs) return verify_user_logged_in @app.route(USER_SCOPE) def user_route(): if use_single_sign_on(app): return render_template("index.html") else: return "Backdrop is running." @app.route(USER_SCOPE + "/sign_in") def oauth_sign_in(): return app.oauth_service.authorize() @app.route(USER_SCOPE + "/sign_out") 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(USER_SCOPE + "/authorized") def oauth_authorized(): 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(ADMIN_UI_HOST, "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(ADMIN_UI_HOST, "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(ADMIN_UI_HOST, "user_route")) @app.route(USER_SCOPE + "/not_authorized") def not_authorized(): return render_template("signon/not_authorized.html") @app.route(USER_SCOPE + "/protected", methods=['GET']) @protected def upload_buckets(): return "hello" if allow_test_signin(app): @app.route(USER_SCOPE + "/sign_in/test", methods=['GET']) def test_signin(): _create_session_user(request.args.get('user'), request.args.get('email')) return "logged in as %s" % session.get('user'), 200 def _create_session_user(name, email): session.update({"user": {"name": name, "email": email}}) @app.route('/<bucket_name>/upload', methods=['GET', 'POST']) @protected def upload(bucket_name): if not bucket_is_valid(bucket_name): return _invalid_upload("Bucket name is invalid") current_user_email = session.get("user").get("email") if not app.permissions.allowed(current_user_email, bucket_name): return abort(404) if request.method == 'GET': return render_template("upload_csv.html") return _store_csv_data(bucket_name) def _store_csv_data(bucket_name): file_stream = request.files["file"].stream try: if request.content_length > MAX_UPLOAD_SIZE: return _invalid_upload("file too large") try: parse_and_store(db, parse_csv(file_stream), bucket_name, app.logger) return render_template("upload_ok.html") except (ParseError, ValidationError) as e: return _invalid_upload(e.message) finally: file_stream.close() def _invalid_upload(msg): return render_template("upload_error.html", message=msg), 400
def test_returns_no_user_details_if_access_token_is_none(self): oauth_service = Signonotron2(None, None, None, "") user_details = oauth_service.user_details(None) assert_that(user_details, is_((None, None)))