diff options
author | Lars Wirzenius <liw@liw.fi> | 2017-08-24 16:45:16 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2017-08-24 16:45:16 +0300 |
commit | 971c186165d1fbc16ba3d3e20ce42e0a685d8f1f (patch) | |
tree | 11940c84b2701cae2ac7c999cabbe0c28f7398af | |
parent | b8d5b711205c73f4e5115573692ec9be0497c70c (diff) | |
parent | c5f8d9ee60f267aaca5efb0d579b3fea09a5cace (diff) | |
download | apifw-971c186165d1fbc16ba3d3e20ce42e0a685d8f1f.tar.gz |
Merge branch 'liw/logging'
-rw-r--r-- | apifw.yarn | 6 | ||||
-rw-r--r-- | apifw/__init__.py | 1 | ||||
-rw-r--r-- | apifw/bottleapp.py | 44 | ||||
-rw-r--r-- | apitest.py | 1 |
4 files changed, 44 insertions, 8 deletions
@@ -24,13 +24,11 @@ It's a silly name. Please suggest something better. GIVEN a running apitest using gunicorn3 WHEN client requests GET /version without token - THEN HTTP status code is 401 Unauthorized - AND response has header WWW-Authenticate containing "Bearer" + THEN HTTP status code is 200 OK WHEN client gets an authorization token with scope "no_version_scope" AND client requests GET /version using token - THEN HTTP status code is 401 Unauthorized - AND response has header WWW-Authenticate containing "Bearer" + THEN HTTP status code is 200 OK WHEN client gets an authorization token with scope "uapi_version_get" AND client requests GET /version using token diff --git a/apifw/__init__.py b/apifw/__init__.py index e157a22..ac7aad1 100644 --- a/apifw/__init__.py +++ b/apifw/__init__.py @@ -13,7 +13,6 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. - from .apixface import Api from .http import ( HttpTransaction, diff --git a/apifw/bottleapp.py b/apifw/bottleapp.py index 9a2916e..9cc9d08 100644 --- a/apifw/bottleapp.py +++ b/apifw/bottleapp.py @@ -78,6 +78,7 @@ class BottleAuthorizationPlugin: self.pubkey = None self.iss = None self.aud = None + self._authz_routes = set() def set_token_signing_public_key(self, pubkey): self.pubkey = Crypto.PublicKey.RSA.importKey(pubkey) @@ -88,16 +89,46 @@ class BottleAuthorizationPlugin: def set_expected_audience(self, aud): self.aud = aud - def apply(self, callback, route): + def set_route_authorization(self, route): + key = self.route_key(route) + if route.get('needs-authorization', True): + self._authz_routes.add(key) + logging.info('Route %r does DOES need authorization', key) + else: + logging.info('Route %r does NOT need authorization', key) + + def route_key(self, route): + # route can be a dict (from find_missing_route), or a + # bottle.Route object. + method = route.get('method', 'GET') + path = route.get('path') + if not path: + rule = route.get('rule') + path = re.sub(r'/<[^>]+>', '', rule) + return (method, path) + def apply(self, callback, route): def wrapper(*args, **kwargs): - if self.is_authorized(route): + + call = False + if self.needs_authorization(route): + call = self.is_authorized(route) + else: + call = True + if call: return callback(*args, **kwargs) self.raise_unauthorized('Something went wrong') return wrapper + def needs_authorization(self, route): + key = self.route_key(route) + logging.debug('route: %r', route) + logging.debug('route_key: %r', key) + logging.debug('authz_routes: %r', self._authz_routes) + return key in self._authz_routes + def is_authorized(self, route): value = self.get_authorization_header(bottle.request) token = self.parse_authorization_header(value) @@ -109,7 +140,7 @@ class BottleAuthorizationPlugin: value = request.get_header('Authorization', '') if not value: self.raise_unauthorized('No Authorization header') - logging.debug('Request has Authorization header: good') + logging.debug('Request has Authorization header: %r', value) return value def parse_authorization_header(self, value): @@ -124,6 +155,7 @@ class BottleAuthorizationPlugin: try: token = apifw.decode_token(token, self.pubkey, audience=self.aud) logging.debug('Request Authorization token can be decoded: good') + logging.debug('Token: %r', token) return token except jwt.InvalidTokenError as e: self.raise_unauthorized(str(e)) @@ -170,10 +202,14 @@ class BottleApplication: self._bottleapp = bottleapp self._bottleapp.add_hook('before_request', self._add_missing_route) self._api = api + self._authz = None def add_plugin(self, plugin): self._bottleapp.install(plugin) + def set_authorization_plugin(self, plugin): + self._authz = plugin + def _add_missing_route(self): try: self._bottleapp.match(bottle.request.environ) @@ -188,6 +224,7 @@ class BottleApplication: 'callback': callback, } self._bottleapp.route(**route_dict) + self._authz.set_route_authorization(route) else: raise @@ -233,5 +270,6 @@ def create_bottle_application(api, counter, logger, config): authz.set_expected_issuer(config['token-issuer']) authz.set_expected_audience(config['token-audience']) app.add_plugin(authz) + app.set_authorization_plugin(authz) return bottleapp @@ -38,6 +38,7 @@ class Api(apifw.Api): { 'path': '/version', 'callback': self.version, + 'needs-authorization': False, }, { 'method': 'PUT', |