From c2ea797565c30b560ecdcdac10e71e962d9ac379 Mon Sep 17 00:00:00 2001 From: Nathan Buckner Date: Thu, 20 Aug 2015 20:14:09 -0500 Subject: [PATCH] Added v3 auth extension and updated v2 auth extension --- syntribos/extensions/identity/client.py | 158 ++++--- .../identity/{auth_config.py => config.py} | 14 +- .../extensions/identity/models/auth_models.py | 424 ------------------ syntribos/extensions/identity/models/base.py | 125 ++++++ syntribos/extensions/identity/models/v2.py | 237 ++++++++++ syntribos/extensions/identity/models/v3.py | 98 ++++ 6 files changed, 569 insertions(+), 487 deletions(-) rename syntribos/extensions/identity/{auth_config.py => config.py} (85%) delete mode 100644 syntribos/extensions/identity/models/auth_models.py create mode 100644 syntribos/extensions/identity/models/base.py create mode 100644 syntribos/extensions/identity/models/v2.py create mode 100644 syntribos/extensions/identity/models/v3.py diff --git a/syntribos/extensions/identity/client.py b/syntribos/extensions/identity/client.py index eeb90c5b..86a4e564 100644 --- a/syntribos/extensions/identity/client.py +++ b/syntribos/extensions/identity/client.py @@ -13,78 +13,112 @@ 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 cafe.engine.behaviors import BaseBehavior from cafe.engine.http.client import AutoMarshallingHTTPClient -from syntribos.extensions.rax_auth.models import auth_models -from syntribos.extensions.rax_auth.auth_config import ( - UserAuthConfig, UserConfig) +from syntribos.extensions.identity.models import v2, v3 +from syntribos.extensions.identity.config import ( + EndpointConfig, UserConfig) -class TokensBehavior(BaseBehavior): - @classmethod - def get_access_data_config(cls, user_config, userauth_config): - return cls.get_access_data( - url=user_config.endpoint or userauth_config.endpoint, - username=user_config.username, - password=user_config.password, - tenant_name=user_config.tenant_name, - tenant_id=user_config.tenant_id, - token=user_config.token, - api_key=user_config.api_key, - serialize_format=userauth_config.serialize_format, - deserialize_format=userauth_config.deserialize_format) +def authenticate_v2( + url, username=None, password=None, tenant_name=None, + tenant_id=None, token=None, domain=None, serialize_format="json", + deserialize_format="json"): + url = '{0}/v2.0/tokens'.format(url) + client = AutoMarshallingHTTPClient(serialize_format, deserialize_format) + client.default_headers["Content-Type"] = "application/{0}".format( + serialize_format) + client.default_headers["Accept"] = "application/{0}".format( + deserialize_format) - @classmethod - def get_access_data(cls, *args, **kwargs): - return cls.authenticate(*args, **kwargs).entity + kwargs = {} + kwargs["tenant_name"] = tenant_name + kwargs["tenant_id"] = tenant_id - @classmethod - def authenticate( - cls, url, username=None, password=None, tenant_name=None, - tenant_id=None, token=None, api_key=None, rsa_key=None, - domain=None, serialize_format="json", deserialize_format="json"): - url = '{0}/tokens'.format(url) - client = AutoMarshallingHTTPClient( - serialize_format, deserialize_format) + if password is not None: + password_creds = v2.PasswordCredentials( + username=username, password=password) - client.default_headers["Content-Type"] = "application/{0}".format( - serialize_format) - client.default_headers["Accept"] = "application/{0}".format( - deserialize_format) + request_entity = v2.Auth( + tenant_name=tenant_name, tenant_id=tenant_id, + password_creds=password_creds) - kwargs = {} - kwargs["tenant_name"] = tenant_name - kwargs["tenant_id"] = tenant_id + r = client.request( + "POST", url, request_entity=request_entity, + response_entity_type=v2.AuthResponse) - if password is not None: - kwargs["password_creds"] = auth_models.PasswordCredentials( - username=username, password=password) + if not r.ok: + raise Exception("Failed to authenticate") - if token is not None: - kwargs["token_creds"] = auth_models.Token(id_=token) - - if api_key is not None: - kwargs["api_key_creds"] = auth_models.APIKeyCredentials( - username=username, api_key=api_key) - - request_entity = auth_models.Auth(**kwargs) - - r = client.request( - "POST", url, request_entity=request_entity, - response_entity_type=auth_models.AuthResponse) - if not r.ok: - raise Exception("Failed to authenticate") - - r.entity = auth_models.AuthResponse.deserialize( - r.content, deserialize_format) - if r.entity is None: - raise Exception("Failed to parse Auth response Body") - return r + if r.entity is None: + raise Exception("Failed to parse Auth response Body") + return r -def get_token(section_name): - access_data = TokensBehavior.get_access_data_config( - UserConfig(section_name=section_name), UserAuthConfig()) +def authenticate_v2_config(user_config, userauth_config): + return authenticate_v2( + url=user_config.endpoint or userauth_config.endpoint, + username=user_config.username, + password=user_config.password, + tenant_name=user_config.tenant_name, + tenant_id=user_config.tenant_id, + token=user_config.token, + serialize_format=userauth_config.serialize_format, + deserialize_format=userauth_config.deserialize_format) + + +def get_token_v2(user_section_name=None, endpoint_section_name=None): + access_data = authenticate_v2_config( + UserConfig(section_name=user_section_name), + EndpointConfig(section_name=endpoint_section_name)).entity return access_data.token.id_ + + +def authenticate_v3( + url, username=None, password=None, user_id=None, domain_id=None, + domain_name=None, token=None): + + url = '{0}/v3/auth/tokens'.format(url) + client = AutoMarshallingHTTPClient("json", "json") + client.default_headers["Content-Type"] = "application/json" + client.default_headers["Accept"] = "application/json" + + if user_id is not None: + domain = None + username = None + else: + domain = v3.Domain(name=domain_name, id_=domain_id) + password = v3.Password(user=v3.User( + name=username, password=password, id_=user_id, domain=domain)) + + if token is not None: + kwargs = {"token": v3.Token(id_=token), "methods": ["token"]} + else: + kwargs = {"password": password, "methods": ["password"]} + request_entity = v3.Auth(identity=v3.Identity(**kwargs)) + + r = client.request( + "POST", url, request_entity=request_entity) + + if not r.ok: + raise Exception("Failed to authenticate") + + return r + + +def authenticate_v3_config(user_config, endpoint_config): + return authenticate_v3( + url=user_config.endpoint or endpoint_config.endpoint, + username=user_config.username, + password=user_config.password, + user_id=user_config.user_id, + domain_id=user_config.domain_id, + domain_name=user_config.domain_name, + token=user_config.token) + + +def get_token_v3(user_section_name=None, endpoint_section_name=None): + r = authenticate_v3_config( + UserConfig(section_name=user_section_name), + EndpointConfig(section_name=endpoint_section_name)) + return r.headers.get("X-Subject-Token") diff --git a/syntribos/extensions/identity/auth_config.py b/syntribos/extensions/identity/config.py similarity index 85% rename from syntribos/extensions/identity/auth_config.py rename to syntribos/extensions/identity/config.py index 12601d3b..0be9acd8 100644 --- a/syntribos/extensions/identity/auth_config.py +++ b/syntribos/extensions/identity/config.py @@ -17,7 +17,7 @@ limitations under the License. from cafe.engine.models.data_interfaces import ConfigSectionInterface -class UserAuthConfig(ConfigSectionInterface): +class EndpointConfig(ConfigSectionInterface): SECTION_NAME = 'auth' @property @@ -44,6 +44,10 @@ class UserConfig(ConfigSectionInterface): def password(self): return self.get_raw("password") + @property + def user_id(self): + return self.get("user_id") + @property def tenant_id(self): return self.get("tenant_id") @@ -62,3 +66,11 @@ class UserConfig(ConfigSectionInterface): example the admin user needs to use an internal endpoint. """ return self.get("endpoint") + + @property + def domain_id(self): + return self.get("domain_id") + + @property + def domain_name(self): + return self.get("domain_name") diff --git a/syntribos/extensions/identity/models/auth_models.py b/syntribos/extensions/identity/models/auth_models.py deleted file mode 100644 index cc144f44..00000000 --- a/syntribos/extensions/identity/models/auth_models.py +++ /dev/null @@ -1,424 +0,0 @@ -""" -Copyright 2015 Rackspace - -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. -""" - -import json -from xml.etree import ElementTree as ET -from cafe.engine.models.base import ( - AutoMarshallingModel, AutoMarshallingListModel) - - -class V2_0Constants(object): - XML_NS = 'http://docs.openstack.org/identity/api/v2.0' - XML_NS_OPENSTACK_COMMON = 'http://docs.openstack.org/common/api/v1.0' - XML_NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance' - XML_NS_OS_KSADM = \ - 'http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0' - XML_NS_OS_KSEC2 = \ - 'http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0' - XML_NS_ATOM = 'http://www.w3.org/2005/Atom' - - -class BaseIdentityModel(AutoMarshallingModel): - _namespaces = V2_0Constants - - def __init__(self, kwargs): - super(BaseIdentityModel, self).__init__() - for var in kwargs: - if var != "self" and not var.startswith("_"): - setattr(self, var, kwargs.get(var)) - - @classmethod - def _remove_xml_namespaces(cls, element): - for key, value in cls._namespaces.__dict__.items(): - if key.startswith("__"): - continue - element = cls._remove_namespace(element, value) - return element - - @classmethod - def _remove_namespace(cls, element, XML_NS): - return cls._remove_xml_etree_namespace(element, XML_NS) - - @classmethod - def _json_to_obj(cls, serialized_str): - data_dict = json.loads(serialized_str) - return cls._dict_to_obj(data_dict) - - @classmethod - def _xml_to_obj(cls, serialized_str): - element = ET.fromstring(serialized_str) - return cls._xml_ele_to_obj( - cls._remove_xml_namespaces(element)) - - def _obj_to_json(self): - return json.dumps(self._obj_to_dict()) - - def _obj_to_xml(self): - element = self._obj_to_xml_ele() - return ET.tostring(element) - - @staticmethod - def _find(element, tag): - if element is None: - return ET.Element(None) - new_element = element.find(tag) - if new_element is None: - return ET.Element(None) - return new_element - - -class BaseIdentityListModel(AutoMarshallingListModel, BaseIdentityModel): - pass - - -class EmptyModel(object): - def _obj_to_dict(self): - return None - - def _obj_to_xml_ele(self): - return ET.Element(None) - - -class AuthResponse(BaseIdentityModel): - def __init__( - self, token=None, service_catalog=None, user=None, metadata=None): - super(AuthResponse, self).__init__(locals()) - - @classmethod - def _dict_to_obj(cls, data): - return cls( - token=AuthResponseToken._dict_to_obj(data.get('token')), - metadata=Metadata._dict_to_obj(data.get('metadata')), - user=User._dict_to_obj(data.get('user')), - service_catalog=ServiceCatalog._dict_to_obj( - data.get('serviceCatalog'))) - - @classmethod - def _json_to_obj(cls, serialized_str): - data_dict = json.loads(serialized_str) - return cls._dict_to_obj(data_dict.get("access")) - - @classmethod - def _xml_ele_to_obj(cls, ele): - return cls( - token=AuthResponseToken._xml_ele_to_obj(ele.find('token')), - metadata=Metadata._xml_ele_to_obj(ele.find('metadata')), - user=User._xml_ele_to_obj(ele.find('user')), - service_catalog=ServiceCatalog._xml_ele_to_obj( - ele.find('serviceCatalog'))) - - def get_service(self, name): - for service in self.service_catalog: - if service.name == name: - return service - return None - - -class Metadata(BaseIdentityModel): - - @classmethod - def _dict_to_obj(cls, data): - return data - - @classmethod - def _xml_ele_to_obj(cls, ele): - return ele.attrib - - -class Tenant(BaseIdentityModel): - def __init__(self, enabled=None, description=None, name=None, id_=None): - super(Tenant, self).__init__(locals()) - - @classmethod - def _xml_ele_to_obj(cls, element): - if element.tag.lower() != "tenant": - raise Exception("wrong element") - enabled = True if element.attrib.get('enabled') == "true" else False - description = element.find('description') - description = "" if description is None else description.text - return cls(enabled=enabled, - description=description, - name=element.attrib.get('name'), - id_=element.attrib.get('id')) - - @classmethod - def _dict_to_obj(cls, data_dict): - return cls(description=data_dict.get('description'), - enabled=data_dict.get('enabled'), - id_=data_dict.get('id'), - name=data_dict.get('name')) - - @classmethod - def _json_to_obj(cls, serialized_str): - data_dict = json.loads(serialized_str) - return cls._dict_to_obj(data_dict.get('tenant')) - - -class AuthResponseToken(BaseIdentityModel): - def __init__(self, id_=None, issued_at=None, expires=None, tenant=None): - super(AuthResponseToken, self).__init__(locals()) - - @classmethod - def _dict_to_obj(cls, data): - return cls(id_=data.get('id'), - expires=data.get('expires'), - issued_at=data.get('issued_at'), - tenant=Tenant._dict_to_obj(data.get('tenant'))) - - @classmethod - def _xml_ele_to_obj(cls, ele): - return cls(id_=ele.attrib.get('id'), - expires=ele.attrib.get('expires'), - issued_at=ele.attrib.get('issued_at'), - tenant=Tenant._xml_ele_to_obj(ele.find('tenant'))) - - -class ServiceCatalog(BaseIdentityListModel): - - @classmethod - def _dict_to_obj(cls, data): - return ServiceCatalog( - [Service._dict_to_obj(service) for service in data]) - - @classmethod - def _xml_ele_to_obj(cls, element): - return ServiceCatalog( - [Service._xml_ele_to_obj(service) for service in element]) - - -class User(BaseIdentityModel): - def __init__( - self, id_=None, name=None, username=None, roles=None, - roles_links=None): - super(User, self).__init__(locals()) - - @classmethod - def _dict_to_obj(cls, data): - return cls( - id_=data.get('id'), - name=data.get('name'), - username=data.get('username'), - roles=Roles._dict_to_obj(data.get('roles')), - roles_links=RolesLinks._dict_to_obj(data.get('roles_links'))) - - @classmethod - def _xml_ele_to_obj(cls, ele): - return cls( - id_=ele.attrib.get('id'), - name=ele.attrib.get('name'), - username=ele.attrib.get('username'), - roles=Roles._xml_ele_to_obj(ele.findall('role')), - roles_links=RolesLinks._xml_ele_to_obj(ele.find('roles_links'))) - - -class Service(BaseIdentityModel): - def __init__( - self, endpoints=None, endpoints_links=None, name=None, type_=None): - super(Service, self).__init__(locals()) - - def get_endpoint(self, region): - """ - Returns the endpoint that matches the provided region, - or None if such an endpoint is not found - """ - for endpoint in self.endpoints: - if getattr(endpoint, 'region'): - if str(endpoint.region).lower() == str(region).lower(): - return endpoint - return None - - @classmethod - def _dict_to_obj(cls, data): - return cls( - endpoints=Endpoints._dict_to_obj(data.get('endpoints')), - endpoints_links=EndpointsLinks._dict_to_obj( - data.get('endpoints_links')), - name=data.get('name'), type_=data.get('type')) - - @classmethod - def _xml_ele_to_obj(cls, ele): - return cls( - endpoints=Endpoints._xml_ele_to_obj(ele.findall("endpoint")), - endpoints_links=EndpointsLinks._xml_ele_to_obj( - ele.find('endpoints_links')), - name=ele.attrib.get('name'), - type_=ele.attrib.get('type')) - - -class Endpoints(BaseIdentityListModel): - @classmethod - def _xml_ele_to_obj(cls, elements): - if not elements: - return cls() - return cls([Endpoint._xml_ele_to_obj(endp) for endp in elements]) - - @classmethod - def _dict_to_obj(cls, data): - if not data: - return cls() - return cls([Endpoint._dict_to_obj(endpoint) for endpoint in data]) - - @classmethod - def _json_to_obj(cls, string): - data = json.loads(string) - return cls._dict_to_obj(data.get("endpoints")) - - -class EndpointsLinks(BaseIdentityListModel): - # always returns an empty list since no documentation on endpoint links - @classmethod - def _dict_to_obj(cls, data): - return EndpointsLinks() - - @classmethod - def _xml_ele_to_obj(cls, ele): - return EndpointsLinks() - - -class Endpoint(BaseIdentityModel): - def __init__( - self, region=None, id_=None, public_url=None, admin_url=None, - internal_url=None, private_url=None, version_id=None, - version_info=None, version_list=None): - super(Endpoint, self).__init__(locals()) - - @classmethod - def _dict_to_obj(cls, data): - return cls(region=data.get('region'), - id_=data.get('Id'), - public_url=data.get('publicURL'), - private_url=data.get('privateURL'), - admin_url=data.get('adminURL'), - internal_url=data.get('internalURL'), - version_id=data.get('versionId'), - version_info=data.get('versionInfo'), - version_list=data.get('versionList')) - - @classmethod - def _xml_ele_to_obj(cls, ele): - return cls(region=ele.attrib.get('region'), - id_=ele.attrib.get('Id'), - public_url=ele.attrib.get('publicURL'), - private_url=ele.attrib.get('privateURL'), - admin_url=ele.attrib.get('adminURL'), - internal_url=ele.attrib.get('internalURL'), - version_id=ele.attrib.get('versionId'), - version_info=ele.attrib.get('versionInfo'), - version_list=ele.attrib.get('versionList')) - - -class Roles(BaseIdentityListModel): - - @classmethod - def _xml_ele_to_obj(cls, elements): - return Roles( - [Role._xml_ele_to_obj(element) for element in elements]) - - @classmethod - def _dict_to_obj(cls, data): - return Roles([Role._dict_to_obj(obj) for obj in data]) - - -class RolesLinks(BaseIdentityListModel): - # always returns an empty list since no documentation on role links - @classmethod - def _dict_to_obj(cls, data): - return RolesLinks() - - @classmethod - def _xml_ele_to_obj(cls, data): - return RolesLinks() - - -class Role(BaseIdentityModel): - def __init__( - self, id_=None, name=None, description=None, - rax_auth_propagate=None, tenant_id=None, service_id=None): - super(Role, self).__init__(locals()) - - @classmethod - def _xml_ele_to_obj(cls, element): - if element is None: - return None - return cls( - id_=element.attrib.get("id"), - name=element.attrib.get("name"), - description=element.attrib.get("description")) - - @classmethod - def _dict_to_obj(cls, data): - if data is None: - return None - return cls( - id_=data.get("id"), - name=data.get("name"), - description=data.get("description")) - - -class Auth(BaseIdentityModel): - - def __init__( - self, username=None, password=None, tenant_name=None, - tenant_id=None, token=None): - super(Auth, self).__init__(locals()) - - def _obj_to_dict(self): - attrs = { - "tenantName": self.tenant_name, - "tenantId": self.tenant_id, - "passwordCredentials": self.password_credentials._obj_to_dict()} - return {'auth': self._remove_empty_values(attrs)} - - def _obj_to_xml_ele(self): - element = ET.Element('auth') - element = self._set_xml_etree_element( - element, {"tenantName": self.tenant_name, - "tenantId": self.tenant_id}) - element.append(self.password_credentials._obj_to_xml_ele()) - return element - - -class PasswordCredentials(BaseIdentityModel): - - def __init__(self, username=None, password=None): - super(PasswordCredentials, self).__init__(locals()) - - def _obj_to_dict(self): - attrs = { - "username": self.username, - "password": self.password} - return self._remove_empty_values(attrs) - - def _obj_to_xml_ele(self): - element = ET.Element('passwordCredentials') - attrs = { - "username": self.username, - "password": self.password} - return self._set_xml_etree_element(element, attrs) - - -class Token(BaseIdentityModel): - def __init__(self, id_=None): - super(Token, self).__init__(locals()) - - def _obj_to_dict(self): - attrs = {"id": self.id_} - return self._remove_empty_values(attrs) - - def _obj_to_xml_ele(self): - element = ET.Element('token') - attrs = {"id": self.id_} - return self._set_xml_etree_element(element, attrs) diff --git a/syntribos/extensions/identity/models/base.py b/syntribos/extensions/identity/models/base.py new file mode 100644 index 00000000..9ab5277f --- /dev/null +++ b/syntribos/extensions/identity/models/base.py @@ -0,0 +1,125 @@ +import json +import six +from xml.etree import ElementTree as ET + +from cafe.engine.models.base import AutoMarshallingModel + + +class Namespaces(object): + XMLNS_XSI = "http://www.w3.org/2001/XMLSchema-instance" + XMLNS = "http://docs.openstack.org/identity/api/v2.0" + XMLNS_KSKEY = "http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0" + XMLNS_RAX_AUTH = "http://docs.rackspace.com/identity/api/ext/RAX-AUTH/v1.0" + + +class BaseIdentityModel(AutoMarshallingModel): + _namespaces = Namespaces + + def __init__(self, kwargs): + super(BaseIdentityModel, self).__init__() + for k, v in kwargs.items(): + if k != "self" and not k.startswith("_"): + setattr(self, k, v) + + @classmethod + def _remove_xml_namespaces(cls, element): + for key, value in vars(cls._namespaces).items(): + if key.startswith("__"): + continue + element = cls._remove_xml_etree_namespace(element, value) + return element + + @classmethod + def _json_to_obj(cls, serialized_str): + data_dict = json.loads(serialized_str, strict=False) + return cls._dict_to_obj(data_dict) + + @classmethod + def _xml_to_obj(cls, serialized_str, encoding="iso-8859-2"): + parser = ET.XMLParser(encoding=encoding) + element = ET.fromstring(serialized_str, parser=parser) + return cls._xml_ele_to_obj(cls._remove_xml_namespaces(element)) + + def _obj_to_json(self): + return json.dumps(self._obj_to_dict()) + + def _obj_to_xml(self): + element = self._obj_to_xml_ele() + element.attrib["xmlns"] = self._namespaces.XMLNS + return ET.tostring(element) + + @staticmethod + def _find(element, tag): + if element is None: + return ET.Element(None) + new_element = element.find(tag) + if new_element is None: + return ET.Element(None) + return new_element + + @staticmethod + def _build_list_model(data, field_name, model): + if data is None: + return [] + if isinstance(data, dict): + if data.get(field_name) is None: + return [] + return [model._dict_to_obj(tmp) for tmp in data.get(field_name)] + return [model._xml_ele_to_obj(tmp) for tmp in data.findall(field_name)] + + @staticmethod + def _build_list(items, element=None): + if element is None: + if items is None: + return [] + return [item._obj_to_dict() for item in items] + else: + if items is None: + return element + for item in items: + element.append(item._obj_to_xml_ele()) + return element + + @staticmethod + def _create_text_element(name, text): + element = ET.Element(name) + if text is True or text is False: + element.text = str(text).lower() + elif text is None: + return ET.Element(None) + else: + element.text = str(text) + return element + + def __ne__(self, obj): + return not self.__eq__(obj) + + @classmethod + def _remove_empty_values(cls, data): + """Returns a new dictionary based on 'dictionary', minus any keys with + values that evaluate to False + """ + if isinstance(data, dict): + return dict( + (k, v) for k, v in six.iteritems(data) if v not in ( + [], {}, None)) + elif isinstance(data, ET.Element): + if data.attrib: + data.attrib = cls._remove_empty_values(data.attrib) + data._children = [ + c for c in data._children if c.tag is not None and ( + c.attrib or c.text is not None or c._children)] + return data + + @staticmethod + def _get_sub_model(model, json=True): + if json: + if model is not None: + return model._obj_to_dict() + else: + return None + else: + if model is not None: + return model._obj_to_xml_ele() + else: + return ET.Element(None) diff --git a/syntribos/extensions/identity/models/v2.py b/syntribos/extensions/identity/models/v2.py new file mode 100644 index 00000000..1473e240 --- /dev/null +++ b/syntribos/extensions/identity/models/v2.py @@ -0,0 +1,237 @@ +""" +Copyright 2015 Rackspace + +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. +""" +import json +from xml.etree import ElementTree as ET + +from syntribos.extensions.identity.models.base import BaseIdentityModel + + +class AuthResponse(BaseIdentityModel): + def __init__( + self, token=None, service_catalog=None, user=None, metadata=None): + super(AuthResponse, self).__init__(locals()) + + @classmethod + def _dict_to_obj(cls, data): + return cls( + token=Token._dict_to_obj(data.get('token')), + metadata=Metadata._dict_to_obj(data.get('metadata')), + user=User._dict_to_obj(data.get('user')), + service_catalog=cls._build_list_model( + data, "serviceCatalog", Service)) + + @classmethod + def _json_to_obj(cls, serialized_str): + data_dict = json.loads(serialized_str) + return cls._dict_to_obj(data_dict.get("access")) + + @classmethod + def _xml_ele_to_obj(cls, data): + return cls( + service_catalog=cls._build_list_model( + cls._find(data, "serviceCatalog"), "service", Service), + token_model=Token._xml_ele_to_obj(cls._find(data, "token")), + user_model=User._xml_ele_to_obj(cls._find(data, "user"))) + + def get_service(self, name): + for service in self.service_catalog: + if service.name == name: + return service + return None + + +class Metadata(BaseIdentityModel): + + @classmethod + def _dict_to_obj(cls, data): + return data + + @classmethod + def _xml_ele_to_obj(cls, data): + return data.attrib + + +class Tenant(BaseIdentityModel): + def __init__(self, enabled=None, description=None, name=None, id_=None): + super(Tenant, self).__init__(locals()) + + @classmethod + def _xml_ele_to_obj(cls, data): + description = data.findtext('description') + return cls( + name=data.attrib.get("name"), + id_=data.attrib.get("id"), + enabled=True if data.attrib.get('enabled') == "true" else False, + description=description) + + @classmethod + def _dict_to_obj(cls, data_dict): + return cls(description=data_dict.get('description'), + enabled=data_dict.get('enabled'), + id_=data_dict.get('id'), + name=data_dict.get('name')) + + +class Token(BaseIdentityModel): + def __init__(self, id_=None, issued_at=None, expires=None, tenant=None): + super(Token, self).__init__(locals()) + + @classmethod + def _dict_to_obj(cls, data): + return cls(id_=data.get('id'), + expires=data.get('expires'), + issued_at=data.get('issued_at'), + tenant=Tenant._dict_to_obj(data.get('tenant'))) + + @classmethod + def _xml_ele_to_obj(cls, data): + return cls(id_=data.attrib.get('id'), + expires=data.attrib.get('expires'), + issued_at=data.attrib.get('issued_at'), + tenant=Tenant._xml_ele_to_obj(data.find('tenant'))) + + +class User(BaseIdentityModel): + def __init__( + self, id_=None, name=None, username=None, roles=None): + super(User, self).__init__(locals()) + + @classmethod + def _dict_to_obj(cls, data): + return cls( + id_=data.get('id'), + name=data.get('name'), + username=data.get('username'), + roles=cls._build_list_model(data, "roles", Role)) + + @classmethod + def _xml_ele_to_obj(cls, data): + return cls( + id_=data.attrib.get('id'), + name=data.attrib.get('name'), + username=data.attrib.get('username'), + roles=cls._build_list_model( + cls._find(data, "roles"), "role", Role)) + + +class Service(BaseIdentityModel): + def __init__( + self, endpoints=None, name=None, type_=None): + super(Service, self).__init__(locals()) + + @classmethod + def _dict_to_obj(cls, data): + return cls( + endpoints=cls._build_list_model(data, "endpoints", Endpoint), + name=data.get("name"), type_=data.get("type")) + + @classmethod + def _xml_ele_to_obj(cls, data): + return cls( + endpoints=cls._build_list_model(data, "endpoint", Endpoint), + name=data.attrib.get("name"), type_=data.attrib.get("type")) + + +class Endpoint(BaseIdentityModel): + def __init__( + self, region=None, id_=None, public_url=None, admin_url=None, + internal_url=None, private_url=None, version_id=None, + version_info=None, version_list=None): + super(Endpoint, self).__init__(locals()) + + @classmethod + def _dict_to_obj(cls, data): + return cls(region=data.get('region'), + id_=data.get('Id'), + public_url=data.get('publicURL'), + private_url=data.get('privateURL'), + admin_url=data.get('adminURL'), + internal_url=data.get('internalURL'), + version_id=data.get('versionId'), + version_info=data.get('versionInfo'), + version_list=data.get('versionList')) + + @classmethod + def _xml_ele_to_obj(cls, ele): + return cls(region=ele.attrib.get('region'), + id_=ele.attrib.get('Id'), + public_url=ele.attrib.get('publicURL'), + private_url=ele.attrib.get('privateURL'), + admin_url=ele.attrib.get('adminURL'), + internal_url=ele.attrib.get('internalURL'), + version_id=ele.attrib.get('versionId'), + version_info=ele.attrib.get('versionInfo'), + version_list=ele.attrib.get('versionList')) + + +class Role(BaseIdentityModel): + def __init__( + self, id_=None, name=None, description=None, + tenant_id=None, service_id=None): + super(Role, self).__init__(locals()) + + @classmethod + def _xml_ele_to_obj(cls, element): + if element is None: + return None + return cls( + id_=element.attrib.get("id"), + name=element.attrib.get("name"), + description=element.attrib.get("description")) + + @classmethod + def _dict_to_obj(cls, data): + if data is None: + return None + return cls( + id_=data.get("id"), + name=data.get("name"), + description=data.get("description")) + + +class Auth(BaseIdentityModel): + def __init__( + self, password_creds=None, tenant_id=None, tenant_name=None): + super(Auth, self).__init__(locals()) + + def _obj_to_dict(self): + dic = {} + dic["passwordCredentials"] = self._get_sub_model( + self.password_creds) + dic["tenantId"] = self.tenant_id + dic["tenantName"] = self.tenant_name + return {"auth": self._remove_empty_values(dic)} + + def _obj_to_xml_ele(self): + ele = ET.Element("auth") + ele.append(self._get_sub_model(self.password_creds, False)) + ele.attrib["tenantId"] = self.tenant_id + return self._remove_empty_values(ele) + + +class PasswordCredentials(BaseIdentityModel): + def __init__(self, username=None, password=None): + super(PasswordCredentials, self).__init__(locals()) + + def _obj_to_dict(self): + dic = {"username": self.username, "password": self.password} + return self._remove_empty_values(dic) + + def _obj_to_xml_ele(self): + ele = ET.Element("passwordCredentials") + ele.attrib["username"] = self.username + ele.attrib["password"] = self.password + return self._remove_empty_values(ele) diff --git a/syntribos/extensions/identity/models/v3.py b/syntribos/extensions/identity/models/v3.py new file mode 100644 index 00000000..533e722d --- /dev/null +++ b/syntribos/extensions/identity/models/v3.py @@ -0,0 +1,98 @@ +""" +Copyright 2015 Rackspace + +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 syntribos.extensions.identity.models.base import BaseIdentityModel + + +class Auth(BaseIdentityModel): + def __init__( + self, identity=None, scope=None): + super(Auth, self).__init__(locals()) + + def _obj_to_dict(self): + return {"auth": self._remove_empty_values({ + "identity": self._get_sub_model(self.identity), + "scope": self._get_sub_model(self.scope)})} + + +class Identity(BaseIdentityModel): + def __init__(self, token=None, password=None, methods=None): + super(Identity, self).__init__(locals()) + + def _obj_to_dict(self): + return self._remove_empty_values({ + "methods": self.methods or [], + "password": self._get_sub_model(self.password), + "token": self._get_sub_model(self.token)}) + + +class Password(BaseIdentityModel): + def __init__(self, user=None): + super(Password, self).__init__(locals()) + + def _obj_to_dict(self): + return self._remove_empty_values({ + "user": self._get_sub_model(self.user)}) + + +class User(BaseIdentityModel): + def __init__(self, id_=None, password=None, name=None, domain=None): + super(User, self).__init__(locals()) + + def _obj_to_dict(self): + return self._remove_empty_values({ + "id": self.id_, + "password": self.password, + "name": self.name, + "domain": self._get_sub_model(self.domain)}) + + +class Token(BaseIdentityModel): + def __init__(self, id_=None): + super(Token, self).__init__(locals()) + + def _obj_to_dict(self): + return self._remove_empty_values({"id": self.id_}) + + +class Scope(BaseIdentityModel): + def __init__(self, project=None, domain=None): + super(Scope, self).__init__(locals()) + + def _obj_to_dict(self): + return self._remove_empty_values({ + "project": self._get_sub_model(self.project), + "domain": self._get_sub_model(self.domain)}) + + +class Domain(BaseIdentityModel): + def __init__(self, name=None, id_=None): + super(Domain, self).__init__(locals()) + + def _obj_to_dict(self): + return self._remove_empty_values({ + "name": self.name, + "id": self.id_}) + + +class Project(Domain): + def __init__(self, name=None, id_=None, domain=None): + super(Project, self).__init__(locals()) + + def _obj_to_dict(self): + return self._remove_empty_values({ + "name": self.name, + "id": self.id_, + "domain": self._get_sub_model(self.domain)})