|
|
@ -1,3 +1,4 @@ |
|
|
|
import logging |
|
|
|
import json |
|
|
|
import time |
|
|
|
import os |
|
|
@ -15,6 +16,8 @@ try: |
|
|
|
except ImportError: |
|
|
|
pass |
|
|
|
|
|
|
|
_logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
|
|
|
def jwk_from_json(json_key): |
|
|
|
key = jwk.JWK() |
|
|
@ -80,15 +83,20 @@ class Main(http.Controller): |
|
|
|
raise OAuthException( |
|
|
|
"client_id param is invalid", OAuthException.INVALID_CLIENT, |
|
|
|
) |
|
|
|
_logger.debug("#### openid client: %s" % client) |
|
|
|
return client |
|
|
|
|
|
|
|
def __validate_redirect_uri(self, client, req, **query): |
|
|
|
_logger.debug("#### openid client: %s" % query) |
|
|
|
if "redirect_uri" not in query: |
|
|
|
raise OAuthException( |
|
|
|
"redirect_uri param is missing", OAuthException.INVALID_GRANT, |
|
|
|
) |
|
|
|
|
|
|
|
redirect_uri = query["redirect_uri"] |
|
|
|
_logger.debug( |
|
|
|
"#### openid client: %s (%s)" % (client.auth_redirect_uri, redirect_uri) |
|
|
|
) |
|
|
|
if client.auth_redirect_uri != redirect_uri: |
|
|
|
raise OAuthException( |
|
|
|
"redirect_uri param doesn't match the pre-configured redirect URI", |
|
|
@ -99,6 +107,9 @@ class Main(http.Controller): |
|
|
|
|
|
|
|
def __validate_client_secret(self, client, req, **query): |
|
|
|
if "client_secret" not in query or query["client_secret"] != client.secret: |
|
|
|
_logger.debug( |
|
|
|
"#### openid client: %s (%s)" % (query["client_secret"], client.secret) |
|
|
|
) |
|
|
|
raise OAuthException( |
|
|
|
"client_secret param is not valid", OAuthException.INVALID_CLIENT, |
|
|
|
) |
|
|
@ -143,6 +154,7 @@ class Main(http.Controller): |
|
|
|
} |
|
|
|
if user.email: |
|
|
|
values["email"] = user.email |
|
|
|
_logger.debug("#### OPENID (3): %s" % values) |
|
|
|
return values |
|
|
|
|
|
|
|
@resource("/oauth/clientinfo", method="GET", auth="client") |
|
|
@ -155,12 +167,14 @@ class Main(http.Controller): |
|
|
|
@http.route("/oauth/authorize", auth="public", type="http", csrf=False) |
|
|
|
def authorize(self, req, **query): |
|
|
|
# First, validate client_id and redirect_uri params. |
|
|
|
_logger.debug("#### OPENID (auth)") |
|
|
|
try: |
|
|
|
client = self.__validate_client(req, **query) |
|
|
|
redirect_uri = self.__validate_redirect_uri(client, req, **query) |
|
|
|
except OAuthException as e: |
|
|
|
# If those are not valid, we must not redirect back to the client |
|
|
|
# - instead, we display a message to the user |
|
|
|
_logger.debug("#### OPENID (4): %s" % e) |
|
|
|
return req.render("galicea_openid_connect.error", {"exception": e}) |
|
|
|
|
|
|
|
scopes = query["scope"].split(" ") if query.get("scope") else [] |
|
|
@ -174,6 +188,7 @@ class Main(http.Controller): |
|
|
|
response_mode = query.get("response_mode") |
|
|
|
try: |
|
|
|
if response_mode and response_mode not in ["query", "fragment"]: |
|
|
|
_logger.debug("#### OPENID (auth 1)") |
|
|
|
response_mode = None |
|
|
|
raise OAuthException( |
|
|
|
"The only supported response_modes are 'query' and 'fragment'", |
|
|
@ -181,11 +196,13 @@ class Main(http.Controller): |
|
|
|
) |
|
|
|
|
|
|
|
if "response_type" not in query: |
|
|
|
_logger.debug("#### OPENID (auth 2)") |
|
|
|
raise OAuthException( |
|
|
|
"response_type param is missing", OAuthException.INVALID_REQUEST, |
|
|
|
) |
|
|
|
response_type = query["response_type"] |
|
|
|
if response_type not in RESPONSE_TYPES_SUPPORTED: |
|
|
|
_logger.debug("#### OPENID (auth 3)") |
|
|
|
raise OAuthException( |
|
|
|
"The only supported response_types are: {}".format( |
|
|
|
", ".join(RESPONSE_TYPES_SUPPORTED) |
|
|
@ -193,12 +210,14 @@ class Main(http.Controller): |
|
|
|
OAuthException.UNSUPPORTED_RESPONSE_TYPE, |
|
|
|
) |
|
|
|
except OAuthException as e: |
|
|
|
_logger.debug("#### OPENID (5): %s" % e) |
|
|
|
response_params["error"] = e.type |
|
|
|
response_params["error_description"] = e.message |
|
|
|
response_params["error_description"] = e |
|
|
|
return self.__redirect( |
|
|
|
redirect_uri, response_params, response_mode or "query" |
|
|
|
) |
|
|
|
|
|
|
|
_logger.debug("#### OPENID (auth 4)") |
|
|
|
if not response_mode: |
|
|
|
response_mode = "query" if response_type == "code" else "fragment" |
|
|
|
|
|
|
@ -218,6 +237,7 @@ class Main(http.Controller): |
|
|
|
werkzeug.url_encode(query) |
|
|
|
) |
|
|
|
} |
|
|
|
_logger.debug("#### OPENID (auth 4.2): %s" % params) |
|
|
|
return self.__redirect("/web/login", params, "query") |
|
|
|
|
|
|
|
response_types = response_type.split() |
|
|
@ -261,7 +281,14 @@ class Main(http.Controller): |
|
|
|
response_params["id_token"] = self.__create_id_token( |
|
|
|
req, user.id, client, extra_claims |
|
|
|
) |
|
|
|
|
|
|
|
_logger.debug( |
|
|
|
"#### OPENID (6): %s, %s, %s" |
|
|
|
% (redirect_uri, response_params, response_mode) |
|
|
|
) |
|
|
|
_logger.debug( |
|
|
|
"#### OPENID (6.1): %s" |
|
|
|
% self.__redirect(redirect_uri, response_params, response_mode) |
|
|
|
) |
|
|
|
return self.__redirect(redirect_uri, response_params, response_mode) |
|
|
|
|
|
|
|
@http.route( |
|
|
@ -281,6 +308,7 @@ class Main(http.Controller): |
|
|
|
return http.Response(status=200, headers=cors_headers) |
|
|
|
|
|
|
|
try: |
|
|
|
_logger.debug("#### OPENID (7): %s" % query) |
|
|
|
if "grant_type" not in query: |
|
|
|
raise OAuthException( |
|
|
|
"grant_type param is missing", OAuthException.INVALID_REQUEST, |
|
|
@ -306,7 +334,7 @@ class Main(http.Controller): |
|
|
|
OAuthException.UNSUPPORTED_GRANT_TYPE, |
|
|
|
) |
|
|
|
except OAuthException as e: |
|
|
|
body = json.dumps({"error": e.type, "error_description": e.message}) |
|
|
|
body = json.dumps({"error": e.type, "error_description": e}) |
|
|
|
return werkzeug.Response(response=body, status=400, headers=cors_headers) |
|
|
|
|
|
|
|
def __handle_grant_type_authorization_code(self, req, **query): |
|
|
@ -320,20 +348,25 @@ class Main(http.Controller): |
|
|
|
) |
|
|
|
try: |
|
|
|
payload = jwt_decode(query["code"], self.__get_authorization_code_jwk(req)) |
|
|
|
_logger.debug("#### OPENID (8): %s" % payload) |
|
|
|
except jwt.JWTExpired: |
|
|
|
_logger.debug("#### OPENID (9): %s" % OAuthException.INVALID_GRANT) |
|
|
|
raise OAuthException( |
|
|
|
"Code expired", OAuthException.INVALID_GRANT, |
|
|
|
) |
|
|
|
except ValueError: |
|
|
|
_logger.debug("#### OPENID (10): %s" % OAuthException.INVALID_GRANT) |
|
|
|
raise OAuthException( |
|
|
|
"code malformed", OAuthException.INVALID_GRANT, |
|
|
|
) |
|
|
|
if payload["client_id"] != client.client_id: |
|
|
|
_logger.debug("#### OPENID (11): %s" % OAuthException.INVALID_GRANT) |
|
|
|
raise OAuthException( |
|
|
|
"client_id doesn't match the authorization request", |
|
|
|
OAuthException.INVALID_GRANT, |
|
|
|
) |
|
|
|
if payload["redirect_uri"] != redirect_uri: |
|
|
|
_logger.debug("#### OPENID (12): %s" % OAuthException.INVALID_GRANT) |
|
|
|
raise OAuthException( |
|
|
|
"redirect_uri doesn't match the authorization request", |
|
|
|
OAuthException.INVALID_GRANT, |
|
|
@ -353,6 +386,7 @@ class Main(http.Controller): |
|
|
|
response["id_token"] = self.__create_id_token( |
|
|
|
req, payload["user_id"], client, extra_claims |
|
|
|
) |
|
|
|
_logger.debug("#### OPENID (12): %s" % response) |
|
|
|
return response |
|
|
|
|
|
|
|
def __handle_grant_type_password(self, req, **query): |
|
|
|