From 57025d4f4b8d3168caf7fa002385a0be497fb677 Mon Sep 17 00:00:00 2001 From: aviau Date: Fri, 1 May 2015 14:42:26 -0400 Subject: [PATCH] Auth: Check token validity + secure bansho/config Change-Id: I417814a16df582ee636c197bcc7008978e084d3a --- etc/surveil/policy.json | 6 +++- surveil/api/authmiddleware/auth.py | 35 +++++++++++-------- surveil/api/controllers/v2/bansho/config.py | 3 ++ surveil/common/util.py | 7 ++-- .../api/controllers/v2/bansho/test_config.py | 17 +++++++-- surveil/tests/api/functionalTest.py | 19 +++++++++- surveil/tests/api/policy.json | 10 ++++-- 7 files changed, 73 insertions(+), 24 deletions(-) diff --git a/etc/surveil/policy.json b/etc/surveil/policy.json index 0f442a1..48df8ab 100644 --- a/etc/surveil/policy.json +++ b/etc/surveil/policy.json @@ -1,6 +1,10 @@ { "admin_required": "role:admin or is_admin:1", + "surveil_required": "role:surveil", + "surveil:admin": "rule:admin_required", + "surveil:authenticated": "rule:surveil_required", + "surveil:break": "!", "surveil:pass": "@" -} \ No newline at end of file +} diff --git a/surveil/api/authmiddleware/auth.py b/surveil/api/authmiddleware/auth.py index dc01fa1..d62c2fd 100644 --- a/surveil/api/authmiddleware/auth.py +++ b/surveil/api/authmiddleware/auth.py @@ -88,23 +88,23 @@ class AuthProtocol(object): """ self._remove_auth_headers(env) - # TODO(aviau): Get token and validate it, then build proper headers - if False: - self._reject_request(env, start_response) + token = self._get_header(env, 'X-Auth-Token', None) - user_headers = { - 'X-Identity-Status': 'Confirmed', - 'X-User-Id': 'surveil-default-user', - 'X-Roles': 'admin', - 'X-Service-Catalog': 'surveil' - } - self._add_headers(env, user_headers) + # TODO(aviau): Validate token, then build proper headers + if token == "aaaaa-bbbbb-ccccc-dddd": + user_headers = { + 'X-Identity-Status': 'Confirmed', + 'X-User-Id': 'surveil-default-user', + 'X-Roles': 'admin,surveil', + 'X-Service-Catalog': 'surveil' + } + self._add_headers(env, user_headers) - service_headers = { - 'X-Service-Identity-Status': 'Confirmed', - 'X-Service-Roles': 'surveil', - } - self._add_headers(env, service_headers) + service_headers = { + 'X-Service-Identity-Status': 'Confirmed', + 'X-Service-Roles': 'surveil', + } + self._add_headers(env, service_headers) return self._call_app(env, start_response) @@ -142,6 +142,11 @@ class AuthProtocol(object): """ return 'HTTP_%s' % key.replace('-', '_').upper() + def _get_header(self, env, key, default=None): + """Get http header from environment.""" + env_key = self._header_to_env_var(key) + return env.get(env_key, default) + def _call_app(self, env, start_response): # NOTE(jamielennox): We wrap the given start response so that if an # application with a 'delay_auth_decision' setting fails, or otherwise diff --git a/surveil/api/controllers/v2/bansho/config.py b/surveil/api/controllers/v2/bansho/config.py index 3f18b85..95dad87 100644 --- a/surveil/api/controllers/v2/bansho/config.py +++ b/surveil/api/controllers/v2/bansho/config.py @@ -18,10 +18,12 @@ from pecan import rest import wsmeext.pecan as wsme_pecan from surveil.api.handlers.bansho import config_handler +from surveil.common import util class ConfigController(rest.RestController): + @util.policy_enforce(['authenticated']) @wsme_pecan.wsexpose(unicode) def get(self): """Retrieve user config, empty dict if no config exists.""" @@ -30,6 +32,7 @@ class ConfigController(rest.RestController): config = handler.get(user_name) return config + @util.policy_enforce(['authenticated']) @wsme_pecan.wsexpose(body=unicode) def post(self, config): """Save user config. diff --git a/surveil/common/util.py b/surveil/common/util.py index f454e57..207ebec 100644 --- a/surveil/common/util.py +++ b/surveil/common/util.py @@ -13,6 +13,8 @@ # under the License. +import functools + import pecan from webob import exc @@ -22,7 +24,8 @@ from surveil.api import rbac # TODO(aviau && Freddrickk): Properly document this decorator def policy_enforce(actions): def policy_enforce_inner(handler): - def handle_stack_method(controller, **kwargs): + @functools.wraps(handler) + def handle_stack_method(*args, **kwargs): request = pecan.request for action in actions: allowed = rbac.enforce(action, request) @@ -30,6 +33,6 @@ def policy_enforce(actions): if not allowed: raise exc.HTTPForbidden() - return handler(controller, **kwargs) + return handler(*args, **kwargs) return handle_stack_method return policy_enforce_inner diff --git a/surveil/tests/api/controllers/v2/bansho/test_config.py b/surveil/tests/api/controllers/v2/bansho/test_config.py index d63c58c..0f359cd 100644 --- a/surveil/tests/api/controllers/v2/bansho/test_config.py +++ b/surveil/tests/api/controllers/v2/bansho/test_config.py @@ -34,15 +34,26 @@ class TestConfigController(functionalTest.FunctionalTest): ) def test_get_post_get(self): + # At first, conf is empty - response = self.app.get('/v2/bansho/config') + self.assertIsNone( + self.mongoconnection.surveil.bansho.config.find_one( + {"user_name": "surveil-default-user"} + ) + ) + response = self.get('/v2/bansho/config') self.assertEqual({}, json.loads(response.body.decode())) # Now, post config config = {"key": "val", "morekey": "moreval"} - self.app.post_json('/v2/bansho/config', params=config) + self.post_json('/v2/bansho/config', params=config) # Now config is what we gave to the API - response = self.app.get('/v2/bansho/config') + response = self.get('/v2/bansho/config') self.assertEqual(config, json.loads(response.body.decode())) + self.assertIsNotNone( + self.mongoconnection.surveil.bansho.config.find_one( + {"user_name": "surveil-default-user"} + ) + ) diff --git a/surveil/tests/api/functionalTest.py b/surveil/tests/api/functionalTest.py index 9712f28..c03edeb 100644 --- a/surveil/tests/api/functionalTest.py +++ b/surveil/tests/api/functionalTest.py @@ -75,10 +75,27 @@ class FunctionalTest(base.BaseTestCase): 'app': { 'root': 'surveil.api.controllers.root.RootController', 'modules': ['surveil.api'], - 'debug': False, + 'debug': True, 'hooks': app_hooks } }) + self.auth_headers = { + 'X-Identity-Status': 'Confirmed', + 'X-User-Id': 'surveil-default-user', + 'X-Roles': 'admin,surveil', + 'X-Service-Catalog': 'surveil', + 'X-Service-Identity-Status': 'Confirmed', + 'X-Service-Roles': 'surveil', + } + def tearDown(self): pecan.set_config({}, overwrite=True) + + def post_json(self, *args, **kwargs): + kwargs.setdefault('headers', self.auth_headers) + return self.app.post_json(*args, **kwargs) + + def get(self, *args, **kwargs): + kwargs.setdefault('headers', self.auth_headers) + return self.app.get(*args, **kwargs) diff --git a/surveil/tests/api/policy.json b/surveil/tests/api/policy.json index 0e746fa..48df8ab 100644 --- a/surveil/tests/api/policy.json +++ b/surveil/tests/api/policy.json @@ -1,4 +1,10 @@ { - "surveil:break":"!", - "surveil:pass":"@" + "admin_required": "role:admin or is_admin:1", + "surveil_required": "role:surveil", + + "surveil:admin": "rule:admin_required", + "surveil:authenticated": "rule:surveil_required", + + "surveil:break": "!", + "surveil:pass": "@" }