diff options
-rw-r--r-- | moon_authz/moon_authz/api/authorization.py | 22 | ||||
-rw-r--r-- | moon_authz/tests/unit_python/utilities.py | 21 | ||||
-rw-r--r-- | moon_interface/moon_interface/api/authz.py | 26 | ||||
-rw-r--r-- | moon_interface/moon_interface/authz_requests.py | 56 | ||||
-rw-r--r-- | moon_interface/moon_interface/http_server.py | 7 | ||||
-rw-r--r-- | moon_interface/moon_interface/server.py | 7 | ||||
-rw-r--r-- | moon_interface/tests/unit_python/api/test_authz.py | 60 | ||||
-rw-r--r-- | moon_interface/tests/unit_python/conftest.py | 13 | ||||
-rw-r--r-- | moon_wrapper/moon_wrapper/api/oslowrapper.py | 7 | ||||
-rw-r--r-- | moon_wrapper/tests/unit_python/api/test_wrapper.py | 47 | ||||
-rw-r--r-- | moon_wrapper/tests/unit_python/conftest.py | 53 | ||||
-rw-r--r-- | python_moonutilities/python_moonutilities/exceptions.py | 15 |
12 files changed, 267 insertions, 67 deletions
diff --git a/moon_authz/moon_authz/api/authorization.py b/moon_authz/moon_authz/api/authorization.py index e939604b..84114466 100644 --- a/moon_authz/moon_authz/api/authorization.py +++ b/moon_authz/moon_authz/api/authorization.py @@ -89,16 +89,28 @@ class Authz(Resource): # Context.update_target(context) if not self.context.pdp_set: raise exceptions.PdpUnknown + if current_header_id not in self.context.pdp_set: + raise Exception('Invalid index') current_pdp = self.context.pdp_set[current_header_id] category_list = list() - category_list.extend(current_pdp["meta_rules"]["subject_categories"]) - category_list.extend(current_pdp["meta_rules"]["object_categories"]) - category_list.extend(current_pdp["meta_rules"]["action_categories"]) + if 'meta_rules' not in current_pdp: + raise exceptions.PdpContentError + try: + category_list.extend(current_pdp["meta_rules"]["subject_categories"]) + category_list.extend(current_pdp["meta_rules"]["object_categories"]) + category_list.extend(current_pdp["meta_rules"]["action_categories"]) + except Exception: + raise exceptions.MetaRuleContentError + if 'target' not in current_pdp: + raise exceptions.PdpContentError for category in category_list: scope = list(current_pdp['target'][category]) scopes_list.append(scope) # policy_id = self.cache.get_policy_from_meta_rules("admin", current_header_id) - + if self.context.current_policy_id not in self.cache.rules: + raise exceptions.PolicyUnknown + if 'rules' not in self.cache.rules[self.context.current_policy_id]: + raise exceptions.RuleUnknown for item in itertools.product(*scopes_list): req = list(item) for rule in self.cache.rules[self.context.current_policy_id]["rules"]: @@ -365,4 +377,4 @@ class Authz(Resource): def head(self, uuid=None, subject_name=None, object_name=None, action_name=None): logger.info("HEAD request") - return "", 200
\ No newline at end of file + return "", 200 diff --git a/moon_authz/tests/unit_python/utilities.py b/moon_authz/tests/unit_python/utilities.py index 19b9354c..e3a111bd 100644 --- a/moon_authz/tests/unit_python/utilities.py +++ b/moon_authz/tests/unit_python/utilities.py @@ -37,11 +37,19 @@ CONF = { "container": "wukongsun/moon_orchestrator:v4.3", "hostname": "orchestrator" }, - "interface": { - "bind": "0.0.0.0", - "port": 8080, - "container": "wukongsun/moon_interface:v4.3", - "hostname": "interface" + "pipeline": { + "interface": { + "bind": "0.0.0.0", + "port": 8080, + "container": "wukongsun/moon_interface:v4.3", + "hostname": "interface" + }, + "authz": { + "bind": "0.0.0.0", + "port": 8081, + "container": "wukongsun/moon_authz:v4.3", + "hostname": "authz" + } } }, "plugins": { @@ -144,7 +152,8 @@ COMPONENTS = ( "slave", "components/manager", "components/orchestrator", - "components/interface", + "components/pipeline", + "components/wrapper", ) diff --git a/moon_interface/moon_interface/api/authz.py b/moon_interface/moon_interface/api/authz.py index bd60d3f6..b82a14f1 100644 --- a/moon_interface/moon_interface/api/authz.py +++ b/moon_interface/moon_interface/api/authz.py @@ -12,6 +12,7 @@ import logging import pickle import time from uuid import uuid4 +from python_moonutilities import exceptions from moon_interface.authz_requests import AuthzRequest @@ -29,18 +30,13 @@ def get_pdp_from_cache(cache, uuid): """ if uuid in cache.pdp: return cache.pdp.get(uuid) - return None + cache.update() -def get_pdp_from_manager(cache, uuid): - """Check if a PDP exist with this ID in the Manager component + if uuid in cache.pdp: + return cache.pdp.get(uuid) - :param cache: Cache to use - :param uuid: Keystone Project ID - :return: True or False - """ - cache.update() - return get_pdp_from_cache(cache, uuid) + raise exceptions.PdpUnknown def create_authz_request(cache, interface_name, manager_url, pdp_id, subject_name, object_name, action_name): @@ -92,7 +88,7 @@ class Authz(Resource): self.MANAGER_URL = kwargs.get("manager_url", "http://manager:8080") self.TIMEOUT = 5 - def get(self, pdp_id=None, subject_name=None, object_name=None, action_name=None): + def get(self, pdp_id, subject_name=None, object_name=None, action_name=None): """Get a response on an authorization request :param pdp_id: uuid of a tenant or an intra_extension @@ -119,13 +115,13 @@ class Authz(Resource): } :internal_api: authz """ - pdp_value = get_pdp_from_cache(self.CACHE, pdp_id) - if not pdp_id: - pdp_value = get_pdp_from_manager(self.CACHE, pdp_id) - if not pdp_id: - return { + try: + get_pdp_from_cache(self.CACHE, pdp_id) + except exceptions.PdpUnknown: + return { "result": False, "message": "Unknown PDP ID."}, 403 + authz_request = create_authz_request( cache=self.CACHE, pdp_id=pdp_id, diff --git a/moon_interface/moon_interface/authz_requests.py b/moon_interface/moon_interface/authz_requests.py index 87e21152..12c190c7 100644 --- a/moon_interface/moon_interface/authz_requests.py +++ b/moon_interface/moon_interface/authz_requests.py @@ -7,6 +7,7 @@ import logging import itertools import pickle import requests +import sys from python_moonutilities import exceptions from python_moonutilities.context import Context from python_moonutilities.cache import Cache @@ -31,51 +32,53 @@ class AuthzRequest: if ctx['project_id'] not in CACHE.container_chaining: raise exceptions.KeystoneProjectError("Unknown Project ID {}".format(ctx['project_id'])) self.container_chaining = CACHE.container_chaining[ctx['project_id']] - if len(self.container_chaining) == 0: + + if len(self.container_chaining) == 0 or not all(k in self.container_data for k in ("container_id", "hostname", "hostip", "port")): raise exceptions.MoonError('Void container chaining') + self.pdp_container = self.container_chaining[0]["container_id"] self.run() def run(self): self.context.delete_cache() req = None - try: - req = requests.post("http://{}:{}/authz".format( - self.container_chaining[0]["hostip"], - self.container_chaining[0]["port"], - ), data=pickle.dumps(self.context)) - if req.status_code != 200: - raise exceptions.AuthzException( - "Receive bad response from Authz function " - "(with IP address - {})".format( - req.status_code - )) - except requests.exceptions.ConnectionError: - logger.error("Cannot connect to {}".format( - "http://{}:{}/authz".format( - self.container_chaining[0]["hostip"], - self.container_chaining[0]["port"] - ))) - except ValueError: + tries = 0 + success = False + + if "hostip" in self.container_chaining[0]: + hostname = self.container_chaining[0]["hostip"] + elif "hostname" in self.container_chaining[0]: + hostname = self.container_chaining[0]["hostname"] + else: + raise exceptions.AuthzException( + "error in address no hostname or hostip" + ) + while tries < 2: try: req = requests.post("http://{}:{}/authz".format( - self.container_chaining[0]["hostname"], + hostname, self.container_chaining[0]["port"], ), data=pickle.dumps(self.context)) if req.status_code != 200: raise exceptions.AuthzException( "Receive bad response from Authz function " - "(with hostname - {})".format( - req.status_code - )) + "(with address - {})".format(req.status_code) + ) + success = True except requests.exceptions.ConnectionError: logger.error("Cannot connect to {}".format( "http://{}:{}/authz".format( - self.container_chaining[0]["hostname"], + hostname, self.container_chaining[0]["port"] ))) - raise exceptions.AuthzException( - "Cannot connect to Authz function") + except: + logger.error("Unexpected error:", sys.exc_info()[0]) + hostname = self.container_chaining[0]["hostname"], + tries += 1 + + if not success: + raise exceptions.AuthzException("Cannot connect to Authz function") + self.context.set_cache(CACHE) if req and len(self.container_chaining) == 1: self.result = pickle.loads(req.content) @@ -132,6 +135,7 @@ class AuthzRequest: authz_results = [] for key in self.result.pdp_set: if "effect" in self.result.pdp_set[key]: + if self.result.pdp_set[key]["effect"] == "grant": # the pdp is a authorization PDP and grant the request authz_results.append(True) diff --git a/moon_interface/moon_interface/http_server.py b/moon_interface/moon_interface/http_server.py index 57170985..82ee5d91 100644 --- a/moon_interface/moon_interface/http_server.py +++ b/moon_interface/moon_interface/http_server.py @@ -113,6 +113,13 @@ class HTTPServer(Server): def get_400_json(e): return jsonify({"result": False, "code": 400, "description": str(e)}), 400 + + ''' + [Note] i have tried to simulate authz post request (authz_Requests/run) to return 500 response code + and an AuthzException thrown from their [Line 63] and catched here , then the server here return response + with 403 code [Forbidden] , is it correct if so why sometime at authz [from Line 137] return a response + with error code , i think we can do it in the same way as the one mentioned? + ''' self.app.register_error_handler(400, lambda e: get_400_json) self.app.register_error_handler(403, exceptions.AuthException) diff --git a/moon_interface/moon_interface/server.py b/moon_interface/moon_interface/server.py index 0af1fd06..d38dae28 100644 --- a/moon_interface/moon_interface/server.py +++ b/moon_interface/moon_interface/server.py @@ -13,8 +13,12 @@ logger = logging.getLogger("moon.interface.server") def create_server(): configuration.init_logging() try: + conf = configuration.get_configuration("components/pipeline").get( "components/pipeline", {}).get("interface", {}) + ''' + [Note] i think pipeline should be changed to interface + ''' hostname = conf.get("hostname", "pipeline") port = conf.get("port", 80) bind = conf.get("bind", "127.0.0.1") @@ -22,6 +26,9 @@ def create_server(): hostname = "interface" bind = "127.0.0.1" port = 80 + ''' + [Note] i think pipeline should be changed to interface + ''' configuration.add_component(uuid="pipeline", name=hostname, port=port, diff --git a/moon_interface/tests/unit_python/api/test_authz.py b/moon_interface/tests/unit_python/api/test_authz.py index 10957218..052bc9c9 100644 --- a/moon_interface/tests/unit_python/api/test_authz.py +++ b/moon_interface/tests/unit_python/api/test_authz.py @@ -1,4 +1,5 @@ import json +import conftest def get_json(data): @@ -6,6 +7,7 @@ def get_json(data): def test_authz_true(context): + import moon_interface.server server = moon_interface.server.create_server() client = server.app.test_client() @@ -19,5 +21,61 @@ def test_authz_true(context): data = get_json(req.data) assert data assert "result" in data - assert data['result'] == True + assert data['result'] is True + +def test_authz_False(context): + import moon_interface.server + server = moon_interface.server.create_server() + client = server.app.test_client() + req = client.get("/authz/{p_id}/{s_id}/{o_id}/{a_id}".format( + p_id=None, + s_id=context["subject_name"], + o_id=context["object_name"], + a_id=context["action_name"], + )) + assert req.status_code == 403 + data = get_json(req.data) + assert data + assert "result" in data + assert data['result'] is False + + +def test_authz_effect_unset(context, set_consul_and_db): + import moon_interface.server + server = moon_interface.server.create_server() + client = server.app.test_client() + + set_consul_and_db.register_uri( + 'POST', 'http://127.0.0.1:8081/authz', + content = conftest.get_pickled_context_invalid() + ) + + req = client.get("/authz/{p_id}/{s_id}/{o_id}/{a_id}".format( + p_id=context["pdp_id"], + s_id=context["subject_name"], + o_id=context["object_name"], + a_id=context["action_name"], + )) + assert req.status_code == 401 + data = get_json(req.data) + assert data + assert "result" in data + assert data['result'] is False + +def test_authz_invalid_ip(context, set_consul_and_db): + import moon_interface.server + server = moon_interface.server.create_server() + client = server.app.test_client() + + set_consul_and_db.register_uri( + 'POST', 'http://127.0.0.1:8081/authz', status_code=500 + ) + + req = client.get("/authz/{p_id}/{s_id}/{o_id}/{a_id}".format( + p_id=context["pdp_id"], + s_id=context["subject_name"], + o_id=context["object_name"], + a_id=context["action_name"], + )) + assert req.status_code == 403 diff --git a/moon_interface/tests/unit_python/conftest.py b/moon_interface/tests/unit_python/conftest.py index a6acbcdd..893a8637 100644 --- a/moon_interface/tests/unit_python/conftest.py +++ b/moon_interface/tests/unit_python/conftest.py @@ -214,6 +214,19 @@ def get_pickled_context(): print(_context.pdp_set) return pickle.dumps(_context) +def get_pickled_context_invalid(): + from python_moonutilities.context import Context + from python_moonutilities.cache import Cache + CACHE = Cache() + CACHE.update() + _context = Context(context(), CACHE) + _context.increment_index() + _context.pdp_set['effect'] = 'invalid' + _context.pdp_set[os.environ['META_RULE_ID']]['effect'] = 'invalid' + print(_context.pdp_set) + return pickle.dumps(_context) + + @pytest.fixture(autouse=True) def set_consul_and_db(monkeypatch): diff --git a/moon_wrapper/moon_wrapper/api/oslowrapper.py b/moon_wrapper/moon_wrapper/api/oslowrapper.py index d2836c08..ad9e430a 100644 --- a/moon_wrapper/moon_wrapper/api/oslowrapper.py +++ b/moon_wrapper/moon_wrapper/api/oslowrapper.py @@ -71,7 +71,7 @@ class OsloWrapper(Resource): logger.info("containers {}".format(containers)) for container in containers: if container.get("keystone_project_id") == project_id: - if "pipeline" in container['name']: + if "interface" in container['name']: return "http://{}:{}".format( container['name'], container['port']) @@ -80,7 +80,7 @@ class OsloWrapper(Resource): for containers in self.CACHE.containers.values(): for container in containers: if container.get("keystone_project_id") == project_id: - if "pipeline" in container['name']: + if "interface" in container['name']: return "http://{}:{}".format( container['name'], container['port']) @@ -109,6 +109,9 @@ class OsloWrapper(Resource): _object, _action )) + ''' + [Note] i think here if status != 200, should raise an exception + ''' logger.debug("Get interface {}".format(req.text)) if req.status_code == 200: if req.json().get("result", False): diff --git a/moon_wrapper/tests/unit_python/api/test_wrapper.py b/moon_wrapper/tests/unit_python/api/test_wrapper.py index 7e9a7421..be3e8576 100644 --- a/moon_wrapper/tests/unit_python/api/test_wrapper.py +++ b/moon_wrapper/tests/unit_python/api/test_wrapper.py @@ -1,3 +1,8 @@ +# Copyright 2015 Open Platform for NFV Project, Inc. and its contributors +# This software is distributed under the terms and conditions of the 'Apache-2.0' +# license which can be found in the file 'LICENSE' in this package distribution +# or at 'http://www.apache.org/licenses/LICENSE-2.0'. + import json @@ -20,9 +25,47 @@ def test_authz_true(context): 'rule': context.get('action_name'), 'target': json.dumps(_target), 'credentials': 'null'} - req = client.post("/authz", data=json.dumps(authz_data)) - assert req.status_code == 200 + req = client.post("/authz/oslo", data=json.dumps(authz_data)) + assert req.status_code is 200 assert req.data assert isinstance(req.data, bytes) assert req.data == b"True" +def test_authz_error_response_code(context): + import moon_wrapper.server + server = moon_wrapper.server.main() + client = server.app.test_client() + _target = { + 'target': { + "name": context.get('object_name'), + }, + "project_id": context.get('invalid_project_id'), + "user_id": context.get('subject_name') + } + authz_data = { + 'rule': context.get('action_name'), + 'target': json.dumps(_target), + 'credentials': 'null'} + req = client.post("/authz/oslo", data=json.dumps(authz_data)) + assert req.status_code is 200 + assert req.data + assert isinstance(req.data, bytes) + assert req.data == b"False" + +def test_authz_error_no_interface_key(context): + import moon_wrapper.server + server = moon_wrapper.server.main() + client = server.app.test_client() + _target = { + 'target': { + "name": context.get('object_name'), + }, + "project_id": context.get('project_with_no_interface_key'), + "user_id": context.get('subject_name') + } + authz_data = { + 'rule': context.get('action_name'), + 'target': json.dumps(_target), + 'credentials': 'null'} + req = client.post("/authz/oslo", data=json.dumps(authz_data)) + assert req.status_code == 403
\ No newline at end of file diff --git a/moon_wrapper/tests/unit_python/conftest.py b/moon_wrapper/tests/unit_python/conftest.py index b160ebf6..621c2014 100644 --- a/moon_wrapper/tests/unit_python/conftest.py +++ b/moon_wrapper/tests/unit_python/conftest.py @@ -1,3 +1,8 @@ +# Copyright 2015 Open Platform for NFV Project, Inc. and its contributors +# This software is distributed under the terms and conditions of the 'Apache-2.0' +# license which can be found in the file 'LICENSE' in this package distribution +# or at 'http://www.apache.org/licenses/LICENSE-2.0'. + import base64 import json import os @@ -5,7 +10,6 @@ import pickle import pytest import requests_mock from uuid import uuid4 -from requests.packages.urllib3.response import HTTPResponse CONF = { "openstack": { @@ -13,7 +17,7 @@ CONF = { "url": "http://keystone:5000/v3", "user": "admin", "check_token": False, - "password": "p4ssw0rd", + "password": "p4ssw0rd", # nosec "domain": "default", "certificate": False, "project": "admin" @@ -21,21 +25,21 @@ CONF = { }, "components": { "wrapper": { - "bind": "0.0.0.0", + "bind": "0.0.0.0", # nosec "port": 8080, "container": "wukongsun/moon_wrapper:v4.3", "timeout": 5, "hostname": "wrapper" }, "manager": { - "bind": "0.0.0.0", + "bind": "0.0.0.0", # nosec "port": 8082, "container": "wukongsun/moon_manager:v4.3", "hostname": "manager" }, "port_start": 31001, "orchestrator": { - "bind": "0.0.0.0", + "bind": "0.0.0.0", # nosec "port": 8083, "container": "wukongsun/moon_orchestrator:v4.3", "hostname": "orchestrator" @@ -60,7 +64,7 @@ CONF = { "logging": { "handlers": { "file": { - "filename": "/tmp/moon.log", + "filename": "/tmp/moon.log", # nosec "class": "logging.handlers.RotatingFileHandler", "level": "DEBUG", "formatter": "custom", @@ -105,7 +109,7 @@ CONF = { "master": { "url": None, "login": None, - "password": None + "password": None # nosec } }, "docker": { @@ -135,6 +139,10 @@ COMPONENTS = ( CONTEXT = { "project_id": "a64beb1cc224474fb4badd43173e7101", + "pdp_id": "b3d3e18abf3340e8b635fd49e6634ccd", + "invalid_project_id" : "invalid_project_id", + "invalid_pdp_id": "invalid_pdp_id", + "project_with_no_interface_key" : "232399a4-de5f-11e7-8001-3863bbb766f3", "subject_name": "testuser", "object_name": "vm1", "action_name": "boot", @@ -206,7 +214,7 @@ def set_env_variables(): def get_pickled_context(): - from python_moonutilities.security_functions import Context + from python_moonutilities.context import Context from python_moonutilities.cache import Cache CACHE = Cache() CACHE.update() @@ -295,6 +303,15 @@ def set_consul_and_db(monkeypatch): "keystone_project_id": "a64beb1cc224474fb4badd43173e7101", "namespace": "moon", "container": "wukongsun/moon_authz:v4.3" + }, + { + "pdp_id": "invalid_pdp_id", + "port": 8080, + "genre": "interface", + "name": "interface-paltry", + "keystone_project_id": "invalid_project_id", + "namespace": "moon", + "container": "wukongsun/moon_authz:v4.3" } ], "232399a4-de5f-11e7-8001-3863bbb766f3": [ @@ -325,6 +342,15 @@ def set_consul_and_db(monkeypatch): ], "name": "pdp_rbac", "keystone_project_id": "a64beb1cc224474fb4badd43173e7101" + }, + "invalid_pdp_id":{ + + "description": "test", + "security_pipeline": [ + "f8f49a779ceb47b3ac810f01ef71b4e0" + ], + "name": "pdp_rbac", + "keystone_project_id": "invalid_project_id" } } } @@ -671,13 +697,22 @@ def set_consul_and_db(monkeypatch): ) m.register_uri( 'GET', 'http://interface-paltry:8080/authz/{}/{}/{}/{}'.format( - CONTEXT.get("project_id"), + CONTEXT.get("pdp_id"), CONTEXT.get("subject_name"), CONTEXT.get("object_name"), CONTEXT.get("action_name"), ), json={"result": True, "message": "================"} ) + m.register_uri( + 'GET', 'http://interface-paltry:8080/authz/{}/{}/{}/{}'.format( + CONTEXT.get("invalid_pdp_id"), + CONTEXT.get("subject_name"), + CONTEXT.get("object_name"), + CONTEXT.get("action_name"), + ), + status_code=500 + ) # from moon_db.db_manager import init_engine, run # engine = init_engine() # run("upgrade", logging.getLogger("db_manager"), engine) diff --git a/python_moonutilities/python_moonutilities/exceptions.py b/python_moonutilities/python_moonutilities/exceptions.py index 2d689287..6db7bf01 100644 --- a/python_moonutilities/python_moonutilities/exceptions.py +++ b/python_moonutilities/python_moonutilities/exceptions.py @@ -443,6 +443,13 @@ class MetaRuleExisting(AdminMetaRule): logger = "ERROR" +class MetaRuleContentError(AdminMetaRule): + description = _("Invalid content of pdp.") + code = 400 + title = 'Meta Rule Error' + logger = "ERROR" + + class RuleExisting(AdminRule): description = _("The rule already exists.") code = 400 @@ -542,6 +549,13 @@ class PdpExisting(MoonError): logger = "Error" +class PdpContentError(MoonError): + description = _("Invalid content of pdp.") + code = 409 + title = 'Pdp Error' + logger = "Error" + + class PdpKeystoneMappingConflict(MoonError): description = _("A pdp is already mapped to that Keystone project.") code = 409 @@ -561,4 +575,3 @@ class PolicyExisting(MoonError): code = 409 title = 'Policy Error' logger = "Error" - |