diff --git a/consent_provider/app.py b/consent_provider/app.py index 8cc51ab3e3381c8314226ba5b160e1d04b484d34..0aacb602114f6ca03eafd39b1d487d8e81216096 100644 --- a/consent_provider/app.py +++ b/consent_provider/app.py @@ -2,7 +2,7 @@ from flask import abort, Flask, redirect, request from flask.views import View from os import urandom, environ from hydra_client import HydraAdmin -from db import User +from db import User, BackendConnectionError HYDRA_ADMIN_URL = environ['HYDRA_ADMIN_URL'] @@ -27,11 +27,19 @@ def home(): hydra = HydraAdmin(HYDRA_ADMIN_URL) challenge = request.args.get("consent_challenge") if not challenge: - abort(400) + abort(403) consent_request = hydra.consent_request(challenge) app_name = consent_request.client["client_name"] username = consent_request.subject - user = User(username) + try: + user = User(username) + except BackendConnectionError as error: + # TODO: replace with propper logging via logger + print("Retrieving user object from GraphQL server failed") + print(error) + return redirect(consent_request.reject( + "Permission denied", + error_description="Login request was denied due to an internal server error")) access_granted = user.has_app_permission(app_name) if access_granted: session = user.get_oauth_session() @@ -40,12 +48,9 @@ def home(): grant_access_token_audience=consent_request.requested_access_token_audience, session=session, )) - else: - return redirect(consent_request.reject( - "Permission denied", - error_description="Login request was denied due to missing application permission") - ) - abort(400) + return redirect(consent_request.reject( + "Permission denied", + error_description="Login request was denied due to missing application permission")) if __name__ == '__main__': app.run() diff --git a/consent_provider/db.py b/consent_provider/db.py index 9f8b149306a0842b466386b2b020fa35f65deb1c..2ad3e80864f7a3a488d5d84b908f6e735e1dd8e4 100644 --- a/consent_provider/db.py +++ b/consent_provider/db.py @@ -1,15 +1,23 @@ from os import environ from hydra_client import HydraAdmin from graphqlclient import GraphQLClient -from json import loads +import urllib +import json GRAPHQL_URL = environ['GRAPHQL_URL'] -graphql_client = GraphQLClient(GRAPHQL_URL) +GRAPHQL_CLIENT = GraphQLClient(GRAPHQL_URL) + class User(): def __init__(self, username): self.username = username - self._load_remote_user_info() + try: + self._load_remote_user_info() + except urllib.error.HTTPError as error: + raise BackendConnectionError( + error.code, + error.headers, + ("Error during retrieval of userdata - " + error.reason)) def _load_remote_user_info(self): querystring = '''{{ @@ -29,20 +37,30 @@ class User(): }} }} }} - }}}}'''.format(self.username).strip() - result = loads(graphql_client.execute(querystring)) - if "data" in result: - data = result["data"]["getUser"] - self.applications = list(map(lambda x: x["node"]["name"], - data["applications"]["edges"])) - self.roles = list(map(lambda x: x["node"]["name"], - data["roles"]["edges"])) - self.email = data["email"] + }}}}'''.format(self.username) + result = json.loads(GRAPHQL_CLIENT.execute(querystring)) + data = result["data"]["getUser"] + self.applications = list(map(lambda x: x["node"]["name"], + data["applications"]["edges"])) + self.roles = list(map(lambda x: x["node"]["name"], + data["roles"]["edges"])) + self.email = data["email"] def has_app_permission(self, appname): return appname in self.applications def get_oauth_session(self): + """Create openID Connect token + + Use the userdata stored in the user object to create an OpenID Connect token. + The token will be passed to Hydra, which will store it and serve it to all OpenID Connect + Clients, that successfully query the /userinfo endpoint. Every field in the "id_token" + dictionary can be accessed through standard scopes and claims. + See https://openid.net/specs/openid-connect-core-1_0.html#Claims + + Returns: + OpenID Connect token of type dict + """ return { "id_token": { "name": self.username, @@ -50,3 +68,17 @@ class User(): "email" : self.email, "openappstack_roles": self.roles} } + + +class BackendConnectionError(Exception): + """Raised when requests to the backend server fail + + Attributes: + code -- http response code + headers -- http response headers + reason -- reson for the error + """ + def __init__(self, code, headers, reason): + self.code = code + self.headers = headers + self.reason = reason diff --git a/login_provider/Dockerfile b/login_provider/Dockerfile index c671dd715c1d64a7ee2bf29f35b1881cffdbec30..cf821f60f279d4a14c866c3aff193b2b2cbeb8ea 100644 --- a/login_provider/Dockerfile +++ b/login_provider/Dockerfile @@ -12,7 +12,7 @@ EXPOSE 5000 ENV FLASK_ENV production ENV FLASK_RUN_HOST 0.0.0.0 ENV FLASK_RUN_PORT 5000 -ENV HYDRA_ADMIN_URL http://localhost:444 +ENV HYDRA_ADMIN_URL http://localhost:4445 ENV GRAPHQL_URL http://localhost:5002/graphql CMD [ "flask", "run" ] diff --git a/login_provider/db.py b/login_provider/db.py index cd7ae5652d5f01f50a7b3d1cd575b9b049894fb0..b0cefa37785745ec372b6fa2d33323675f7cf664 100644 --- a/login_provider/db.py +++ b/login_provider/db.py @@ -21,9 +21,9 @@ class User(UserMixin): getUser(username: "{0}"){{ email, active - }}}}'''.format(self.username) + }}}}'''.format(self.username) result = json.loads(graphql_client.execute(querystring)) - if "data" in result and result["data"]["getUser"] is not None: + if "data" in result: self.active = result["data"]["getUser"]["active"] self.email = result["data"]["getUser"]["email"]