summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2017-08-24 16:45:16 +0300
committerLars Wirzenius <liw@liw.fi>2017-08-24 16:45:16 +0300
commit971c186165d1fbc16ba3d3e20ce42e0a685d8f1f (patch)
tree11940c84b2701cae2ac7c999cabbe0c28f7398af
parentb8d5b711205c73f4e5115573692ec9be0497c70c (diff)
parentc5f8d9ee60f267aaca5efb0d579b3fea09a5cace (diff)
downloadapifw-971c186165d1fbc16ba3d3e20ce42e0a685d8f1f.tar.gz
Merge branch 'liw/logging'
-rw-r--r--apifw.yarn6
-rw-r--r--apifw/__init__.py1
-rw-r--r--apifw/bottleapp.py44
-rw-r--r--apitest.py1
4 files changed, 44 insertions, 8 deletions
diff --git a/apifw.yarn b/apifw.yarn
index 3df47c4..3e69c38 100644
--- a/apifw.yarn
+++ b/apifw.yarn
@@ -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
diff --git a/apitest.py b/apitest.py
index 8710476..6e1867a 100644
--- a/apitest.py
+++ b/apitest.py
@@ -38,6 +38,7 @@ class Api(apifw.Api):
{
'path': '/version',
'callback': self.version,
+ 'needs-authorization': False,
},
{
'method': 'PUT',