diff options
-rw-r--r-- | pycadf/audit/__init__.py | 28 | ||||
-rw-r--r-- | pycadf/audit/api.py | 115 | ||||
-rw-r--r-- | pycadf/credential.py | 49 | ||||
-rw-r--r-- | pycadf/endpoint.py | 52 | ||||
-rw-r--r-- | pycadf/event.py | 24 | ||||
-rw-r--r-- | pycadf/host.py | 65 | ||||
-rw-r--r-- | pycadf/reporterstep.py | 2 | ||||
-rw-r--r-- | pycadf/resource.py | 40 | ||||
-rw-r--r-- | pycadf/tests/audit/test_api.py | 43 | ||||
-rw-r--r-- | pycadf/tests/test_cadf_spec.py | 37 |
10 files changed, 340 insertions, 115 deletions
diff --git a/pycadf/audit/__init__.py b/pycadf/audit/__init__.py index 3a143f2..e69de29 100644 --- a/pycadf/audit/__init__.py +++ b/pycadf/audit/__init__.py @@ -1,28 +0,0 @@ -# -*- encoding: utf-8 -*- -# -# Copyright 2013 IBM Corp. -# -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo.config import cfg - -CONF = cfg.CONF - -opts = [ - cfg.StrOpt('api_audit_map', - default='/etc/pycadf/api_audit_map.conf', - help='File containing mapping for api paths and ' - 'service endpoints'), -] -CONF.register_opts(opts, group='audit') diff --git a/pycadf/audit/api.py b/pycadf/audit/api.py index 8a0859c..b34debb 100644 --- a/pycadf/audit/api.py +++ b/pycadf/audit/api.py @@ -23,7 +23,10 @@ import urlparse from pycadf import cadftaxonomy as taxonomy from pycadf import cadftype +from pycadf import credential +from pycadf import endpoint from pycadf import eventfactory as factory +from pycadf import host from pycadf import identifier from pycadf import reason from pycadf import reporterstep @@ -31,36 +34,28 @@ from pycadf import resource from pycadf import tag from pycadf import timestamp -cfg.CONF.import_opt('api_audit_map', 'pycadf.audit', group='audit') CONF = cfg.CONF - - -class ServiceResource(resource.Resource): - def __init__(self, admin_url=None, private_url=None, - public_url=None, **kwargs): - super(ServiceResource, self).__init__(**kwargs) - if admin_url is not None: - self.adminURL = admin_url - if private_url is not None: - self.privateURL = private_url - if public_url is not None: - self.publicURL = public_url +opts = [ + cfg.StrOpt('api_audit_map', + default='api_audit_map.conf', + help='File containing mapping for api paths and ' + 'service endpoints'), +] +CONF.register_opts(opts, group='audit') class ClientResource(resource.Resource): - def __init__(self, client_addr=None, user_agent=None, - token=None, tenant=None, status=None, **kwargs): + def __init__(self, project_id=None, **kwargs): super(ClientResource, self).__init__(**kwargs) - if client_addr is not None: - self.client_addr = client_addr - if user_agent is not None: - self.user_agent = user_agent - if token is not None: - self.token = token - if tenant is not None: - self.tenant = tenant - if status is not None: - self.status = status + if project_id is not None: + self.project_id = project_id + + +class KeystoneCredential(credential.Credential): + def __init__(self, identity_status=None, **kwargs): + super(KeystoneCredential, self).__init__(**kwargs) + if identity_status is not None: + self.identity_status = identity_status class PycadfAuditApiConfigError(Exception): @@ -156,46 +151,60 @@ class OpenStackAuditApi(object): def create_event(self, req, correlation_id): action = self._get_action(req) + initiator_host = host.Host(address=req.client_addr, + agent=req.user_agent) catalog = ast.literal_eval(req.environ['HTTP_X_SERVICE_CATALOG']) - for endpoint in catalog: + for endp in catalog: admin_urlparse = urlparse.urlparse( - endpoint['endpoints'][0]['adminURL']) + endp['endpoints'][0]['adminURL']) public_urlparse = urlparse.urlparse( - endpoint['endpoints'][0]['publicURL']) + endp['endpoints'][0]['publicURL']) req_url = urlparse.urlparse(req.host_url) if (req_url.netloc == admin_urlparse.netloc or req_url.netloc == public_urlparse.netloc): - service_type = self._SERVICE_ENDPOINTS.get(endpoint['type'], + service_type = self._SERVICE_ENDPOINTS.get(endp['type'], taxonomy.UNKNOWN) - service_name = endpoint['name'] - admin_url = endpoint['endpoints'][0]['adminURL'] - private_url = endpoint['endpoints'][0]['internalURL'] - public_url = endpoint['endpoints'][0]['publicURL'] - service_id = endpoint['endpoints'][0]['id'] + service_name = endp['name'] + admin_end = endpoint.Endpoint( + name='admin', + url=endp['endpoints'][0]['adminURL']) + private_end = endpoint.Endpoint( + name='private', + url=endp['endpoints'][0]['internalURL']) + public_end = endpoint.Endpoint( + name='public', + url=endp['endpoints'][0]['publicURL']) + service_id = endp['endpoints'][0]['id'] break else: service_type = service_id = service_name = taxonomy.UNKNOWN - admin_url = private_url = public_url = None - + admin_end = private_end = public_end = None + + initiator = ClientResource( + typeURI=taxonomy.ACCOUNT_USER, + id=str(req.environ['HTTP_X_USER_ID']), + name=req.environ['HTTP_X_USER_NAME'], + host=initiator_host, + credential=KeystoneCredential( + token=req.environ['HTTP_X_AUTH_TOKEN'], + identity_status=req.environ['HTTP_X_IDENTITY_STATUS']), + project_id=req.environ['HTTP_X_PROJECT_ID']) + target = resource.Resource(typeURI=service_type, + id=service_id, + name=service_name) + if admin_end: + target.add_address(admin_end) + if private_end: + target.add_address(private_end) + if public_end: + target.add_address(public_end) event = factory.EventFactory().new_event( eventType=cadftype.EVENTTYPE_ACTIVITY, outcome=taxonomy.OUTCOME_PENDING, action=action, - initiator=ClientResource( - typeURI=taxonomy.ACCOUNT_USER, - id=str(req.environ['HTTP_X_USER_ID']), - name=req.environ['HTTP_X_USER_NAME'], - client_addr=req.client_addr, - user_agent=req.user_agent, - token=req.environ['HTTP_X_AUTH_TOKEN'], - tenant=req.environ['HTTP_X_PROJECT_ID'], - status=req.environ['HTTP_X_IDENTITY_STATUS']), - target=ServiceResource(typeURI=service_type, - id=service_id, - name=service_name, - private_url=private_url, - public_url=public_url, - admin_url=admin_url)) + initiator=initiator, + target=target, + observer='target') event.add_tag(tag.generate_name_value_tag('correlation_id', correlation_id)) return event @@ -208,10 +217,6 @@ class OpenStackAuditApi(object): correlation_id = identifier.generate_uuid() req.environ['CADF_EVENT_CORRELATION_ID'] = correlation_id event = self.create_event(req, correlation_id) - event.add_reporterstep( - reporterstep.Reporterstep( - role=cadftype.REPORTER_ROLE_OBSERVER, - reporter='target')) setattr(req, 'cadf_model', event) req.environ['CADF_EVENT'] = event.as_dict() diff --git a/pycadf/credential.py b/pycadf/credential.py new file mode 100644 index 0000000..f58f6b6 --- /dev/null +++ b/pycadf/credential.py @@ -0,0 +1,49 @@ +# -*- encoding: utf-8 -*- +# +# Copyright © 2013 IBM Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from pycadf import cadftype + +TYPE_URI_CRED = cadftype.CADF_VERSION_1_0_0 + 'credential' + +CRED_KEYNAME_TYPE = "type" +CRED_KEYNAME_TOKEN = "token" + +CRED_KEYNAMES = [CRED_KEYNAME_TYPE, + CRED_KEYNAME_TOKEN] + + +class Credential(cadftype.CADFAbstractType): + + type = cadftype.ValidatorDescriptor( + CRED_KEYNAME_TYPE, + lambda x: isinstance(x, basestring)) + token = cadftype.ValidatorDescriptor( + CRED_KEYNAME_TOKEN, + lambda x: isinstance(x, basestring)) + + def __init__(self, token, type=None): + + # Credential.token + setattr(self, CRED_KEYNAME_TOKEN, token) + + # Credential.type + if type is not None: + setattr(self, CRED_KEYNAME_TYPE, type) + + # TODO(mrutkows): validate this cadf:Credential type against schema + def is_valid(self): + # TODO(mrutkows): validate specific attribute type/format + return hasattr(self, CRED_KEYNAME_TOKEN) diff --git a/pycadf/endpoint.py b/pycadf/endpoint.py new file mode 100644 index 0000000..046d7cd --- /dev/null +++ b/pycadf/endpoint.py @@ -0,0 +1,52 @@ +# -*- encoding: utf-8 -*- +# +# Copyright © 2013 IBM Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from pycadf import cadftype + +TYPE_URI_ENDPOINT = cadftype.CADF_VERSION_1_0_0 + 'endpoint' + +ENDPOINT_KEYNAME_URL = "url" +ENDPOINT_KEYNAME_NAME = "name" +ENDPOINT_KEYNAME_PORT = "port" + +ENDPOINT_KEYNAMES = [ENDPOINT_KEYNAME_URL, + ENDPOINT_KEYNAME_NAME, + ENDPOINT_KEYNAME_PORT] + + +class Endpoint(cadftype.CADFAbstractType): + + url = cadftype.ValidatorDescriptor( + ENDPOINT_KEYNAME_URL, lambda x: isinstance(x, basestring)) + name = cadftype.ValidatorDescriptor( + ENDPOINT_KEYNAME_NAME, lambda x: isinstance(x, basestring)) + port = cadftype.ValidatorDescriptor( + ENDPOINT_KEYNAME_PORT, lambda x: isinstance(x, basestring)) + + def __init__(self, url, name=None, port=None): + + # ENDPOINT.url + setattr(self, ENDPOINT_KEYNAME_URL, url) + # ENDPOINT.name + if name is not None: + setattr(self, ENDPOINT_KEYNAME_NAME, name) + # ENDPOINT.port + if port is not None: + setattr(self, ENDPOINT_KEYNAME_PORT, port) + + # TODO(mrutkows): validate this cadf:ENDPOINT type against schema + def is_valid(self): + return hasattr(self, ENDPOINT_KEYNAME_URL) diff --git a/pycadf/event.py b/pycadf/event.py index 916b4c3..70972f6 100644 --- a/pycadf/event.py +++ b/pycadf/event.py @@ -45,6 +45,7 @@ EVENT_KEYNAME_SEVERITY = "severity" EVENT_KEYNAME_MEASUREMENTS = "measurements" EVENT_KEYNAME_TAGS = "tags" EVENT_KEYNAME_ATTACHMENTS = "attachments" +EVENT_KEYNAME_OBSERVER = "observer" EVENT_KEYNAME_REPORTERCHAIN = "reporterchain" EVENT_KEYNAMES = [EVENT_KEYNAME_TYPEURI, @@ -62,6 +63,7 @@ EVENT_KEYNAMES = [EVENT_KEYNAME_TYPEURI, EVENT_KEYNAME_MEASUREMENTS, EVENT_KEYNAME_TAGS, EVENT_KEYNAME_ATTACHMENTS, + EVENT_KEYNAME_OBSERVER, EVENT_KEYNAME_REPORTERCHAIN] @@ -94,13 +96,18 @@ class Event(cadftype.CADFAbstractType): severity = cadftype.ValidatorDescriptor(EVENT_KEYNAME_SEVERITY, lambda x: isinstance(x, basestring)) + observer = cadftype.ValidatorDescriptor( + EVENT_KEYNAME_OBSERVER, + (lambda x: isinstance(x, resource.Resource) or + (isinstance(x, basestring) and + (x == 'initiator' or x == 'target')))) def __init__(self, eventType=cadftype.EVENTTYPE_ACTIVITY, id=identifier.generate_uuid(), eventTime=timestamp.get_utc_now(), action=cadftaxonomy.UNKNOWN, outcome=cadftaxonomy.UNKNOWN, initiator=None, initiatorId=None, target=None, targetId=None, - severity=None, reason=None): + severity=None, reason=None, observer=None): # Establish typeURI for the CADF Event data type # TODO(mrutkows): support extended typeURIs for Event subtypes @@ -121,6 +128,9 @@ class Event(cadftype.CADFAbstractType): # Event.outcome (Mandatory) setattr(self, EVENT_KEYNAME_OUTCOME, outcome) + # Event.observer (Mandatory) + setattr(self, EVENT_KEYNAME_OBSERVER, observer) + # Event.initiator (Mandatory if no initiatorId) if initiator is not None: setattr(self, EVENT_KEYNAME_INITIATOR, initiator) @@ -145,15 +155,14 @@ class Event(cadftype.CADFAbstractType): if reason is not None: setattr(self, EVENT_KEYNAME_REASON, reason) - # Event.reporterchain (Mandatory) - # Prepare the Event.reporterchain (list of cadf:Reporterstep) since - # at least one cadf:Reporterstep entry is required - setattr(self, EVENT_KEYNAME_REPORTERCHAIN, list()) - # Event.reporterchain def add_reporterstep(self, step): if step is not None and isinstance(step, reporterstep.Reporterstep): if step.is_valid(): + # Create the list of Reportersteps if needed + if not hasattr(self, EVENT_KEYNAME_REPORTERCHAIN): + setattr(self, EVENT_KEYNAME_REPORTERCHAIN, list()) + reporterchain = getattr(self, EVENT_KEYNAME_REPORTERCHAIN) reporterchain.append(step) @@ -224,6 +233,5 @@ class Event(cadftype.CADFAbstractType): hasattr(self, EVENT_KEYNAME_ACTION) and hasattr(self, EVENT_KEYNAME_OUTCOME) and hasattr(self, EVENT_KEYNAME_INITIATOR) and - hasattr(self, EVENT_KEYNAME_TARGET) and - hasattr(self, EVENT_KEYNAME_REPORTERCHAIN) + hasattr(self, EVENT_KEYNAME_TARGET) ) diff --git a/pycadf/host.py b/pycadf/host.py new file mode 100644 index 0000000..565f00e --- /dev/null +++ b/pycadf/host.py @@ -0,0 +1,65 @@ +# -*- encoding: utf-8 -*- +# +# Copyright © 2013 IBM Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from pycadf import cadftype +from pycadf import identifier + +TYPE_URI_HOST = cadftype.CADF_VERSION_1_0_0 + 'host' + +HOST_KEYNAME_ID = "id" +HOST_KEYNAME_ADDR = "address" +HOST_KEYNAME_AGENT = "agent" +HOST_KEYNAME_PLATFORM = "platform" + +HOST_KEYNAMES = [HOST_KEYNAME_ID, + HOST_KEYNAME_ADDR, + HOST_KEYNAME_AGENT, + HOST_KEYNAME_PLATFORM] + + +class Host(cadftype.CADFAbstractType): + + id = cadftype.ValidatorDescriptor( + HOST_KEYNAME_ID, lambda x: identifier.is_valid(x)) + address = cadftype.ValidatorDescriptor( + HOST_KEYNAME_ADDR, lambda x: isinstance(x, basestring)) + agent = cadftype.ValidatorDescriptor( + HOST_KEYNAME_AGENT, lambda x: isinstance(x, basestring)) + platform = cadftype.ValidatorDescriptor( + HOST_KEYNAME_PLATFORM, lambda x: isinstance(x, basestring)) + + def __init__(self, id=None, address=None, agent=None, + platform=None): + + # Host.id + if id is not None: + setattr(self, HOST_KEYNAME_ID, id) + # Host.address + if address is not None: + setattr(self, HOST_KEYNAME_ADDR, address) + # Host.agent + if agent is not None: + setattr(self, HOST_KEYNAME_AGENT, agent) + # Host.platform + if platform is not None: + setattr(self, HOST_KEYNAME_PLATFORM, platform) + + # TODO(mrutkows): validate this cadf:Host type against schema + def is_valid(self): + return (hasattr(self, HOST_KEYNAME_ID) or + hasattr(self, HOST_KEYNAME_ADDR) or + hasattr(self, HOST_KEYNAME_AGENT) or + hasattr(self, HOST_KEYNAME_PLATFORM)) diff --git a/pycadf/reporterstep.py b/pycadf/reporterstep.py index 29cab96..1f620b8 100644 --- a/pycadf/reporterstep.py +++ b/pycadf/reporterstep.py @@ -50,7 +50,7 @@ class Reporterstep(cadftype.CADFAbstractType): reporterTime = cadftype.ValidatorDescriptor( REPORTERSTEP_KEYNAME_REPORTERTIME, lambda x: timestamp.is_valid(x)) - def __init__(self, role=cadftype.REPORTER_ROLE_OBSERVER, + def __init__(self, role=cadftype.REPORTER_ROLE_MODIFIER, reporterTime=None, reporter=None, reporterId=None): # Reporterstep.role setattr(self, REPORTERSTEP_KEYNAME_ROLE, role) diff --git a/pycadf/resource.py b/pycadf/resource.py index 0d30a42..9c447e6 100644 --- a/pycadf/resource.py +++ b/pycadf/resource.py @@ -19,7 +19,10 @@ from pycadf import attachment from pycadf import cadftaxonomy from pycadf import cadftype +from pycadf import credential +from pycadf import endpoint from pycadf import geolocation +from pycadf import host from pycadf import identifier TYPE_URI_RESOURCE = cadftype.CADF_VERSION_1_0_0 + 'resource' @@ -28,18 +31,24 @@ RESOURCE_KEYNAME_TYPEURI = "typeURI" RESOURCE_KEYNAME_ID = "id" RESOURCE_KEYNAME_NAME = "name" RESOURCE_KEYNAME_DOMAIN = "domain" +RESOURCE_KEYNAME_CRED = "credential" RESOURCE_KEYNAME_REF = "ref" RESOURCE_KEYNAME_GEO = "geolocation" RESOURCE_KEYNAME_GEOID = "geolocationId" +RESOURCE_KEYNAME_HOST = "host" +RESOURCE_KEYNAME_ADDRS = "addresses" RESOURCE_KEYNAME_ATTACHMENTS = "attachments" RESOURCE_KEYNAMES = [RESOURCE_KEYNAME_TYPEURI, RESOURCE_KEYNAME_ID, RESOURCE_KEYNAME_NAME, RESOURCE_KEYNAME_DOMAIN, + RESOURCE_KEYNAME_CRED, RESOURCE_KEYNAME_REF, RESOURCE_KEYNAME_GEO, RESOURCE_KEYNAME_GEOID, + RESOURCE_KEYNAME_HOST, + RESOURCE_KEYNAME_ADDRS, RESOURCE_KEYNAME_ATTACHMENTS] @@ -53,6 +62,11 @@ class Resource(cadftype.CADFAbstractType): lambda x: isinstance(x, basestring)) domain = cadftype.ValidatorDescriptor(RESOURCE_KEYNAME_DOMAIN, lambda x: isinstance(x, basestring)) + credential = cadftype.ValidatorDescriptor( + RESOURCE_KEYNAME_CRED, (lambda x: isinstance(x, credential.Credential) + and x.is_valid())) + host = cadftype.ValidatorDescriptor( + RESOURCE_KEYNAME_HOST, lambda x: isinstance(x, host.Host)) # TODO(mrutkows): validate the "ref" attribute is indeed a URI (format), # If it is a URL, we do not need to validate it is accessible/working, # for audit purposes this could have been a valid URL at some point @@ -68,7 +82,8 @@ class Resource(cadftype.CADFAbstractType): def __init__(self, id=identifier.generate_uuid(), typeURI=cadftaxonomy.UNKNOWN, name=None, ref=None, - domain=None, geolocation=None, geolocationId=None): + domain=None, credential=None, host=None, + geolocation=None, geolocationId=None): # Resource.id setattr(self, RESOURCE_KEYNAME_ID, id) @@ -88,6 +103,14 @@ class Resource(cadftype.CADFAbstractType): if domain is not None: setattr(self, RESOURCE_KEYNAME_DOMAIN, domain) + # Resource.credential + if credential is not None: + setattr(self, RESOURCE_KEYNAME_CRED, credential) + + # Resource.host + if host is not None: + setattr(self, RESOURCE_KEYNAME_HOST, host) + # Resource.geolocation if geolocation is not None: setattr(self, RESOURCE_KEYNAME_GEO, geolocation) @@ -96,6 +119,21 @@ class Resource(cadftype.CADFAbstractType): if geolocationId: setattr(self, RESOURCE_KEYNAME_GEOID, geolocationId) + # Resource.address + def add_address(self, addr): + if (addr is not None and isinstance(addr, endpoint.Endpoint)): + if addr.is_valid(): + # Create the list of Endpoints if needed + if not hasattr(self, RESOURCE_KEYNAME_ADDRS): + setattr(self, RESOURCE_KEYNAME_ADDRS, list()) + + addrs = getattr(self, RESOURCE_KEYNAME_ADDRS) + addrs.append(addr) + else: + raise ValueError('Invalid endpoint') + else: + raise ValueError('Invalid endpoint. Value must be an Endpoint') + # Resource.attachments def add_attachment(self, attach_val): if (attach_val is not None diff --git a/pycadf/tests/audit/test_api.py b/pycadf/tests/audit/test_api.py index 7d2fbde..20fb275 100644 --- a/pycadf/tests/audit/test_api.py +++ b/pycadf/tests/audit/test_api.py @@ -54,7 +54,8 @@ class TestAuditApi(base.TestCase): def api_request(self, method, url): self.ENV_HEADERS['REQUEST_METHOD'] = method - req = webob.Request.blank(url, environ=self.ENV_HEADERS) + req = webob.Request.blank(url, environ=self.ENV_HEADERS, + remote_addr='192.168.0.1') self.audit_api.append_audit_event(req) self.assertIn('CADF_EVENT_CORRELATION_ID', req.environ) return req @@ -67,25 +68,26 @@ class TestAuditApi(base.TestCase): 'http://schemas.dmtf.org/cloud/audit/1.0/event') self.assertEqual(payload['outcome'], 'pending') self.assertEqual(payload['eventType'], 'activity') - self.assertEqual(payload['target']['publicURL'], - 'http://host:8774/v2/public') - self.assertEqual(payload['target']['privateURL'], - 'http://host:8774/v2/internal') - self.assertEqual(payload['target']['adminURL'], - 'http://host:8774/v2/admin') self.assertEqual(payload['target']['name'], 'nova') self.assertEqual(payload['target']['id'], 'resource_id') self.assertEqual(payload['target']['typeURI'], 'service/compute') + self.assertEqual(len(payload['target']['addresses']), 3) + self.assertEqual(payload['target']['addresses'][0]['name'], 'admin') + self.assertEqual(payload['target']['addresses'][0]['url'], + 'http://host:8774/v2/admin') self.assertEqual(payload['initiator']['id'], 'user_id') self.assertEqual(payload['initiator']['name'], 'user_name') - self.assertEqual(payload['initiator']['token'], 'token') - self.assertEqual(payload['initiator']['tenant'], 'tenant_id') + self.assertEqual(payload['initiator']['project_id'], 'tenant_id') + self.assertEqual(payload['initiator']['host']['address'], + '192.168.0.1') self.assertEqual(payload['initiator']['typeURI'], 'service/security/account/user') + self.assertEqual(payload['initiator']['credential']['token'], 'token') + self.assertEqual(payload['initiator']['credential']['identity_status'], + 'Confirmed') self.assertNotIn('reason', payload) - self.assertEqual(len(payload['reporterchain']), 1) - self.assertEqual(payload['reporterchain'][0]['role'], 'observer') - self.assertEqual(payload['reporterchain'][0]['reporter'], 'target') + self.assertNotIn('reporterchain', payload) + self.assertEqual(payload['observer'], 'target') def test_get_read(self): req = self.api_request('GET', @@ -158,9 +160,9 @@ class TestAuditApi(base.TestCase): self.assertEqual(payload2['outcome'], 'success') self.assertEqual(payload2['reason']['reasonType'], 'HTTP') self.assertEqual(payload2['reason']['reasonCode'], '200') - self.assertEqual(len(payload2['reporterchain']), 2) - self.assertEqual(payload2['reporterchain'][1]['role'], 'modifier') - self.assertEqual(payload2['reporterchain'][1]['reporter'], 'target') + self.assertEqual(len(payload2['reporterchain']), 1) + self.assertEqual(payload2['reporterchain'][0]['role'], 'modifier') + self.assertEqual(payload2['reporterchain'][0]['reporter'], 'target') def test_no_response(self): req = self.api_request('GET', 'http://host:8774/v2/public/servers') @@ -171,9 +173,9 @@ class TestAuditApi(base.TestCase): self.assertEqual(payload['tags'], payload2['tags']) self.assertEqual(payload2['outcome'], 'unknown') self.assertNotIn('reason', payload2) - self.assertEqual(len(payload2['reporterchain']), 2) - self.assertEqual(payload2['reporterchain'][1]['role'], 'modifier') - self.assertEqual(payload2['reporterchain'][1]['reporter'], 'target') + self.assertEqual(len(payload2['reporterchain']), 1) + self.assertEqual(payload2['reporterchain'][0]['role'], 'modifier') + self.assertEqual(payload2['reporterchain'][0]['reporter'], 'target') def test_missing_req(self): self.ENV_HEADERS['REQUEST_METHOD'] = 'GET' @@ -187,6 +189,5 @@ class TestAuditApi(base.TestCase): self.assertEqual(payload['outcome'], 'success') self.assertEqual(payload['reason']['reasonType'], 'HTTP') self.assertEqual(payload['reason']['reasonCode'], '200') - self.assertEqual(len(payload['reporterchain']), 1) - self.assertEqual(payload['reporterchain'][0]['role'], 'observer') - self.assertEqual(payload['reporterchain'][0]['reporter'], 'target') + self.assertEqual(payload['observer'], 'target') + self.assertNotIn('reporterchain', payload) diff --git a/pycadf/tests/test_cadf_spec.py b/pycadf/tests/test_cadf_spec.py index 6ab6ac9..869d34d 100644 --- a/pycadf/tests/test_cadf_spec.py +++ b/pycadf/tests/test_cadf_spec.py @@ -16,8 +16,11 @@ import testtools from pycadf import attachment +from pycadf import credential +from pycadf import endpoint from pycadf import event from pycadf import geolocation +from pycadf import host from pycadf import identifier from pycadf import measurement from pycadf import metric @@ -29,6 +32,30 @@ from pycadf import timestamp class TestCADFSpec(testtools.TestCase): + def test_endpoint(self): + endp = endpoint.Endpoint(url='http://192.168.0.1', + name='endpoint name', + port='8080') + dict_endp = endp.as_dict() + for key in endpoint.ENDPOINT_KEYNAMES: + self.assertIn(key, dict_endp) + + def test_host(self): + h = host.Host(id=identifier.generate_uuid(), + address='192.168.0.1', + agent='client', + platform='AIX') + dict_host = h.as_dict() + for key in host.HOST_KEYNAMES: + self.assertIn(key, dict_host) + + def test_credential(self): + cred = credential.Credential(type='auth token', + token=identifier.generate_uuid()) + dict_cred = cred.as_dict() + for key in credential.CRED_KEYNAMES: + self.assertIn(key, dict_cred) + def test_geolocation(self): geo = geolocation.Geolocation(id=identifier.generate_uuid(), latitude='43.6481 N', @@ -75,7 +102,7 @@ class TestCADFSpec(testtools.TestCase): def test_reporterstep(self): step = reporterstep.Reporterstep( - role='observer', + role='modifier', reporter=resource.Resource(typeURI='storage'), reporterId=identifier.generate_uuid(), reporterTime=timestamp.get_utc_now()) @@ -98,12 +125,16 @@ class TestCADFSpec(testtools.TestCase): name='res_name', domain='res_domain', ref='res_ref', + credential=credential.Credential( + token=identifier.generate_uuid()), + host=host.Host(address='192.168.0.1'), geolocation=geolocation.Geolocation(), geolocationId=identifier.generate_uuid()) res.add_attachment(attachment.Attachment(typeURI='attachURI', content='content', name='attachment_name')) + res.add_address(endpoint.Endpoint(url='http://192.168.0.1')) dict_res = res.as_dict() for key in resource.RESOURCE_KEYNAMES: self.assertIn(key, dict_res) @@ -117,6 +148,7 @@ class TestCADFSpec(testtools.TestCase): action='read', target=resource.Resource(typeURI='storage'), targetId=identifier.generate_uuid(), + observer='target', outcome='success', reason=reason.Reason(reasonType='HTTP', reasonCode='200'), @@ -126,8 +158,11 @@ class TestCADFSpec(testtools.TestCase): ev.add_attachment(attachment.Attachment(typeURI='attachURI', content='content', name='attachment_name')) + ev.observer = resource.Resource(typeURI='service/security') ev.add_reporterstep(reporterstep.Reporterstep( role='observer', + reporter=resource.Resource(typeURI='service/security'))) + ev.add_reporterstep(reporterstep.Reporterstep( reporterId=identifier.generate_uuid())) dict_ev = ev.as_dict() |