aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--moon_authz/moon_authz/api/authorization.py22
-rw-r--r--moon_authz/tests/unit_python/utilities.py21
-rw-r--r--moon_interface/moon_interface/api/authz.py26
-rw-r--r--moon_interface/moon_interface/authz_requests.py56
-rw-r--r--moon_interface/moon_interface/http_server.py7
-rw-r--r--moon_interface/moon_interface/server.py7
-rw-r--r--moon_interface/tests/unit_python/api/test_authz.py60
-rw-r--r--moon_interface/tests/unit_python/conftest.py13
-rw-r--r--moon_wrapper/moon_wrapper/api/oslowrapper.py7
-rw-r--r--moon_wrapper/tests/unit_python/api/test_wrapper.py47
-rw-r--r--moon_wrapper/tests/unit_python/conftest.py53
-rw-r--r--python_moonutilities/python_moonutilities/exceptions.py15
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"
-