1 # Copyright (C) 2022 Wind River Systems, Inc.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 from werkzeug.wrappers import Request, Response
16 from o2common.helper import o2logging
17 from o2common.authmw.authprov import auth_definer
18 from flask_restx._http import HTTPStatus
21 logger = o2logging.get_logger(__name__)
24 class AuthRequiredExp(Exception):
25 def __init__(self, value):
30 'WWW-Authenticate': '{}'.format(self.value)}
33 class AuthProblemDetails():
34 def __init__(self, code: int, detail: str, path: str,
35 title=None, instance=None
40 self.title = title if title is not None else self.getTitle(code)
41 self.instance = instance if instance is not None else []
43 def getTitle(self, code):
44 return HTTPStatus(code).phrase
49 if key == 'ns' or key.startswith('__') or \
50 callable(getattr(self, key)):
53 details[key] = getattr(self, key)
54 return json.dumps(details, indent=True)
57 class AuthFailureExp(Exception):
58 def __init__(self, value):
63 'WWW-Authenticate': '{}'.format(self.value)}
66 def _response_wrapper(environ, start_response, header, detail):
67 res = Response(headers=header,
68 mimetype='application/json', status=401, response=detail)
69 return res(environ, start_response)
72 def _internal_err_response_wrapper(environ, start_response, detail):
73 res = Response(mimetype='application/json', status=500, response=detail)
74 return res(environ, start_response)
77 class authmiddleware():
83 def __init__(self, app):
86 def __call__(self, environ, start_response):
87 logger.debug(__name__ + 'authentication middleware')
88 req = Request(environ, populate_request=True, shallow=True)
91 auth_header = req.headers.get('Authorization', None)
93 auth_token = auth_header.split(" ")[1]
95 ad = auth_definer('oauth')
96 # invoke underlying auth mdw to make k8s/keystone api
97 ret = ad.authenticate(auth_token)
100 "auth success with oauth token: " + auth_token)
102 return self.app(environ, start_response)
103 except Exception as ex:
105 'Internal exception happend \
106 ed {}'.format(str(ex)), exc_info=True)
107 prb = AuthProblemDetails(
108 500, 'Internal error.', req.path)
110 _internal_err_response_wrapper(
112 start_response, prb.serialize())
114 raise AuthFailureExp(
115 'Bearer realm="Authentication Failed"')
117 raise AuthRequiredExp('Bearer realm="Authentication Required"')
118 except AuthRequiredExp as ex:
119 prb = AuthProblemDetails(401, ex.value, req.path)
120 return _response_wrapper(environ, start_response,
121 ex.dictize(), prb.serialize())
122 except AuthFailureExp as ex:
123 prb = AuthProblemDetails(401, ex.value, req.path)
124 return _response_wrapper(environ, start_response,
125 ex.dictize(), prb.serialize())
126 except Exception as ex:
128 logger.error('Internal exception happended {}'.format(
129 str(ex)), exc_info=True)
130 prb = AuthProblemDetails(500, 'Internal error.', req.path)
132 _internal_err_response_wrapper(
133 environ, start_response, prb.serialize())
135 logger.debug('Auth token missing or not obtained.')
136 ex = AuthRequiredExp('Bearer realm="Authentication Required"')
137 prb = AuthProblemDetails(401, ex.value, req.path)
138 return _response_wrapper(environ, start_response,
139 ex.dictize(), prb.serialize())