You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ma...@apache.org on 2019/10/11 20:42:07 UTC

[airavata-custos] branch master updated (841c5cd -> 4cd85e1)

This is an automated email from the ASF dual-hosted git repository.

machristie pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git.


    from 841c5cd  Merge pull request #7 from apache/python-lib
     new dc05791  first draft of the python SDK containing methods related to keycloak authentication and admin operations
     new 0997716  added documentation for custos python SDK
     new 7d3e8c8  added the functionality to load server configuration from ini file
     new f1b591f  adding sample settings file
     new bbda71e  addressing review comments
     new 6b8da39  refactored the name
     new 4844ef6  refactored the code to improve usability
     new baaea62  corrected typos and addressed review comments
     new 4cd85e1  Merge pull request #8 from aarushiibisht/abisht_custos_master

The 24 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 clients/python/README.md                           |   52 +
 .../airavata_custos/admin/iam_admin_client.py      |  150 +
 clients/python/airavata_custos/sample_settings.ini |   10 +
 .../security}/__init__.py                          |    0
 .../airavata_custos/security/client_credentials.py |   68 +
 .../security/keycloak_connectors.py                |  170 +
 clients/python/airavata_custos/settings.py         |   39 +
 clients/python/airavata_custos/utils.py            |   78 +
 clients/python/{tests => custos}/__init__.py       |    0
 .../python/{tests => custos/commons}/__init__.py   |    0
 .../{tests => custos/commons/model}/__init__.py    |    0
 .../custos/commons/model/security/__init__.py      |    1 +
 .../custos/commons/model/security/constants.py     |   14 +
 .../python/custos/commons/model/security/ttypes.py |  104 +
 .../python/{tests => custos/profile}/__init__.py   |    0
 .../{tests => custos/profile/iam}/__init__.py      |    0
 .../profile/iam/admin}/__init__.py                 |    0
 .../profile/iam/admin/services}/__init__.py        |    0
 .../iam/admin/services/cpi/IamAdminServices-remote |  222 ++
 .../iam/admin/services/cpi/IamAdminServices.py     | 3863 ++++++++++++++++++++
 .../profile/iam/admin/services/cpi/__init__.py     |    1 +
 .../profile/iam/admin/services/cpi/constants.py    |   16 +
 .../iam/admin/services/cpi/error/__init__.py       |    1 +
 .../iam/admin/services/cpi/error/constants.py      |   14 +
 .../profile/iam/admin/services/cpi/error/ttypes.py |   85 +
 .../profile/iam/admin/services/cpi/ttypes.py       |   23 +
 .../python/custos/profile/model/User/__init__.py   |    1 +
 .../python/custos/profile/model/User/constants.py  |   16 +
 clients/python/custos/profile/model/User/ttypes.py |  788 ++++
 .../{tests => custos/profile/model}/__init__.py    |    0
 .../python/custos/profile/model/tenant/__init__.py |    1 +
 .../custos/profile/model/tenant/constants.py       |   14 +
 .../python/custos/profile/model/tenant/ttypes.py   |  604 +++
 .../custos/profile/model/workspace/__init__.py     |    1 +
 .../custos/profile/model/workspace/constants.py    |   14 +
 .../custos/profile/model/workspace/ttypes.py       |  345 ++
 clients/python/requirements_dev.txt                |    5 +
 37 files changed, 6700 insertions(+)
 create mode 100644 clients/python/airavata_custos/admin/iam_admin_client.py
 create mode 100644 clients/python/airavata_custos/sample_settings.ini
 copy clients/python/{tests => airavata_custos/security}/__init__.py (100%)
 create mode 100644 clients/python/airavata_custos/security/client_credentials.py
 create mode 100644 clients/python/airavata_custos/security/keycloak_connectors.py
 create mode 100644 clients/python/airavata_custos/settings.py
 create mode 100644 clients/python/airavata_custos/utils.py
 copy clients/python/{tests => custos}/__init__.py (100%)
 copy clients/python/{tests => custos/commons}/__init__.py (100%)
 copy clients/python/{tests => custos/commons/model}/__init__.py (100%)
 create mode 100644 clients/python/custos/commons/model/security/__init__.py
 create mode 100644 clients/python/custos/commons/model/security/constants.py
 create mode 100644 clients/python/custos/commons/model/security/ttypes.py
 copy clients/python/{tests => custos/profile}/__init__.py (100%)
 copy clients/python/{tests => custos/profile/iam}/__init__.py (100%)
 copy clients/python/{tests => custos/profile/iam/admin}/__init__.py (100%)
 copy clients/python/{tests => custos/profile/iam/admin/services}/__init__.py (100%)
 create mode 100644 clients/python/custos/profile/iam/admin/services/cpi/IamAdminServices-remote
 create mode 100644 clients/python/custos/profile/iam/admin/services/cpi/IamAdminServices.py
 create mode 100644 clients/python/custos/profile/iam/admin/services/cpi/__init__.py
 create mode 100644 clients/python/custos/profile/iam/admin/services/cpi/constants.py
 create mode 100644 clients/python/custos/profile/iam/admin/services/cpi/error/__init__.py
 create mode 100644 clients/python/custos/profile/iam/admin/services/cpi/error/constants.py
 create mode 100644 clients/python/custos/profile/iam/admin/services/cpi/error/ttypes.py
 create mode 100644 clients/python/custos/profile/iam/admin/services/cpi/ttypes.py
 create mode 100644 clients/python/custos/profile/model/User/__init__.py
 create mode 100644 clients/python/custos/profile/model/User/constants.py
 create mode 100644 clients/python/custos/profile/model/User/ttypes.py
 copy clients/python/{tests => custos/profile/model}/__init__.py (100%)
 create mode 100644 clients/python/custos/profile/model/tenant/__init__.py
 create mode 100644 clients/python/custos/profile/model/tenant/constants.py
 create mode 100644 clients/python/custos/profile/model/tenant/ttypes.py
 create mode 100644 clients/python/custos/profile/model/workspace/__init__.py
 create mode 100644 clients/python/custos/profile/model/workspace/constants.py
 create mode 100644 clients/python/custos/profile/model/workspace/ttypes.py


[airavata-custos] 17/24: added documentation for custos python SDK

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit 09977165fe06d2de81fa471660914e6fe7d9f4ee
Author: Aarushi <aa...@gmail.com>
AuthorDate: Sat Sep 14 17:50:48 2019 -0400

    added documentation for custos python SDK
---
 clients/python/README.md                           |  10 +-
 .../airavata_custos/admin/iam_admin_client.py      |  65 ++++++++----
 .../airavata_custos/security/client_credentials.py |  69 ++++++++-----
 ...tion_token.py => custos_authorization_token.py} |  13 ++-
 .../security/keycloak_connectors.py                | 113 ++++++++++++++++-----
 .../airavata_custos/security/model/__init__.py     |   0
 .../airavata_custos/security/model/ttypes.py       |  97 ------------------
 clients/python/airavata_custos/settings.py         |   8 +-
 clients/python/airavata_custos/utils.py            |  70 ++++++++++++-
 9 files changed, 258 insertions(+), 187 deletions(-)

diff --git a/clients/python/README.md b/clients/python/README.md
index 46690eb..907817d 100644
--- a/clients/python/README.md
+++ b/clients/python/README.md
@@ -1,8 +1,10 @@
 # Airavata Custos Python SDK
 
-####Create a virtual environment
+Create a virtual environment
+- python3 -m venv venv
 
-####Activate the virtual environment
+Activate the virtual environment
+- source venv/bin/activate
 
-####Install dependencies
-pip install -r requirements_dev.txt
\ No newline at end of file
+Install dependencies
+- pip install -r requirements_dev.txt
\ No newline at end of file
diff --git a/clients/python/airavata_custos/admin/iam_admin_client.py b/clients/python/airavata_custos/admin/iam_admin_client.py
index 6f83b58..ee9c11b 100644
--- a/clients/python/airavata_custos/admin/iam_admin_client.py
+++ b/clients/python/airavata_custos/admin/iam_admin_client.py
@@ -1,3 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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 logging
 from airavata_custos.utils import iamadmin_client_pool
 
@@ -71,41 +88,51 @@ def is_user_exist(authz_token, username):
     :param username: The username of the user
     :return: boolean
     """
-    return iamadmin_client_pool.isUserExist(authz_token, username)
+    try:
+        return iamadmin_client_pool.isUserExist(authz_token, username)
+    except Exception:
+        return None
 
 
 def get_user(authz_token, username):
     """
 
-    :param authz_token:
-    :param username:
-    :return:
+    :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
+    :param username: username of the user
+    :return: object of class UserProfile
     """
-    return iamadmin_client_pool.getUser(authz_token, username)
+    try:
+        return iamadmin_client_pool.getUser(authz_token, username)
+    except Exception:
+        return None
 
 
-def get_users(authz_token, offset, limit, search=None):
+def get_users(authz_token, offset=0, limit=-1, search=None):
     """
 
-    :param authz_token:
-    :param offset:
-    :param limit:
-    :param search:
-    :return:
+    :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
+    :param offset: start index
+    :param limit: end index
+    :param search: search criteria for filtering users
+    :return: list of UserProfile class objects
     """
-    return iamadmin_client_pool.getUsers(authz_token, offset, limit, search)
+    try:
+        return iamadmin_client_pool.getUsers(authz_token, offset, limit, search)
+    except Exception:
+        return None
 
 
 def reset_user_password(authz_token, username, new_password):
     """
 
-    :param authz_token:
-    :param username:
-    :param new_password:
+    :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
+    :param username: username of the user
+    :param new_password: new password for the user
     :return:
     """
-    return iamadmin_client_pool.resetUserPassword(
-        authz_token, username, new_password)
+    try:
+        return iamadmin_client_pool.resetUserPassword(
+            authz_token, username, new_password)
+    except Exception:
+        return None
 
-def set_up_tenant(authz_token, gateway, tenantAdminPasswordCredentials):
-    pass
diff --git a/clients/python/airavata_custos/security/client_credentials.py b/clients/python/airavata_custos/security/client_credentials.py
index 432dc51..a0934d1 100644
--- a/clients/python/airavata_custos/security/client_credentials.py
+++ b/clients/python/airavata_custos/security/client_credentials.py
@@ -18,38 +18,51 @@
 
 class ClientCredentials(object):
     """
-
-    Attributes:
-        client_id:  Client ID, which you get from tenant registration with Keycloak
-        client_secret: Client Secret, which you get from tenant registration with Keycloak
-        username:  Username of the tenant user that needs to be authenticated
-        password:  Password of the tenant user that needs to be authenticated
-        authorization_code_url: URL of the authorization server’s authorization endpoint
-        state:
-        redirect_uri: Redirect URI you registered as callback
-        refresh_token:
-        verify_ssl: Flag to indicate ssl verification is required
-
+    This is the base class for passing parameters required to authenticate with keycloak
     """
-    client_id = None
-    client_secret = None
-    verify_ssl = None
-    authorization_code_url = None
-    state = None
-    redirect_uri = None
-    username = None
-    password = None
-    refresh_token = None
-
-    def __init__(self, client_id, client_secret, verify_ssl=False, authorization_code_url=None, state=None, redirect_uri=None, username=None, password=None, refresh_token=None):
+    def __init__(self, client_id, client_secret):
+        """
+        This is the constructor for ClientCredentials class
+        :param client_id: client identifier received after registering the tenant
+        :param client_secret: client password received after registering the tenant
+        """
         self.client_id = client_id
         self.client_secret = client_secret
-        self.verify_ssl = verify_ssl
-        self.authorization_code_url = authorization_code_url
-        self.state = state
-        self.redirect_uri = redirect_uri
+
+
+class UserCredentials(ClientCredentials):
+    """
+    This class inherits from ClientCredentials class. Used for passing parameters required to authenticate user
+    with keycloak
+    """
+    def __init__(self, client_id, client_secret, username, password):
+        """
+        This is the constructor for UserCredentials class
+        :param client_id: client identifier received after registering the tenant
+        :param client_secret: client password received after registering the tenant
+        :param username: username of the user which needs to be authenticated
+        :param password: password of the user which needs to be authenticated
+        """
+        super().__init__(client_id, client_secret)
         self.username = username
         self.password = password
-        self.refresh_token = refresh_token
 
 
+class AccountCredentials(ClientCredentials):
+    """
+    This class inherits from ClientCredentials class. Used for passing parameters required to authenticate service
+    account with keycloak
+    """
+    def __init__(self, client_id, client_secret, authorization_code_url, state, redirect_uri):
+        """
+        This is the constructor for AccountCredentials class
+        :param client_id: client identifier received after registering the tenant
+        :param client_secret: client password received after registering the tenant
+        :param authorization_code_url: The URL that the user will be redirected back from the keycloak to the client
+        :param state: An state string for CSRF protection.
+        :param redirect_uri: URI for the callback entry point of the client
+        """
+        super().__init__(client_id, client_secret)
+        self.authorization_code_url = authorization_code_url
+        self.state = state
+        self.redirect_uri = redirect_uri
diff --git a/clients/python/airavata_custos/security/authorization_token.py b/clients/python/airavata_custos/security/custos_authorization_token.py
similarity index 73%
rename from clients/python/airavata_custos/security/authorization_token.py
rename to clients/python/airavata_custos/security/custos_authorization_token.py
index cbe4fe2..dd4b531 100644
--- a/clients/python/airavata_custos/security/authorization_token.py
+++ b/clients/python/airavata_custos/security/custos_authorization_token.py
@@ -17,10 +17,18 @@
 from airavata_custos import settings
 from oauthlib.oauth2 import BackendApplicationClient
 from requests_oauthlib import OAuth2Session
-from airavata_custos.security.model.ttypes import AuthzToken
+from custos.commons.model.security.ttypes import AuthzToken
 
 
-def create_authorization_token(client_credentials, tenant_id, username=None):
+def get_authorization_token(client_credentials, tenant_id, username=None):
+    """
+    This method created a authorization token for the user or a service account
+    In case of a service account username will be null
+    :param client_credentials: object of class client_credentials
+    :param tenant_id: gateway id of the client
+    :param username: username of the user for which authorization token is being created
+    :return: AuthzToken
+    """
     client = BackendApplicationClient(client_id=client_credentials.client_id)
     oauth = OAuth2Session(client=client)
     token = oauth.fetch_token(
@@ -32,5 +40,4 @@ def create_authorization_token(client_credentials, tenant_id, username=None):
     access_token = token.get('access_token')
     return AuthzToken(
         accessToken=access_token,
-        # This is a service account, so leaving out userName for now
         claimsMap={'gatewayID': tenant_id, 'userName': username})
diff --git a/clients/python/airavata_custos/security/keycloak_connectors.py b/clients/python/airavata_custos/security/keycloak_connectors.py
index 054c787..d31e5a8 100644
--- a/clients/python/airavata_custos/security/keycloak_connectors.py
+++ b/clients/python/airavata_custos/security/keycloak_connectors.py
@@ -14,6 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+import time
 from oauthlib.oauth2 import LegacyApplicationClient
 from requests_oauthlib import OAuth2Session
 import requests
@@ -21,29 +22,46 @@ from airavata_custos import settings
 
 
 class KeycloakBackend(object):
-    def authenticate(self, client_credentials):
-        """This method authenticates a client with keycloak
 
-        Parameters:
-        client_credentials (client_credentials): This object has client credentials which needs to be authenticated
+    def authenticate_user(self, user_credentials):
+        """
+        Method to authenticate a gateway user with keycloak
+        :param user_credentials: object of UserCredentials class
+        :return: Token object, UserInfo object
+        """
+        try:
+            token, user_info = self._get_token_and_user_info_password_flow(user_credentials)
+            return token, user_info
+        except Exception as e:
+            return None
+
+    def authenticate_account(self, account_credentials):
+        """
+
+        :param account_credentials: object of AccountCredentials class
+        :return: Token object, UserInfo object
+        """
+        try:
+            token, user_info = self._get_token_and_user_info_redirect_flow(account_credentials)
+            return token, user_info
+        except Exception as e:
+            return None
 
-        Returns:
-        String:token
-        String:userInfo
-       """
+    def authenticate_using_refresh_token(self, client_credentials, refresh_token):
+        """
+
+        :param client_credentials: object of ClientCredentials class
+        :param refresh_token: openid connect refresh token
+        :return: Token object
+        """
         try:
-            if client_credentials.username and client_credentials.password:
-                token, userinfo = self._get_token_and_userinfo_password_flow(client_credentials)
-            elif client_credentials.refresh_token:
-                token = self._get_token_from_refresh_token(client_credentials)
-            elif client_credentials.red:
-                token, userinfo = self._get_token_and_userinfo_redirect_flow(client_credentials)
-
-            return token, userinfo
+            token = self._get_token_from_refresh_token(client_credentials, refresh_token)
+            return token
         except Exception as e:
             return None
 
-    def _get_token_and_userinfo_password_flow(self, client_credentials):
+    @classmethod
+    def _get_token_and_user_info_password_flow(cls, client_credentials):
 
         oauth2_session = OAuth2Session(client=LegacyApplicationClient(client_id=client_credentials.client_id))
         token = oauth2_session.fetch_token(token_url=settings.KEYCLOAK_TOKEN_URL,
@@ -51,11 +69,12 @@ class KeycloakBackend(object):
                                            password=client_credentials.password,
                                            client_id=client_credentials.client_id,
                                            client_secret=client_credentials.client_secret,
-                                           verify=client_credentials.verify_ssl)
-        userinfo = oauth2_session.get(settings.KEYCLOAK_USERINFO_URL).json()
-        return token, userinfo
+                                           verify=settings.VERIFY_SSL)
+        user_info = oauth2_session.get(settings.KEYCLOAK_USERINFO_URL).json()
+        return cls._process_token(token), cls._process_userinfo(user_info)
 
-    def _get_token_and_userinfo_redirect_flow(self, client_credentials):
+    @classmethod
+    def _get_token_and_user_info_redirect_flow(cls, client_credentials):
         oauth2_session = OAuth2Session(client_credentials.client_id,
                                        scope='openid',
                                        redirect_uri=client_credentials.redirect_uri,
@@ -63,16 +82,54 @@ class KeycloakBackend(object):
         token = oauth2_session.fetch_token(settings.KEYCLOAK_TOKEN_URL,
                                            client_secret=client_credentials.client_secret,
                                            authorization_response=client_credentials.authorization_code_url,
-                                           verify=client_credentials.verify_ssl)
-        userinfo = oauth2_session.get(settings.KEYCLOAK_USERINFO_URL).json()
-        return token, userinfo
+                                           verify=settings.VERIFY_SSL)
+        user_info = oauth2_session.get(settings.KEYCLOAK_USERINFO_URL).json()
+        return cls._process_token(token), cls._process_userinfo(user_info)
 
-    def _get_token_from_refresh_token(self, client_credentials):
+    @classmethod
+    def _get_token_from_refresh_token(cls, client_credentials, refresh_token):
 
         oauth2_session = OAuth2Session(client_credentials.client_id, scope='openid')
         auth = requests.auth.HTTPBasicAuth(client_credentials.client_id, client_credentials.client_secret)
         token = oauth2_session.refresh_token(token_url=settings.KEYCLOAK_TOKEN_URL,
-                                             refresh_token=client_credentials.refresh_token,
+                                             refresh_token=refresh_token,
                                              auth=auth,
-                                             verify=client_credentials.verify_ssl)
-        return token
\ No newline at end of file
+                                             verify=settings.VERIFY_SSL)
+        return cls._process_token(token)
+
+    @classmethod
+    def _process_token(cls, token):
+
+        now = time.time()
+        access_token = token['access_token']
+        access_token_expires_at = now + token['expires_in']
+        refresh_token = token['refresh_token']
+        refresh_token_expires_at = now + token['refresh_expires_in']
+        return Token(access_token, access_token_expires_at, refresh_token, refresh_token_expires_at)
+
+    @classmethod
+    def _process_userinfo(cls, userinfo):
+
+        username = userinfo['preferred_username']
+        email = userinfo['email']
+        first_name = userinfo['given_name']
+        last_name = userinfo['family_name']
+        return UserInfo(username, email, first_name, last_name)
+
+
+class Token(object):
+
+    def __init__(self, access_token, access_token_expires_at, refresh_token, refresh_token_expires_at):
+        self.access_token = access_token
+        self.access_token_expires_at = access_token_expires_at
+        self.refresh_token = refresh_token
+        self.refresh_token_expires_at = refresh_token_expires_at
+
+
+class UserInfo(object):
+
+    def __init__(self, username, email, first_name, last_name):
+        self.username = username
+        self.email = email
+        self.first_name = first_name
+        self.last_name = last_name
diff --git a/clients/python/airavata_custos/security/model/__init__.py b/clients/python/airavata_custos/security/model/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/clients/python/airavata_custos/security/model/ttypes.py b/clients/python/airavata_custos/security/model/ttypes.py
deleted file mode 100644
index 596f6ed..0000000
--- a/clients/python/airavata_custos/security/model/ttypes.py
+++ /dev/null
@@ -1,97 +0,0 @@
-#
-# Autogenerated by Thrift Compiler (0.10.0)
-#
-# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
-#
-#  options string: py
-#
-
-from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException
-from thrift.protocol.TProtocol import TProtocolException
-import sys
-
-from thrift.transport import TTransport
-
-
-class AuthzToken(object):
-    """
-    Attributes:
-     - accessToken
-     - claimsMap
-    """
-
-    thrift_spec = (
-        None,  # 0
-        (1, TType.STRING, 'accessToken', 'UTF8', None, ),  # 1
-        (2, TType.MAP, 'claimsMap', (TType.STRING, 'UTF8', TType.STRING, 'UTF8', False), None, ),  # 2
-    )
-
-    def __init__(self, accessToken=None, claimsMap=None,):
-        self.accessToken = accessToken
-        self.claimsMap = claimsMap
-
-    def read(self, iprot):
-        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
-            iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec))
-            return
-        iprot.readStructBegin()
-        while True:
-            (fname, ftype, fid) = iprot.readFieldBegin()
-            if ftype == TType.STOP:
-                break
-            if fid == 1:
-                if ftype == TType.STRING:
-                    self.accessToken = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
-                else:
-                    iprot.skip(ftype)
-            elif fid == 2:
-                if ftype == TType.MAP:
-                    self.claimsMap = {}
-                    (_ktype1, _vtype2, _size0) = iprot.readMapBegin()
-                    for _i4 in range(_size0):
-                        _key5 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
-                        _val6 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
-                        self.claimsMap[_key5] = _val6
-                    iprot.readMapEnd()
-                else:
-                    iprot.skip(ftype)
-            else:
-                iprot.skip(ftype)
-            iprot.readFieldEnd()
-        iprot.readStructEnd()
-
-    def write(self, oprot):
-        if oprot._fast_encode is not None and self.thrift_spec is not None:
-            oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec)))
-            return
-        oprot.writeStructBegin('AuthzToken')
-        if self.accessToken is not None:
-            oprot.writeFieldBegin('accessToken', TType.STRING, 1)
-            oprot.writeString(self.accessToken.encode('utf-8') if sys.version_info[0] == 2 else self.accessToken)
-            oprot.writeFieldEnd()
-        if self.claimsMap is not None:
-            oprot.writeFieldBegin('claimsMap', TType.MAP, 2)
-            oprot.writeMapBegin(TType.STRING, TType.STRING, len(self.claimsMap))
-            for kiter7, viter8 in self.claimsMap.items():
-                oprot.writeString(kiter7.encode('utf-8') if sys.version_info[0] == 2 else kiter7)
-                oprot.writeString(viter8.encode('utf-8') if sys.version_info[0] == 2 else viter8)
-            oprot.writeMapEnd()
-            oprot.writeFieldEnd()
-        oprot.writeFieldStop()
-        oprot.writeStructEnd()
-
-    def validate(self):
-        if self.accessToken is None:
-            raise TProtocolException(message='Required field accessToken is unset!')
-        return
-
-    def __repr__(self):
-        L = ['%s=%r' % (key, value)
-             for key, value in self.__dict__.items()]
-        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
-
-    def __eq__(self, other):
-        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
-
-    def __ne__(self, other):
-        return not (self == other)
diff --git a/clients/python/airavata_custos/settings.py b/clients/python/airavata_custos/settings.py
index c36909e..89a05ef 100644
--- a/clients/python/airavata_custos/settings.py
+++ b/clients/python/airavata_custos/settings.py
@@ -26,6 +26,8 @@ KEYCLOAK_LOGOUT_URL = 'https://localhost:8443/auth/realms/default/protocol/openi
 THRIFT_CLIENT_POOL_KEEPALIVE = 5
 
 # Profile Service Configuration
-PROFILE_SERVICE_HOST = ''
-PROFILE_SERVICE_PORT = ''
-PROFILE_SERVICE_SECURE = False
\ No newline at end of file
+PROFILE_SERVICE_HOST = '0.0.0.0'
+PROFILE_SERVICE_PORT = '8081'
+PROFILE_SERVICE_SECURE = False
+
+VERIFY_SSL = False
diff --git a/clients/python/airavata_custos/utils.py b/clients/python/airavata_custos/utils.py
index afad780..17fdf59 100644
--- a/clients/python/airavata_custos/utils.py
+++ b/clients/python/airavata_custos/utils.py
@@ -1,6 +1,70 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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 logging
 import thrift_connector.connection_pool as connection_pool
+from thrift.protocol import TBinaryProtocol
+from thrift.protocol.TMultiplexedProtocol import TMultiplexedProtocol
+from thrift.transport import TSocket, TSSLSocket, TTransport
+
 from airavata_custos import settings
-from custos.service.profile.iam.admin.services.cpi import IamAdminServices, constants
+from custos.profile.iam.admin.services.cpi import IamAdminServices, constants
+
+log = logging.getLogger(__name__)
+
+
+class MultiplexThriftClientMixin:
+    service_name = None
+
+    @classmethod
+    def get_protoco_factory(cls):
+        def factory(transport):
+            protocol = TBinaryProtocol.TBinaryProtocol(transport)
+            multiplex_prot = TMultiplexedProtocol(protocol, cls.service_name)
+            return multiplex_prot
+        return factory
+
+
+class CustomThriftClient(connection_pool.ThriftClient):
+    secure = False
+    validate = False
+
+    @classmethod
+    def get_socket_factory(cls):
+        if not cls.secure:
+            return super().get_socket_factory()
+        else:
+            def factory(host, port):
+                return TSSLSocket.TSSLSocket(host, port, validate=cls.validate)
+            return factory
+
+    def ping(self):
+        try:
+            self.client.getAPIVersion()
+        except Exception as e:
+            log.debug("getAPIVersion failed: {}".format(str(e)))
+            raise
+
+
+class IAMAdminServiceThriftClient(MultiplexThriftClientMixin,
+                                  CustomThriftClient):
+    service_name = constants.IAM_ADMIN_SERVICES_CPI_NAME
+    secure = settings.PROFILE_SERVICE_SECURE
+
 
 iamadmin_client_pool = connection_pool.ClientPool(
     IamAdminServices,
@@ -10,7 +74,3 @@ iamadmin_client_pool = connection_pool.ClientPool(
     keepalive=settings.THRIFT_CLIENT_POOL_KEEPALIVE
 )
 
-class IAMAdminServiceThriftClient(MultiplexThriftClientMixin,
-                                  CustomThriftClient):
-    service_name = constants.IAM_ADMIN_SERVICES_CPI_NAME
-    secure = settings.PROFILE_SERVICE_SECURE
\ No newline at end of file


[airavata-custos] 12/24: Adding license

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit 67dce3fdc8d1b182795bb03c2476341f390d85ad
Author: Dimuthu Wannipurage <di...@gmail.com>
AuthorDate: Thu Aug 22 12:43:56 2019 -0400

    Adding license
---
 LICENSE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 201 insertions(+)

diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f49a4e1
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
\ No newline at end of file


[airavata-custos] 16/24: first draft of the python SDK containing methods related to keycloak authentication and admin operations

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit dc05791a48196135115478083fff208251c0af82
Author: Aarushi <aa...@gmail.com>
AuthorDate: Fri Sep 13 13:24:48 2019 -0400

    first draft of the python SDK containing methods related to keycloak authentication and admin operations
---
 clients/python/README.md                           |    7 +
 .../airavata_custos/admin/iam_admin_client.py      |  111 +
 .../python/airavata_custos/security/__init__.py    |    0
 .../security/authorization_token.py                |   36 +
 .../airavata_custos/security/client_credentials.py |   55 +
 .../security/keycloak_connectors.py                |   78 +
 .../airavata_custos/security/model/__init__.py     |    0
 .../airavata_custos/security/model/ttypes.py       |   97 +
 clients/python/airavata_custos/settings.py         |   31 +
 clients/python/airavata_custos/utils.py            |   16 +
 clients/python/custos/__init__.py                  |    0
 clients/python/custos/commons/__init__.py          |    0
 clients/python/custos/commons/model/__init__.py    |    0
 .../custos/commons/model/security/__init__.py      |    1 +
 .../custos/commons/model/security/constants.py     |   14 +
 .../python/custos/commons/model/security/ttypes.py |  104 +
 clients/python/custos/profile/__init__.py          |    0
 clients/python/custos/profile/iam/__init__.py      |    0
 .../python/custos/profile/iam/admin/__init__.py    |    0
 .../custos/profile/iam/admin/services/__init__.py  |    0
 .../iam/admin/services/cpi/IamAdminServices-remote |  222 ++
 .../iam/admin/services/cpi/IamAdminServices.py     | 3863 ++++++++++++++++++++
 .../profile/iam/admin/services/cpi/__init__.py     |    1 +
 .../profile/iam/admin/services/cpi/constants.py    |   16 +
 .../iam/admin/services/cpi/error/__init__.py       |    1 +
 .../iam/admin/services/cpi/error/constants.py      |   14 +
 .../profile/iam/admin/services/cpi/error/ttypes.py |   85 +
 .../profile/iam/admin/services/cpi/ttypes.py       |   23 +
 .../python/custos/profile/model/User/__init__.py   |    1 +
 .../python/custos/profile/model/User/constants.py  |   16 +
 clients/python/custos/profile/model/User/ttypes.py |  788 ++++
 clients/python/custos/profile/model/__init__.py    |    0
 .../python/custos/profile/model/tenant/__init__.py |    1 +
 .../custos/profile/model/tenant/constants.py       |   14 +
 .../python/custos/profile/model/tenant/ttypes.py   |  604 +++
 .../custos/profile/model/workspace/__init__.py     |    1 +
 .../custos/profile/model/workspace/constants.py    |   14 +
 .../custos/profile/model/workspace/ttypes.py       |  345 ++
 clients/python/requirements_dev.txt                |    5 +
 39 files changed, 6564 insertions(+)

diff --git a/clients/python/README.md b/clients/python/README.md
index 5c4de03..46690eb 100644
--- a/clients/python/README.md
+++ b/clients/python/README.md
@@ -1 +1,8 @@
 # Airavata Custos Python SDK
+
+####Create a virtual environment
+
+####Activate the virtual environment
+
+####Install dependencies
+pip install -r requirements_dev.txt
\ No newline at end of file
diff --git a/clients/python/airavata_custos/admin/iam_admin_client.py b/clients/python/airavata_custos/admin/iam_admin_client.py
new file mode 100644
index 0000000..6f83b58
--- /dev/null
+++ b/clients/python/airavata_custos/admin/iam_admin_client.py
@@ -0,0 +1,111 @@
+import logging
+from airavata_custos.utils import iamadmin_client_pool
+
+logger = logging.getLogger(__name__)
+
+
+def is_username_available(authz_token, username):
+    """
+    This method validates if the username is available or not
+    :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
+    :param username: The username whose availability needs to be verified
+    :return: boolean
+    """
+    return iamadmin_client_pool.isUsernameAvailable(authz_token, username)
+
+
+def register_user(authz_token, username, email_address, first_name, last_name, password):
+    """
+    This method registers the user with the keycloak instance returns true if successful, false if the registration fails
+    :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
+    :param username: The username of the user that needs to be registered
+    :param email_address: The email address of the user that needs to be registered
+    :param first_name: The first name of the user that needs to be registered
+    :param last_name: The last name of the user that needs to be registered
+    :param password: The password of the user that needs to be registered
+    :return: boolean
+    """
+    return iamadmin_client_pool.registerUser(
+        authz_token,
+        username,
+        email_address,
+        first_name,
+        last_name,
+        password)
+
+
+def is_user_enabled(authz_token, username):
+    """
+    Checks the user is enabled/disabled in keycloak. Only the enabled user can login
+    :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
+    :param username: The username of the user
+    :return: boolean
+    """
+    return iamadmin_client_pool.isUserEnabled(authz_token, username)
+
+
+def enable_user(authz_token, username):
+    """
+    The method to enable a disabled user
+    :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
+    :param username: The username of the user
+    :return: Object of UserProfile class, containing user details
+    """
+    return iamadmin_client_pool.enableUser(authz_token, username)
+
+
+def delete_user(authz_token, username):
+    """
+    This method deleted the user from keycloak. Returns true if delete is successful
+    :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
+    :param username: The username of the user
+    :return: boolean
+    """
+    return iamadmin_client_pool.deleteUser(authz_token, username)
+
+
+def is_user_exist(authz_token, username):
+    """
+    This method checks if the user exists in keycloak. Returns true if the user exists otherwise returns false
+    :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
+    :param username: The username of the user
+    :return: boolean
+    """
+    return iamadmin_client_pool.isUserExist(authz_token, username)
+
+
+def get_user(authz_token, username):
+    """
+
+    :param authz_token:
+    :param username:
+    :return:
+    """
+    return iamadmin_client_pool.getUser(authz_token, username)
+
+
+def get_users(authz_token, offset, limit, search=None):
+    """
+
+    :param authz_token:
+    :param offset:
+    :param limit:
+    :param search:
+    :return:
+    """
+    return iamadmin_client_pool.getUsers(authz_token, offset, limit, search)
+
+
+def reset_user_password(authz_token, username, new_password):
+    """
+
+    :param authz_token:
+    :param username:
+    :param new_password:
+    :return:
+    """
+    return iamadmin_client_pool.resetUserPassword(
+        authz_token, username, new_password)
+
+def set_up_tenant(authz_token, gateway, tenantAdminPasswordCredentials):
+    pass
diff --git a/clients/python/airavata_custos/security/__init__.py b/clients/python/airavata_custos/security/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/clients/python/airavata_custos/security/authorization_token.py b/clients/python/airavata_custos/security/authorization_token.py
new file mode 100644
index 0000000..cbe4fe2
--- /dev/null
+++ b/clients/python/airavata_custos/security/authorization_token.py
@@ -0,0 +1,36 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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 airavata_custos import settings
+from oauthlib.oauth2 import BackendApplicationClient
+from requests_oauthlib import OAuth2Session
+from airavata_custos.security.model.ttypes import AuthzToken
+
+
+def create_authorization_token(client_credentials, tenant_id, username=None):
+    client = BackendApplicationClient(client_id=client_credentials.client_id)
+    oauth = OAuth2Session(client=client)
+    token = oauth.fetch_token(
+        token_url=settings.token_url,
+        client_id=client_credentials.client_id,
+        client_secret=client_credentials.client_secret,
+        verify=client_credentials.verify_ssl)
+
+    access_token = token.get('access_token')
+    return AuthzToken(
+        accessToken=access_token,
+        # This is a service account, so leaving out userName for now
+        claimsMap={'gatewayID': tenant_id, 'userName': username})
diff --git a/clients/python/airavata_custos/security/client_credentials.py b/clients/python/airavata_custos/security/client_credentials.py
new file mode 100644
index 0000000..432dc51
--- /dev/null
+++ b/clients/python/airavata_custos/security/client_credentials.py
@@ -0,0 +1,55 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+
+
+class ClientCredentials(object):
+    """
+
+    Attributes:
+        client_id:  Client ID, which you get from tenant registration with Keycloak
+        client_secret: Client Secret, which you get from tenant registration with Keycloak
+        username:  Username of the tenant user that needs to be authenticated
+        password:  Password of the tenant user that needs to be authenticated
+        authorization_code_url: URL of the authorization server’s authorization endpoint
+        state:
+        redirect_uri: Redirect URI you registered as callback
+        refresh_token:
+        verify_ssl: Flag to indicate ssl verification is required
+
+    """
+    client_id = None
+    client_secret = None
+    verify_ssl = None
+    authorization_code_url = None
+    state = None
+    redirect_uri = None
+    username = None
+    password = None
+    refresh_token = None
+
+    def __init__(self, client_id, client_secret, verify_ssl=False, authorization_code_url=None, state=None, redirect_uri=None, username=None, password=None, refresh_token=None):
+        self.client_id = client_id
+        self.client_secret = client_secret
+        self.verify_ssl = verify_ssl
+        self.authorization_code_url = authorization_code_url
+        self.state = state
+        self.redirect_uri = redirect_uri
+        self.username = username
+        self.password = password
+        self.refresh_token = refresh_token
+
+
diff --git a/clients/python/airavata_custos/security/keycloak_connectors.py b/clients/python/airavata_custos/security/keycloak_connectors.py
new file mode 100644
index 0000000..054c787
--- /dev/null
+++ b/clients/python/airavata_custos/security/keycloak_connectors.py
@@ -0,0 +1,78 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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 oauthlib.oauth2 import LegacyApplicationClient
+from requests_oauthlib import OAuth2Session
+import requests
+from airavata_custos import settings
+
+
+class KeycloakBackend(object):
+    def authenticate(self, client_credentials):
+        """This method authenticates a client with keycloak
+
+        Parameters:
+        client_credentials (client_credentials): This object has client credentials which needs to be authenticated
+
+        Returns:
+        String:token
+        String:userInfo
+       """
+        try:
+            if client_credentials.username and client_credentials.password:
+                token, userinfo = self._get_token_and_userinfo_password_flow(client_credentials)
+            elif client_credentials.refresh_token:
+                token = self._get_token_from_refresh_token(client_credentials)
+            elif client_credentials.red:
+                token, userinfo = self._get_token_and_userinfo_redirect_flow(client_credentials)
+
+            return token, userinfo
+        except Exception as e:
+            return None
+
+    def _get_token_and_userinfo_password_flow(self, client_credentials):
+
+        oauth2_session = OAuth2Session(client=LegacyApplicationClient(client_id=client_credentials.client_id))
+        token = oauth2_session.fetch_token(token_url=settings.KEYCLOAK_TOKEN_URL,
+                                           username=client_credentials.username,
+                                           password=client_credentials.password,
+                                           client_id=client_credentials.client_id,
+                                           client_secret=client_credentials.client_secret,
+                                           verify=client_credentials.verify_ssl)
+        userinfo = oauth2_session.get(settings.KEYCLOAK_USERINFO_URL).json()
+        return token, userinfo
+
+    def _get_token_and_userinfo_redirect_flow(self, client_credentials):
+        oauth2_session = OAuth2Session(client_credentials.client_id,
+                                       scope='openid',
+                                       redirect_uri=client_credentials.redirect_uri,
+                                       state=client_credentials.state)
+        token = oauth2_session.fetch_token(settings.KEYCLOAK_TOKEN_URL,
+                                           client_secret=client_credentials.client_secret,
+                                           authorization_response=client_credentials.authorization_code_url,
+                                           verify=client_credentials.verify_ssl)
+        userinfo = oauth2_session.get(settings.KEYCLOAK_USERINFO_URL).json()
+        return token, userinfo
+
+    def _get_token_from_refresh_token(self, client_credentials):
+
+        oauth2_session = OAuth2Session(client_credentials.client_id, scope='openid')
+        auth = requests.auth.HTTPBasicAuth(client_credentials.client_id, client_credentials.client_secret)
+        token = oauth2_session.refresh_token(token_url=settings.KEYCLOAK_TOKEN_URL,
+                                             refresh_token=client_credentials.refresh_token,
+                                             auth=auth,
+                                             verify=client_credentials.verify_ssl)
+        return token
\ No newline at end of file
diff --git a/clients/python/airavata_custos/security/model/__init__.py b/clients/python/airavata_custos/security/model/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/clients/python/airavata_custos/security/model/ttypes.py b/clients/python/airavata_custos/security/model/ttypes.py
new file mode 100644
index 0000000..596f6ed
--- /dev/null
+++ b/clients/python/airavata_custos/security/model/ttypes.py
@@ -0,0 +1,97 @@
+#
+# Autogenerated by Thrift Compiler (0.10.0)
+#
+# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+#
+#  options string: py
+#
+
+from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException
+from thrift.protocol.TProtocol import TProtocolException
+import sys
+
+from thrift.transport import TTransport
+
+
+class AuthzToken(object):
+    """
+    Attributes:
+     - accessToken
+     - claimsMap
+    """
+
+    thrift_spec = (
+        None,  # 0
+        (1, TType.STRING, 'accessToken', 'UTF8', None, ),  # 1
+        (2, TType.MAP, 'claimsMap', (TType.STRING, 'UTF8', TType.STRING, 'UTF8', False), None, ),  # 2
+    )
+
+    def __init__(self, accessToken=None, claimsMap=None,):
+        self.accessToken = accessToken
+        self.claimsMap = claimsMap
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec))
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRING:
+                    self.accessToken = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.MAP:
+                    self.claimsMap = {}
+                    (_ktype1, _vtype2, _size0) = iprot.readMapBegin()
+                    for _i4 in range(_size0):
+                        _key5 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                        _val6 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                        self.claimsMap[_key5] = _val6
+                    iprot.readMapEnd()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec)))
+            return
+        oprot.writeStructBegin('AuthzToken')
+        if self.accessToken is not None:
+            oprot.writeFieldBegin('accessToken', TType.STRING, 1)
+            oprot.writeString(self.accessToken.encode('utf-8') if sys.version_info[0] == 2 else self.accessToken)
+            oprot.writeFieldEnd()
+        if self.claimsMap is not None:
+            oprot.writeFieldBegin('claimsMap', TType.MAP, 2)
+            oprot.writeMapBegin(TType.STRING, TType.STRING, len(self.claimsMap))
+            for kiter7, viter8 in self.claimsMap.items():
+                oprot.writeString(kiter7.encode('utf-8') if sys.version_info[0] == 2 else kiter7)
+                oprot.writeString(viter8.encode('utf-8') if sys.version_info[0] == 2 else viter8)
+            oprot.writeMapEnd()
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.accessToken is None:
+            raise TProtocolException(message='Required field accessToken is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
diff --git a/clients/python/airavata_custos/settings.py b/clients/python/airavata_custos/settings.py
new file mode 100644
index 0000000..c36909e
--- /dev/null
+++ b/clients/python/airavata_custos/settings.py
@@ -0,0 +1,31 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+
+KEYCLOAK_AUTHORIZE_URL = 'https://localhost:8443/auth/realms/default/protocol/openid-connect/auth'
+KEYCLOAK_TOKEN_URL = 'https://localhost:8443/auth/realms/default/protocol/openid-connect/token'
+KEYCLOAK_USERINFO_URL = 'https://localhost:8443/auth/realms/default/protocol/openid-connect/userinfo'
+KEYCLOAK_LOGOUT_URL = 'https://localhost:8443/auth/realms/default/protocol/openid-connect/logout'
+
+# Seconds each connection in the pool is able to stay alive. If open connection
+# has lived longer than this period, it will be closed.
+# (https://github.com/Thriftpy/thrift_connector)
+THRIFT_CLIENT_POOL_KEEPALIVE = 5
+
+# Profile Service Configuration
+PROFILE_SERVICE_HOST = ''
+PROFILE_SERVICE_PORT = ''
+PROFILE_SERVICE_SECURE = False
\ No newline at end of file
diff --git a/clients/python/airavata_custos/utils.py b/clients/python/airavata_custos/utils.py
new file mode 100644
index 0000000..afad780
--- /dev/null
+++ b/clients/python/airavata_custos/utils.py
@@ -0,0 +1,16 @@
+import thrift_connector.connection_pool as connection_pool
+from airavata_custos import settings
+from custos.service.profile.iam.admin.services.cpi import IamAdminServices, constants
+
+iamadmin_client_pool = connection_pool.ClientPool(
+    IamAdminServices,
+    settings.PROFILE_SERVICE_HOST,
+    settings.PROFILE_SERVICE_PORT,
+    connection_class=IAMAdminServiceThriftClient,
+    keepalive=settings.THRIFT_CLIENT_POOL_KEEPALIVE
+)
+
+class IAMAdminServiceThriftClient(MultiplexThriftClientMixin,
+                                  CustomThriftClient):
+    service_name = constants.IAM_ADMIN_SERVICES_CPI_NAME
+    secure = settings.PROFILE_SERVICE_SECURE
\ No newline at end of file
diff --git a/clients/python/custos/__init__.py b/clients/python/custos/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/clients/python/custos/commons/__init__.py b/clients/python/custos/commons/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/clients/python/custos/commons/model/__init__.py b/clients/python/custos/commons/model/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/clients/python/custos/commons/model/security/__init__.py b/clients/python/custos/commons/model/security/__init__.py
new file mode 100644
index 0000000..adefd8e
--- /dev/null
+++ b/clients/python/custos/commons/model/security/__init__.py
@@ -0,0 +1 @@
+__all__ = ['ttypes', 'constants']
diff --git a/clients/python/custos/commons/model/security/constants.py b/clients/python/custos/commons/model/security/constants.py
new file mode 100644
index 0000000..c59352d
--- /dev/null
+++ b/clients/python/custos/commons/model/security/constants.py
@@ -0,0 +1,14 @@
+#
+# Autogenerated by Thrift Compiler (0.12.0)
+#
+# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+#
+#  options string: py
+#
+
+from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException
+from thrift.protocol.TProtocol import TProtocolException
+from thrift.TRecursive import fix_spec
+
+import sys
+from .ttypes import *
diff --git a/clients/python/custos/commons/model/security/ttypes.py b/clients/python/custos/commons/model/security/ttypes.py
new file mode 100644
index 0000000..fe22b60
--- /dev/null
+++ b/clients/python/custos/commons/model/security/ttypes.py
@@ -0,0 +1,104 @@
+#
+# Autogenerated by Thrift Compiler (0.12.0)
+#
+# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+#
+#  options string: py
+#
+
+from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException
+from thrift.protocol.TProtocol import TProtocolException
+from thrift.TRecursive import fix_spec
+
+import sys
+
+from thrift.transport import TTransport
+all_structs = []
+
+
+class AuthzToken(object):
+    """
+    Attributes:
+     - accessToken
+     - claimsMap
+
+    """
+
+
+    def __init__(self, accessToken=None, claimsMap=None,):
+        self.accessToken = accessToken
+        self.claimsMap = claimsMap
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRING:
+                    self.accessToken = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.MAP:
+                    self.claimsMap = {}
+                    (_ktype1, _vtype2, _size0) = iprot.readMapBegin()
+                    for _i4 in range(_size0):
+                        _key5 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                        _val6 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                        self.claimsMap[_key5] = _val6
+                    iprot.readMapEnd()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('AuthzToken')
+        if self.accessToken is not None:
+            oprot.writeFieldBegin('accessToken', TType.STRING, 1)
+            oprot.writeString(self.accessToken.encode('utf-8') if sys.version_info[0] == 2 else self.accessToken)
+            oprot.writeFieldEnd()
+        if self.claimsMap is not None:
+            oprot.writeFieldBegin('claimsMap', TType.MAP, 2)
+            oprot.writeMapBegin(TType.STRING, TType.STRING, len(self.claimsMap))
+            for kiter7, viter8 in self.claimsMap.items():
+                oprot.writeString(kiter7.encode('utf-8') if sys.version_info[0] == 2 else kiter7)
+                oprot.writeString(viter8.encode('utf-8') if sys.version_info[0] == 2 else viter8)
+            oprot.writeMapEnd()
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.accessToken is None:
+            raise TProtocolException(message='Required field accessToken is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(AuthzToken)
+AuthzToken.thrift_spec = (
+    None,  # 0
+    (1, TType.STRING, 'accessToken', 'UTF8', None, ),  # 1
+    (2, TType.MAP, 'claimsMap', (TType.STRING, 'UTF8', TType.STRING, 'UTF8', False), None, ),  # 2
+)
+fix_spec(all_structs)
+del all_structs
diff --git a/clients/python/custos/profile/__init__.py b/clients/python/custos/profile/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/clients/python/custos/profile/iam/__init__.py b/clients/python/custos/profile/iam/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/clients/python/custos/profile/iam/admin/__init__.py b/clients/python/custos/profile/iam/admin/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/clients/python/custos/profile/iam/admin/services/__init__.py b/clients/python/custos/profile/iam/admin/services/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/clients/python/custos/profile/iam/admin/services/cpi/IamAdminServices-remote b/clients/python/custos/profile/iam/admin/services/cpi/IamAdminServices-remote
new file mode 100644
index 0000000..bb6e133
--- /dev/null
+++ b/clients/python/custos/profile/iam/admin/services/cpi/IamAdminServices-remote
@@ -0,0 +1,222 @@
+#!/usr/bin/env python
+#
+# Autogenerated by Thrift Compiler (0.12.0)
+#
+# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+#
+#  options string: py
+#
+
+import sys
+import pprint
+if sys.version_info[0] > 2:
+    from urllib.parse import urlparse
+else:
+    from urlparse import urlparse
+from thrift.transport import TTransport, TSocket, TSSLSocket, THttpClient
+from thrift.protocol.TBinaryProtocol import TBinaryProtocol
+
+from custos.profile.iam.admin.services.cpi import IamAdminServices
+from custos.profile.iam.admin.services.cpi.ttypes import *
+
+if len(sys.argv) <= 1 or sys.argv[1] == '--help':
+    print('')
+    print('Usage: ' + sys.argv[0] + ' [-h host[:port]] [-u url] [-f[ramed]] [-s[sl]] [-novalidate] [-ca_certs certs] [-keyfile keyfile] [-certfile certfile] function [arg1 [arg2...]]')
+    print('')
+    print('Functions:')
+    print('  string getAPIVersion()')
+    print('  Gateway setUpGateway(AuthzToken authzToken, Gateway gateway, PasswordCredential tenantAdminPasswordCredential)')
+    print('  bool isUsernameAvailable(AuthzToken authzToken, string username)')
+    print('  bool registerUser(AuthzToken authzToken, string username, string emailAddress, string firstName, string lastName, string newPassword)')
+    print('  bool deleteUser(AuthzToken authzToken, string username)')
+    print('  UserProfile enableUser(AuthzToken authzToken, string username)')
+    print('  bool isUserEnabled(AuthzToken authzToken, string username)')
+    print('  bool isUserExist(AuthzToken authzToken, string username)')
+    print('  UserProfile getUser(AuthzToken authzToken, string username)')
+    print('   getUsers(AuthzToken authzToken, i32 offset, i32 limit, string search)')
+    print('  bool resetUserPassword(AuthzToken authzToken, string username, string newPassword)')
+    print('   findUsers(AuthzToken authzToken, string email, string userId)')
+    print('  void updateUserProfile(AuthzToken authzToken, UserProfile userDetails)')
+    print('  bool addRoleToUser(AuthzToken authzToken, string username, string roleName, PasswordCredential tenantAdminPasswordCredential)')
+    print('  bool removeRoleFromUser(AuthzToken authzToken, string username, string roleName, PasswordCredential tenantAdminPasswordCredential)')
+    print('   getUsersWithRole(AuthzToken authzToken, string roleName, PasswordCredential tenantAdminPasswordCredential)')
+    print('')
+    sys.exit(0)
+
+pp = pprint.PrettyPrinter(indent=2)
+host = 'localhost'
+port = 9090
+uri = ''
+framed = False
+ssl = False
+validate = True
+ca_certs = None
+keyfile = None
+certfile = None
+http = False
+argi = 1
+
+if sys.argv[argi] == '-h':
+    parts = sys.argv[argi + 1].split(':')
+    host = parts[0]
+    if len(parts) > 1:
+        port = int(parts[1])
+    argi += 2
+
+if sys.argv[argi] == '-u':
+    url = urlparse(sys.argv[argi + 1])
+    parts = url[1].split(':')
+    host = parts[0]
+    if len(parts) > 1:
+        port = int(parts[1])
+    else:
+        port = 80
+    uri = url[2]
+    if url[4]:
+        uri += '?%s' % url[4]
+    http = True
+    argi += 2
+
+if sys.argv[argi] == '-f' or sys.argv[argi] == '-framed':
+    framed = True
+    argi += 1
+
+if sys.argv[argi] == '-s' or sys.argv[argi] == '-ssl':
+    ssl = True
+    argi += 1
+
+if sys.argv[argi] == '-novalidate':
+    validate = False
+    argi += 1
+
+if sys.argv[argi] == '-ca_certs':
+    ca_certs = sys.argv[argi+1]
+    argi += 2
+
+if sys.argv[argi] == '-keyfile':
+    keyfile = sys.argv[argi+1]
+    argi += 2
+
+if sys.argv[argi] == '-certfile':
+    certfile = sys.argv[argi+1]
+    argi += 2
+
+cmd = sys.argv[argi]
+args = sys.argv[argi + 1:]
+
+if http:
+    transport = THttpClient.THttpClient(host, port, uri)
+else:
+    if ssl:
+        socket = TSSLSocket.TSSLSocket(host, port, validate=validate, ca_certs=ca_certs, keyfile=keyfile, certfile=certfile)
+    else:
+        socket = TSocket.TSocket(host, port)
+    if framed:
+        transport = TTransport.TFramedTransport(socket)
+    else:
+        transport = TTransport.TBufferedTransport(socket)
+protocol = TBinaryProtocol(transport)
+client = IamAdminServices.Client(protocol)
+transport.open()
+
+if cmd == 'getAPIVersion':
+    if len(args) != 0:
+        print('getAPIVersion requires 0 args')
+        sys.exit(1)
+    pp.pprint(client.getAPIVersion())
+
+elif cmd == 'setUpGateway':
+    if len(args) != 3:
+        print('setUpGateway requires 3 args')
+        sys.exit(1)
+    pp.pprint(client.setUpGateway(eval(args[0]), eval(args[1]), eval(args[2]),))
+
+elif cmd == 'isUsernameAvailable':
+    if len(args) != 2:
+        print('isUsernameAvailable requires 2 args')
+        sys.exit(1)
+    pp.pprint(client.isUsernameAvailable(eval(args[0]), args[1],))
+
+elif cmd == 'registerUser':
+    if len(args) != 6:
+        print('registerUser requires 6 args')
+        sys.exit(1)
+    pp.pprint(client.registerUser(eval(args[0]), args[1], args[2], args[3], args[4], args[5],))
+
+elif cmd == 'deleteUser':
+    if len(args) != 2:
+        print('deleteUser requires 2 args')
+        sys.exit(1)
+    pp.pprint(client.deleteUser(eval(args[0]), args[1],))
+
+elif cmd == 'enableUser':
+    if len(args) != 2:
+        print('enableUser requires 2 args')
+        sys.exit(1)
+    pp.pprint(client.enableUser(eval(args[0]), args[1],))
+
+elif cmd == 'isUserEnabled':
+    if len(args) != 2:
+        print('isUserEnabled requires 2 args')
+        sys.exit(1)
+    pp.pprint(client.isUserEnabled(eval(args[0]), args[1],))
+
+elif cmd == 'isUserExist':
+    if len(args) != 2:
+        print('isUserExist requires 2 args')
+        sys.exit(1)
+    pp.pprint(client.isUserExist(eval(args[0]), args[1],))
+
+elif cmd == 'getUser':
+    if len(args) != 2:
+        print('getUser requires 2 args')
+        sys.exit(1)
+    pp.pprint(client.getUser(eval(args[0]), args[1],))
+
+elif cmd == 'getUsers':
+    if len(args) != 4:
+        print('getUsers requires 4 args')
+        sys.exit(1)
+    pp.pprint(client.getUsers(eval(args[0]), eval(args[1]), eval(args[2]), args[3],))
+
+elif cmd == 'resetUserPassword':
+    if len(args) != 3:
+        print('resetUserPassword requires 3 args')
+        sys.exit(1)
+    pp.pprint(client.resetUserPassword(eval(args[0]), args[1], args[2],))
+
+elif cmd == 'findUsers':
+    if len(args) != 3:
+        print('findUsers requires 3 args')
+        sys.exit(1)
+    pp.pprint(client.findUsers(eval(args[0]), args[1], args[2],))
+
+elif cmd == 'updateUserProfile':
+    if len(args) != 2:
+        print('updateUserProfile requires 2 args')
+        sys.exit(1)
+    pp.pprint(client.updateUserProfile(eval(args[0]), eval(args[1]),))
+
+elif cmd == 'addRoleToUser':
+    if len(args) != 4:
+        print('addRoleToUser requires 4 args')
+        sys.exit(1)
+    pp.pprint(client.addRoleToUser(eval(args[0]), args[1], args[2], eval(args[3]),))
+
+elif cmd == 'removeRoleFromUser':
+    if len(args) != 4:
+        print('removeRoleFromUser requires 4 args')
+        sys.exit(1)
+    pp.pprint(client.removeRoleFromUser(eval(args[0]), args[1], args[2], eval(args[3]),))
+
+elif cmd == 'getUsersWithRole':
+    if len(args) != 3:
+        print('getUsersWithRole requires 3 args')
+        sys.exit(1)
+    pp.pprint(client.getUsersWithRole(eval(args[0]), args[1], eval(args[2]),))
+
+else:
+    print('Unrecognized method %s' % cmd)
+    sys.exit(1)
+
+transport.close()
diff --git a/clients/python/custos/profile/iam/admin/services/cpi/IamAdminServices.py b/clients/python/custos/profile/iam/admin/services/cpi/IamAdminServices.py
new file mode 100644
index 0000000..ffd3247
--- /dev/null
+++ b/clients/python/custos/profile/iam/admin/services/cpi/IamAdminServices.py
@@ -0,0 +1,3863 @@
+#
+# Autogenerated by Thrift Compiler (0.12.0)
+#
+# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+#
+#  options string: py
+#
+
+from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException
+from thrift.protocol.TProtocol import TProtocolException
+from thrift.TRecursive import fix_spec
+
+import sys
+import logging
+from .ttypes import *
+from thrift.Thrift import TProcessor
+from thrift.transport import TTransport
+all_structs = []
+
+
+class Iface(object):
+    def getAPIVersion(self):
+        pass
+
+    def setUpGateway(self, authzToken, gateway, tenantAdminPasswordCredential):
+        """
+        Parameters:
+         - authzToken
+         - gateway
+         - tenantAdminPasswordCredential
+
+        """
+        pass
+
+    def isUsernameAvailable(self, authzToken, username):
+        """
+        Parameters:
+         - authzToken
+         - username
+
+        """
+        pass
+
+    def registerUser(self, authzToken, username, emailAddress, firstName, lastName, newPassword):
+        """
+        Parameters:
+         - authzToken
+         - username
+         - emailAddress
+         - firstName
+         - lastName
+         - newPassword
+
+        """
+        pass
+
+    def deleteUser(self, authzToken, username):
+        """
+        Parameters:
+         - authzToken
+         - username
+
+        """
+        pass
+
+    def enableUser(self, authzToken, username):
+        """
+        Parameters:
+         - authzToken
+         - username
+
+        """
+        pass
+
+    def isUserEnabled(self, authzToken, username):
+        """
+        Parameters:
+         - authzToken
+         - username
+
+        """
+        pass
+
+    def isUserExist(self, authzToken, username):
+        """
+        Parameters:
+         - authzToken
+         - username
+
+        """
+        pass
+
+    def getUser(self, authzToken, username):
+        """
+        Parameters:
+         - authzToken
+         - username
+
+        """
+        pass
+
+    def getUsers(self, authzToken, offset, limit, search):
+        """
+        Parameters:
+         - authzToken
+         - offset
+         - limit
+         - search
+
+        """
+        pass
+
+    def resetUserPassword(self, authzToken, username, newPassword):
+        """
+        Parameters:
+         - authzToken
+         - username
+         - newPassword
+
+        """
+        pass
+
+    def findUsers(self, authzToken, email, userId):
+        """
+        Parameters:
+         - authzToken
+         - email
+         - userId
+
+        """
+        pass
+
+    def updateUserProfile(self, authzToken, userDetails):
+        """
+        Parameters:
+         - authzToken
+         - userDetails
+
+        """
+        pass
+
+    def addRoleToUser(self, authzToken, username, roleName, tenantAdminPasswordCredential):
+        """
+        Parameters:
+         - authzToken
+         - username
+         - roleName
+         - tenantAdminPasswordCredential
+
+        """
+        pass
+
+    def removeRoleFromUser(self, authzToken, username, roleName, tenantAdminPasswordCredential):
+        """
+        Parameters:
+         - authzToken
+         - username
+         - roleName
+         - tenantAdminPasswordCredential
+
+        """
+        pass
+
+    def getUsersWithRole(self, authzToken, roleName, tenantAdminPasswordCredential):
+        """
+        Parameters:
+         - authzToken
+         - roleName
+         - tenantAdminPasswordCredential
+
+        """
+        pass
+
+
+class Client(Iface):
+    def __init__(self, iprot, oprot=None):
+        self._iprot = self._oprot = iprot
+        if oprot is not None:
+            self._oprot = oprot
+        self._seqid = 0
+
+    def getAPIVersion(self):
+        self.send_getAPIVersion()
+        return self.recv_getAPIVersion()
+
+    def send_getAPIVersion(self):
+        self._oprot.writeMessageBegin('getAPIVersion', TMessageType.CALL, self._seqid)
+        args = getAPIVersion_args()
+        args.write(self._oprot)
+        self._oprot.writeMessageEnd()
+        self._oprot.trans.flush()
+
+    def recv_getAPIVersion(self):
+        iprot = self._iprot
+        (fname, mtype, rseqid) = iprot.readMessageBegin()
+        if mtype == TMessageType.EXCEPTION:
+            x = TApplicationException()
+            x.read(iprot)
+            iprot.readMessageEnd()
+            raise x
+        result = getAPIVersion_result()
+        result.read(iprot)
+        iprot.readMessageEnd()
+        if result.success is not None:
+            return result.success
+        if result.Idse is not None:
+            raise result.Idse
+        raise TApplicationException(TApplicationException.MISSING_RESULT, "getAPIVersion failed: unknown result")
+
+    def setUpGateway(self, authzToken, gateway, tenantAdminPasswordCredential):
+        """
+        Parameters:
+         - authzToken
+         - gateway
+         - tenantAdminPasswordCredential
+
+        """
+        self.send_setUpGateway(authzToken, gateway, tenantAdminPasswordCredential)
+        return self.recv_setUpGateway()
+
+    def send_setUpGateway(self, authzToken, gateway, tenantAdminPasswordCredential):
+        self._oprot.writeMessageBegin('setUpGateway', TMessageType.CALL, self._seqid)
+        args = setUpGateway_args()
+        args.authzToken = authzToken
+        args.gateway = gateway
+        args.tenantAdminPasswordCredential = tenantAdminPasswordCredential
+        args.write(self._oprot)
+        self._oprot.writeMessageEnd()
+        self._oprot.trans.flush()
+
+    def recv_setUpGateway(self):
+        iprot = self._iprot
+        (fname, mtype, rseqid) = iprot.readMessageBegin()
+        if mtype == TMessageType.EXCEPTION:
+            x = TApplicationException()
+            x.read(iprot)
+            iprot.readMessageEnd()
+            raise x
+        result = setUpGateway_result()
+        result.read(iprot)
+        iprot.readMessageEnd()
+        if result.success is not None:
+            return result.success
+        if result.Idse is not None:
+            raise result.Idse
+        raise TApplicationException(TApplicationException.MISSING_RESULT, "setUpGateway failed: unknown result")
+
+    def isUsernameAvailable(self, authzToken, username):
+        """
+        Parameters:
+         - authzToken
+         - username
+
+        """
+        self.send_isUsernameAvailable(authzToken, username)
+        return self.recv_isUsernameAvailable()
+
+    def send_isUsernameAvailable(self, authzToken, username):
+        self._oprot.writeMessageBegin('isUsernameAvailable', TMessageType.CALL, self._seqid)
+        args = isUsernameAvailable_args()
+        args.authzToken = authzToken
+        args.username = username
+        args.write(self._oprot)
+        self._oprot.writeMessageEnd()
+        self._oprot.trans.flush()
+
+    def recv_isUsernameAvailable(self):
+        iprot = self._iprot
+        (fname, mtype, rseqid) = iprot.readMessageBegin()
+        if mtype == TMessageType.EXCEPTION:
+            x = TApplicationException()
+            x.read(iprot)
+            iprot.readMessageEnd()
+            raise x
+        result = isUsernameAvailable_result()
+        result.read(iprot)
+        iprot.readMessageEnd()
+        if result.success is not None:
+            return result.success
+        if result.Idse is not None:
+            raise result.Idse
+        raise TApplicationException(TApplicationException.MISSING_RESULT, "isUsernameAvailable failed: unknown result")
+
+    def registerUser(self, authzToken, username, emailAddress, firstName, lastName, newPassword):
+        """
+        Parameters:
+         - authzToken
+         - username
+         - emailAddress
+         - firstName
+         - lastName
+         - newPassword
+
+        """
+        self.send_registerUser(authzToken, username, emailAddress, firstName, lastName, newPassword)
+        return self.recv_registerUser()
+
+    def send_registerUser(self, authzToken, username, emailAddress, firstName, lastName, newPassword):
+        self._oprot.writeMessageBegin('registerUser', TMessageType.CALL, self._seqid)
+        args = registerUser_args()
+        args.authzToken = authzToken
+        args.username = username
+        args.emailAddress = emailAddress
+        args.firstName = firstName
+        args.lastName = lastName
+        args.newPassword = newPassword
+        args.write(self._oprot)
+        self._oprot.writeMessageEnd()
+        self._oprot.trans.flush()
+
+    def recv_registerUser(self):
+        iprot = self._iprot
+        (fname, mtype, rseqid) = iprot.readMessageBegin()
+        if mtype == TMessageType.EXCEPTION:
+            x = TApplicationException()
+            x.read(iprot)
+            iprot.readMessageEnd()
+            raise x
+        result = registerUser_result()
+        result.read(iprot)
+        iprot.readMessageEnd()
+        if result.success is not None:
+            return result.success
+        if result.Idse is not None:
+            raise result.Idse
+        raise TApplicationException(TApplicationException.MISSING_RESULT, "registerUser failed: unknown result")
+
+    def deleteUser(self, authzToken, username):
+        """
+        Parameters:
+         - authzToken
+         - username
+
+        """
+        self.send_deleteUser(authzToken, username)
+        return self.recv_deleteUser()
+
+    def send_deleteUser(self, authzToken, username):
+        self._oprot.writeMessageBegin('deleteUser', TMessageType.CALL, self._seqid)
+        args = deleteUser_args()
+        args.authzToken = authzToken
+        args.username = username
+        args.write(self._oprot)
+        self._oprot.writeMessageEnd()
+        self._oprot.trans.flush()
+
+    def recv_deleteUser(self):
+        iprot = self._iprot
+        (fname, mtype, rseqid) = iprot.readMessageBegin()
+        if mtype == TMessageType.EXCEPTION:
+            x = TApplicationException()
+            x.read(iprot)
+            iprot.readMessageEnd()
+            raise x
+        result = deleteUser_result()
+        result.read(iprot)
+        iprot.readMessageEnd()
+        if result.success is not None:
+            return result.success
+        if result.Idse is not None:
+            raise result.Idse
+        raise TApplicationException(TApplicationException.MISSING_RESULT, "deleteUser failed: unknown result")
+
+    def enableUser(self, authzToken, username):
+        """
+        Parameters:
+         - authzToken
+         - username
+
+        """
+        self.send_enableUser(authzToken, username)
+        return self.recv_enableUser()
+
+    def send_enableUser(self, authzToken, username):
+        self._oprot.writeMessageBegin('enableUser', TMessageType.CALL, self._seqid)
+        args = enableUser_args()
+        args.authzToken = authzToken
+        args.username = username
+        args.write(self._oprot)
+        self._oprot.writeMessageEnd()
+        self._oprot.trans.flush()
+
+    def recv_enableUser(self):
+        iprot = self._iprot
+        (fname, mtype, rseqid) = iprot.readMessageBegin()
+        if mtype == TMessageType.EXCEPTION:
+            x = TApplicationException()
+            x.read(iprot)
+            iprot.readMessageEnd()
+            raise x
+        result = enableUser_result()
+        result.read(iprot)
+        iprot.readMessageEnd()
+        if result.success is not None:
+            return result.success
+        if result.Idse is not None:
+            raise result.Idse
+        raise TApplicationException(TApplicationException.MISSING_RESULT, "enableUser failed: unknown result")
+
+    def isUserEnabled(self, authzToken, username):
+        """
+        Parameters:
+         - authzToken
+         - username
+
+        """
+        self.send_isUserEnabled(authzToken, username)
+        return self.recv_isUserEnabled()
+
+    def send_isUserEnabled(self, authzToken, username):
+        self._oprot.writeMessageBegin('isUserEnabled', TMessageType.CALL, self._seqid)
+        args = isUserEnabled_args()
+        args.authzToken = authzToken
+        args.username = username
+        args.write(self._oprot)
+        self._oprot.writeMessageEnd()
+        self._oprot.trans.flush()
+
+    def recv_isUserEnabled(self):
+        iprot = self._iprot
+        (fname, mtype, rseqid) = iprot.readMessageBegin()
+        if mtype == TMessageType.EXCEPTION:
+            x = TApplicationException()
+            x.read(iprot)
+            iprot.readMessageEnd()
+            raise x
+        result = isUserEnabled_result()
+        result.read(iprot)
+        iprot.readMessageEnd()
+        if result.success is not None:
+            return result.success
+        if result.Idse is not None:
+            raise result.Idse
+        raise TApplicationException(TApplicationException.MISSING_RESULT, "isUserEnabled failed: unknown result")
+
+    def isUserExist(self, authzToken, username):
+        """
+        Parameters:
+         - authzToken
+         - username
+
+        """
+        self.send_isUserExist(authzToken, username)
+        return self.recv_isUserExist()
+
+    def send_isUserExist(self, authzToken, username):
+        self._oprot.writeMessageBegin('isUserExist', TMessageType.CALL, self._seqid)
+        args = isUserExist_args()
+        args.authzToken = authzToken
+        args.username = username
+        args.write(self._oprot)
+        self._oprot.writeMessageEnd()
+        self._oprot.trans.flush()
+
+    def recv_isUserExist(self):
+        iprot = self._iprot
+        (fname, mtype, rseqid) = iprot.readMessageBegin()
+        if mtype == TMessageType.EXCEPTION:
+            x = TApplicationException()
+            x.read(iprot)
+            iprot.readMessageEnd()
+            raise x
+        result = isUserExist_result()
+        result.read(iprot)
+        iprot.readMessageEnd()
+        if result.success is not None:
+            return result.success
+        if result.Idse is not None:
+            raise result.Idse
+        raise TApplicationException(TApplicationException.MISSING_RESULT, "isUserExist failed: unknown result")
+
+    def getUser(self, authzToken, username):
+        """
+        Parameters:
+         - authzToken
+         - username
+
+        """
+        self.send_getUser(authzToken, username)
+        return self.recv_getUser()
+
+    def send_getUser(self, authzToken, username):
+        self._oprot.writeMessageBegin('getUser', TMessageType.CALL, self._seqid)
+        args = getUser_args()
+        args.authzToken = authzToken
+        args.username = username
+        args.write(self._oprot)
+        self._oprot.writeMessageEnd()
+        self._oprot.trans.flush()
+
+    def recv_getUser(self):
+        iprot = self._iprot
+        (fname, mtype, rseqid) = iprot.readMessageBegin()
+        if mtype == TMessageType.EXCEPTION:
+            x = TApplicationException()
+            x.read(iprot)
+            iprot.readMessageEnd()
+            raise x
+        result = getUser_result()
+        result.read(iprot)
+        iprot.readMessageEnd()
+        if result.success is not None:
+            return result.success
+        if result.Idse is not None:
+            raise result.Idse
+        raise TApplicationException(TApplicationException.MISSING_RESULT, "getUser failed: unknown result")
+
+    def getUsers(self, authzToken, offset, limit, search):
+        """
+        Parameters:
+         - authzToken
+         - offset
+         - limit
+         - search
+
+        """
+        self.send_getUsers(authzToken, offset, limit, search)
+        return self.recv_getUsers()
+
+    def send_getUsers(self, authzToken, offset, limit, search):
+        self._oprot.writeMessageBegin('getUsers', TMessageType.CALL, self._seqid)
+        args = getUsers_args()
+        args.authzToken = authzToken
+        args.offset = offset
+        args.limit = limit
+        args.search = search
+        args.write(self._oprot)
+        self._oprot.writeMessageEnd()
+        self._oprot.trans.flush()
+
+    def recv_getUsers(self):
+        iprot = self._iprot
+        (fname, mtype, rseqid) = iprot.readMessageBegin()
+        if mtype == TMessageType.EXCEPTION:
+            x = TApplicationException()
+            x.read(iprot)
+            iprot.readMessageEnd()
+            raise x
+        result = getUsers_result()
+        result.read(iprot)
+        iprot.readMessageEnd()
+        if result.success is not None:
+            return result.success
+        if result.Idse is not None:
+            raise result.Idse
+        raise TApplicationException(TApplicationException.MISSING_RESULT, "getUsers failed: unknown result")
+
+    def resetUserPassword(self, authzToken, username, newPassword):
+        """
+        Parameters:
+         - authzToken
+         - username
+         - newPassword
+
+        """
+        self.send_resetUserPassword(authzToken, username, newPassword)
+        return self.recv_resetUserPassword()
+
+    def send_resetUserPassword(self, authzToken, username, newPassword):
+        self._oprot.writeMessageBegin('resetUserPassword', TMessageType.CALL, self._seqid)
+        args = resetUserPassword_args()
+        args.authzToken = authzToken
+        args.username = username
+        args.newPassword = newPassword
+        args.write(self._oprot)
+        self._oprot.writeMessageEnd()
+        self._oprot.trans.flush()
+
+    def recv_resetUserPassword(self):
+        iprot = self._iprot
+        (fname, mtype, rseqid) = iprot.readMessageBegin()
+        if mtype == TMessageType.EXCEPTION:
+            x = TApplicationException()
+            x.read(iprot)
+            iprot.readMessageEnd()
+            raise x
+        result = resetUserPassword_result()
+        result.read(iprot)
+        iprot.readMessageEnd()
+        if result.success is not None:
+            return result.success
+        if result.Idse is not None:
+            raise result.Idse
+        raise TApplicationException(TApplicationException.MISSING_RESULT, "resetUserPassword failed: unknown result")
+
+    def findUsers(self, authzToken, email, userId):
+        """
+        Parameters:
+         - authzToken
+         - email
+         - userId
+
+        """
+        self.send_findUsers(authzToken, email, userId)
+        return self.recv_findUsers()
+
+    def send_findUsers(self, authzToken, email, userId):
+        self._oprot.writeMessageBegin('findUsers', TMessageType.CALL, self._seqid)
+        args = findUsers_args()
+        args.authzToken = authzToken
+        args.email = email
+        args.userId = userId
+        args.write(self._oprot)
+        self._oprot.writeMessageEnd()
+        self._oprot.trans.flush()
+
+    def recv_findUsers(self):
+        iprot = self._iprot
+        (fname, mtype, rseqid) = iprot.readMessageBegin()
+        if mtype == TMessageType.EXCEPTION:
+            x = TApplicationException()
+            x.read(iprot)
+            iprot.readMessageEnd()
+            raise x
+        result = findUsers_result()
+        result.read(iprot)
+        iprot.readMessageEnd()
+        if result.success is not None:
+            return result.success
+        if result.Idse is not None:
+            raise result.Idse
+        raise TApplicationException(TApplicationException.MISSING_RESULT, "findUsers failed: unknown result")
+
+    def updateUserProfile(self, authzToken, userDetails):
+        """
+        Parameters:
+         - authzToken
+         - userDetails
+
+        """
+        self.send_updateUserProfile(authzToken, userDetails)
+        self.recv_updateUserProfile()
+
+    def send_updateUserProfile(self, authzToken, userDetails):
+        self._oprot.writeMessageBegin('updateUserProfile', TMessageType.CALL, self._seqid)
+        args = updateUserProfile_args()
+        args.authzToken = authzToken
+        args.userDetails = userDetails
+        args.write(self._oprot)
+        self._oprot.writeMessageEnd()
+        self._oprot.trans.flush()
+
+    def recv_updateUserProfile(self):
+        iprot = self._iprot
+        (fname, mtype, rseqid) = iprot.readMessageBegin()
+        if mtype == TMessageType.EXCEPTION:
+            x = TApplicationException()
+            x.read(iprot)
+            iprot.readMessageEnd()
+            raise x
+        result = updateUserProfile_result()
+        result.read(iprot)
+        iprot.readMessageEnd()
+        if result.Idse is not None:
+            raise result.Idse
+        return
+
+    def addRoleToUser(self, authzToken, username, roleName, tenantAdminPasswordCredential):
+        """
+        Parameters:
+         - authzToken
+         - username
+         - roleName
+         - tenantAdminPasswordCredential
+
+        """
+        self.send_addRoleToUser(authzToken, username, roleName, tenantAdminPasswordCredential)
+        return self.recv_addRoleToUser()
+
+    def send_addRoleToUser(self, authzToken, username, roleName, tenantAdminPasswordCredential):
+        self._oprot.writeMessageBegin('addRoleToUser', TMessageType.CALL, self._seqid)
+        args = addRoleToUser_args()
+        args.authzToken = authzToken
+        args.username = username
+        args.roleName = roleName
+        args.tenantAdminPasswordCredential = tenantAdminPasswordCredential
+        args.write(self._oprot)
+        self._oprot.writeMessageEnd()
+        self._oprot.trans.flush()
+
+    def recv_addRoleToUser(self):
+        iprot = self._iprot
+        (fname, mtype, rseqid) = iprot.readMessageBegin()
+        if mtype == TMessageType.EXCEPTION:
+            x = TApplicationException()
+            x.read(iprot)
+            iprot.readMessageEnd()
+            raise x
+        result = addRoleToUser_result()
+        result.read(iprot)
+        iprot.readMessageEnd()
+        if result.success is not None:
+            return result.success
+        if result.Idse is not None:
+            raise result.Idse
+        raise TApplicationException(TApplicationException.MISSING_RESULT, "addRoleToUser failed: unknown result")
+
+    def removeRoleFromUser(self, authzToken, username, roleName, tenantAdminPasswordCredential):
+        """
+        Parameters:
+         - authzToken
+         - username
+         - roleName
+         - tenantAdminPasswordCredential
+
+        """
+        self.send_removeRoleFromUser(authzToken, username, roleName, tenantAdminPasswordCredential)
+        return self.recv_removeRoleFromUser()
+
+    def send_removeRoleFromUser(self, authzToken, username, roleName, tenantAdminPasswordCredential):
+        self._oprot.writeMessageBegin('removeRoleFromUser', TMessageType.CALL, self._seqid)
+        args = removeRoleFromUser_args()
+        args.authzToken = authzToken
+        args.username = username
+        args.roleName = roleName
+        args.tenantAdminPasswordCredential = tenantAdminPasswordCredential
+        args.write(self._oprot)
+        self._oprot.writeMessageEnd()
+        self._oprot.trans.flush()
+
+    def recv_removeRoleFromUser(self):
+        iprot = self._iprot
+        (fname, mtype, rseqid) = iprot.readMessageBegin()
+        if mtype == TMessageType.EXCEPTION:
+            x = TApplicationException()
+            x.read(iprot)
+            iprot.readMessageEnd()
+            raise x
+        result = removeRoleFromUser_result()
+        result.read(iprot)
+        iprot.readMessageEnd()
+        if result.success is not None:
+            return result.success
+        if result.Idse is not None:
+            raise result.Idse
+        raise TApplicationException(TApplicationException.MISSING_RESULT, "removeRoleFromUser failed: unknown result")
+
+    def getUsersWithRole(self, authzToken, roleName, tenantAdminPasswordCredential):
+        """
+        Parameters:
+         - authzToken
+         - roleName
+         - tenantAdminPasswordCredential
+
+        """
+        self.send_getUsersWithRole(authzToken, roleName, tenantAdminPasswordCredential)
+        return self.recv_getUsersWithRole()
+
+    def send_getUsersWithRole(self, authzToken, roleName, tenantAdminPasswordCredential):
+        self._oprot.writeMessageBegin('getUsersWithRole', TMessageType.CALL, self._seqid)
+        args = getUsersWithRole_args()
+        args.authzToken = authzToken
+        args.roleName = roleName
+        args.tenantAdminPasswordCredential = tenantAdminPasswordCredential
+        args.write(self._oprot)
+        self._oprot.writeMessageEnd()
+        self._oprot.trans.flush()
+
+    def recv_getUsersWithRole(self):
+        iprot = self._iprot
+        (fname, mtype, rseqid) = iprot.readMessageBegin()
+        if mtype == TMessageType.EXCEPTION:
+            x = TApplicationException()
+            x.read(iprot)
+            iprot.readMessageEnd()
+            raise x
+        result = getUsersWithRole_result()
+        result.read(iprot)
+        iprot.readMessageEnd()
+        if result.success is not None:
+            return result.success
+        if result.Idse is not None:
+            raise result.Idse
+        raise TApplicationException(TApplicationException.MISSING_RESULT, "getUsersWithRole failed: unknown result")
+
+
+class Processor(Iface, TProcessor):
+    def __init__(self, handler):
+        self._handler = handler
+        self._processMap = {}
+        self._processMap["getAPIVersion"] = Processor.process_getAPIVersion
+        self._processMap["setUpGateway"] = Processor.process_setUpGateway
+        self._processMap["isUsernameAvailable"] = Processor.process_isUsernameAvailable
+        self._processMap["registerUser"] = Processor.process_registerUser
+        self._processMap["deleteUser"] = Processor.process_deleteUser
+        self._processMap["enableUser"] = Processor.process_enableUser
+        self._processMap["isUserEnabled"] = Processor.process_isUserEnabled
+        self._processMap["isUserExist"] = Processor.process_isUserExist
+        self._processMap["getUser"] = Processor.process_getUser
+        self._processMap["getUsers"] = Processor.process_getUsers
+        self._processMap["resetUserPassword"] = Processor.process_resetUserPassword
+        self._processMap["findUsers"] = Processor.process_findUsers
+        self._processMap["updateUserProfile"] = Processor.process_updateUserProfile
+        self._processMap["addRoleToUser"] = Processor.process_addRoleToUser
+        self._processMap["removeRoleFromUser"] = Processor.process_removeRoleFromUser
+        self._processMap["getUsersWithRole"] = Processor.process_getUsersWithRole
+
+    def process(self, iprot, oprot):
+        (name, type, seqid) = iprot.readMessageBegin()
+        if name not in self._processMap:
+            iprot.skip(TType.STRUCT)
+            iprot.readMessageEnd()
+            x = TApplicationException(TApplicationException.UNKNOWN_METHOD, 'Unknown function %s' % (name))
+            oprot.writeMessageBegin(name, TMessageType.EXCEPTION, seqid)
+            x.write(oprot)
+            oprot.writeMessageEnd()
+            oprot.trans.flush()
+            return
+        else:
+            self._processMap[name](self, seqid, iprot, oprot)
+        return True
+
+    def process_getAPIVersion(self, seqid, iprot, oprot):
+        args = getAPIVersion_args()
+        args.read(iprot)
+        iprot.readMessageEnd()
+        result = getAPIVersion_result()
+        try:
+            result.success = self._handler.getAPIVersion()
+            msg_type = TMessageType.REPLY
+        except TTransport.TTransportException:
+            raise
+        except custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException as Idse:
+            msg_type = TMessageType.REPLY
+            result.Idse = Idse
+        except TApplicationException as ex:
+            logging.exception('TApplication exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = ex
+        except Exception:
+            logging.exception('Unexpected exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+        oprot.writeMessageBegin("getAPIVersion", msg_type, seqid)
+        result.write(oprot)
+        oprot.writeMessageEnd()
+        oprot.trans.flush()
+
+    def process_setUpGateway(self, seqid, iprot, oprot):
+        args = setUpGateway_args()
+        args.read(iprot)
+        iprot.readMessageEnd()
+        result = setUpGateway_result()
+        try:
+            result.success = self._handler.setUpGateway(args.authzToken, args.gateway, args.tenantAdminPasswordCredential)
+            msg_type = TMessageType.REPLY
+        except TTransport.TTransportException:
+            raise
+        except custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException as Idse:
+            msg_type = TMessageType.REPLY
+            result.Idse = Idse
+        except TApplicationException as ex:
+            logging.exception('TApplication exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = ex
+        except Exception:
+            logging.exception('Unexpected exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+        oprot.writeMessageBegin("setUpGateway", msg_type, seqid)
+        result.write(oprot)
+        oprot.writeMessageEnd()
+        oprot.trans.flush()
+
+    def process_isUsernameAvailable(self, seqid, iprot, oprot):
+        args = isUsernameAvailable_args()
+        args.read(iprot)
+        iprot.readMessageEnd()
+        result = isUsernameAvailable_result()
+        try:
+            result.success = self._handler.isUsernameAvailable(args.authzToken, args.username)
+            msg_type = TMessageType.REPLY
+        except TTransport.TTransportException:
+            raise
+        except custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException as Idse:
+            msg_type = TMessageType.REPLY
+            result.Idse = Idse
+        except TApplicationException as ex:
+            logging.exception('TApplication exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = ex
+        except Exception:
+            logging.exception('Unexpected exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+        oprot.writeMessageBegin("isUsernameAvailable", msg_type, seqid)
+        result.write(oprot)
+        oprot.writeMessageEnd()
+        oprot.trans.flush()
+
+    def process_registerUser(self, seqid, iprot, oprot):
+        args = registerUser_args()
+        args.read(iprot)
+        iprot.readMessageEnd()
+        result = registerUser_result()
+        try:
+            result.success = self._handler.registerUser(args.authzToken, args.username, args.emailAddress, args.firstName, args.lastName, args.newPassword)
+            msg_type = TMessageType.REPLY
+        except TTransport.TTransportException:
+            raise
+        except custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException as Idse:
+            msg_type = TMessageType.REPLY
+            result.Idse = Idse
+        except TApplicationException as ex:
+            logging.exception('TApplication exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = ex
+        except Exception:
+            logging.exception('Unexpected exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+        oprot.writeMessageBegin("registerUser", msg_type, seqid)
+        result.write(oprot)
+        oprot.writeMessageEnd()
+        oprot.trans.flush()
+
+    def process_deleteUser(self, seqid, iprot, oprot):
+        args = deleteUser_args()
+        args.read(iprot)
+        iprot.readMessageEnd()
+        result = deleteUser_result()
+        try:
+            result.success = self._handler.deleteUser(args.authzToken, args.username)
+            msg_type = TMessageType.REPLY
+        except TTransport.TTransportException:
+            raise
+        except custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException as Idse:
+            msg_type = TMessageType.REPLY
+            result.Idse = Idse
+        except TApplicationException as ex:
+            logging.exception('TApplication exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = ex
+        except Exception:
+            logging.exception('Unexpected exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+        oprot.writeMessageBegin("deleteUser", msg_type, seqid)
+        result.write(oprot)
+        oprot.writeMessageEnd()
+        oprot.trans.flush()
+
+    def process_enableUser(self, seqid, iprot, oprot):
+        args = enableUser_args()
+        args.read(iprot)
+        iprot.readMessageEnd()
+        result = enableUser_result()
+        try:
+            result.success = self._handler.enableUser(args.authzToken, args.username)
+            msg_type = TMessageType.REPLY
+        except TTransport.TTransportException:
+            raise
+        except custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException as Idse:
+            msg_type = TMessageType.REPLY
+            result.Idse = Idse
+        except TApplicationException as ex:
+            logging.exception('TApplication exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = ex
+        except Exception:
+            logging.exception('Unexpected exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+        oprot.writeMessageBegin("enableUser", msg_type, seqid)
+        result.write(oprot)
+        oprot.writeMessageEnd()
+        oprot.trans.flush()
+
+    def process_isUserEnabled(self, seqid, iprot, oprot):
+        args = isUserEnabled_args()
+        args.read(iprot)
+        iprot.readMessageEnd()
+        result = isUserEnabled_result()
+        try:
+            result.success = self._handler.isUserEnabled(args.authzToken, args.username)
+            msg_type = TMessageType.REPLY
+        except TTransport.TTransportException:
+            raise
+        except custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException as Idse:
+            msg_type = TMessageType.REPLY
+            result.Idse = Idse
+        except TApplicationException as ex:
+            logging.exception('TApplication exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = ex
+        except Exception:
+            logging.exception('Unexpected exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+        oprot.writeMessageBegin("isUserEnabled", msg_type, seqid)
+        result.write(oprot)
+        oprot.writeMessageEnd()
+        oprot.trans.flush()
+
+    def process_isUserExist(self, seqid, iprot, oprot):
+        args = isUserExist_args()
+        args.read(iprot)
+        iprot.readMessageEnd()
+        result = isUserExist_result()
+        try:
+            result.success = self._handler.isUserExist(args.authzToken, args.username)
+            msg_type = TMessageType.REPLY
+        except TTransport.TTransportException:
+            raise
+        except custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException as Idse:
+            msg_type = TMessageType.REPLY
+            result.Idse = Idse
+        except TApplicationException as ex:
+            logging.exception('TApplication exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = ex
+        except Exception:
+            logging.exception('Unexpected exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+        oprot.writeMessageBegin("isUserExist", msg_type, seqid)
+        result.write(oprot)
+        oprot.writeMessageEnd()
+        oprot.trans.flush()
+
+    def process_getUser(self, seqid, iprot, oprot):
+        args = getUser_args()
+        args.read(iprot)
+        iprot.readMessageEnd()
+        result = getUser_result()
+        try:
+            result.success = self._handler.getUser(args.authzToken, args.username)
+            msg_type = TMessageType.REPLY
+        except TTransport.TTransportException:
+            raise
+        except custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException as Idse:
+            msg_type = TMessageType.REPLY
+            result.Idse = Idse
+        except TApplicationException as ex:
+            logging.exception('TApplication exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = ex
+        except Exception:
+            logging.exception('Unexpected exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+        oprot.writeMessageBegin("getUser", msg_type, seqid)
+        result.write(oprot)
+        oprot.writeMessageEnd()
+        oprot.trans.flush()
+
+    def process_getUsers(self, seqid, iprot, oprot):
+        args = getUsers_args()
+        args.read(iprot)
+        iprot.readMessageEnd()
+        result = getUsers_result()
+        try:
+            result.success = self._handler.getUsers(args.authzToken, args.offset, args.limit, args.search)
+            msg_type = TMessageType.REPLY
+        except TTransport.TTransportException:
+            raise
+        except custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException as Idse:
+            msg_type = TMessageType.REPLY
+            result.Idse = Idse
+        except TApplicationException as ex:
+            logging.exception('TApplication exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = ex
+        except Exception:
+            logging.exception('Unexpected exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+        oprot.writeMessageBegin("getUsers", msg_type, seqid)
+        result.write(oprot)
+        oprot.writeMessageEnd()
+        oprot.trans.flush()
+
+    def process_resetUserPassword(self, seqid, iprot, oprot):
+        args = resetUserPassword_args()
+        args.read(iprot)
+        iprot.readMessageEnd()
+        result = resetUserPassword_result()
+        try:
+            result.success = self._handler.resetUserPassword(args.authzToken, args.username, args.newPassword)
+            msg_type = TMessageType.REPLY
+        except TTransport.TTransportException:
+            raise
+        except custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException as Idse:
+            msg_type = TMessageType.REPLY
+            result.Idse = Idse
+        except TApplicationException as ex:
+            logging.exception('TApplication exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = ex
+        except Exception:
+            logging.exception('Unexpected exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+        oprot.writeMessageBegin("resetUserPassword", msg_type, seqid)
+        result.write(oprot)
+        oprot.writeMessageEnd()
+        oprot.trans.flush()
+
+    def process_findUsers(self, seqid, iprot, oprot):
+        args = findUsers_args()
+        args.read(iprot)
+        iprot.readMessageEnd()
+        result = findUsers_result()
+        try:
+            result.success = self._handler.findUsers(args.authzToken, args.email, args.userId)
+            msg_type = TMessageType.REPLY
+        except TTransport.TTransportException:
+            raise
+        except custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException as Idse:
+            msg_type = TMessageType.REPLY
+            result.Idse = Idse
+        except TApplicationException as ex:
+            logging.exception('TApplication exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = ex
+        except Exception:
+            logging.exception('Unexpected exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+        oprot.writeMessageBegin("findUsers", msg_type, seqid)
+        result.write(oprot)
+        oprot.writeMessageEnd()
+        oprot.trans.flush()
+
+    def process_updateUserProfile(self, seqid, iprot, oprot):
+        args = updateUserProfile_args()
+        args.read(iprot)
+        iprot.readMessageEnd()
+        result = updateUserProfile_result()
+        try:
+            self._handler.updateUserProfile(args.authzToken, args.userDetails)
+            msg_type = TMessageType.REPLY
+        except TTransport.TTransportException:
+            raise
+        except custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException as Idse:
+            msg_type = TMessageType.REPLY
+            result.Idse = Idse
+        except TApplicationException as ex:
+            logging.exception('TApplication exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = ex
+        except Exception:
+            logging.exception('Unexpected exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+        oprot.writeMessageBegin("updateUserProfile", msg_type, seqid)
+        result.write(oprot)
+        oprot.writeMessageEnd()
+        oprot.trans.flush()
+
+    def process_addRoleToUser(self, seqid, iprot, oprot):
+        args = addRoleToUser_args()
+        args.read(iprot)
+        iprot.readMessageEnd()
+        result = addRoleToUser_result()
+        try:
+            result.success = self._handler.addRoleToUser(args.authzToken, args.username, args.roleName, args.tenantAdminPasswordCredential)
+            msg_type = TMessageType.REPLY
+        except TTransport.TTransportException:
+            raise
+        except custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException as Idse:
+            msg_type = TMessageType.REPLY
+            result.Idse = Idse
+        except TApplicationException as ex:
+            logging.exception('TApplication exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = ex
+        except Exception:
+            logging.exception('Unexpected exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+        oprot.writeMessageBegin("addRoleToUser", msg_type, seqid)
+        result.write(oprot)
+        oprot.writeMessageEnd()
+        oprot.trans.flush()
+
+    def process_removeRoleFromUser(self, seqid, iprot, oprot):
+        args = removeRoleFromUser_args()
+        args.read(iprot)
+        iprot.readMessageEnd()
+        result = removeRoleFromUser_result()
+        try:
+            result.success = self._handler.removeRoleFromUser(args.authzToken, args.username, args.roleName, args.tenantAdminPasswordCredential)
+            msg_type = TMessageType.REPLY
+        except TTransport.TTransportException:
+            raise
+        except custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException as Idse:
+            msg_type = TMessageType.REPLY
+            result.Idse = Idse
+        except TApplicationException as ex:
+            logging.exception('TApplication exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = ex
+        except Exception:
+            logging.exception('Unexpected exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+        oprot.writeMessageBegin("removeRoleFromUser", msg_type, seqid)
+        result.write(oprot)
+        oprot.writeMessageEnd()
+        oprot.trans.flush()
+
+    def process_getUsersWithRole(self, seqid, iprot, oprot):
+        args = getUsersWithRole_args()
+        args.read(iprot)
+        iprot.readMessageEnd()
+        result = getUsersWithRole_result()
+        try:
+            result.success = self._handler.getUsersWithRole(args.authzToken, args.roleName, args.tenantAdminPasswordCredential)
+            msg_type = TMessageType.REPLY
+        except TTransport.TTransportException:
+            raise
+        except custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException as Idse:
+            msg_type = TMessageType.REPLY
+            result.Idse = Idse
+        except TApplicationException as ex:
+            logging.exception('TApplication exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = ex
+        except Exception:
+            logging.exception('Unexpected exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+        oprot.writeMessageBegin("getUsersWithRole", msg_type, seqid)
+        result.write(oprot)
+        oprot.writeMessageEnd()
+        oprot.trans.flush()
+
+# HELPER FUNCTIONS AND STRUCTURES
+
+
+class getAPIVersion_args(object):
+
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('getAPIVersion_args')
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(getAPIVersion_args)
+getAPIVersion_args.thrift_spec = (
+)
+
+
+class getAPIVersion_result(object):
+    """
+    Attributes:
+     - success
+     - Idse
+
+    """
+
+
+    def __init__(self, success=None, Idse=None,):
+        self.success = success
+        self.Idse = Idse
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 0:
+                if ftype == TType.STRING:
+                    self.success = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 1:
+                if ftype == TType.STRUCT:
+                    self.Idse = custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException()
+                    self.Idse.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('getAPIVersion_result')
+        if self.success is not None:
+            oprot.writeFieldBegin('success', TType.STRING, 0)
+            oprot.writeString(self.success.encode('utf-8') if sys.version_info[0] == 2 else self.success)
+            oprot.writeFieldEnd()
+        if self.Idse is not None:
+            oprot.writeFieldBegin('Idse', TType.STRUCT, 1)
+            self.Idse.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(getAPIVersion_result)
+getAPIVersion_result.thrift_spec = (
+    (0, TType.STRING, 'success', 'UTF8', None, ),  # 0
+    (1, TType.STRUCT, 'Idse', [custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException, None], None, ),  # 1
+)
+
+
+class setUpGateway_args(object):
+    """
+    Attributes:
+     - authzToken
+     - gateway
+     - tenantAdminPasswordCredential
+
+    """
+
+
+    def __init__(self, authzToken=None, gateway=None, tenantAdminPasswordCredential=None,):
+        self.authzToken = authzToken
+        self.gateway = gateway
+        self.tenantAdminPasswordCredential = tenantAdminPasswordCredential
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRUCT:
+                    self.authzToken = custos.commons.model.security.ttypes.AuthzToken()
+                    self.authzToken.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRUCT:
+                    self.gateway = custos.profile.model.workspace.ttypes.Gateway()
+                    self.gateway.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 3:
+                if ftype == TType.STRUCT:
+                    self.tenantAdminPasswordCredential = custos.profile.model.tenant.ttypes.PasswordCredential()
+                    self.tenantAdminPasswordCredential.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('setUpGateway_args')
+        if self.authzToken is not None:
+            oprot.writeFieldBegin('authzToken', TType.STRUCT, 1)
+            self.authzToken.write(oprot)
+            oprot.writeFieldEnd()
+        if self.gateway is not None:
+            oprot.writeFieldBegin('gateway', TType.STRUCT, 2)
+            self.gateway.write(oprot)
+            oprot.writeFieldEnd()
+        if self.tenantAdminPasswordCredential is not None:
+            oprot.writeFieldBegin('tenantAdminPasswordCredential', TType.STRUCT, 3)
+            self.tenantAdminPasswordCredential.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.authzToken is None:
+            raise TProtocolException(message='Required field authzToken is unset!')
+        if self.gateway is None:
+            raise TProtocolException(message='Required field gateway is unset!')
+        if self.tenantAdminPasswordCredential is None:
+            raise TProtocolException(message='Required field tenantAdminPasswordCredential is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(setUpGateway_args)
+setUpGateway_args.thrift_spec = (
+    None,  # 0
+    (1, TType.STRUCT, 'authzToken', [custos.commons.model.security.ttypes.AuthzToken, None], None, ),  # 1
+    (2, TType.STRUCT, 'gateway', [custos.profile.model.workspace.ttypes.Gateway, None], None, ),  # 2
+    (3, TType.STRUCT, 'tenantAdminPasswordCredential', [custos.profile.model.tenant.ttypes.PasswordCredential, None], None, ),  # 3
+)
+
+
+class setUpGateway_result(object):
+    """
+    Attributes:
+     - success
+     - Idse
+
+    """
+
+
+    def __init__(self, success=None, Idse=None,):
+        self.success = success
+        self.Idse = Idse
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 0:
+                if ftype == TType.STRUCT:
+                    self.success = custos.profile.model.workspace.ttypes.Gateway()
+                    self.success.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 1:
+                if ftype == TType.STRUCT:
+                    self.Idse = custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException()
+                    self.Idse.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('setUpGateway_result')
+        if self.success is not None:
+            oprot.writeFieldBegin('success', TType.STRUCT, 0)
+            self.success.write(oprot)
+            oprot.writeFieldEnd()
+        if self.Idse is not None:
+            oprot.writeFieldBegin('Idse', TType.STRUCT, 1)
+            self.Idse.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(setUpGateway_result)
+setUpGateway_result.thrift_spec = (
+    (0, TType.STRUCT, 'success', [custos.profile.model.workspace.ttypes.Gateway, None], None, ),  # 0
+    (1, TType.STRUCT, 'Idse', [custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException, None], None, ),  # 1
+)
+
+
+class isUsernameAvailable_args(object):
+    """
+    Attributes:
+     - authzToken
+     - username
+
+    """
+
+
+    def __init__(self, authzToken=None, username=None,):
+        self.authzToken = authzToken
+        self.username = username
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRUCT:
+                    self.authzToken = custos.commons.model.security.ttypes.AuthzToken()
+                    self.authzToken.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.username = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('isUsernameAvailable_args')
+        if self.authzToken is not None:
+            oprot.writeFieldBegin('authzToken', TType.STRUCT, 1)
+            self.authzToken.write(oprot)
+            oprot.writeFieldEnd()
+        if self.username is not None:
+            oprot.writeFieldBegin('username', TType.STRING, 2)
+            oprot.writeString(self.username.encode('utf-8') if sys.version_info[0] == 2 else self.username)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.authzToken is None:
+            raise TProtocolException(message='Required field authzToken is unset!')
+        if self.username is None:
+            raise TProtocolException(message='Required field username is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(isUsernameAvailable_args)
+isUsernameAvailable_args.thrift_spec = (
+    None,  # 0
+    (1, TType.STRUCT, 'authzToken', [custos.commons.model.security.ttypes.AuthzToken, None], None, ),  # 1
+    (2, TType.STRING, 'username', 'UTF8', None, ),  # 2
+)
+
+
+class isUsernameAvailable_result(object):
+    """
+    Attributes:
+     - success
+     - Idse
+
+    """
+
+
+    def __init__(self, success=None, Idse=None,):
+        self.success = success
+        self.Idse = Idse
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 0:
+                if ftype == TType.BOOL:
+                    self.success = iprot.readBool()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 1:
+                if ftype == TType.STRUCT:
+                    self.Idse = custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException()
+                    self.Idse.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('isUsernameAvailable_result')
+        if self.success is not None:
+            oprot.writeFieldBegin('success', TType.BOOL, 0)
+            oprot.writeBool(self.success)
+            oprot.writeFieldEnd()
+        if self.Idse is not None:
+            oprot.writeFieldBegin('Idse', TType.STRUCT, 1)
+            self.Idse.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(isUsernameAvailable_result)
+isUsernameAvailable_result.thrift_spec = (
+    (0, TType.BOOL, 'success', None, None, ),  # 0
+    (1, TType.STRUCT, 'Idse', [custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException, None], None, ),  # 1
+)
+
+
+class registerUser_args(object):
+    """
+    Attributes:
+     - authzToken
+     - username
+     - emailAddress
+     - firstName
+     - lastName
+     - newPassword
+
+    """
+
+
+    def __init__(self, authzToken=None, username=None, emailAddress=None, firstName=None, lastName=None, newPassword=None,):
+        self.authzToken = authzToken
+        self.username = username
+        self.emailAddress = emailAddress
+        self.firstName = firstName
+        self.lastName = lastName
+        self.newPassword = newPassword
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRUCT:
+                    self.authzToken = custos.commons.model.security.ttypes.AuthzToken()
+                    self.authzToken.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.username = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 3:
+                if ftype == TType.STRING:
+                    self.emailAddress = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 4:
+                if ftype == TType.STRING:
+                    self.firstName = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 5:
+                if ftype == TType.STRING:
+                    self.lastName = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 6:
+                if ftype == TType.STRING:
+                    self.newPassword = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('registerUser_args')
+        if self.authzToken is not None:
+            oprot.writeFieldBegin('authzToken', TType.STRUCT, 1)
+            self.authzToken.write(oprot)
+            oprot.writeFieldEnd()
+        if self.username is not None:
+            oprot.writeFieldBegin('username', TType.STRING, 2)
+            oprot.writeString(self.username.encode('utf-8') if sys.version_info[0] == 2 else self.username)
+            oprot.writeFieldEnd()
+        if self.emailAddress is not None:
+            oprot.writeFieldBegin('emailAddress', TType.STRING, 3)
+            oprot.writeString(self.emailAddress.encode('utf-8') if sys.version_info[0] == 2 else self.emailAddress)
+            oprot.writeFieldEnd()
+        if self.firstName is not None:
+            oprot.writeFieldBegin('firstName', TType.STRING, 4)
+            oprot.writeString(self.firstName.encode('utf-8') if sys.version_info[0] == 2 else self.firstName)
+            oprot.writeFieldEnd()
+        if self.lastName is not None:
+            oprot.writeFieldBegin('lastName', TType.STRING, 5)
+            oprot.writeString(self.lastName.encode('utf-8') if sys.version_info[0] == 2 else self.lastName)
+            oprot.writeFieldEnd()
+        if self.newPassword is not None:
+            oprot.writeFieldBegin('newPassword', TType.STRING, 6)
+            oprot.writeString(self.newPassword.encode('utf-8') if sys.version_info[0] == 2 else self.newPassword)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.authzToken is None:
+            raise TProtocolException(message='Required field authzToken is unset!')
+        if self.username is None:
+            raise TProtocolException(message='Required field username is unset!')
+        if self.emailAddress is None:
+            raise TProtocolException(message='Required field emailAddress is unset!')
+        if self.firstName is None:
+            raise TProtocolException(message='Required field firstName is unset!')
+        if self.lastName is None:
+            raise TProtocolException(message='Required field lastName is unset!')
+        if self.newPassword is None:
+            raise TProtocolException(message='Required field newPassword is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(registerUser_args)
+registerUser_args.thrift_spec = (
+    None,  # 0
+    (1, TType.STRUCT, 'authzToken', [custos.commons.model.security.ttypes.AuthzToken, None], None, ),  # 1
+    (2, TType.STRING, 'username', 'UTF8', None, ),  # 2
+    (3, TType.STRING, 'emailAddress', 'UTF8', None, ),  # 3
+    (4, TType.STRING, 'firstName', 'UTF8', None, ),  # 4
+    (5, TType.STRING, 'lastName', 'UTF8', None, ),  # 5
+    (6, TType.STRING, 'newPassword', 'UTF8', None, ),  # 6
+)
+
+
+class registerUser_result(object):
+    """
+    Attributes:
+     - success
+     - Idse
+
+    """
+
+
+    def __init__(self, success=None, Idse=None,):
+        self.success = success
+        self.Idse = Idse
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 0:
+                if ftype == TType.BOOL:
+                    self.success = iprot.readBool()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 1:
+                if ftype == TType.STRUCT:
+                    self.Idse = custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException()
+                    self.Idse.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('registerUser_result')
+        if self.success is not None:
+            oprot.writeFieldBegin('success', TType.BOOL, 0)
+            oprot.writeBool(self.success)
+            oprot.writeFieldEnd()
+        if self.Idse is not None:
+            oprot.writeFieldBegin('Idse', TType.STRUCT, 1)
+            self.Idse.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(registerUser_result)
+registerUser_result.thrift_spec = (
+    (0, TType.BOOL, 'success', None, None, ),  # 0
+    (1, TType.STRUCT, 'Idse', [custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException, None], None, ),  # 1
+)
+
+
+class deleteUser_args(object):
+    """
+    Attributes:
+     - authzToken
+     - username
+
+    """
+
+
+    def __init__(self, authzToken=None, username=None,):
+        self.authzToken = authzToken
+        self.username = username
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRUCT:
+                    self.authzToken = custos.commons.model.security.ttypes.AuthzToken()
+                    self.authzToken.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.username = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('deleteUser_args')
+        if self.authzToken is not None:
+            oprot.writeFieldBegin('authzToken', TType.STRUCT, 1)
+            self.authzToken.write(oprot)
+            oprot.writeFieldEnd()
+        if self.username is not None:
+            oprot.writeFieldBegin('username', TType.STRING, 2)
+            oprot.writeString(self.username.encode('utf-8') if sys.version_info[0] == 2 else self.username)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.authzToken is None:
+            raise TProtocolException(message='Required field authzToken is unset!')
+        if self.username is None:
+            raise TProtocolException(message='Required field username is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(deleteUser_args)
+deleteUser_args.thrift_spec = (
+    None,  # 0
+    (1, TType.STRUCT, 'authzToken', [custos.commons.model.security.ttypes.AuthzToken, None], None, ),  # 1
+    (2, TType.STRING, 'username', 'UTF8', None, ),  # 2
+)
+
+
+class deleteUser_result(object):
+    """
+    Attributes:
+     - success
+     - Idse
+
+    """
+
+
+    def __init__(self, success=None, Idse=None,):
+        self.success = success
+        self.Idse = Idse
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 0:
+                if ftype == TType.BOOL:
+                    self.success = iprot.readBool()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 1:
+                if ftype == TType.STRUCT:
+                    self.Idse = custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException()
+                    self.Idse.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('deleteUser_result')
+        if self.success is not None:
+            oprot.writeFieldBegin('success', TType.BOOL, 0)
+            oprot.writeBool(self.success)
+            oprot.writeFieldEnd()
+        if self.Idse is not None:
+            oprot.writeFieldBegin('Idse', TType.STRUCT, 1)
+            self.Idse.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(deleteUser_result)
+deleteUser_result.thrift_spec = (
+    (0, TType.BOOL, 'success', None, None, ),  # 0
+    (1, TType.STRUCT, 'Idse', [custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException, None], None, ),  # 1
+)
+
+
+class enableUser_args(object):
+    """
+    Attributes:
+     - authzToken
+     - username
+
+    """
+
+
+    def __init__(self, authzToken=None, username=None,):
+        self.authzToken = authzToken
+        self.username = username
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRUCT:
+                    self.authzToken = custos.commons.model.security.ttypes.AuthzToken()
+                    self.authzToken.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.username = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('enableUser_args')
+        if self.authzToken is not None:
+            oprot.writeFieldBegin('authzToken', TType.STRUCT, 1)
+            self.authzToken.write(oprot)
+            oprot.writeFieldEnd()
+        if self.username is not None:
+            oprot.writeFieldBegin('username', TType.STRING, 2)
+            oprot.writeString(self.username.encode('utf-8') if sys.version_info[0] == 2 else self.username)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.authzToken is None:
+            raise TProtocolException(message='Required field authzToken is unset!')
+        if self.username is None:
+            raise TProtocolException(message='Required field username is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(enableUser_args)
+enableUser_args.thrift_spec = (
+    None,  # 0
+    (1, TType.STRUCT, 'authzToken', [custos.commons.model.security.ttypes.AuthzToken, None], None, ),  # 1
+    (2, TType.STRING, 'username', 'UTF8', None, ),  # 2
+)
+
+
+class enableUser_result(object):
+    """
+    Attributes:
+     - success
+     - Idse
+
+    """
+
+
+    def __init__(self, success=None, Idse=None,):
+        self.success = success
+        self.Idse = Idse
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 0:
+                if ftype == TType.STRUCT:
+                    self.success = custos.profile.model.User.ttypes.UserProfile()
+                    self.success.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 1:
+                if ftype == TType.STRUCT:
+                    self.Idse = custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException()
+                    self.Idse.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('enableUser_result')
+        if self.success is not None:
+            oprot.writeFieldBegin('success', TType.STRUCT, 0)
+            self.success.write(oprot)
+            oprot.writeFieldEnd()
+        if self.Idse is not None:
+            oprot.writeFieldBegin('Idse', TType.STRUCT, 1)
+            self.Idse.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(enableUser_result)
+enableUser_result.thrift_spec = (
+    (0, TType.STRUCT, 'success', [custos.profile.model.User.ttypes.UserProfile, None], None, ),  # 0
+    (1, TType.STRUCT, 'Idse', [custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException, None], None, ),  # 1
+)
+
+
+class isUserEnabled_args(object):
+    """
+    Attributes:
+     - authzToken
+     - username
+
+    """
+
+
+    def __init__(self, authzToken=None, username=None,):
+        self.authzToken = authzToken
+        self.username = username
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRUCT:
+                    self.authzToken = custos.commons.model.security.ttypes.AuthzToken()
+                    self.authzToken.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.username = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('isUserEnabled_args')
+        if self.authzToken is not None:
+            oprot.writeFieldBegin('authzToken', TType.STRUCT, 1)
+            self.authzToken.write(oprot)
+            oprot.writeFieldEnd()
+        if self.username is not None:
+            oprot.writeFieldBegin('username', TType.STRING, 2)
+            oprot.writeString(self.username.encode('utf-8') if sys.version_info[0] == 2 else self.username)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.authzToken is None:
+            raise TProtocolException(message='Required field authzToken is unset!')
+        if self.username is None:
+            raise TProtocolException(message='Required field username is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(isUserEnabled_args)
+isUserEnabled_args.thrift_spec = (
+    None,  # 0
+    (1, TType.STRUCT, 'authzToken', [custos.commons.model.security.ttypes.AuthzToken, None], None, ),  # 1
+    (2, TType.STRING, 'username', 'UTF8', None, ),  # 2
+)
+
+
+class isUserEnabled_result(object):
+    """
+    Attributes:
+     - success
+     - Idse
+
+    """
+
+
+    def __init__(self, success=None, Idse=None,):
+        self.success = success
+        self.Idse = Idse
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 0:
+                if ftype == TType.BOOL:
+                    self.success = iprot.readBool()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 1:
+                if ftype == TType.STRUCT:
+                    self.Idse = custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException()
+                    self.Idse.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('isUserEnabled_result')
+        if self.success is not None:
+            oprot.writeFieldBegin('success', TType.BOOL, 0)
+            oprot.writeBool(self.success)
+            oprot.writeFieldEnd()
+        if self.Idse is not None:
+            oprot.writeFieldBegin('Idse', TType.STRUCT, 1)
+            self.Idse.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(isUserEnabled_result)
+isUserEnabled_result.thrift_spec = (
+    (0, TType.BOOL, 'success', None, None, ),  # 0
+    (1, TType.STRUCT, 'Idse', [custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException, None], None, ),  # 1
+)
+
+
+class isUserExist_args(object):
+    """
+    Attributes:
+     - authzToken
+     - username
+
+    """
+
+
+    def __init__(self, authzToken=None, username=None,):
+        self.authzToken = authzToken
+        self.username = username
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRUCT:
+                    self.authzToken = custos.commons.model.security.ttypes.AuthzToken()
+                    self.authzToken.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.username = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('isUserExist_args')
+        if self.authzToken is not None:
+            oprot.writeFieldBegin('authzToken', TType.STRUCT, 1)
+            self.authzToken.write(oprot)
+            oprot.writeFieldEnd()
+        if self.username is not None:
+            oprot.writeFieldBegin('username', TType.STRING, 2)
+            oprot.writeString(self.username.encode('utf-8') if sys.version_info[0] == 2 else self.username)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.authzToken is None:
+            raise TProtocolException(message='Required field authzToken is unset!')
+        if self.username is None:
+            raise TProtocolException(message='Required field username is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(isUserExist_args)
+isUserExist_args.thrift_spec = (
+    None,  # 0
+    (1, TType.STRUCT, 'authzToken', [custos.commons.model.security.ttypes.AuthzToken, None], None, ),  # 1
+    (2, TType.STRING, 'username', 'UTF8', None, ),  # 2
+)
+
+
+class isUserExist_result(object):
+    """
+    Attributes:
+     - success
+     - Idse
+
+    """
+
+
+    def __init__(self, success=None, Idse=None,):
+        self.success = success
+        self.Idse = Idse
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 0:
+                if ftype == TType.BOOL:
+                    self.success = iprot.readBool()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 1:
+                if ftype == TType.STRUCT:
+                    self.Idse = custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException()
+                    self.Idse.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('isUserExist_result')
+        if self.success is not None:
+            oprot.writeFieldBegin('success', TType.BOOL, 0)
+            oprot.writeBool(self.success)
+            oprot.writeFieldEnd()
+        if self.Idse is not None:
+            oprot.writeFieldBegin('Idse', TType.STRUCT, 1)
+            self.Idse.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(isUserExist_result)
+isUserExist_result.thrift_spec = (
+    (0, TType.BOOL, 'success', None, None, ),  # 0
+    (1, TType.STRUCT, 'Idse', [custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException, None], None, ),  # 1
+)
+
+
+class getUser_args(object):
+    """
+    Attributes:
+     - authzToken
+     - username
+
+    """
+
+
+    def __init__(self, authzToken=None, username=None,):
+        self.authzToken = authzToken
+        self.username = username
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRUCT:
+                    self.authzToken = custos.commons.model.security.ttypes.AuthzToken()
+                    self.authzToken.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.username = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('getUser_args')
+        if self.authzToken is not None:
+            oprot.writeFieldBegin('authzToken', TType.STRUCT, 1)
+            self.authzToken.write(oprot)
+            oprot.writeFieldEnd()
+        if self.username is not None:
+            oprot.writeFieldBegin('username', TType.STRING, 2)
+            oprot.writeString(self.username.encode('utf-8') if sys.version_info[0] == 2 else self.username)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.authzToken is None:
+            raise TProtocolException(message='Required field authzToken is unset!')
+        if self.username is None:
+            raise TProtocolException(message='Required field username is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(getUser_args)
+getUser_args.thrift_spec = (
+    None,  # 0
+    (1, TType.STRUCT, 'authzToken', [custos.commons.model.security.ttypes.AuthzToken, None], None, ),  # 1
+    (2, TType.STRING, 'username', 'UTF8', None, ),  # 2
+)
+
+
+class getUser_result(object):
+    """
+    Attributes:
+     - success
+     - Idse
+
+    """
+
+
+    def __init__(self, success=None, Idse=None,):
+        self.success = success
+        self.Idse = Idse
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 0:
+                if ftype == TType.STRUCT:
+                    self.success = custos.profile.model.User.ttypes.UserProfile()
+                    self.success.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 1:
+                if ftype == TType.STRUCT:
+                    self.Idse = custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException()
+                    self.Idse.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('getUser_result')
+        if self.success is not None:
+            oprot.writeFieldBegin('success', TType.STRUCT, 0)
+            self.success.write(oprot)
+            oprot.writeFieldEnd()
+        if self.Idse is not None:
+            oprot.writeFieldBegin('Idse', TType.STRUCT, 1)
+            self.Idse.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(getUser_result)
+getUser_result.thrift_spec = (
+    (0, TType.STRUCT, 'success', [custos.profile.model.User.ttypes.UserProfile, None], None, ),  # 0
+    (1, TType.STRUCT, 'Idse', [custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException, None], None, ),  # 1
+)
+
+
+class getUsers_args(object):
+    """
+    Attributes:
+     - authzToken
+     - offset
+     - limit
+     - search
+
+    """
+
+
+    def __init__(self, authzToken=None, offset=None, limit=None, search=None,):
+        self.authzToken = authzToken
+        self.offset = offset
+        self.limit = limit
+        self.search = search
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRUCT:
+                    self.authzToken = custos.commons.model.security.ttypes.AuthzToken()
+                    self.authzToken.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.I32:
+                    self.offset = iprot.readI32()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 3:
+                if ftype == TType.I32:
+                    self.limit = iprot.readI32()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 4:
+                if ftype == TType.STRING:
+                    self.search = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('getUsers_args')
+        if self.authzToken is not None:
+            oprot.writeFieldBegin('authzToken', TType.STRUCT, 1)
+            self.authzToken.write(oprot)
+            oprot.writeFieldEnd()
+        if self.offset is not None:
+            oprot.writeFieldBegin('offset', TType.I32, 2)
+            oprot.writeI32(self.offset)
+            oprot.writeFieldEnd()
+        if self.limit is not None:
+            oprot.writeFieldBegin('limit', TType.I32, 3)
+            oprot.writeI32(self.limit)
+            oprot.writeFieldEnd()
+        if self.search is not None:
+            oprot.writeFieldBegin('search', TType.STRING, 4)
+            oprot.writeString(self.search.encode('utf-8') if sys.version_info[0] == 2 else self.search)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.authzToken is None:
+            raise TProtocolException(message='Required field authzToken is unset!')
+        if self.offset is None:
+            raise TProtocolException(message='Required field offset is unset!')
+        if self.limit is None:
+            raise TProtocolException(message='Required field limit is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(getUsers_args)
+getUsers_args.thrift_spec = (
+    None,  # 0
+    (1, TType.STRUCT, 'authzToken', [custos.commons.model.security.ttypes.AuthzToken, None], None, ),  # 1
+    (2, TType.I32, 'offset', None, None, ),  # 2
+    (3, TType.I32, 'limit', None, None, ),  # 3
+    (4, TType.STRING, 'search', 'UTF8', None, ),  # 4
+)
+
+
+class getUsers_result(object):
+    """
+    Attributes:
+     - success
+     - Idse
+
+    """
+
+
+    def __init__(self, success=None, Idse=None,):
+        self.success = success
+        self.Idse = Idse
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 0:
+                if ftype == TType.LIST:
+                    self.success = []
+                    (_etype3, _size0) = iprot.readListBegin()
+                    for _i4 in range(_size0):
+                        _elem5 = custos.profile.model.User.ttypes.UserProfile()
+                        _elem5.read(iprot)
+                        self.success.append(_elem5)
+                    iprot.readListEnd()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 1:
+                if ftype == TType.STRUCT:
+                    self.Idse = custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException()
+                    self.Idse.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('getUsers_result')
+        if self.success is not None:
+            oprot.writeFieldBegin('success', TType.LIST, 0)
+            oprot.writeListBegin(TType.STRUCT, len(self.success))
+            for iter6 in self.success:
+                iter6.write(oprot)
+            oprot.writeListEnd()
+            oprot.writeFieldEnd()
+        if self.Idse is not None:
+            oprot.writeFieldBegin('Idse', TType.STRUCT, 1)
+            self.Idse.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(getUsers_result)
+getUsers_result.thrift_spec = (
+    (0, TType.LIST, 'success', (TType.STRUCT, [custos.profile.model.User.ttypes.UserProfile, None], False), None, ),  # 0
+    (1, TType.STRUCT, 'Idse', [custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException, None], None, ),  # 1
+)
+
+
+class resetUserPassword_args(object):
+    """
+    Attributes:
+     - authzToken
+     - username
+     - newPassword
+
+    """
+
+
+    def __init__(self, authzToken=None, username=None, newPassword=None,):
+        self.authzToken = authzToken
+        self.username = username
+        self.newPassword = newPassword
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRUCT:
+                    self.authzToken = custos.commons.model.security.ttypes.AuthzToken()
+                    self.authzToken.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.username = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 3:
+                if ftype == TType.STRING:
+                    self.newPassword = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('resetUserPassword_args')
+        if self.authzToken is not None:
+            oprot.writeFieldBegin('authzToken', TType.STRUCT, 1)
+            self.authzToken.write(oprot)
+            oprot.writeFieldEnd()
+        if self.username is not None:
+            oprot.writeFieldBegin('username', TType.STRING, 2)
+            oprot.writeString(self.username.encode('utf-8') if sys.version_info[0] == 2 else self.username)
+            oprot.writeFieldEnd()
+        if self.newPassword is not None:
+            oprot.writeFieldBegin('newPassword', TType.STRING, 3)
+            oprot.writeString(self.newPassword.encode('utf-8') if sys.version_info[0] == 2 else self.newPassword)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.authzToken is None:
+            raise TProtocolException(message='Required field authzToken is unset!')
+        if self.username is None:
+            raise TProtocolException(message='Required field username is unset!')
+        if self.newPassword is None:
+            raise TProtocolException(message='Required field newPassword is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(resetUserPassword_args)
+resetUserPassword_args.thrift_spec = (
+    None,  # 0
+    (1, TType.STRUCT, 'authzToken', [custos.commons.model.security.ttypes.AuthzToken, None], None, ),  # 1
+    (2, TType.STRING, 'username', 'UTF8', None, ),  # 2
+    (3, TType.STRING, 'newPassword', 'UTF8', None, ),  # 3
+)
+
+
+class resetUserPassword_result(object):
+    """
+    Attributes:
+     - success
+     - Idse
+
+    """
+
+
+    def __init__(self, success=None, Idse=None,):
+        self.success = success
+        self.Idse = Idse
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 0:
+                if ftype == TType.BOOL:
+                    self.success = iprot.readBool()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 1:
+                if ftype == TType.STRUCT:
+                    self.Idse = custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException()
+                    self.Idse.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('resetUserPassword_result')
+        if self.success is not None:
+            oprot.writeFieldBegin('success', TType.BOOL, 0)
+            oprot.writeBool(self.success)
+            oprot.writeFieldEnd()
+        if self.Idse is not None:
+            oprot.writeFieldBegin('Idse', TType.STRUCT, 1)
+            self.Idse.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(resetUserPassword_result)
+resetUserPassword_result.thrift_spec = (
+    (0, TType.BOOL, 'success', None, None, ),  # 0
+    (1, TType.STRUCT, 'Idse', [custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException, None], None, ),  # 1
+)
+
+
+class findUsers_args(object):
+    """
+    Attributes:
+     - authzToken
+     - email
+     - userId
+
+    """
+
+
+    def __init__(self, authzToken=None, email=None, userId=None,):
+        self.authzToken = authzToken
+        self.email = email
+        self.userId = userId
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRUCT:
+                    self.authzToken = custos.commons.model.security.ttypes.AuthzToken()
+                    self.authzToken.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.email = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 3:
+                if ftype == TType.STRING:
+                    self.userId = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('findUsers_args')
+        if self.authzToken is not None:
+            oprot.writeFieldBegin('authzToken', TType.STRUCT, 1)
+            self.authzToken.write(oprot)
+            oprot.writeFieldEnd()
+        if self.email is not None:
+            oprot.writeFieldBegin('email', TType.STRING, 2)
+            oprot.writeString(self.email.encode('utf-8') if sys.version_info[0] == 2 else self.email)
+            oprot.writeFieldEnd()
+        if self.userId is not None:
+            oprot.writeFieldBegin('userId', TType.STRING, 3)
+            oprot.writeString(self.userId.encode('utf-8') if sys.version_info[0] == 2 else self.userId)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.authzToken is None:
+            raise TProtocolException(message='Required field authzToken is unset!')
+        if self.email is None:
+            raise TProtocolException(message='Required field email is unset!')
+        if self.userId is None:
+            raise TProtocolException(message='Required field userId is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(findUsers_args)
+findUsers_args.thrift_spec = (
+    None,  # 0
+    (1, TType.STRUCT, 'authzToken', [custos.commons.model.security.ttypes.AuthzToken, None], None, ),  # 1
+    (2, TType.STRING, 'email', 'UTF8', None, ),  # 2
+    (3, TType.STRING, 'userId', 'UTF8', None, ),  # 3
+)
+
+
+class findUsers_result(object):
+    """
+    Attributes:
+     - success
+     - Idse
+
+    """
+
+
+    def __init__(self, success=None, Idse=None,):
+        self.success = success
+        self.Idse = Idse
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 0:
+                if ftype == TType.LIST:
+                    self.success = []
+                    (_etype10, _size7) = iprot.readListBegin()
+                    for _i11 in range(_size7):
+                        _elem12 = custos.profile.model.User.ttypes.UserProfile()
+                        _elem12.read(iprot)
+                        self.success.append(_elem12)
+                    iprot.readListEnd()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 1:
+                if ftype == TType.STRUCT:
+                    self.Idse = custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException()
+                    self.Idse.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('findUsers_result')
+        if self.success is not None:
+            oprot.writeFieldBegin('success', TType.LIST, 0)
+            oprot.writeListBegin(TType.STRUCT, len(self.success))
+            for iter13 in self.success:
+                iter13.write(oprot)
+            oprot.writeListEnd()
+            oprot.writeFieldEnd()
+        if self.Idse is not None:
+            oprot.writeFieldBegin('Idse', TType.STRUCT, 1)
+            self.Idse.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(findUsers_result)
+findUsers_result.thrift_spec = (
+    (0, TType.LIST, 'success', (TType.STRUCT, [custos.profile.model.User.ttypes.UserProfile, None], False), None, ),  # 0
+    (1, TType.STRUCT, 'Idse', [custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException, None], None, ),  # 1
+)
+
+
+class updateUserProfile_args(object):
+    """
+    Attributes:
+     - authzToken
+     - userDetails
+
+    """
+
+
+    def __init__(self, authzToken=None, userDetails=None,):
+        self.authzToken = authzToken
+        self.userDetails = userDetails
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRUCT:
+                    self.authzToken = custos.commons.model.security.ttypes.AuthzToken()
+                    self.authzToken.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRUCT:
+                    self.userDetails = custos.profile.model.User.ttypes.UserProfile()
+                    self.userDetails.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('updateUserProfile_args')
+        if self.authzToken is not None:
+            oprot.writeFieldBegin('authzToken', TType.STRUCT, 1)
+            self.authzToken.write(oprot)
+            oprot.writeFieldEnd()
+        if self.userDetails is not None:
+            oprot.writeFieldBegin('userDetails', TType.STRUCT, 2)
+            self.userDetails.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.authzToken is None:
+            raise TProtocolException(message='Required field authzToken is unset!')
+        if self.userDetails is None:
+            raise TProtocolException(message='Required field userDetails is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(updateUserProfile_args)
+updateUserProfile_args.thrift_spec = (
+    None,  # 0
+    (1, TType.STRUCT, 'authzToken', [custos.commons.model.security.ttypes.AuthzToken, None], None, ),  # 1
+    (2, TType.STRUCT, 'userDetails', [custos.profile.model.User.ttypes.UserProfile, None], None, ),  # 2
+)
+
+
+class updateUserProfile_result(object):
+    """
+    Attributes:
+     - Idse
+
+    """
+
+
+    def __init__(self, Idse=None,):
+        self.Idse = Idse
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRUCT:
+                    self.Idse = custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException()
+                    self.Idse.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('updateUserProfile_result')
+        if self.Idse is not None:
+            oprot.writeFieldBegin('Idse', TType.STRUCT, 1)
+            self.Idse.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(updateUserProfile_result)
+updateUserProfile_result.thrift_spec = (
+    None,  # 0
+    (1, TType.STRUCT, 'Idse', [custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException, None], None, ),  # 1
+)
+
+
+class addRoleToUser_args(object):
+    """
+    Attributes:
+     - authzToken
+     - username
+     - roleName
+     - tenantAdminPasswordCredential
+
+    """
+
+
+    def __init__(self, authzToken=None, username=None, roleName=None, tenantAdminPasswordCredential=None,):
+        self.authzToken = authzToken
+        self.username = username
+        self.roleName = roleName
+        self.tenantAdminPasswordCredential = tenantAdminPasswordCredential
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRUCT:
+                    self.authzToken = custos.commons.model.security.ttypes.AuthzToken()
+                    self.authzToken.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.username = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 3:
+                if ftype == TType.STRING:
+                    self.roleName = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 4:
+                if ftype == TType.STRUCT:
+                    self.tenantAdminPasswordCredential = custos.profile.model.tenant.ttypes.PasswordCredential()
+                    self.tenantAdminPasswordCredential.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('addRoleToUser_args')
+        if self.authzToken is not None:
+            oprot.writeFieldBegin('authzToken', TType.STRUCT, 1)
+            self.authzToken.write(oprot)
+            oprot.writeFieldEnd()
+        if self.username is not None:
+            oprot.writeFieldBegin('username', TType.STRING, 2)
+            oprot.writeString(self.username.encode('utf-8') if sys.version_info[0] == 2 else self.username)
+            oprot.writeFieldEnd()
+        if self.roleName is not None:
+            oprot.writeFieldBegin('roleName', TType.STRING, 3)
+            oprot.writeString(self.roleName.encode('utf-8') if sys.version_info[0] == 2 else self.roleName)
+            oprot.writeFieldEnd()
+        if self.tenantAdminPasswordCredential is not None:
+            oprot.writeFieldBegin('tenantAdminPasswordCredential', TType.STRUCT, 4)
+            self.tenantAdminPasswordCredential.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.authzToken is None:
+            raise TProtocolException(message='Required field authzToken is unset!')
+        if self.username is None:
+            raise TProtocolException(message='Required field username is unset!')
+        if self.roleName is None:
+            raise TProtocolException(message='Required field roleName is unset!')
+        if self.tenantAdminPasswordCredential is None:
+            raise TProtocolException(message='Required field tenantAdminPasswordCredential is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(addRoleToUser_args)
+addRoleToUser_args.thrift_spec = (
+    None,  # 0
+    (1, TType.STRUCT, 'authzToken', [custos.commons.model.security.ttypes.AuthzToken, None], None, ),  # 1
+    (2, TType.STRING, 'username', 'UTF8', None, ),  # 2
+    (3, TType.STRING, 'roleName', 'UTF8', None, ),  # 3
+    (4, TType.STRUCT, 'tenantAdminPasswordCredential', [custos.profile.model.tenant.ttypes.PasswordCredential, None], None, ),  # 4
+)
+
+
+class addRoleToUser_result(object):
+    """
+    Attributes:
+     - success
+     - Idse
+
+    """
+
+
+    def __init__(self, success=None, Idse=None,):
+        self.success = success
+        self.Idse = Idse
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 0:
+                if ftype == TType.BOOL:
+                    self.success = iprot.readBool()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 1:
+                if ftype == TType.STRUCT:
+                    self.Idse = custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException()
+                    self.Idse.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('addRoleToUser_result')
+        if self.success is not None:
+            oprot.writeFieldBegin('success', TType.BOOL, 0)
+            oprot.writeBool(self.success)
+            oprot.writeFieldEnd()
+        if self.Idse is not None:
+            oprot.writeFieldBegin('Idse', TType.STRUCT, 1)
+            self.Idse.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(addRoleToUser_result)
+addRoleToUser_result.thrift_spec = (
+    (0, TType.BOOL, 'success', None, None, ),  # 0
+    (1, TType.STRUCT, 'Idse', [custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException, None], None, ),  # 1
+)
+
+
+class removeRoleFromUser_args(object):
+    """
+    Attributes:
+     - authzToken
+     - username
+     - roleName
+     - tenantAdminPasswordCredential
+
+    """
+
+
+    def __init__(self, authzToken=None, username=None, roleName=None, tenantAdminPasswordCredential=None,):
+        self.authzToken = authzToken
+        self.username = username
+        self.roleName = roleName
+        self.tenantAdminPasswordCredential = tenantAdminPasswordCredential
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRUCT:
+                    self.authzToken = custos.commons.model.security.ttypes.AuthzToken()
+                    self.authzToken.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.username = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 3:
+                if ftype == TType.STRING:
+                    self.roleName = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 4:
+                if ftype == TType.STRUCT:
+                    self.tenantAdminPasswordCredential = custos.profile.model.tenant.ttypes.PasswordCredential()
+                    self.tenantAdminPasswordCredential.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('removeRoleFromUser_args')
+        if self.authzToken is not None:
+            oprot.writeFieldBegin('authzToken', TType.STRUCT, 1)
+            self.authzToken.write(oprot)
+            oprot.writeFieldEnd()
+        if self.username is not None:
+            oprot.writeFieldBegin('username', TType.STRING, 2)
+            oprot.writeString(self.username.encode('utf-8') if sys.version_info[0] == 2 else self.username)
+            oprot.writeFieldEnd()
+        if self.roleName is not None:
+            oprot.writeFieldBegin('roleName', TType.STRING, 3)
+            oprot.writeString(self.roleName.encode('utf-8') if sys.version_info[0] == 2 else self.roleName)
+            oprot.writeFieldEnd()
+        if self.tenantAdminPasswordCredential is not None:
+            oprot.writeFieldBegin('tenantAdminPasswordCredential', TType.STRUCT, 4)
+            self.tenantAdminPasswordCredential.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.authzToken is None:
+            raise TProtocolException(message='Required field authzToken is unset!')
+        if self.username is None:
+            raise TProtocolException(message='Required field username is unset!')
+        if self.roleName is None:
+            raise TProtocolException(message='Required field roleName is unset!')
+        if self.tenantAdminPasswordCredential is None:
+            raise TProtocolException(message='Required field tenantAdminPasswordCredential is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(removeRoleFromUser_args)
+removeRoleFromUser_args.thrift_spec = (
+    None,  # 0
+    (1, TType.STRUCT, 'authzToken', [custos.commons.model.security.ttypes.AuthzToken, None], None, ),  # 1
+    (2, TType.STRING, 'username', 'UTF8', None, ),  # 2
+    (3, TType.STRING, 'roleName', 'UTF8', None, ),  # 3
+    (4, TType.STRUCT, 'tenantAdminPasswordCredential', [custos.profile.model.tenant.ttypes.PasswordCredential, None], None, ),  # 4
+)
+
+
+class removeRoleFromUser_result(object):
+    """
+    Attributes:
+     - success
+     - Idse
+
+    """
+
+
+    def __init__(self, success=None, Idse=None,):
+        self.success = success
+        self.Idse = Idse
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 0:
+                if ftype == TType.BOOL:
+                    self.success = iprot.readBool()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 1:
+                if ftype == TType.STRUCT:
+                    self.Idse = custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException()
+                    self.Idse.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('removeRoleFromUser_result')
+        if self.success is not None:
+            oprot.writeFieldBegin('success', TType.BOOL, 0)
+            oprot.writeBool(self.success)
+            oprot.writeFieldEnd()
+        if self.Idse is not None:
+            oprot.writeFieldBegin('Idse', TType.STRUCT, 1)
+            self.Idse.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(removeRoleFromUser_result)
+removeRoleFromUser_result.thrift_spec = (
+    (0, TType.BOOL, 'success', None, None, ),  # 0
+    (1, TType.STRUCT, 'Idse', [custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException, None], None, ),  # 1
+)
+
+
+class getUsersWithRole_args(object):
+    """
+    Attributes:
+     - authzToken
+     - roleName
+     - tenantAdminPasswordCredential
+
+    """
+
+
+    def __init__(self, authzToken=None, roleName=None, tenantAdminPasswordCredential=None,):
+        self.authzToken = authzToken
+        self.roleName = roleName
+        self.tenantAdminPasswordCredential = tenantAdminPasswordCredential
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRUCT:
+                    self.authzToken = custos.commons.model.security.ttypes.AuthzToken()
+                    self.authzToken.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.roleName = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 3:
+                if ftype == TType.STRUCT:
+                    self.tenantAdminPasswordCredential = custos.profile.model.tenant.ttypes.PasswordCredential()
+                    self.tenantAdminPasswordCredential.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('getUsersWithRole_args')
+        if self.authzToken is not None:
+            oprot.writeFieldBegin('authzToken', TType.STRUCT, 1)
+            self.authzToken.write(oprot)
+            oprot.writeFieldEnd()
+        if self.roleName is not None:
+            oprot.writeFieldBegin('roleName', TType.STRING, 2)
+            oprot.writeString(self.roleName.encode('utf-8') if sys.version_info[0] == 2 else self.roleName)
+            oprot.writeFieldEnd()
+        if self.tenantAdminPasswordCredential is not None:
+            oprot.writeFieldBegin('tenantAdminPasswordCredential', TType.STRUCT, 3)
+            self.tenantAdminPasswordCredential.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.authzToken is None:
+            raise TProtocolException(message='Required field authzToken is unset!')
+        if self.roleName is None:
+            raise TProtocolException(message='Required field roleName is unset!')
+        if self.tenantAdminPasswordCredential is None:
+            raise TProtocolException(message='Required field tenantAdminPasswordCredential is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(getUsersWithRole_args)
+getUsersWithRole_args.thrift_spec = (
+    None,  # 0
+    (1, TType.STRUCT, 'authzToken', [custos.commons.model.security.ttypes.AuthzToken, None], None, ),  # 1
+    (2, TType.STRING, 'roleName', 'UTF8', None, ),  # 2
+    (3, TType.STRUCT, 'tenantAdminPasswordCredential', [custos.profile.model.tenant.ttypes.PasswordCredential, None], None, ),  # 3
+)
+
+
+class getUsersWithRole_result(object):
+    """
+    Attributes:
+     - success
+     - Idse
+
+    """
+
+
+    def __init__(self, success=None, Idse=None,):
+        self.success = success
+        self.Idse = Idse
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 0:
+                if ftype == TType.LIST:
+                    self.success = []
+                    (_etype17, _size14) = iprot.readListBegin()
+                    for _i18 in range(_size14):
+                        _elem19 = custos.profile.model.User.ttypes.UserProfile()
+                        _elem19.read(iprot)
+                        self.success.append(_elem19)
+                    iprot.readListEnd()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 1:
+                if ftype == TType.STRUCT:
+                    self.Idse = custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException()
+                    self.Idse.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('getUsersWithRole_result')
+        if self.success is not None:
+            oprot.writeFieldBegin('success', TType.LIST, 0)
+            oprot.writeListBegin(TType.STRUCT, len(self.success))
+            for iter20 in self.success:
+                iter20.write(oprot)
+            oprot.writeListEnd()
+            oprot.writeFieldEnd()
+        if self.Idse is not None:
+            oprot.writeFieldBegin('Idse', TType.STRUCT, 1)
+            self.Idse.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(getUsersWithRole_result)
+getUsersWithRole_result.thrift_spec = (
+    (0, TType.LIST, 'success', (TType.STRUCT, [custos.profile.model.User.ttypes.UserProfile, None], False), None, ),  # 0
+    (1, TType.STRUCT, 'Idse', [custos.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException, None], None, ),  # 1
+)
+fix_spec(all_structs)
+del all_structs
+
diff --git a/clients/python/custos/profile/iam/admin/services/cpi/__init__.py b/clients/python/custos/profile/iam/admin/services/cpi/__init__.py
new file mode 100644
index 0000000..58a44a7
--- /dev/null
+++ b/clients/python/custos/profile/iam/admin/services/cpi/__init__.py
@@ -0,0 +1 @@
+__all__ = ['ttypes', 'constants', 'IamAdminServices']
diff --git a/clients/python/custos/profile/iam/admin/services/cpi/constants.py b/clients/python/custos/profile/iam/admin/services/cpi/constants.py
new file mode 100644
index 0000000..31ef52e
--- /dev/null
+++ b/clients/python/custos/profile/iam/admin/services/cpi/constants.py
@@ -0,0 +1,16 @@
+#
+# Autogenerated by Thrift Compiler (0.12.0)
+#
+# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+#
+#  options string: py
+#
+
+from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException
+from thrift.protocol.TProtocol import TProtocolException
+from thrift.TRecursive import fix_spec
+
+import sys
+from .ttypes import *
+IAM_ADMIN_SERVICES_CPI_VERSION = "0.17"
+IAM_ADMIN_SERVICES_CPI_NAME = "IamAdminServices"
diff --git a/clients/python/custos/profile/iam/admin/services/cpi/error/__init__.py b/clients/python/custos/profile/iam/admin/services/cpi/error/__init__.py
new file mode 100644
index 0000000..adefd8e
--- /dev/null
+++ b/clients/python/custos/profile/iam/admin/services/cpi/error/__init__.py
@@ -0,0 +1 @@
+__all__ = ['ttypes', 'constants']
diff --git a/clients/python/custos/profile/iam/admin/services/cpi/error/constants.py b/clients/python/custos/profile/iam/admin/services/cpi/error/constants.py
new file mode 100644
index 0000000..c59352d
--- /dev/null
+++ b/clients/python/custos/profile/iam/admin/services/cpi/error/constants.py
@@ -0,0 +1,14 @@
+#
+# Autogenerated by Thrift Compiler (0.12.0)
+#
+# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+#
+#  options string: py
+#
+
+from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException
+from thrift.protocol.TProtocol import TProtocolException
+from thrift.TRecursive import fix_spec
+
+import sys
+from .ttypes import *
diff --git a/clients/python/custos/profile/iam/admin/services/cpi/error/ttypes.py b/clients/python/custos/profile/iam/admin/services/cpi/error/ttypes.py
new file mode 100644
index 0000000..441af21
--- /dev/null
+++ b/clients/python/custos/profile/iam/admin/services/cpi/error/ttypes.py
@@ -0,0 +1,85 @@
+#
+# Autogenerated by Thrift Compiler (0.12.0)
+#
+# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+#
+#  options string: py
+#
+
+from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException
+from thrift.protocol.TProtocol import TProtocolException
+from thrift.TRecursive import fix_spec
+
+import sys
+
+from thrift.transport import TTransport
+all_structs = []
+
+
+class IamAdminServicesException(TException):
+    """
+    Attributes:
+     - message
+
+    """
+
+
+    def __init__(self, message=None,):
+        self.message = message
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRING:
+                    self.message = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('IamAdminServicesException')
+        if self.message is not None:
+            oprot.writeFieldBegin('message', TType.STRING, 1)
+            oprot.writeString(self.message.encode('utf-8') if sys.version_info[0] == 2 else self.message)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.message is None:
+            raise TProtocolException(message='Required field message is unset!')
+        return
+
+    def __str__(self):
+        return repr(self)
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(IamAdminServicesException)
+IamAdminServicesException.thrift_spec = (
+    None,  # 0
+    (1, TType.STRING, 'message', 'UTF8', None, ),  # 1
+)
+fix_spec(all_structs)
+del all_structs
diff --git a/clients/python/custos/profile/iam/admin/services/cpi/ttypes.py b/clients/python/custos/profile/iam/admin/services/cpi/ttypes.py
new file mode 100644
index 0000000..5fb60ee
--- /dev/null
+++ b/clients/python/custos/profile/iam/admin/services/cpi/ttypes.py
@@ -0,0 +1,23 @@
+#
+# Autogenerated by Thrift Compiler (0.12.0)
+#
+# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+#
+#  options string: py
+#
+
+from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException
+from thrift.protocol.TProtocol import TProtocolException
+from thrift.TRecursive import fix_spec
+
+import sys
+import custos.commons.model.security.ttypes
+import custos.profile.model.workspace.ttypes
+import custos.profile.model.User.ttypes
+import custos.profile.model.tenant.ttypes
+import custos.profile.iam.admin.services.cpi.error.ttypes
+
+from thrift.transport import TTransport
+all_structs = []
+fix_spec(all_structs)
+del all_structs
diff --git a/clients/python/custos/profile/model/User/__init__.py b/clients/python/custos/profile/model/User/__init__.py
new file mode 100644
index 0000000..adefd8e
--- /dev/null
+++ b/clients/python/custos/profile/model/User/__init__.py
@@ -0,0 +1 @@
+__all__ = ['ttypes', 'constants']
diff --git a/clients/python/custos/profile/model/User/constants.py b/clients/python/custos/profile/model/User/constants.py
new file mode 100644
index 0000000..5cf48c3
--- /dev/null
+++ b/clients/python/custos/profile/model/User/constants.py
@@ -0,0 +1,16 @@
+#
+# Autogenerated by Thrift Compiler (0.12.0)
+#
+# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+#
+#  options string: py
+#
+
+from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException
+from thrift.protocol.TProtocol import TProtocolException
+from thrift.TRecursive import fix_spec
+
+import sys
+from .ttypes import *
+USER_PROFILE_VERSION = "1.0"
+DEFAULT_ID = "DO_NOT_SET_AT_CLIENTS"
diff --git a/clients/python/custos/profile/model/User/ttypes.py b/clients/python/custos/profile/model/User/ttypes.py
new file mode 100644
index 0000000..85a1b2b
--- /dev/null
+++ b/clients/python/custos/profile/model/User/ttypes.py
@@ -0,0 +1,788 @@
+#
+# Autogenerated by Thrift Compiler (0.12.0)
+#
+# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+#
+#  options string: py
+#
+
+from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException
+from thrift.protocol.TProtocol import TProtocolException
+from thrift.TRecursive import fix_spec
+
+import sys
+
+from thrift.transport import TTransport
+all_structs = []
+
+
+class Status(object):
+    ACTIVE = 0
+    CONFIRMED = 1
+    APPROVED = 2
+    DELETED = 3
+    DUPLICATE = 4
+    GRACE_PERIOD = 5
+    INVITED = 6
+    DENIED = 7
+    PENDING = 8
+    PENDING_APPROVAL = 9
+    PENDING_CONFIRMATION = 10
+    SUSPENDED = 11
+    DECLINED = 12
+    EXPIRED = 13
+
+    _VALUES_TO_NAMES = {
+        0: "ACTIVE",
+        1: "CONFIRMED",
+        2: "APPROVED",
+        3: "DELETED",
+        4: "DUPLICATE",
+        5: "GRACE_PERIOD",
+        6: "INVITED",
+        7: "DENIED",
+        8: "PENDING",
+        9: "PENDING_APPROVAL",
+        10: "PENDING_CONFIRMATION",
+        11: "SUSPENDED",
+        12: "DECLINED",
+        13: "EXPIRED",
+    }
+
+    _NAMES_TO_VALUES = {
+        "ACTIVE": 0,
+        "CONFIRMED": 1,
+        "APPROVED": 2,
+        "DELETED": 3,
+        "DUPLICATE": 4,
+        "GRACE_PERIOD": 5,
+        "INVITED": 6,
+        "DENIED": 7,
+        "PENDING": 8,
+        "PENDING_APPROVAL": 9,
+        "PENDING_CONFIRMATION": 10,
+        "SUSPENDED": 11,
+        "DECLINED": 12,
+        "EXPIRED": 13,
+    }
+
+
+class USCitizenship(object):
+    """
+    U.S. Citizen (see: http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
+
+
+    """
+    US_CITIZEN = 0
+    US_PERMANENT_RESIDENT = 1
+    OTHER_NON_US_CITIZEN = 2
+
+    _VALUES_TO_NAMES = {
+        0: "US_CITIZEN",
+        1: "US_PERMANENT_RESIDENT",
+        2: "OTHER_NON_US_CITIZEN",
+    }
+
+    _NAMES_TO_VALUES = {
+        "US_CITIZEN": 0,
+        "US_PERMANENT_RESIDENT": 1,
+        "OTHER_NON_US_CITIZEN": 2,
+    }
+
+
+class ethnicity(object):
+    """
+    Hispanic or Latino - a person of Mexican, Puerto Rican, Cuban, South or
+     Central American, or other Spanish culture or origin, regardless of race.
+
+
+    """
+    HISPANIC_LATINO = 0
+    NOT_HISPANIC_LATINO = 1
+
+    _VALUES_TO_NAMES = {
+        0: "HISPANIC_LATINO",
+        1: "NOT_HISPANIC_LATINO",
+    }
+
+    _NAMES_TO_VALUES = {
+        "HISPANIC_LATINO": 0,
+        "NOT_HISPANIC_LATINO": 1,
+    }
+
+
+class race(object):
+    """
+    * Asian - a person having origins in any of the original peoples of the Far East,
+    *      Southeast Asia, or the Indian subcontinent including, for example, Cambodia,
+     *      China, India, Japan, Korea, Malaysia, Pakistan, the Philippine Islands,
+     *      Thailand, and Vietnam.
+    * American Indian or Alaskan Native - a person having origins in any of the original
+     *     peoples of North and South America (including Central America), and who maintains
+      *     tribal affiliation or community attachment.
+    * Black or African American - a person having origins in any of the black racial groups
+    *      of Africa.
+    * Native Hawaiian or Pacific Islander - a person having origins in any of the original
+    *      peoples of Hawaii, Guan, Samoa, or other Pacific Islands.
+    * White - a person having origins in any of the original peoples of Europe, the Middle East, or North Africa.
+    *
+
+    """
+    ASIAN = 0
+    AMERICAN_INDIAN_OR_ALASKAN_NATIVE = 1
+    BLACK_OR_AFRICAN_AMERICAN = 2
+    NATIVE_HAWAIIAN_OR_PACIFIC_ISLANDER = 3
+    WHITE = 4
+
+    _VALUES_TO_NAMES = {
+        0: "ASIAN",
+        1: "AMERICAN_INDIAN_OR_ALASKAN_NATIVE",
+        2: "BLACK_OR_AFRICAN_AMERICAN",
+        3: "NATIVE_HAWAIIAN_OR_PACIFIC_ISLANDER",
+        4: "WHITE",
+    }
+
+    _NAMES_TO_VALUES = {
+        "ASIAN": 0,
+        "AMERICAN_INDIAN_OR_ALASKAN_NATIVE": 1,
+        "BLACK_OR_AFRICAN_AMERICAN": 2,
+        "NATIVE_HAWAIIAN_OR_PACIFIC_ISLANDER": 3,
+        "WHITE": 4,
+    }
+
+
+class disability(object):
+    HEARING_IMAPAIRED = 0
+    VISUAL_IMPAIRED = 1
+    MOBILITY_OR_ORTHOPEDIC_IMPAIRMENT = 2
+    OTHER_IMPAIRMENT = 3
+
+    _VALUES_TO_NAMES = {
+        0: "HEARING_IMAPAIRED",
+        1: "VISUAL_IMPAIRED",
+        2: "MOBILITY_OR_ORTHOPEDIC_IMPAIRMENT",
+        3: "OTHER_IMPAIRMENT",
+    }
+
+    _NAMES_TO_VALUES = {
+        "HEARING_IMAPAIRED": 0,
+        "VISUAL_IMPAIRED": 1,
+        "MOBILITY_OR_ORTHOPEDIC_IMPAIRMENT": 2,
+        "OTHER_IMPAIRMENT": 3,
+    }
+
+
+class NSFDemographics(object):
+    """
+    A structure holding the NSF Demographic information.
+
+
+
+    Attributes:
+     - custosInternalUserId
+     - gender
+     - usCitizenship
+     - ethnicities
+     - races
+     - disabilities
+
+    """
+
+
+    def __init__(self, custosInternalUserId="DO_NOT_SET_AT_CLIENTS", gender=None, usCitizenship=None, ethnicities=None, races=None, disabilities=None,):
+        self.custosInternalUserId = custosInternalUserId
+        self.gender = gender
+        self.usCitizenship = usCitizenship
+        self.ethnicities = ethnicities
+        self.races = races
+        self.disabilities = disabilities
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRING:
+                    self.custosInternalUserId = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.gender = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 3:
+                if ftype == TType.I32:
+                    self.usCitizenship = iprot.readI32()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 4:
+                if ftype == TType.LIST:
+                    self.ethnicities = []
+                    (_etype3, _size0) = iprot.readListBegin()
+                    for _i4 in range(_size0):
+                        _elem5 = iprot.readI32()
+                        self.ethnicities.append(_elem5)
+                    iprot.readListEnd()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 5:
+                if ftype == TType.LIST:
+                    self.races = []
+                    (_etype9, _size6) = iprot.readListBegin()
+                    for _i10 in range(_size6):
+                        _elem11 = iprot.readI32()
+                        self.races.append(_elem11)
+                    iprot.readListEnd()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 6:
+                if ftype == TType.LIST:
+                    self.disabilities = []
+                    (_etype15, _size12) = iprot.readListBegin()
+                    for _i16 in range(_size12):
+                        _elem17 = iprot.readI32()
+                        self.disabilities.append(_elem17)
+                    iprot.readListEnd()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('NSFDemographics')
+        if self.custosInternalUserId is not None:
+            oprot.writeFieldBegin('custosInternalUserId', TType.STRING, 1)
+            oprot.writeString(self.custosInternalUserId.encode('utf-8') if sys.version_info[0] == 2 else self.custosInternalUserId)
+            oprot.writeFieldEnd()
+        if self.gender is not None:
+            oprot.writeFieldBegin('gender', TType.STRING, 2)
+            oprot.writeString(self.gender.encode('utf-8') if sys.version_info[0] == 2 else self.gender)
+            oprot.writeFieldEnd()
+        if self.usCitizenship is not None:
+            oprot.writeFieldBegin('usCitizenship', TType.I32, 3)
+            oprot.writeI32(self.usCitizenship)
+            oprot.writeFieldEnd()
+        if self.ethnicities is not None:
+            oprot.writeFieldBegin('ethnicities', TType.LIST, 4)
+            oprot.writeListBegin(TType.I32, len(self.ethnicities))
+            for iter18 in self.ethnicities:
+                oprot.writeI32(iter18)
+            oprot.writeListEnd()
+            oprot.writeFieldEnd()
+        if self.races is not None:
+            oprot.writeFieldBegin('races', TType.LIST, 5)
+            oprot.writeListBegin(TType.I32, len(self.races))
+            for iter19 in self.races:
+                oprot.writeI32(iter19)
+            oprot.writeListEnd()
+            oprot.writeFieldEnd()
+        if self.disabilities is not None:
+            oprot.writeFieldBegin('disabilities', TType.LIST, 6)
+            oprot.writeListBegin(TType.I32, len(self.disabilities))
+            for iter20 in self.disabilities:
+                oprot.writeI32(iter20)
+            oprot.writeListEnd()
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.custosInternalUserId is None:
+            raise TProtocolException(message='Required field custosInternalUserId is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+
+
+class UserProfile(object):
+    """
+    * A structure holding the user profile and its child models.
+    *
+    * Notes:
+    *  The model does not include passwords as it is assumed an external identity provider is used to authenticate user.
+    *  References:
+    *     NSF Demographic Information - http://www.nsf.gov/pubs/2000/00form1225/00form1225.doc
+    *     LDAP Schema - https://tools.ietf.org/html/rfc4519
+    *     SCIM 2.0 - https://tools.ietf.org/html/rfc7643
+    *
+    * userModelVersion:
+    *  Version number of profile
+    *
+    * custosInternalUserId:
+    *  internal to custos, not intended to be used outside of the custos platform or possibly by gateways
+    *  (that is, never shown to users), never reassigned, REQUIRED
+    *
+    * userId:
+    *  Externally assertable unique identifier. SAML (primarly in higher education, academic) tends to keep
+    *   user name less opaque. OpenID Connect maintains them to be opaque.
+    *
+    * firstName, middleName, lastName:
+    *  First and Last names as assertede by the user
+    *
+    * namePrefix, nameSuffix:
+    *  prefix and suffix to the users name as asserted by the user
+    *
+    * emails:
+    *   Email identifier are Verified, REQUIRED and MULTIVALUED
+    *
+    * userName:
+    *  Name-based identifiers can be multivalues. To keep it simple, custos will make it a string.
+    *   In the future these can be enumerated as:
+        *   Official name (as asserted possibly by some external identity provider)
+        *   Prefered name (as asserted or suggested by user directly)
+        *   Components:
+        *      givenName
+        *      surname (familyName)
+        *      displayName (often asserted by user to handle things like middle names, suffix, prefix, and the like)
+    *
+    * orcidId: ORCID ID - http://orcid.org/about/what-is-orcid)
+    *
+    * phones: Telephone MULTIVALUED
+    *
+    * country: Country of Residance
+    *
+    * nationality Countries of citizenship
+    *
+    * comments:
+    *   Free-form information (treated as opaque by custos and simply passed to resource).
+    *
+    * labeledURI:
+      * Google Scholar, Web of Science, ACS, e.t.c
+    *
+    * timeZone:
+    *  User’s preferred timezone - IANA Timezone Databases - http://www.iana.org/time-zones.
+    *
+
+    Attributes:
+     - userModelVersion
+     - custosInternalUserId
+     - userId
+     - gatewayId
+     - emails
+     - firstName
+     - lastName
+     - middleName
+     - namePrefix
+     - nameSuffix
+     - orcidId
+     - phones
+     - country
+     - nationality
+     - homeOrganization
+     - orginationAffiliation
+     - creationTime
+     - lastAccessTime
+     - validUntil
+     - State
+     - comments
+     - labeledURI
+     - gpgKey
+     - timeZone
+     - nsfDemographics
+
+    """
+
+
+    def __init__(self, userModelVersion="1.0", custosInternalUserId="DO_NOT_SET_AT_CLIENTS", userId=None, gatewayId=None, emails=None, firstName=None, lastName=None, middleName=None, namePrefix=None, nameSuffix=None, orcidId=None, phones=None, country=None, nationality=None, homeOrganization=None, orginationAffiliation=None, creationTime=None, lastAccessTime=None, validUntil=None, State=None, comments=None, labeledURI=None, gpgKey=None, timeZone=None, nsfDemographics=None,):
+        self.userModelVersion = userModelVersion
+        self.custosInternalUserId = custosInternalUserId
+        self.userId = userId
+        self.gatewayId = gatewayId
+        self.emails = emails
+        self.firstName = firstName
+        self.lastName = lastName
+        self.middleName = middleName
+        self.namePrefix = namePrefix
+        self.nameSuffix = nameSuffix
+        self.orcidId = orcidId
+        self.phones = phones
+        self.country = country
+        self.nationality = nationality
+        self.homeOrganization = homeOrganization
+        self.orginationAffiliation = orginationAffiliation
+        self.creationTime = creationTime
+        self.lastAccessTime = lastAccessTime
+        self.validUntil = validUntil
+        self.State = State
+        self.comments = comments
+        self.labeledURI = labeledURI
+        self.gpgKey = gpgKey
+        self.timeZone = timeZone
+        self.nsfDemographics = nsfDemographics
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRING:
+                    self.userModelVersion = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.custosInternalUserId = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 3:
+                if ftype == TType.STRING:
+                    self.userId = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 4:
+                if ftype == TType.STRING:
+                    self.gatewayId = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 5:
+                if ftype == TType.LIST:
+                    self.emails = []
+                    (_etype24, _size21) = iprot.readListBegin()
+                    for _i25 in range(_size21):
+                        _elem26 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                        self.emails.append(_elem26)
+                    iprot.readListEnd()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 6:
+                if ftype == TType.STRING:
+                    self.firstName = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 7:
+                if ftype == TType.STRING:
+                    self.lastName = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 8:
+                if ftype == TType.STRING:
+                    self.middleName = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 9:
+                if ftype == TType.STRING:
+                    self.namePrefix = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 10:
+                if ftype == TType.STRING:
+                    self.nameSuffix = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 11:
+                if ftype == TType.STRING:
+                    self.orcidId = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 12:
+                if ftype == TType.LIST:
+                    self.phones = []
+                    (_etype30, _size27) = iprot.readListBegin()
+                    for _i31 in range(_size27):
+                        _elem32 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                        self.phones.append(_elem32)
+                    iprot.readListEnd()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 13:
+                if ftype == TType.STRING:
+                    self.country = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 14:
+                if ftype == TType.LIST:
+                    self.nationality = []
+                    (_etype36, _size33) = iprot.readListBegin()
+                    for _i37 in range(_size33):
+                        _elem38 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                        self.nationality.append(_elem38)
+                    iprot.readListEnd()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 15:
+                if ftype == TType.STRING:
+                    self.homeOrganization = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 16:
+                if ftype == TType.STRING:
+                    self.orginationAffiliation = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 17:
+                if ftype == TType.I64:
+                    self.creationTime = iprot.readI64()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 18:
+                if ftype == TType.I64:
+                    self.lastAccessTime = iprot.readI64()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 19:
+                if ftype == TType.I64:
+                    self.validUntil = iprot.readI64()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 20:
+                if ftype == TType.I32:
+                    self.State = iprot.readI32()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 21:
+                if ftype == TType.STRING:
+                    self.comments = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 22:
+                if ftype == TType.LIST:
+                    self.labeledURI = []
+                    (_etype42, _size39) = iprot.readListBegin()
+                    for _i43 in range(_size39):
+                        _elem44 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                        self.labeledURI.append(_elem44)
+                    iprot.readListEnd()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 23:
+                if ftype == TType.STRING:
+                    self.gpgKey = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 24:
+                if ftype == TType.STRING:
+                    self.timeZone = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 25:
+                if ftype == TType.STRUCT:
+                    self.nsfDemographics = NSFDemographics()
+                    self.nsfDemographics.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('UserProfile')
+        if self.userModelVersion is not None:
+            oprot.writeFieldBegin('userModelVersion', TType.STRING, 1)
+            oprot.writeString(self.userModelVersion.encode('utf-8') if sys.version_info[0] == 2 else self.userModelVersion)
+            oprot.writeFieldEnd()
+        if self.custosInternalUserId is not None:
+            oprot.writeFieldBegin('custosInternalUserId', TType.STRING, 2)
+            oprot.writeString(self.custosInternalUserId.encode('utf-8') if sys.version_info[0] == 2 else self.custosInternalUserId)
+            oprot.writeFieldEnd()
+        if self.userId is not None:
+            oprot.writeFieldBegin('userId', TType.STRING, 3)
+            oprot.writeString(self.userId.encode('utf-8') if sys.version_info[0] == 2 else self.userId)
+            oprot.writeFieldEnd()
+        if self.gatewayId is not None:
+            oprot.writeFieldBegin('gatewayId', TType.STRING, 4)
+            oprot.writeString(self.gatewayId.encode('utf-8') if sys.version_info[0] == 2 else self.gatewayId)
+            oprot.writeFieldEnd()
+        if self.emails is not None:
+            oprot.writeFieldBegin('emails', TType.LIST, 5)
+            oprot.writeListBegin(TType.STRING, len(self.emails))
+            for iter45 in self.emails:
+                oprot.writeString(iter45.encode('utf-8') if sys.version_info[0] == 2 else iter45)
+            oprot.writeListEnd()
+            oprot.writeFieldEnd()
+        if self.firstName is not None:
+            oprot.writeFieldBegin('firstName', TType.STRING, 6)
+            oprot.writeString(self.firstName.encode('utf-8') if sys.version_info[0] == 2 else self.firstName)
+            oprot.writeFieldEnd()
+        if self.lastName is not None:
+            oprot.writeFieldBegin('lastName', TType.STRING, 7)
+            oprot.writeString(self.lastName.encode('utf-8') if sys.version_info[0] == 2 else self.lastName)
+            oprot.writeFieldEnd()
+        if self.middleName is not None:
+            oprot.writeFieldBegin('middleName', TType.STRING, 8)
+            oprot.writeString(self.middleName.encode('utf-8') if sys.version_info[0] == 2 else self.middleName)
+            oprot.writeFieldEnd()
+        if self.namePrefix is not None:
+            oprot.writeFieldBegin('namePrefix', TType.STRING, 9)
+            oprot.writeString(self.namePrefix.encode('utf-8') if sys.version_info[0] == 2 else self.namePrefix)
+            oprot.writeFieldEnd()
+        if self.nameSuffix is not None:
+            oprot.writeFieldBegin('nameSuffix', TType.STRING, 10)
+            oprot.writeString(self.nameSuffix.encode('utf-8') if sys.version_info[0] == 2 else self.nameSuffix)
+            oprot.writeFieldEnd()
+        if self.orcidId is not None:
+            oprot.writeFieldBegin('orcidId', TType.STRING, 11)
+            oprot.writeString(self.orcidId.encode('utf-8') if sys.version_info[0] == 2 else self.orcidId)
+            oprot.writeFieldEnd()
+        if self.phones is not None:
+            oprot.writeFieldBegin('phones', TType.LIST, 12)
+            oprot.writeListBegin(TType.STRING, len(self.phones))
+            for iter46 in self.phones:
+                oprot.writeString(iter46.encode('utf-8') if sys.version_info[0] == 2 else iter46)
+            oprot.writeListEnd()
+            oprot.writeFieldEnd()
+        if self.country is not None:
+            oprot.writeFieldBegin('country', TType.STRING, 13)
+            oprot.writeString(self.country.encode('utf-8') if sys.version_info[0] == 2 else self.country)
+            oprot.writeFieldEnd()
+        if self.nationality is not None:
+            oprot.writeFieldBegin('nationality', TType.LIST, 14)
+            oprot.writeListBegin(TType.STRING, len(self.nationality))
+            for iter47 in self.nationality:
+                oprot.writeString(iter47.encode('utf-8') if sys.version_info[0] == 2 else iter47)
+            oprot.writeListEnd()
+            oprot.writeFieldEnd()
+        if self.homeOrganization is not None:
+            oprot.writeFieldBegin('homeOrganization', TType.STRING, 15)
+            oprot.writeString(self.homeOrganization.encode('utf-8') if sys.version_info[0] == 2 else self.homeOrganization)
+            oprot.writeFieldEnd()
+        if self.orginationAffiliation is not None:
+            oprot.writeFieldBegin('orginationAffiliation', TType.STRING, 16)
+            oprot.writeString(self.orginationAffiliation.encode('utf-8') if sys.version_info[0] == 2 else self.orginationAffiliation)
+            oprot.writeFieldEnd()
+        if self.creationTime is not None:
+            oprot.writeFieldBegin('creationTime', TType.I64, 17)
+            oprot.writeI64(self.creationTime)
+            oprot.writeFieldEnd()
+        if self.lastAccessTime is not None:
+            oprot.writeFieldBegin('lastAccessTime', TType.I64, 18)
+            oprot.writeI64(self.lastAccessTime)
+            oprot.writeFieldEnd()
+        if self.validUntil is not None:
+            oprot.writeFieldBegin('validUntil', TType.I64, 19)
+            oprot.writeI64(self.validUntil)
+            oprot.writeFieldEnd()
+        if self.State is not None:
+            oprot.writeFieldBegin('State', TType.I32, 20)
+            oprot.writeI32(self.State)
+            oprot.writeFieldEnd()
+        if self.comments is not None:
+            oprot.writeFieldBegin('comments', TType.STRING, 21)
+            oprot.writeString(self.comments.encode('utf-8') if sys.version_info[0] == 2 else self.comments)
+            oprot.writeFieldEnd()
+        if self.labeledURI is not None:
+            oprot.writeFieldBegin('labeledURI', TType.LIST, 22)
+            oprot.writeListBegin(TType.STRING, len(self.labeledURI))
+            for iter48 in self.labeledURI:
+                oprot.writeString(iter48.encode('utf-8') if sys.version_info[0] == 2 else iter48)
+            oprot.writeListEnd()
+            oprot.writeFieldEnd()
+        if self.gpgKey is not None:
+            oprot.writeFieldBegin('gpgKey', TType.STRING, 23)
+            oprot.writeString(self.gpgKey.encode('utf-8') if sys.version_info[0] == 2 else self.gpgKey)
+            oprot.writeFieldEnd()
+        if self.timeZone is not None:
+            oprot.writeFieldBegin('timeZone', TType.STRING, 24)
+            oprot.writeString(self.timeZone.encode('utf-8') if sys.version_info[0] == 2 else self.timeZone)
+            oprot.writeFieldEnd()
+        if self.nsfDemographics is not None:
+            oprot.writeFieldBegin('nsfDemographics', TType.STRUCT, 25)
+            self.nsfDemographics.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.userModelVersion is None:
+            raise TProtocolException(message='Required field userModelVersion is unset!')
+        if self.custosInternalUserId is None:
+            raise TProtocolException(message='Required field custosInternalUserId is unset!')
+        if self.userId is None:
+            raise TProtocolException(message='Required field userId is unset!')
+        if self.gatewayId is None:
+            raise TProtocolException(message='Required field gatewayId is unset!')
+        if self.emails is None:
+            raise TProtocolException(message='Required field emails is unset!')
+        if self.firstName is None:
+            raise TProtocolException(message='Required field firstName is unset!')
+        if self.lastName is None:
+            raise TProtocolException(message='Required field lastName is unset!')
+        if self.creationTime is None:
+            raise TProtocolException(message='Required field creationTime is unset!')
+        if self.lastAccessTime is None:
+            raise TProtocolException(message='Required field lastAccessTime is unset!')
+        if self.validUntil is None:
+            raise TProtocolException(message='Required field validUntil is unset!')
+        if self.State is None:
+            raise TProtocolException(message='Required field State is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(NSFDemographics)
+NSFDemographics.thrift_spec = (
+    None,  # 0
+    (1, TType.STRING, 'custosInternalUserId', 'UTF8', "DO_NOT_SET_AT_CLIENTS", ),  # 1
+    (2, TType.STRING, 'gender', 'UTF8', None, ),  # 2
+    (3, TType.I32, 'usCitizenship', None, None, ),  # 3
+    (4, TType.LIST, 'ethnicities', (TType.I32, None, False), None, ),  # 4
+    (5, TType.LIST, 'races', (TType.I32, None, False), None, ),  # 5
+    (6, TType.LIST, 'disabilities', (TType.I32, None, False), None, ),  # 6
+)
+all_structs.append(UserProfile)
+UserProfile.thrift_spec = (
+    None,  # 0
+    (1, TType.STRING, 'userModelVersion', 'UTF8', "1.0", ),  # 1
+    (2, TType.STRING, 'custosInternalUserId', 'UTF8', "DO_NOT_SET_AT_CLIENTS", ),  # 2
+    (3, TType.STRING, 'userId', 'UTF8', None, ),  # 3
+    (4, TType.STRING, 'gatewayId', 'UTF8', None, ),  # 4
+    (5, TType.LIST, 'emails', (TType.STRING, 'UTF8', False), None, ),  # 5
+    (6, TType.STRING, 'firstName', 'UTF8', None, ),  # 6
+    (7, TType.STRING, 'lastName', 'UTF8', None, ),  # 7
+    (8, TType.STRING, 'middleName', 'UTF8', None, ),  # 8
+    (9, TType.STRING, 'namePrefix', 'UTF8', None, ),  # 9
+    (10, TType.STRING, 'nameSuffix', 'UTF8', None, ),  # 10
+    (11, TType.STRING, 'orcidId', 'UTF8', None, ),  # 11
+    (12, TType.LIST, 'phones', (TType.STRING, 'UTF8', False), None, ),  # 12
+    (13, TType.STRING, 'country', 'UTF8', None, ),  # 13
+    (14, TType.LIST, 'nationality', (TType.STRING, 'UTF8', False), None, ),  # 14
+    (15, TType.STRING, 'homeOrganization', 'UTF8', None, ),  # 15
+    (16, TType.STRING, 'orginationAffiliation', 'UTF8', None, ),  # 16
+    (17, TType.I64, 'creationTime', None, None, ),  # 17
+    (18, TType.I64, 'lastAccessTime', None, None, ),  # 18
+    (19, TType.I64, 'validUntil', None, None, ),  # 19
+    (20, TType.I32, 'State', None, None, ),  # 20
+    (21, TType.STRING, 'comments', 'UTF8', None, ),  # 21
+    (22, TType.LIST, 'labeledURI', (TType.STRING, 'UTF8', False), None, ),  # 22
+    (23, TType.STRING, 'gpgKey', 'UTF8', None, ),  # 23
+    (24, TType.STRING, 'timeZone', 'UTF8', None, ),  # 24
+    (25, TType.STRUCT, 'nsfDemographics', [NSFDemographics, None], None, ),  # 25
+)
+fix_spec(all_structs)
+del all_structs
diff --git a/clients/python/custos/profile/model/__init__.py b/clients/python/custos/profile/model/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/clients/python/custos/profile/model/tenant/__init__.py b/clients/python/custos/profile/model/tenant/__init__.py
new file mode 100644
index 0000000..adefd8e
--- /dev/null
+++ b/clients/python/custos/profile/model/tenant/__init__.py
@@ -0,0 +1 @@
+__all__ = ['ttypes', 'constants']
diff --git a/clients/python/custos/profile/model/tenant/constants.py b/clients/python/custos/profile/model/tenant/constants.py
new file mode 100644
index 0000000..c59352d
--- /dev/null
+++ b/clients/python/custos/profile/model/tenant/constants.py
@@ -0,0 +1,14 @@
+#
+# Autogenerated by Thrift Compiler (0.12.0)
+#
+# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+#
+#  options string: py
+#
+
+from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException
+from thrift.protocol.TProtocol import TProtocolException
+from thrift.TRecursive import fix_spec
+
+import sys
+from .ttypes import *
diff --git a/clients/python/custos/profile/model/tenant/ttypes.py b/clients/python/custos/profile/model/tenant/ttypes.py
new file mode 100644
index 0000000..f216058
--- /dev/null
+++ b/clients/python/custos/profile/model/tenant/ttypes.py
@@ -0,0 +1,604 @@
+#
+# Autogenerated by Thrift Compiler (0.12.0)
+#
+# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+#
+#  options string: py
+#
+
+from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException
+from thrift.protocol.TProtocol import TProtocolException
+from thrift.TRecursive import fix_spec
+
+import sys
+
+from thrift.transport import TTransport
+all_structs = []
+
+
+class TenantApprovalStatus(object):
+    REQUESTED = 0
+    APPROVED = 1
+    ACTIVE = 2
+    DEACTIVATED = 3
+    CANCELLED = 4
+    DENIED = 5
+    CREATED = 6
+    DEPLOYED = 7
+
+    _VALUES_TO_NAMES = {
+        0: "REQUESTED",
+        1: "APPROVED",
+        2: "ACTIVE",
+        3: "DEACTIVATED",
+        4: "CANCELLED",
+        5: "DENIED",
+        6: "CREATED",
+        7: "DEPLOYED",
+    }
+
+    _NAMES_TO_VALUES = {
+        "REQUESTED": 0,
+        "APPROVED": 1,
+        "ACTIVE": 2,
+        "DEACTIVATED": 3,
+        "CANCELLED": 4,
+        "DENIED": 5,
+        "CREATED": 6,
+        "DEPLOYED": 7,
+    }
+
+
+class TenantPreferences(object):
+    """
+    Attributes:
+     - tenantAdminFirstName
+     - tenantAdminLastName
+     - tenantAdminEmail
+
+    """
+
+
+    def __init__(self, tenantAdminFirstName=None, tenantAdminLastName=None, tenantAdminEmail=None,):
+        self.tenantAdminFirstName = tenantAdminFirstName
+        self.tenantAdminLastName = tenantAdminLastName
+        self.tenantAdminEmail = tenantAdminEmail
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 10:
+                if ftype == TType.STRING:
+                    self.tenantAdminFirstName = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 11:
+                if ftype == TType.STRING:
+                    self.tenantAdminLastName = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 12:
+                if ftype == TType.STRING:
+                    self.tenantAdminEmail = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('TenantPreferences')
+        if self.tenantAdminFirstName is not None:
+            oprot.writeFieldBegin('tenantAdminFirstName', TType.STRING, 10)
+            oprot.writeString(self.tenantAdminFirstName.encode('utf-8') if sys.version_info[0] == 2 else self.tenantAdminFirstName)
+            oprot.writeFieldEnd()
+        if self.tenantAdminLastName is not None:
+            oprot.writeFieldBegin('tenantAdminLastName', TType.STRING, 11)
+            oprot.writeString(self.tenantAdminLastName.encode('utf-8') if sys.version_info[0] == 2 else self.tenantAdminLastName)
+            oprot.writeFieldEnd()
+        if self.tenantAdminEmail is not None:
+            oprot.writeFieldBegin('tenantAdminEmail', TType.STRING, 12)
+            oprot.writeString(self.tenantAdminEmail.encode('utf-8') if sys.version_info[0] == 2 else self.tenantAdminEmail)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+
+
+class TenantConfig(object):
+    """
+    Attributes:
+     - oauthClientId
+     - oauthClientSecret
+     - identityServerUserName
+     - identityServerPasswordToken
+
+    """
+
+
+    def __init__(self, oauthClientId=None, oauthClientSecret=None, identityServerUserName=None, identityServerPasswordToken=None,):
+        self.oauthClientId = oauthClientId
+        self.oauthClientSecret = oauthClientSecret
+        self.identityServerUserName = identityServerUserName
+        self.identityServerPasswordToken = identityServerPasswordToken
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 16:
+                if ftype == TType.STRING:
+                    self.oauthClientId = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 17:
+                if ftype == TType.STRING:
+                    self.oauthClientSecret = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 13:
+                if ftype == TType.STRING:
+                    self.identityServerUserName = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 14:
+                if ftype == TType.STRING:
+                    self.identityServerPasswordToken = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('TenantConfig')
+        if self.identityServerUserName is not None:
+            oprot.writeFieldBegin('identityServerUserName', TType.STRING, 13)
+            oprot.writeString(self.identityServerUserName.encode('utf-8') if sys.version_info[0] == 2 else self.identityServerUserName)
+            oprot.writeFieldEnd()
+        if self.identityServerPasswordToken is not None:
+            oprot.writeFieldBegin('identityServerPasswordToken', TType.STRING, 14)
+            oprot.writeString(self.identityServerPasswordToken.encode('utf-8') if sys.version_info[0] == 2 else self.identityServerPasswordToken)
+            oprot.writeFieldEnd()
+        if self.oauthClientId is not None:
+            oprot.writeFieldBegin('oauthClientId', TType.STRING, 16)
+            oprot.writeString(self.oauthClientId.encode('utf-8') if sys.version_info[0] == 2 else self.oauthClientId)
+            oprot.writeFieldEnd()
+        if self.oauthClientSecret is not None:
+            oprot.writeFieldBegin('oauthClientSecret', TType.STRING, 17)
+            oprot.writeString(self.oauthClientSecret.encode('utf-8') if sys.version_info[0] == 2 else self.oauthClientSecret)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+
+
+class Tenant(object):
+    """
+    Attributes:
+     - tenantId
+     - tenantApprovalStatus
+     - tenantName
+     - domain
+     - emailAddress
+     - tenantAcronym
+     - tenantURL
+     - tenantPublicAbstract
+     - reviewProposalDescription
+     - declinedReason
+     - requestCreationTime
+     - requesterUsername
+
+    """
+
+
+    def __init__(self, tenantId=None, tenantApprovalStatus=None, tenantName=None, domain=None, emailAddress=None, tenantAcronym=None, tenantURL=None, tenantPublicAbstract=None, reviewProposalDescription=None, declinedReason=None, requestCreationTime=None, requesterUsername=None,):
+        self.tenantId = tenantId
+        self.tenantApprovalStatus = tenantApprovalStatus
+        self.tenantName = tenantName
+        self.domain = domain
+        self.emailAddress = emailAddress
+        self.tenantAcronym = tenantAcronym
+        self.tenantURL = tenantURL
+        self.tenantPublicAbstract = tenantPublicAbstract
+        self.reviewProposalDescription = reviewProposalDescription
+        self.declinedReason = declinedReason
+        self.requestCreationTime = requestCreationTime
+        self.requesterUsername = requesterUsername
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRING:
+                    self.tenantId = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.I32:
+                    self.tenantApprovalStatus = iprot.readI32()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 3:
+                if ftype == TType.STRING:
+                    self.tenantName = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 4:
+                if ftype == TType.STRING:
+                    self.domain = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 5:
+                if ftype == TType.STRING:
+                    self.emailAddress = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 6:
+                if ftype == TType.STRING:
+                    self.tenantAcronym = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 7:
+                if ftype == TType.STRING:
+                    self.tenantURL = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 8:
+                if ftype == TType.STRING:
+                    self.tenantPublicAbstract = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 9:
+                if ftype == TType.STRING:
+                    self.reviewProposalDescription = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 15:
+                if ftype == TType.STRING:
+                    self.declinedReason = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 18:
+                if ftype == TType.I64:
+                    self.requestCreationTime = iprot.readI64()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 19:
+                if ftype == TType.STRING:
+                    self.requesterUsername = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('Tenant')
+        if self.tenantId is not None:
+            oprot.writeFieldBegin('tenantId', TType.STRING, 1)
+            oprot.writeString(self.tenantId.encode('utf-8') if sys.version_info[0] == 2 else self.tenantId)
+            oprot.writeFieldEnd()
+        if self.tenantApprovalStatus is not None:
+            oprot.writeFieldBegin('tenantApprovalStatus', TType.I32, 2)
+            oprot.writeI32(self.tenantApprovalStatus)
+            oprot.writeFieldEnd()
+        if self.tenantName is not None:
+            oprot.writeFieldBegin('tenantName', TType.STRING, 3)
+            oprot.writeString(self.tenantName.encode('utf-8') if sys.version_info[0] == 2 else self.tenantName)
+            oprot.writeFieldEnd()
+        if self.domain is not None:
+            oprot.writeFieldBegin('domain', TType.STRING, 4)
+            oprot.writeString(self.domain.encode('utf-8') if sys.version_info[0] == 2 else self.domain)
+            oprot.writeFieldEnd()
+        if self.emailAddress is not None:
+            oprot.writeFieldBegin('emailAddress', TType.STRING, 5)
+            oprot.writeString(self.emailAddress.encode('utf-8') if sys.version_info[0] == 2 else self.emailAddress)
+            oprot.writeFieldEnd()
+        if self.tenantAcronym is not None:
+            oprot.writeFieldBegin('tenantAcronym', TType.STRING, 6)
+            oprot.writeString(self.tenantAcronym.encode('utf-8') if sys.version_info[0] == 2 else self.tenantAcronym)
+            oprot.writeFieldEnd()
+        if self.tenantURL is not None:
+            oprot.writeFieldBegin('tenantURL', TType.STRING, 7)
+            oprot.writeString(self.tenantURL.encode('utf-8') if sys.version_info[0] == 2 else self.tenantURL)
+            oprot.writeFieldEnd()
+        if self.tenantPublicAbstract is not None:
+            oprot.writeFieldBegin('tenantPublicAbstract', TType.STRING, 8)
+            oprot.writeString(self.tenantPublicAbstract.encode('utf-8') if sys.version_info[0] == 2 else self.tenantPublicAbstract)
+            oprot.writeFieldEnd()
+        if self.reviewProposalDescription is not None:
+            oprot.writeFieldBegin('reviewProposalDescription', TType.STRING, 9)
+            oprot.writeString(self.reviewProposalDescription.encode('utf-8') if sys.version_info[0] == 2 else self.reviewProposalDescription)
+            oprot.writeFieldEnd()
+        if self.declinedReason is not None:
+            oprot.writeFieldBegin('declinedReason', TType.STRING, 15)
+            oprot.writeString(self.declinedReason.encode('utf-8') if sys.version_info[0] == 2 else self.declinedReason)
+            oprot.writeFieldEnd()
+        if self.requestCreationTime is not None:
+            oprot.writeFieldBegin('requestCreationTime', TType.I64, 18)
+            oprot.writeI64(self.requestCreationTime)
+            oprot.writeFieldEnd()
+        if self.requesterUsername is not None:
+            oprot.writeFieldBegin('requesterUsername', TType.STRING, 19)
+            oprot.writeString(self.requesterUsername.encode('utf-8') if sys.version_info[0] == 2 else self.requesterUsername)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.tenantId is None:
+            raise TProtocolException(message='Required field tenantId is unset!')
+        if self.tenantApprovalStatus is None:
+            raise TProtocolException(message='Required field tenantApprovalStatus is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+
+
+class PasswordCredential(object):
+    """
+    Attributes:
+     - gatewayId
+     - portalUserName
+     - loginUserName
+     - password
+     - description
+     - persistedTime
+     - token
+
+    """
+
+
+    def __init__(self, gatewayId=None, portalUserName=None, loginUserName=None, password=None, description=None, persistedTime=None, token=None,):
+        self.gatewayId = gatewayId
+        self.portalUserName = portalUserName
+        self.loginUserName = loginUserName
+        self.password = password
+        self.description = description
+        self.persistedTime = persistedTime
+        self.token = token
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRING:
+                    self.gatewayId = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.portalUserName = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 3:
+                if ftype == TType.STRING:
+                    self.loginUserName = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 4:
+                if ftype == TType.STRING:
+                    self.password = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 5:
+                if ftype == TType.STRING:
+                    self.description = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 6:
+                if ftype == TType.I64:
+                    self.persistedTime = iprot.readI64()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 7:
+                if ftype == TType.STRING:
+                    self.token = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('PasswordCredential')
+        if self.gatewayId is not None:
+            oprot.writeFieldBegin('gatewayId', TType.STRING, 1)
+            oprot.writeString(self.gatewayId.encode('utf-8') if sys.version_info[0] == 2 else self.gatewayId)
+            oprot.writeFieldEnd()
+        if self.portalUserName is not None:
+            oprot.writeFieldBegin('portalUserName', TType.STRING, 2)
+            oprot.writeString(self.portalUserName.encode('utf-8') if sys.version_info[0] == 2 else self.portalUserName)
+            oprot.writeFieldEnd()
+        if self.loginUserName is not None:
+            oprot.writeFieldBegin('loginUserName', TType.STRING, 3)
+            oprot.writeString(self.loginUserName.encode('utf-8') if sys.version_info[0] == 2 else self.loginUserName)
+            oprot.writeFieldEnd()
+        if self.password is not None:
+            oprot.writeFieldBegin('password', TType.STRING, 4)
+            oprot.writeString(self.password.encode('utf-8') if sys.version_info[0] == 2 else self.password)
+            oprot.writeFieldEnd()
+        if self.description is not None:
+            oprot.writeFieldBegin('description', TType.STRING, 5)
+            oprot.writeString(self.description.encode('utf-8') if sys.version_info[0] == 2 else self.description)
+            oprot.writeFieldEnd()
+        if self.persistedTime is not None:
+            oprot.writeFieldBegin('persistedTime', TType.I64, 6)
+            oprot.writeI64(self.persistedTime)
+            oprot.writeFieldEnd()
+        if self.token is not None:
+            oprot.writeFieldBegin('token', TType.STRING, 7)
+            oprot.writeString(self.token.encode('utf-8') if sys.version_info[0] == 2 else self.token)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.gatewayId is None:
+            raise TProtocolException(message='Required field gatewayId is unset!')
+        if self.portalUserName is None:
+            raise TProtocolException(message='Required field portalUserName is unset!')
+        if self.loginUserName is None:
+            raise TProtocolException(message='Required field loginUserName is unset!')
+        if self.password is None:
+            raise TProtocolException(message='Required field password is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(TenantPreferences)
+TenantPreferences.thrift_spec = (
+    None,  # 0
+    None,  # 1
+    None,  # 2
+    None,  # 3
+    None,  # 4
+    None,  # 5
+    None,  # 6
+    None,  # 7
+    None,  # 8
+    None,  # 9
+    (10, TType.STRING, 'tenantAdminFirstName', 'UTF8', None, ),  # 10
+    (11, TType.STRING, 'tenantAdminLastName', 'UTF8', None, ),  # 11
+    (12, TType.STRING, 'tenantAdminEmail', 'UTF8', None, ),  # 12
+)
+all_structs.append(TenantConfig)
+TenantConfig.thrift_spec = (
+    None,  # 0
+    None,  # 1
+    None,  # 2
+    None,  # 3
+    None,  # 4
+    None,  # 5
+    None,  # 6
+    None,  # 7
+    None,  # 8
+    None,  # 9
+    None,  # 10
+    None,  # 11
+    None,  # 12
+    (13, TType.STRING, 'identityServerUserName', 'UTF8', None, ),  # 13
+    (14, TType.STRING, 'identityServerPasswordToken', 'UTF8', None, ),  # 14
+    None,  # 15
+    (16, TType.STRING, 'oauthClientId', 'UTF8', None, ),  # 16
+    (17, TType.STRING, 'oauthClientSecret', 'UTF8', None, ),  # 17
+)
+all_structs.append(Tenant)
+Tenant.thrift_spec = (
+    None,  # 0
+    (1, TType.STRING, 'tenantId', 'UTF8', None, ),  # 1
+    (2, TType.I32, 'tenantApprovalStatus', None, None, ),  # 2
+    (3, TType.STRING, 'tenantName', 'UTF8', None, ),  # 3
+    (4, TType.STRING, 'domain', 'UTF8', None, ),  # 4
+    (5, TType.STRING, 'emailAddress', 'UTF8', None, ),  # 5
+    (6, TType.STRING, 'tenantAcronym', 'UTF8', None, ),  # 6
+    (7, TType.STRING, 'tenantURL', 'UTF8', None, ),  # 7
+    (8, TType.STRING, 'tenantPublicAbstract', 'UTF8', None, ),  # 8
+    (9, TType.STRING, 'reviewProposalDescription', 'UTF8', None, ),  # 9
+    None,  # 10
+    None,  # 11
+    None,  # 12
+    None,  # 13
+    None,  # 14
+    (15, TType.STRING, 'declinedReason', 'UTF8', None, ),  # 15
+    None,  # 16
+    None,  # 17
+    (18, TType.I64, 'requestCreationTime', None, None, ),  # 18
+    (19, TType.STRING, 'requesterUsername', 'UTF8', None, ),  # 19
+)
+all_structs.append(PasswordCredential)
+PasswordCredential.thrift_spec = (
+    None,  # 0
+    (1, TType.STRING, 'gatewayId', 'UTF8', None, ),  # 1
+    (2, TType.STRING, 'portalUserName', 'UTF8', None, ),  # 2
+    (3, TType.STRING, 'loginUserName', 'UTF8', None, ),  # 3
+    (4, TType.STRING, 'password', 'UTF8', None, ),  # 4
+    (5, TType.STRING, 'description', 'UTF8', None, ),  # 5
+    (6, TType.I64, 'persistedTime', None, None, ),  # 6
+    (7, TType.STRING, 'token', 'UTF8', None, ),  # 7
+)
+fix_spec(all_structs)
+del all_structs
diff --git a/clients/python/custos/profile/model/workspace/__init__.py b/clients/python/custos/profile/model/workspace/__init__.py
new file mode 100644
index 0000000..adefd8e
--- /dev/null
+++ b/clients/python/custos/profile/model/workspace/__init__.py
@@ -0,0 +1 @@
+__all__ = ['ttypes', 'constants']
diff --git a/clients/python/custos/profile/model/workspace/constants.py b/clients/python/custos/profile/model/workspace/constants.py
new file mode 100644
index 0000000..c59352d
--- /dev/null
+++ b/clients/python/custos/profile/model/workspace/constants.py
@@ -0,0 +1,14 @@
+#
+# Autogenerated by Thrift Compiler (0.12.0)
+#
+# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+#
+#  options string: py
+#
+
+from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException
+from thrift.protocol.TProtocol import TProtocolException
+from thrift.TRecursive import fix_spec
+
+import sys
+from .ttypes import *
diff --git a/clients/python/custos/profile/model/workspace/ttypes.py b/clients/python/custos/profile/model/workspace/ttypes.py
new file mode 100644
index 0000000..1600229
--- /dev/null
+++ b/clients/python/custos/profile/model/workspace/ttypes.py
@@ -0,0 +1,345 @@
+#
+# Autogenerated by Thrift Compiler (0.12.0)
+#
+# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+#
+#  options string: py
+#
+
+from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException
+from thrift.protocol.TProtocol import TProtocolException
+from thrift.TRecursive import fix_spec
+
+import sys
+
+from thrift.transport import TTransport
+all_structs = []
+
+
+class GatewayApprovalStatus(object):
+    REQUESTED = 0
+    APPROVED = 1
+    ACTIVE = 2
+    DEACTIVATED = 3
+    CANCELLED = 4
+    DENIED = 5
+    CREATED = 6
+    DEPLOYED = 7
+
+    _VALUES_TO_NAMES = {
+        0: "REQUESTED",
+        1: "APPROVED",
+        2: "ACTIVE",
+        3: "DEACTIVATED",
+        4: "CANCELLED",
+        5: "DENIED",
+        6: "CREATED",
+        7: "DEPLOYED",
+    }
+
+    _NAMES_TO_VALUES = {
+        "REQUESTED": 0,
+        "APPROVED": 1,
+        "ACTIVE": 2,
+        "DEACTIVATED": 3,
+        "CANCELLED": 4,
+        "DENIED": 5,
+        "CREATED": 6,
+        "DEPLOYED": 7,
+    }
+
+
+class Gateway(object):
+    """
+    Attributes:
+     - custosInternalGatewayId
+     - gatewayId
+     - gatewayApprovalStatus
+     - gatewayName
+     - domain
+     - emailAddress
+     - gatewayAcronym
+     - gatewayURL
+     - gatewayPublicAbstract
+     - reviewProposalDescription
+     - gatewayAdminFirstName
+     - gatewayAdminLastName
+     - gatewayAdminEmail
+     - identityServerUserName
+     - identityServerPasswordToken
+     - declinedReason
+     - oauthClientId
+     - oauthClientSecret
+     - requestCreationTime
+     - requesterUsername
+
+    """
+
+
+    def __init__(self, custosInternalGatewayId=None, gatewayId=None, gatewayApprovalStatus=None, gatewayName=None, domain=None, emailAddress=None, gatewayAcronym=None, gatewayURL=None, gatewayPublicAbstract=None, reviewProposalDescription=None, gatewayAdminFirstName=None, gatewayAdminLastName=None, gatewayAdminEmail=None, identityServerUserName=None, identityServerPasswordToken=None, declinedReason=None, oauthClientId=None, oauthClientSecret=None, requestCreationTime=None, requesterUsern [...]
+        self.custosInternalGatewayId = custosInternalGatewayId
+        self.gatewayId = gatewayId
+        self.gatewayApprovalStatus = gatewayApprovalStatus
+        self.gatewayName = gatewayName
+        self.domain = domain
+        self.emailAddress = emailAddress
+        self.gatewayAcronym = gatewayAcronym
+        self.gatewayURL = gatewayURL
+        self.gatewayPublicAbstract = gatewayPublicAbstract
+        self.reviewProposalDescription = reviewProposalDescription
+        self.gatewayAdminFirstName = gatewayAdminFirstName
+        self.gatewayAdminLastName = gatewayAdminLastName
+        self.gatewayAdminEmail = gatewayAdminEmail
+        self.identityServerUserName = identityServerUserName
+        self.identityServerPasswordToken = identityServerPasswordToken
+        self.declinedReason = declinedReason
+        self.oauthClientId = oauthClientId
+        self.oauthClientSecret = oauthClientSecret
+        self.requestCreationTime = requestCreationTime
+        self.requesterUsername = requesterUsername
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRING:
+                    self.custosInternalGatewayId = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.gatewayId = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 3:
+                if ftype == TType.I32:
+                    self.gatewayApprovalStatus = iprot.readI32()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 4:
+                if ftype == TType.STRING:
+                    self.gatewayName = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 5:
+                if ftype == TType.STRING:
+                    self.domain = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 6:
+                if ftype == TType.STRING:
+                    self.emailAddress = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 7:
+                if ftype == TType.STRING:
+                    self.gatewayAcronym = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 8:
+                if ftype == TType.STRING:
+                    self.gatewayURL = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 9:
+                if ftype == TType.STRING:
+                    self.gatewayPublicAbstract = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 10:
+                if ftype == TType.STRING:
+                    self.reviewProposalDescription = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 11:
+                if ftype == TType.STRING:
+                    self.gatewayAdminFirstName = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 12:
+                if ftype == TType.STRING:
+                    self.gatewayAdminLastName = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 13:
+                if ftype == TType.STRING:
+                    self.gatewayAdminEmail = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 14:
+                if ftype == TType.STRING:
+                    self.identityServerUserName = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 15:
+                if ftype == TType.STRING:
+                    self.identityServerPasswordToken = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 16:
+                if ftype == TType.STRING:
+                    self.declinedReason = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 17:
+                if ftype == TType.STRING:
+                    self.oauthClientId = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 18:
+                if ftype == TType.STRING:
+                    self.oauthClientSecret = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 19:
+                if ftype == TType.I64:
+                    self.requestCreationTime = iprot.readI64()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 20:
+                if ftype == TType.STRING:
+                    self.requesterUsername = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))
+            return
+        oprot.writeStructBegin('Gateway')
+        if self.custosInternalGatewayId is not None:
+            oprot.writeFieldBegin('custosInternalGatewayId', TType.STRING, 1)
+            oprot.writeString(self.custosInternalGatewayId.encode('utf-8') if sys.version_info[0] == 2 else self.custosInternalGatewayId)
+            oprot.writeFieldEnd()
+        if self.gatewayId is not None:
+            oprot.writeFieldBegin('gatewayId', TType.STRING, 2)
+            oprot.writeString(self.gatewayId.encode('utf-8') if sys.version_info[0] == 2 else self.gatewayId)
+            oprot.writeFieldEnd()
+        if self.gatewayApprovalStatus is not None:
+            oprot.writeFieldBegin('gatewayApprovalStatus', TType.I32, 3)
+            oprot.writeI32(self.gatewayApprovalStatus)
+            oprot.writeFieldEnd()
+        if self.gatewayName is not None:
+            oprot.writeFieldBegin('gatewayName', TType.STRING, 4)
+            oprot.writeString(self.gatewayName.encode('utf-8') if sys.version_info[0] == 2 else self.gatewayName)
+            oprot.writeFieldEnd()
+        if self.domain is not None:
+            oprot.writeFieldBegin('domain', TType.STRING, 5)
+            oprot.writeString(self.domain.encode('utf-8') if sys.version_info[0] == 2 else self.domain)
+            oprot.writeFieldEnd()
+        if self.emailAddress is not None:
+            oprot.writeFieldBegin('emailAddress', TType.STRING, 6)
+            oprot.writeString(self.emailAddress.encode('utf-8') if sys.version_info[0] == 2 else self.emailAddress)
+            oprot.writeFieldEnd()
+        if self.gatewayAcronym is not None:
+            oprot.writeFieldBegin('gatewayAcronym', TType.STRING, 7)
+            oprot.writeString(self.gatewayAcronym.encode('utf-8') if sys.version_info[0] == 2 else self.gatewayAcronym)
+            oprot.writeFieldEnd()
+        if self.gatewayURL is not None:
+            oprot.writeFieldBegin('gatewayURL', TType.STRING, 8)
+            oprot.writeString(self.gatewayURL.encode('utf-8') if sys.version_info[0] == 2 else self.gatewayURL)
+            oprot.writeFieldEnd()
+        if self.gatewayPublicAbstract is not None:
+            oprot.writeFieldBegin('gatewayPublicAbstract', TType.STRING, 9)
+            oprot.writeString(self.gatewayPublicAbstract.encode('utf-8') if sys.version_info[0] == 2 else self.gatewayPublicAbstract)
+            oprot.writeFieldEnd()
+        if self.reviewProposalDescription is not None:
+            oprot.writeFieldBegin('reviewProposalDescription', TType.STRING, 10)
+            oprot.writeString(self.reviewProposalDescription.encode('utf-8') if sys.version_info[0] == 2 else self.reviewProposalDescription)
+            oprot.writeFieldEnd()
+        if self.gatewayAdminFirstName is not None:
+            oprot.writeFieldBegin('gatewayAdminFirstName', TType.STRING, 11)
+            oprot.writeString(self.gatewayAdminFirstName.encode('utf-8') if sys.version_info[0] == 2 else self.gatewayAdminFirstName)
+            oprot.writeFieldEnd()
+        if self.gatewayAdminLastName is not None:
+            oprot.writeFieldBegin('gatewayAdminLastName', TType.STRING, 12)
+            oprot.writeString(self.gatewayAdminLastName.encode('utf-8') if sys.version_info[0] == 2 else self.gatewayAdminLastName)
+            oprot.writeFieldEnd()
+        if self.gatewayAdminEmail is not None:
+            oprot.writeFieldBegin('gatewayAdminEmail', TType.STRING, 13)
+            oprot.writeString(self.gatewayAdminEmail.encode('utf-8') if sys.version_info[0] == 2 else self.gatewayAdminEmail)
+            oprot.writeFieldEnd()
+        if self.identityServerUserName is not None:
+            oprot.writeFieldBegin('identityServerUserName', TType.STRING, 14)
+            oprot.writeString(self.identityServerUserName.encode('utf-8') if sys.version_info[0] == 2 else self.identityServerUserName)
+            oprot.writeFieldEnd()
+        if self.identityServerPasswordToken is not None:
+            oprot.writeFieldBegin('identityServerPasswordToken', TType.STRING, 15)
+            oprot.writeString(self.identityServerPasswordToken.encode('utf-8') if sys.version_info[0] == 2 else self.identityServerPasswordToken)
+            oprot.writeFieldEnd()
+        if self.declinedReason is not None:
+            oprot.writeFieldBegin('declinedReason', TType.STRING, 16)
+            oprot.writeString(self.declinedReason.encode('utf-8') if sys.version_info[0] == 2 else self.declinedReason)
+            oprot.writeFieldEnd()
+        if self.oauthClientId is not None:
+            oprot.writeFieldBegin('oauthClientId', TType.STRING, 17)
+            oprot.writeString(self.oauthClientId.encode('utf-8') if sys.version_info[0] == 2 else self.oauthClientId)
+            oprot.writeFieldEnd()
+        if self.oauthClientSecret is not None:
+            oprot.writeFieldBegin('oauthClientSecret', TType.STRING, 18)
+            oprot.writeString(self.oauthClientSecret.encode('utf-8') if sys.version_info[0] == 2 else self.oauthClientSecret)
+            oprot.writeFieldEnd()
+        if self.requestCreationTime is not None:
+            oprot.writeFieldBegin('requestCreationTime', TType.I64, 19)
+            oprot.writeI64(self.requestCreationTime)
+            oprot.writeFieldEnd()
+        if self.requesterUsername is not None:
+            oprot.writeFieldBegin('requesterUsername', TType.STRING, 20)
+            oprot.writeString(self.requesterUsername.encode('utf-8') if sys.version_info[0] == 2 else self.requesterUsername)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.gatewayId is None:
+            raise TProtocolException(message='Required field gatewayId is unset!')
+        if self.gatewayApprovalStatus is None:
+            raise TProtocolException(message='Required field gatewayApprovalStatus is unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(Gateway)
+Gateway.thrift_spec = (
+    None,  # 0
+    (1, TType.STRING, 'custosInternalGatewayId', 'UTF8', None, ),  # 1
+    (2, TType.STRING, 'gatewayId', 'UTF8', None, ),  # 2
+    (3, TType.I32, 'gatewayApprovalStatus', None, None, ),  # 3
+    (4, TType.STRING, 'gatewayName', 'UTF8', None, ),  # 4
+    (5, TType.STRING, 'domain', 'UTF8', None, ),  # 5
+    (6, TType.STRING, 'emailAddress', 'UTF8', None, ),  # 6
+    (7, TType.STRING, 'gatewayAcronym', 'UTF8', None, ),  # 7
+    (8, TType.STRING, 'gatewayURL', 'UTF8', None, ),  # 8
+    (9, TType.STRING, 'gatewayPublicAbstract', 'UTF8', None, ),  # 9
+    (10, TType.STRING, 'reviewProposalDescription', 'UTF8', None, ),  # 10
+    (11, TType.STRING, 'gatewayAdminFirstName', 'UTF8', None, ),  # 11
+    (12, TType.STRING, 'gatewayAdminLastName', 'UTF8', None, ),  # 12
+    (13, TType.STRING, 'gatewayAdminEmail', 'UTF8', None, ),  # 13
+    (14, TType.STRING, 'identityServerUserName', 'UTF8', None, ),  # 14
+    (15, TType.STRING, 'identityServerPasswordToken', 'UTF8', None, ),  # 15
+    (16, TType.STRING, 'declinedReason', 'UTF8', None, ),  # 16
+    (17, TType.STRING, 'oauthClientId', 'UTF8', None, ),  # 17
+    (18, TType.STRING, 'oauthClientSecret', 'UTF8', None, ),  # 18
+    (19, TType.I64, 'requestCreationTime', None, None, ),  # 19
+    (20, TType.STRING, 'requesterUsername', 'UTF8', None, ),  # 20
+)
+fix_spec(all_structs)
+del all_structs
diff --git a/clients/python/requirements_dev.txt b/clients/python/requirements_dev.txt
index a1d25f4..353a50e 100644
--- a/clients/python/requirements_dev.txt
+++ b/clients/python/requirements_dev.txt
@@ -11,3 +11,8 @@ pyflakes==2.1.1
 pytest==4.3.0
 pytest-runner==4.4
 six==1.12.0
+oauthlib==3.1.0
+requests_oauthlib==1.2.0
+requests==2.22.0
+thrift_connector==0.24
+thrift==0.11.0
\ No newline at end of file


[airavata-custos] 11/24: Setting gateway -> tenant

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit 5fe57c8c477e45cc47eb5dca6b402d929f124ed7
Author: Dimuthu Wannipurage <di...@gmail.com>
AuthorDate: Thu Jul 11 01:23:38 2019 -0400

    Setting gateway -> tenant
---
 .../credential/api/controllers/AWSCredentialController.java  | 12 ++++++------
 .../credential/api/controllers/SSHCredentialsController.java | 12 ++++++------
 .../airavata/custos/credentials/aws/AWSCredentialEntity.java |  4 ++--
 .../airavata/custos/credentials/ssh/SSHCredentialEntity.java |  6 +++---
 .../java/org/apache/airavata/custos/vault/VaultManager.java  | 10 +++++-----
 5 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/AWSCredentialController.java b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/AWSCredentialController.java
index edeecef..82d1bcd 100644
--- a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/AWSCredentialController.java
+++ b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/AWSCredentialController.java
@@ -37,16 +37,16 @@ public class AWSCredentialController {
     @Autowired
     private DozerBeanMapper mapper;
 
-    @RequestMapping(value = "/{gateway}/{token}", method = RequestMethod.GET)
-    public AWSCredntial getAWSCredential(@PathVariable("gateway") String gateway, @PathVariable("token") String token) throws Exception {
-        AWSCredentialEntity credentialEntity = vaultManager.getCredentialEntity(AWSCredentialEntity.class, token, gateway);
+    @RequestMapping(value = "/{tenant}/{token}", method = RequestMethod.GET)
+    public AWSCredntial getAWSCredential(@PathVariable("tenant") String tenant, @PathVariable("token") String token) throws Exception {
+        AWSCredentialEntity credentialEntity = vaultManager.getCredentialEntity(AWSCredentialEntity.class, token, tenant);
         return mapper.map(credentialEntity, AWSCredntial.class);
     }
 
-    @RequestMapping(value = "/{gateway}", method = RequestMethod.POST)
-    public String createAWSCredential(@RequestBody AWSCredntial credntial, @PathVariable("gateway") String gateway) throws Exception {
+    @RequestMapping(value = "/{tenant}", method = RequestMethod.POST)
+    public String createAWSCredential(@RequestBody AWSCredntial credntial, @PathVariable("tenant") String tenant) throws Exception {
         AWSCredentialEntity credentialEntity = mapper.map(credntial, AWSCredentialEntity.class);
-        String token = vaultManager.saveCredentialEntity(credentialEntity, gateway);
+        String token = vaultManager.saveCredentialEntity(credentialEntity, tenant);
         return token;
     }
 }
diff --git a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
index bb9075b..8b2b1e4 100644
--- a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
+++ b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
@@ -36,16 +36,16 @@ public class SSHCredentialsController {
     @Autowired
     private DozerBeanMapper mapper;
 
-    @RequestMapping(value = "/{gateway}/{token}", method = RequestMethod.GET)
-    public SSHCredential getSSHCredential(@PathVariable("gateway") String gateway, @PathVariable("token") String token) throws Exception {
-        SSHCredentialEntity credentialEntity = vaultManager.getCredentialEntity(SSHCredentialEntity.class, token, gateway);
+    @RequestMapping(value = "/{tenant}/{token}", method = RequestMethod.GET)
+    public SSHCredential getSSHCredential(@PathVariable("tenant") String tenant, @PathVariable("token") String token) throws Exception {
+        SSHCredentialEntity credentialEntity = vaultManager.getCredentialEntity(SSHCredentialEntity.class, token, tenant);
         return mapper.map(credentialEntity, SSHCredential.class);
     }
 
-    @RequestMapping(value = "/{gateway}", method = RequestMethod.POST)
-    public String createSSHCredential(@RequestBody SSHCredential sshCredential, @PathVariable("gateway") String gateway) throws Exception {
+    @RequestMapping(value = "/{tenant}", method = RequestMethod.POST)
+    public String createSSHCredential(@RequestBody SSHCredential sshCredential, @PathVariable("tenant") String tenant) throws Exception {
         SSHCredentialEntity credentialEntity = mapper.map(sshCredential, SSHCredentialEntity.class);
-        String token = vaultManager.saveCredentialEntity(credentialEntity, gateway);
+        String token = vaultManager.saveCredentialEntity(credentialEntity, tenant);
         return token;
     }
 }
diff --git a/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/aws/AWSCredentialEntity.java b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/aws/AWSCredentialEntity.java
index 0368e48..8aa6966 100644
--- a/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/aws/AWSCredentialEntity.java
+++ b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/aws/AWSCredentialEntity.java
@@ -24,10 +24,10 @@ import org.apache.airavata.custos.vault.annotations.VaultPath;
 
 public class AWSCredentialEntity extends BaseCredentialEntity {
 
-    @VaultPath(path = "secret/aws/{gateway}/{token}", name = "access_key_id", required = true)
+    @VaultPath(path = "secret/aws/{tenant}/{token}", name = "access_key_id", required = true)
     private String accessKey;
 
-    @VaultPath(path = "secret/aws/{gateway}/{token}", name = "secret_access_key")
+    @VaultPath(path = "secret/aws/{tenant}/{token}", name = "secret_access_key")
     private String secretKey;
 
     public String getAccessKey() {
diff --git a/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/ssh/SSHCredentialEntity.java b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/ssh/SSHCredentialEntity.java
index 2f32180..6c397c9 100644
--- a/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/ssh/SSHCredentialEntity.java
+++ b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/ssh/SSHCredentialEntity.java
@@ -23,13 +23,13 @@ import org.apache.airavata.custos.credentials.BaseCredentialEntity;
 import org.apache.airavata.custos.vault.annotations.VaultPath;
 
 public class SSHCredentialEntity extends BaseCredentialEntity {
-    @VaultPath(path = "secret/ssh/{gateway}/{token}", name = "private", required = true)
+    @VaultPath(path = "secret/ssh/{tenant}/{token}", name = "private", required = true)
     private String privateKey;
 
-    @VaultPath(path = "secret/ssh/{gateway}/{token}", name = "passphrase")
+    @VaultPath(path = "secret/ssh/{tenant}/{token}", name = "passphrase")
     private String passphrase;
 
-    @VaultPath(path = "secret/ssh/{gateway}/{token}", name = "public", required = true)
+    @VaultPath(path = "secret/ssh/{tenant}/{token}", name = "public", required = true)
     private String publicKey;
 
     public String getPrivateKey() {
diff --git a/credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java
index d9f56cc..4bb0e73 100644
--- a/credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java
+++ b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java
@@ -47,11 +47,11 @@ public class VaultManager {
         vault = new Vault(config);
     }
 
-    public <T extends BaseCredentialEntity> T getCredentialEntity(Class<T> clazz, final String token, final String gateway) throws Exception {
+    public <T extends BaseCredentialEntity> T getCredentialEntity(Class<T> clazz, final String token, final String tenant) throws Exception {
 
         Map<String, String> params = new HashMap<String, String>() {{
             put("token", token);
-            put("gateway", gateway);
+            put("tenant", tenant);
         }};
 
         Constructor<T> ctor = clazz.getConstructor();
@@ -72,15 +72,15 @@ public class VaultManager {
         return obj;
     }
 
-    public <T extends BaseCredentialEntity> String saveCredentialEntity(final T credentialEntity, final String gateway) throws Exception {
+    public <T extends BaseCredentialEntity> String saveCredentialEntity(final T credentialEntity, final String tenant) throws Exception {
         final String token = UUID.randomUUID().toString();
 
-        credentialEntity.setGateway(gateway);
+        credentialEntity.setGateway(tenant);
         credentialEntity.setToken(token);
 
         Map<String, String> params = new HashMap<String, String>() {{
             put("token", token);
-            put("gateway", gateway);
+            put("tenant", tenant);
         }};
 
         Map<String, Map<String, Object>> summary = new HashMap<>();


[airavata-custos] 09/24: Adding license headers

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit 2d03fadc751f3e3027770f278cbd43f07a8815dd
Author: Dimuthu Wannipurage <di...@gmail.com>
AuthorDate: Wed Jul 10 18:10:35 2019 -0400

    Adding license headers
---
 credential-store/credential-api/pom.xml            | 22 +++++++++++++++++++++-
 .../apache/custos/credential/api/AppConfig.java    | 19 +++++++++++++++++++
 .../apache/custos/credential/api/Application.java  | 19 +++++++++++++++++++
 .../api/controllers/AWSCredentialController.java   | 19 +++++++++++++++++++
 .../api/controllers/SSHCredentialsController.java  | 19 +++++++++++++++++++
 .../credential/api/resources/AWSCredntial.java     | 19 +++++++++++++++++++
 .../credential/api/resources/SSHCredential.java    | 19 +++++++++++++++++++
 credential-store/credential-core/pom.xml           | 21 +++++++++++++++++++++
 .../custos/credentials/BaseCredentialEntity.java   | 19 +++++++++++++++++++
 .../credentials/aws/AWSCredentialEntity.java       | 19 +++++++++++++++++++
 .../credentials/ssh/SSHCredentialEntity.java       | 19 +++++++++++++++++++
 .../apache/airavata/custos/vault/VaultManager.java | 21 ++++++++++++++++++++-
 .../custos/vault/annotations/VaultPath.java        | 19 +++++++++++++++++++
 credential-store/pom.xml                           | 21 +++++++++++++++++++++
 14 files changed, 273 insertions(+), 2 deletions(-)

diff --git a/credential-store/credential-api/pom.xml b/credential-store/credential-api/pom.xml
index 817ef74..c841644 100644
--- a/credential-store/credential-api/pom.xml
+++ b/credential-store/credential-api/pom.xml
@@ -1,5 +1,25 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
+<!--
+
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+
+--><project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
diff --git a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/AppConfig.java b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/AppConfig.java
index 8a2ee86..fc30571 100644
--- a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/AppConfig.java
+++ b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/AppConfig.java
@@ -1,3 +1,22 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
 package org.apache.custos.credential.api;
 
 import org.dozer.DozerBeanMapper;
diff --git a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/Application.java b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/Application.java
index d4f7ce4..024954a 100644
--- a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/Application.java
+++ b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/Application.java
@@ -1,3 +1,22 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
 package org.apache.custos.credential.api;
 
 import org.springframework.boot.SpringApplication;
diff --git a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/AWSCredentialController.java b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/AWSCredentialController.java
index 1b757be..edeecef 100644
--- a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/AWSCredentialController.java
+++ b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/AWSCredentialController.java
@@ -1,3 +1,22 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
 package org.apache.custos.credential.api.controllers;
 
 import org.apache.airavata.custos.credentials.aws.AWSCredentialEntity;
diff --git a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
index dbd8c94..bb9075b 100644
--- a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
+++ b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
@@ -1,3 +1,22 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
 package org.apache.custos.credential.api.controllers;
 
 import org.apache.airavata.custos.credentials.ssh.SSHCredentialEntity;
diff --git a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/AWSCredntial.java b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/AWSCredntial.java
index befab46..92a8b2f 100644
--- a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/AWSCredntial.java
+++ b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/AWSCredntial.java
@@ -1,3 +1,22 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
 package org.apache.custos.credential.api.resources;
 
 public class AWSCredntial {
diff --git a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/SSHCredential.java b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/SSHCredential.java
index e80e77a..ac05516 100644
--- a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/SSHCredential.java
+++ b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/SSHCredential.java
@@ -1,3 +1,22 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
 package org.apache.custos.credential.api.resources;
 
 public class SSHCredential {
diff --git a/credential-store/credential-core/pom.xml b/credential-store/credential-core/pom.xml
index 090c1bf..81cd99b 100644
--- a/credential-store/credential-core/pom.xml
+++ b/credential-store/credential-core/pom.xml
@@ -1,4 +1,25 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+
+-->
 <project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
diff --git a/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/BaseCredentialEntity.java b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/BaseCredentialEntity.java
index 45ba2aa..00be76c 100644
--- a/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/BaseCredentialEntity.java
+++ b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/BaseCredentialEntity.java
@@ -1,3 +1,22 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
 package org.apache.airavata.custos.credentials;
 
 public class BaseCredentialEntity {
diff --git a/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/aws/AWSCredentialEntity.java b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/aws/AWSCredentialEntity.java
index 5991264..0368e48 100644
--- a/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/aws/AWSCredentialEntity.java
+++ b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/aws/AWSCredentialEntity.java
@@ -1,3 +1,22 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
 package org.apache.airavata.custos.credentials.aws;
 
 import org.apache.airavata.custos.credentials.BaseCredentialEntity;
diff --git a/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/ssh/SSHCredentialEntity.java b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/ssh/SSHCredentialEntity.java
index 1e8ddcf..2f32180 100644
--- a/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/ssh/SSHCredentialEntity.java
+++ b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/ssh/SSHCredentialEntity.java
@@ -1,3 +1,22 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
 package org.apache.airavata.custos.credentials.ssh;
 
 import org.apache.airavata.custos.credentials.BaseCredentialEntity;
diff --git a/credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java
index 4e333c0..d9f56cc 100644
--- a/credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java
+++ b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java
@@ -1,3 +1,22 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
 package org.apache.airavata.custos.vault;
 
 import com.bettercloud.vault.Vault;
@@ -18,7 +37,7 @@ import java.util.*;
 public class VaultManager {
 
     private String vaultAddress = "http://127.0.0.1:8200";
-    private String vaultToken = "s.PFc3SbOz1N2wSpV5hWZ56yVI";
+    private String vaultToken = "s.AQ3iZYawSgsE4duk8sKs2MGW";
 
     private Vault vault;
 
diff --git a/credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/annotations/VaultPath.java b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/annotations/VaultPath.java
index b36d4bb..ff36fa7 100644
--- a/credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/annotations/VaultPath.java
+++ b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/annotations/VaultPath.java
@@ -1,3 +1,22 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
 package org.apache.airavata.custos.vault.annotations;
 
 import java.lang.annotation.ElementType;
diff --git a/credential-store/pom.xml b/credential-store/pom.xml
index c64d1d2..7b1fcfa 100644
--- a/credential-store/pom.xml
+++ b/credential-store/pom.xml
@@ -1,4 +1,25 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+
+-->
 <project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">


[airavata-custos] 24/24: Merge pull request #8 from aarushiibisht/abisht_custos_master

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit 4cd85e1923a0d5c180ddc9c719e6739715563743
Merge: 841c5cd baaea62
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Fri Oct 11 16:42:02 2019 -0400

    Merge pull request #8 from aarushiibisht/abisht_custos_master
    
    Python SDK for authentication was keycloak and for other admin services

 clients/python/README.md                           |   52 +
 .../airavata_custos/admin/iam_admin_client.py      |  150 +
 clients/python/airavata_custos/sample_settings.ini |   10 +
 .../python/airavata_custos/security/__init__.py    |    0
 .../airavata_custos/security/client_credentials.py |   68 +
 .../security/keycloak_connectors.py                |  170 +
 clients/python/airavata_custos/settings.py         |   39 +
 clients/python/airavata_custos/utils.py            |   78 +
 clients/python/custos/__init__.py                  |    0
 clients/python/custos/commons/__init__.py          |    0
 clients/python/custos/commons/model/__init__.py    |    0
 .../custos/commons/model/security/__init__.py      |    1 +
 .../custos/commons/model/security/constants.py     |   14 +
 .../python/custos/commons/model/security/ttypes.py |  104 +
 clients/python/custos/profile/__init__.py          |    0
 clients/python/custos/profile/iam/__init__.py      |    0
 .../python/custos/profile/iam/admin/__init__.py    |    0
 .../custos/profile/iam/admin/services/__init__.py  |    0
 .../iam/admin/services/cpi/IamAdminServices-remote |  222 ++
 .../iam/admin/services/cpi/IamAdminServices.py     | 3863 ++++++++++++++++++++
 .../profile/iam/admin/services/cpi/__init__.py     |    1 +
 .../profile/iam/admin/services/cpi/constants.py    |   16 +
 .../iam/admin/services/cpi/error/__init__.py       |    1 +
 .../iam/admin/services/cpi/error/constants.py      |   14 +
 .../profile/iam/admin/services/cpi/error/ttypes.py |   85 +
 .../profile/iam/admin/services/cpi/ttypes.py       |   23 +
 .../python/custos/profile/model/User/__init__.py   |    1 +
 .../python/custos/profile/model/User/constants.py  |   16 +
 clients/python/custos/profile/model/User/ttypes.py |  788 ++++
 clients/python/custos/profile/model/__init__.py    |    0
 .../python/custos/profile/model/tenant/__init__.py |    1 +
 .../custos/profile/model/tenant/constants.py       |   14 +
 .../python/custos/profile/model/tenant/ttypes.py   |  604 +++
 .../custos/profile/model/workspace/__init__.py     |    1 +
 .../custos/profile/model/workspace/constants.py    |   14 +
 .../custos/profile/model/workspace/ttypes.py       |  345 ++
 clients/python/requirements_dev.txt                |    5 +
 37 files changed, 6700 insertions(+)


[airavata-custos] 18/24: added the functionality to load server configuration from ini file

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit 7d3e8c8c18916e28d1776bf4a1ed470ad3ee34f2
Author: Aarushi <aa...@gmail.com>
AuthorDate: Tue Sep 17 12:22:01 2019 -0400

    added the functionality to load server configuration from ini file
---
 .../airavata_custos/admin/iam_admin_client.py      | 244 +++++++++++----------
 clients/python/airavata_custos/sample_settings.ini |  10 +
 .../security/custos_authorization_token.py         |  43 ----
 .../security/keycloak_connectors.py                |  78 +++++--
 clients/python/airavata_custos/settings.py         |  30 ++-
 clients/python/airavata_custos/utils.py            |  20 +-
 clients/python/requirements_dev.txt                |   3 +-
 7 files changed, 228 insertions(+), 200 deletions(-)

diff --git a/clients/python/airavata_custos/admin/iam_admin_client.py b/clients/python/airavata_custos/admin/iam_admin_client.py
index ee9c11b..f72df76 100644
--- a/clients/python/airavata_custos/admin/iam_admin_client.py
+++ b/clients/python/airavata_custos/admin/iam_admin_client.py
@@ -16,123 +16,135 @@
 #
 
 import logging
-from airavata_custos.utils import iamadmin_client_pool
+import configparser
+from airavata_custos import utils
+from airavata_custos.settings import ProfileSettings
 
 logger = logging.getLogger(__name__)
 
 
-def is_username_available(authz_token, username):
-    """
-    This method validates if the username is available or not
-    :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
-    :param username: The username whose availability needs to be verified
-    :return: boolean
-    """
-    return iamadmin_client_pool.isUsernameAvailable(authz_token, username)
-
-
-def register_user(authz_token, username, email_address, first_name, last_name, password):
-    """
-    This method registers the user with the keycloak instance returns true if successful, false if the registration fails
-    :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
-    :param username: The username of the user that needs to be registered
-    :param email_address: The email address of the user that needs to be registered
-    :param first_name: The first name of the user that needs to be registered
-    :param last_name: The last name of the user that needs to be registered
-    :param password: The password of the user that needs to be registered
-    :return: boolean
-    """
-    return iamadmin_client_pool.registerUser(
-        authz_token,
-        username,
-        email_address,
-        first_name,
-        last_name,
-        password)
-
-
-def is_user_enabled(authz_token, username):
-    """
-    Checks the user is enabled/disabled in keycloak. Only the enabled user can login
-    :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
-    :param username: The username of the user
-    :return: boolean
-    """
-    return iamadmin_client_pool.isUserEnabled(authz_token, username)
-
-
-def enable_user(authz_token, username):
-    """
-    The method to enable a disabled user
-    :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
-    :param username: The username of the user
-    :return: Object of UserProfile class, containing user details
-    """
-    return iamadmin_client_pool.enableUser(authz_token, username)
-
-
-def delete_user(authz_token, username):
-    """
-    This method deleted the user from keycloak. Returns true if delete is successful
-    :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
-    :param username: The username of the user
-    :return: boolean
-    """
-    return iamadmin_client_pool.deleteUser(authz_token, username)
-
-
-def is_user_exist(authz_token, username):
-    """
-    This method checks if the user exists in keycloak. Returns true if the user exists otherwise returns false
-    :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
-    :param username: The username of the user
-    :return: boolean
-    """
-    try:
-        return iamadmin_client_pool.isUserExist(authz_token, username)
-    except Exception:
-        return None
-
-
-def get_user(authz_token, username):
-    """
-
-    :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
-    :param username: username of the user
-    :return: object of class UserProfile
-    """
-    try:
-        return iamadmin_client_pool.getUser(authz_token, username)
-    except Exception:
-        return None
-
-
-def get_users(authz_token, offset=0, limit=-1, search=None):
-    """
-
-    :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
-    :param offset: start index
-    :param limit: end index
-    :param search: search criteria for filtering users
-    :return: list of UserProfile class objects
-    """
-    try:
-        return iamadmin_client_pool.getUsers(authz_token, offset, limit, search)
-    except Exception:
-        return None
-
-
-def reset_user_password(authz_token, username, new_password):
-    """
-
-    :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
-    :param username: username of the user
-    :param new_password: new password for the user
-    :return:
-    """
-    try:
-        return iamadmin_client_pool.resetUserPassword(
-            authz_token, username, new_password)
-    except Exception:
-        return None
-
+class IAMAdminClient(object):
+
+    def __init__(self, configuration_file_location):
+        """
+        constructor for IAMAdminClient class
+        :param configuration_file_location: takes the location of the ini file containing server configuration
+        """
+        self.profile_settings = ProfileSettings()
+        self._load_settings(configuration_file_location)
+        self.iamadmin_client_pool = utils.initialize_iamadmin_client_pool(self.profile_settings.PROFILE_SERVICE_HOST,
+                                                                          self.profile_settings.PROFILE_SERVICE_PORT)
+
+    def is_username_available(self, authz_token, username):
+        """
+        This method validates if the username is available or not
+        :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
+        :param username: The username whose availability needs to be verified
+        :return: boolean
+        """
+        return self.iamadmin_client_pool.isUsernameAvailable(authz_token, username)
+
+    def register_user(self, authz_token, username, email_address, first_name, last_name, password):
+        """
+        This method registers the user with the keycloak instance returns true if successful, false if the registration fails
+        :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
+        :param username: The username of the user that needs to be registered
+        :param email_address: The email address of the user that needs to be registered
+        :param first_name: The first name of the user that needs to be registered
+        :param last_name: The last name of the user that needs to be registered
+        :param password: The password of the user that needs to be registered
+        :return: boolean
+        """
+        return self.iamadmin_client_pool.registerUser(
+            authz_token,
+            username,
+            email_address,
+            first_name,
+            last_name,
+            password)
+
+    def is_user_enabled(self, authz_token, username):
+        """
+        Checks the user is enabled/disabled in keycloak. Only the enabled user can login
+        :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
+        :param username: The username of the user
+        :return: boolean
+        """
+        return self.iamadmin_client_pool.isUserEnabled(authz_token, username)
+
+    def enable_user(self, authz_token, username):
+        """
+        The method to enable a disabled user
+        :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
+        :param username: The username of the user
+        :return: Object of UserProfile class, containing user details
+        """
+        return self.iamadmin_client_pool.enableUser(authz_token, username)
+
+    def delete_user(self, authz_token, username):
+        """
+        This method deleted the user from keycloak. Returns true if delete is successful
+        :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
+        :param username: The username of the user
+        :return: boolean
+        """
+        return self.iamadmin_client_pool.deleteUser(authz_token, username)
+
+    def is_user_exist(self, authz_token, username):
+        """
+        This method checks if the user exists in keycloak. Returns true if the user exists otherwise returns false
+        :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
+        :param username: The username of the user
+        :return: boolean
+        """
+        try:
+            return self.iamadmin_client_pool.isUserExist(authz_token, username)
+        except Exception:
+            return None
+
+    def get_user(self, authz_token, username):
+        """
+
+        :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
+        :param username: username of the user
+        :return: object of class UserProfile
+        """
+        try:
+            return self.iamadmin_client_pool.getUser(authz_token, username)
+        except Exception:
+            return None
+
+    def get_users(self, authz_token, offset=0, limit=-1, search=None):
+        """
+
+        :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
+        :param offset: start index
+        :param limit: end index
+        :param search: search criteria for filtering users
+        :return: list of UserProfile class objects
+        """
+        try:
+            return self.iamadmin_client_pool.getUsers(authz_token, offset, limit, search)
+        except Exception:
+            return None
+
+    def reset_user_password(self, authz_token, username, new_password):
+        """
+
+        :param authz_token: Object of AuthzToken class containing access token, username, gatewayId of the active user
+        :param username: username of the user
+        :param new_password: new password for the user
+        :return:
+        """
+        try:
+            return self.iamadmin_client_pool.resetUserPassword(
+                authz_token, username, new_password)
+        except Exception:
+            return None
+
+    def _load_settings(self, configuration_file_location):
+        config = configparser.ConfigParser()
+        config.read(configuration_file_location)
+        settings = config['ProfileServerSettings']
+        self.profile_settings.PROFILE_SERVICE_HOST = settings['PROFILE_SERVICE_HOST']
+        self.profile_settings.PROFILE_SERVICE_PORT = settings['PROFILE_SERVICE_PORT']
diff --git a/clients/python/airavata_custos/sample_settings.ini b/clients/python/airavata_custos/sample_settings.ini
new file mode 100644
index 0000000..c9a1e1c
--- /dev/null
+++ b/clients/python/airavata_custos/sample_settings.ini
@@ -0,0 +1,10 @@
+[IAMSeverSettings]
+KEYCLOAK_AUTHORIZE_URL = https://localhost:8443/auth/realms/default/protocol/openid-connect/auth
+KEYCLOAK_TOKEN_URL = https://localhost:8443/auth/realms/default/protocol/openid-connect/token
+KEYCLOAK_USERINFO_URL = https://localhost:8443/auth/realms/default/protocol/openid-connect/userinfo
+KEYCLOAK_LOGOUT_URL = https://localhost:8443/auth/realms/default/protocol/openid-connect/logout
+VERIFY_SSL = no
+
+[ProfileServerSettings]
+PROFILE_SERVICE_HOST = '0.0.0.0'
+PROFILE_SERVICE_PORT = '8081'
\ No newline at end of file
diff --git a/clients/python/airavata_custos/security/custos_authorization_token.py b/clients/python/airavata_custos/security/custos_authorization_token.py
deleted file mode 100644
index dd4b531..0000000
--- a/clients/python/airavata_custos/security/custos_authorization_token.py
+++ /dev/null
@@ -1,43 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You 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 airavata_custos import settings
-from oauthlib.oauth2 import BackendApplicationClient
-from requests_oauthlib import OAuth2Session
-from custos.commons.model.security.ttypes import AuthzToken
-
-
-def get_authorization_token(client_credentials, tenant_id, username=None):
-    """
-    This method created a authorization token for the user or a service account
-    In case of a service account username will be null
-    :param client_credentials: object of class client_credentials
-    :param tenant_id: gateway id of the client
-    :param username: username of the user for which authorization token is being created
-    :return: AuthzToken
-    """
-    client = BackendApplicationClient(client_id=client_credentials.client_id)
-    oauth = OAuth2Session(client=client)
-    token = oauth.fetch_token(
-        token_url=settings.token_url,
-        client_id=client_credentials.client_id,
-        client_secret=client_credentials.client_secret,
-        verify=client_credentials.verify_ssl)
-
-    access_token = token.get('access_token')
-    return AuthzToken(
-        accessToken=access_token,
-        claimsMap={'gatewayID': tenant_id, 'userName': username})
diff --git a/clients/python/airavata_custos/security/keycloak_connectors.py b/clients/python/airavata_custos/security/keycloak_connectors.py
index d31e5a8..05816c2 100644
--- a/clients/python/airavata_custos/security/keycloak_connectors.py
+++ b/clients/python/airavata_custos/security/keycloak_connectors.py
@@ -16,13 +16,24 @@
 #
 import time
 from oauthlib.oauth2 import LegacyApplicationClient
-from requests_oauthlib import OAuth2Session
 import requests
-from airavata_custos import settings
+import configparser
+from airavata_custos.settings import IAMSettings
+from oauthlib.oauth2 import BackendApplicationClient
+from requests_oauthlib import OAuth2Session
+from custos.commons.model.security.ttypes import AuthzToken
 
 
 class KeycloakBackend(object):
 
+    def __init__(self, configuration_file_location):
+        """
+        constructor for KeycloakBackend class
+        :param configuration_file_location: takes the location of the ini file containing server configuration
+        """
+        self.keycloak_settings = IAMSettings()
+        self._load_settings(configuration_file_location)
+
     def authenticate_user(self, user_credentials):
         """
         Method to authenticate a gateway user with keycloak
@@ -60,42 +71,61 @@ class KeycloakBackend(object):
         except Exception as e:
             return None
 
-    @classmethod
-    def _get_token_and_user_info_password_flow(cls, client_credentials):
+    def _get_token_and_user_info_password_flow(self, client_credentials):
 
         oauth2_session = OAuth2Session(client=LegacyApplicationClient(client_id=client_credentials.client_id))
-        token = oauth2_session.fetch_token(token_url=settings.KEYCLOAK_TOKEN_URL,
+        token = oauth2_session.fetch_token(token_url=self.keycloak_settings.KEYCLOAK_TOKEN_URL,
                                            username=client_credentials.username,
                                            password=client_credentials.password,
                                            client_id=client_credentials.client_id,
                                            client_secret=client_credentials.client_secret,
-                                           verify=settings.VERIFY_SSL)
-        user_info = oauth2_session.get(settings.KEYCLOAK_USERINFO_URL).json()
-        return cls._process_token(token), cls._process_userinfo(user_info)
+                                           verify=self.keycloak_settings.VERIFY_SSL)
+        user_info = oauth2_session.get(self.keycloak_settings.KEYCLOAK_USERINFO_URL).json()
+        return self._process_token(token), self._process_userinfo(user_info)
 
-    @classmethod
-    def _get_token_and_user_info_redirect_flow(cls, client_credentials):
+    def _get_token_and_user_info_redirect_flow(self, client_credentials):
         oauth2_session = OAuth2Session(client_credentials.client_id,
                                        scope='openid',
                                        redirect_uri=client_credentials.redirect_uri,
                                        state=client_credentials.state)
-        token = oauth2_session.fetch_token(settings.KEYCLOAK_TOKEN_URL,
+        token = oauth2_session.fetch_token(self.keycloak_settings.KEYCLOAK_TOKEN_URL,
                                            client_secret=client_credentials.client_secret,
                                            authorization_response=client_credentials.authorization_code_url,
-                                           verify=settings.VERIFY_SSL)
-        user_info = oauth2_session.get(settings.KEYCLOAK_USERINFO_URL).json()
-        return cls._process_token(token), cls._process_userinfo(user_info)
+                                           verify=self.keycloak_settings.VERIFY_SSL)
+        user_info = oauth2_session.get(self.keycloak_settings.KEYCLOAK_USERINFO_URL).json()
+        return self._process_token(token), self._process_userinfo(user_info)
 
-    @classmethod
-    def _get_token_from_refresh_token(cls, client_credentials, refresh_token):
+    def _get_token_from_refresh_token(self, client_credentials, refresh_token):
 
         oauth2_session = OAuth2Session(client_credentials.client_id, scope='openid')
         auth = requests.auth.HTTPBasicAuth(client_credentials.client_id, client_credentials.client_secret)
-        token = oauth2_session.refresh_token(token_url=settings.KEYCLOAK_TOKEN_URL,
+        token = oauth2_session.refresh_token(token_url=self.keycloak_settings.KEYCLOAK_TOKEN_URL,
                                              refresh_token=refresh_token,
                                              auth=auth,
-                                             verify=settings.VERIFY_SSL)
-        return cls._process_token(token)
+                                             verify=self.keycloak_settings.VERIFY_SSL)
+        return self._process_token(token)
+
+    def get_authorization_token(self, client_credentials, tenant_id, username=None):
+        """
+        This method created a authorization token for the user or a service account
+        In case of a service account username will be null
+        :param client_credentials: object of class client_credentials
+        :param tenant_id: gateway id of the client
+        :param username: username of the user for which authorization token is being created
+        :return: AuthzToken
+        """
+        client = BackendApplicationClient(client_id=client_credentials.client_id)
+        oauth = OAuth2Session(client=client)
+        token = oauth.fetch_token(
+            token_url=self.keycloak_settings.token_url,
+            client_id=client_credentials.client_id,
+            client_secret=client_credentials.client_secret,
+            verify=client_credentials.verify_ssl)
+
+        access_token = token.get('access_token')
+        return AuthzToken(
+            accessToken=access_token,
+            claimsMap={'gatewayID': tenant_id, 'userName': username})
 
     @classmethod
     def _process_token(cls, token):
@@ -116,6 +146,16 @@ class KeycloakBackend(object):
         last_name = userinfo['family_name']
         return UserInfo(username, email, first_name, last_name)
 
+    def _load_settings(self, configuration_file_location):
+        config = configparser.ConfigParser()
+        config.read(configuration_file_location)
+        settings = config['IAMSeverSettings']
+        self.keycloak_settings.KEYCLOAK_AUTHORIZE_URL = settings['KEYCLOAK_AUTHORIZE_URL']
+        self.keycloak_settings.KEYCLOAK_LOGOUT_URL = settings['KEYCLOAK_LOGOUT_URL']
+        self.keycloak_settings.KEYCLOAK_TOKEN_URL = settings['KEYCLOAK_TOKEN_URL']
+        self.keycloak_settings.KEYCLOAK_USERINFO_URL = settings['KEYCLOAK_USERINFO_URL']
+        self.keycloak_settings.VERIFY_SSL = settings.getboolean('VERIFY_SSL')
+
 
 class Token(object):
 
diff --git a/clients/python/airavata_custos/settings.py b/clients/python/airavata_custos/settings.py
index 89a05ef..dc4d101 100644
--- a/clients/python/airavata_custos/settings.py
+++ b/clients/python/airavata_custos/settings.py
@@ -15,19 +15,25 @@
 # limitations under the License.
 #
 
-KEYCLOAK_AUTHORIZE_URL = 'https://localhost:8443/auth/realms/default/protocol/openid-connect/auth'
-KEYCLOAK_TOKEN_URL = 'https://localhost:8443/auth/realms/default/protocol/openid-connect/token'
-KEYCLOAK_USERINFO_URL = 'https://localhost:8443/auth/realms/default/protocol/openid-connect/userinfo'
-KEYCLOAK_LOGOUT_URL = 'https://localhost:8443/auth/realms/default/protocol/openid-connect/logout'
+
+class ProfileSettings(object):
+    # Profile Service Configuration
+
+    def __init__(self):
+        self.PROFILE_SERVICE_HOST = '0.0.0.0'
+        self.PROFILE_SERVICE_PORT = '8081'
+
+
+class IAMSettings(object):
+
+    def __init__(self):
+        self.KEYCLOAK_AUTHORIZE_URL = 'https://localhost:8443/auth/realms/default/protocol/openid-connect/auth'
+        self.KEYCLOAK_TOKEN_URL = 'https://localhost:8443/auth/realms/default/protocol/openid-connect/token'
+        self.KEYCLOAK_USERINFO_URL = 'https://localhost:8443/auth/realms/default/protocol/openid-connect/userinfo'
+        self.KEYCLOAK_LOGOUT_URL = 'https://localhost:8443/auth/realms/default/protocol/openid-connect/logout'
+        self.VERIFY_SSL = False
 
 # Seconds each connection in the pool is able to stay alive. If open connection
 # has lived longer than this period, it will be closed.
 # (https://github.com/Thriftpy/thrift_connector)
-THRIFT_CLIENT_POOL_KEEPALIVE = 5
-
-# Profile Service Configuration
-PROFILE_SERVICE_HOST = '0.0.0.0'
-PROFILE_SERVICE_PORT = '8081'
-PROFILE_SERVICE_SECURE = False
-
-VERIFY_SSL = False
+THRIFT_CLIENT_POOL_KEEPALIVE = 5
\ No newline at end of file
diff --git a/clients/python/airavata_custos/utils.py b/clients/python/airavata_custos/utils.py
index 17fdf59..e025077 100644
--- a/clients/python/airavata_custos/utils.py
+++ b/clients/python/airavata_custos/utils.py
@@ -21,8 +21,8 @@ from thrift.protocol import TBinaryProtocol
 from thrift.protocol.TMultiplexedProtocol import TMultiplexedProtocol
 from thrift.transport import TSocket, TSSLSocket, TTransport
 
-from airavata_custos import settings
 from custos.profile.iam.admin.services.cpi import IamAdminServices, constants
+from airavata_custos import settings
 
 log = logging.getLogger(__name__)
 
@@ -63,14 +63,16 @@ class CustomThriftClient(connection_pool.ThriftClient):
 class IAMAdminServiceThriftClient(MultiplexThriftClientMixin,
                                   CustomThriftClient):
     service_name = constants.IAM_ADMIN_SERVICES_CPI_NAME
-    secure = settings.PROFILE_SERVICE_SECURE
+    secure = True
 
 
-iamadmin_client_pool = connection_pool.ClientPool(
-    IamAdminServices,
-    settings.PROFILE_SERVICE_HOST,
-    settings.PROFILE_SERVICE_PORT,
-    connection_class=IAMAdminServiceThriftClient,
-    keepalive=settings.THRIFT_CLIENT_POOL_KEEPALIVE
-)
+def initialize_iamadmin_client_pool(host, port):
+    iamadmin_client_pool = connection_pool.ClientPool(
+        IamAdminServices,
+        host,
+        port,
+        connection_class=IAMAdminServiceThriftClient,
+        keepalive=settings.THRIFT_CLIENT_POOL_KEEPALIVE
+    )
+    return iamadmin_client_pool
 
diff --git a/clients/python/requirements_dev.txt b/clients/python/requirements_dev.txt
index 353a50e..e0209cb 100644
--- a/clients/python/requirements_dev.txt
+++ b/clients/python/requirements_dev.txt
@@ -15,4 +15,5 @@ oauthlib==3.1.0
 requests_oauthlib==1.2.0
 requests==2.22.0
 thrift_connector==0.24
-thrift==0.11.0
\ No newline at end of file
+thrift==0.11.0
+configparser==4.0.2


[airavata-custos] 23/24: corrected typos and addressed review comments

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit baaea62220da77ba7fdb2e76984a6d097b1468e5
Author: Aarushi <aa...@gmail.com>
AuthorDate: Thu Oct 10 13:02:53 2019 -0400

    corrected typos and addressed review comments
---
 clients/python/README.md                                       | 2 +-
 clients/python/airavata_custos/sample_settings.ini             | 2 +-
 clients/python/airavata_custos/security/keycloak_connectors.py | 4 ++--
 clients/python/requirements_dev.txt                            | 1 -
 4 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/clients/python/README.md b/clients/python/README.md
index dd70088..7cfef23 100644
--- a/clients/python/README.md
+++ b/clients/python/README.md
@@ -23,7 +23,7 @@ Install dependencies
 
 Server configuration should be kept in a INI file in the following format. For more information refer to sample_settings.ini file  
     
-    [IAMSeverSettings]
+    [IAMServerSettings]
     KEYCLOAK_AUTHORIZE_URL =
     KEYCLOAK_TOKEN_URL = 
     KEYCLOAK_USERINFO_URL = 
diff --git a/clients/python/airavata_custos/sample_settings.ini b/clients/python/airavata_custos/sample_settings.ini
index 18eb04b..2af0074 100644
--- a/clients/python/airavata_custos/sample_settings.ini
+++ b/clients/python/airavata_custos/sample_settings.ini
@@ -1,4 +1,4 @@
-[IAMSeverSettings]
+[IAMServerSettings]
 KEYCLOAK_AUTHORIZE_URL = https://localhost:8443/auth/realms/default/protocol/openid-connect/auth
 KEYCLOAK_TOKEN_URL = https://localhost:8443/auth/realms/default/protocol/openid-connect/token
 KEYCLOAK_USERINFO_URL = https://localhost:8443/auth/realms/default/protocol/openid-connect/userinfo
diff --git a/clients/python/airavata_custos/security/keycloak_connectors.py b/clients/python/airavata_custos/security/keycloak_connectors.py
index cf105f7..6b30df7 100644
--- a/clients/python/airavata_custos/security/keycloak_connectors.py
+++ b/clients/python/airavata_custos/security/keycloak_connectors.py
@@ -80,7 +80,7 @@ class KeycloakBackend(object):
         :param idp_alias: name of the idp
         :return: object of class IdpCredentials
         """
-        redirect_uri += '?idp_alias=' + quote(idp_alias)
+
         base_authorize_url = self.keycloak_settings.KEYCLOAK_AUTHORIZE_URL
         oauth2_session = OAuth2Session(client_id, scope='openid', redirect_uri=redirect_uri)
         authorization_url, state = oauth2_session.authorization_url(base_authorize_url)
@@ -161,7 +161,7 @@ class KeycloakBackend(object):
     def _load_settings(self, configuration_file_location):
         config = configparser.ConfigParser()
         config.read(configuration_file_location)
-        settings = config['IAMSeverSettings']
+        settings = config['IAMServerSettings']
         self.keycloak_settings.KEYCLOAK_AUTHORIZE_URL = settings['KEYCLOAK_AUTHORIZE_URL']
         self.keycloak_settings.KEYCLOAK_LOGOUT_URL = settings['KEYCLOAK_LOGOUT_URL']
         self.keycloak_settings.KEYCLOAK_TOKEN_URL = settings['KEYCLOAK_TOKEN_URL']
diff --git a/clients/python/requirements_dev.txt b/clients/python/requirements_dev.txt
index e0209cb..0c4db3a 100644
--- a/clients/python/requirements_dev.txt
+++ b/clients/python/requirements_dev.txt
@@ -16,4 +16,3 @@ requests_oauthlib==1.2.0
 requests==2.22.0
 thrift_connector==0.24
 thrift==0.11.0
-configparser==4.0.2


[airavata-custos] 10/24: Adding a custom authorization filter

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit 5c795999ad1bba7ad657f8b796f55e3bf862c6e7
Author: Dimuthu Wannipurage <di...@gmail.com>
AuthorDate: Thu Jul 11 01:19:02 2019 -0400

    Adding a custom authorization filter
---
 .../custos/credential/api/filters/AuthFilter.java  | 59 ++++++++++++++++++++++
 1 file changed, 59 insertions(+)

diff --git a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/filters/AuthFilter.java b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/filters/AuthFilter.java
new file mode 100644
index 0000000..b080142
--- /dev/null
+++ b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/filters/AuthFilter.java
@@ -0,0 +1,59 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.custos.credential.api.filters;
+
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Enumeration;
+
+@Component
+@Order(Ordered.HIGHEST_PRECEDENCE)
+public class AuthFilter implements Filter{
+
+    public AuthFilter() {}
+
+
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+        HttpServletResponse response = (HttpServletResponse) servletResponse;
+        HttpServletRequest request = (HttpServletRequest) servletRequest;
+
+        HttpServletRequest httpRequest = (HttpServletRequest) request;
+        Enumeration<String> headerNames = httpRequest.getHeaderNames();
+
+        if (headerNames != null) {
+            while (headerNames.hasMoreElements()) {
+                String name = headerNames.nextElement();
+                if ("authorization".equals(name)) {
+                    String headerValue = httpRequest.getHeader(name);
+                    // TODO Validate the authorization header
+                }
+            }
+        }
+
+
+        filterChain.doFilter(request, response);
+    }
+}


[airavata-custos] 01/24: adding a readme and testing the repo

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit 3802acd4ad807696cbc58ee7537f796d35f7eb2c
Author: Suresh Marru <sm...@apache.org>
AuthorDate: Fri Feb 15 14:08:48 2019 -0500

    adding a readme and testing the repo
---
 README | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/README b/README
new file mode 100644
index 0000000..95a8c48
--- /dev/null
+++ b/README
@@ -0,0 +1,3 @@
+Apache Airavata Custos Security 
+
+Custos: Generalized versions of Apache Airavata Security server components and client libraries.


[airavata-custos] 20/24: addressing review comments

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit bbda71e571d3b98f4bd02f0871a1504a19a37051
Author: Aarushi <aa...@gmail.com>
AuthorDate: Sun Sep 29 14:06:52 2019 -0400

    addressing review comments
---
 .../security/keycloak_connectors.py                | 67 ++++++++--------------
 1 file changed, 23 insertions(+), 44 deletions(-)

diff --git a/clients/python/airavata_custos/security/keycloak_connectors.py b/clients/python/airavata_custos/security/keycloak_connectors.py
index 07e8dd8..99d4c72 100644
--- a/clients/python/airavata_custos/security/keycloak_connectors.py
+++ b/clients/python/airavata_custos/security/keycloak_connectors.py
@@ -22,6 +22,7 @@ from airavata_custos.settings import IAMSettings
 from oauthlib.oauth2 import BackendApplicationClient
 from requests_oauthlib import OAuth2Session
 from custos.commons.model.security.ttypes import AuthzToken
+from urllib.parse import quote
 
 
 class KeycloakBackend(object):
@@ -38,7 +39,7 @@ class KeycloakBackend(object):
         """
         Method to authenticate a gateway user with keycloak
         :param user_credentials: object of UserCredentials class
-        :return: Token object, UserInfo object
+        :return: openid token, openid user information
         """
         try:
             token, user_info = self._get_token_and_user_info_password_flow(user_credentials)
@@ -50,7 +51,7 @@ class KeycloakBackend(object):
         """
 
         :param account_credentials: object of AccountCredentials class
-        :return: Token object, Account info object
+        :return: openid token, openid user information
         """
         try:
             token, account_info = self._get_token_and_user_info_redirect_flow(account_credentials)
@@ -63,7 +64,7 @@ class KeycloakBackend(object):
 
         :param client_credentials: object of ClientCredentials class
         :param refresh_token: openid connect refresh token
-        :return: Token object
+        :return: openid token
         """
         try:
             token = self._get_token_from_refresh_token(client_credentials, refresh_token)
@@ -71,6 +72,21 @@ class KeycloakBackend(object):
         except Exception as e:
             return None
 
+    def redirect_login(self, client_id, redirect_uri, idp_alias):
+        """
+
+        :param client_id: client identifier received after registering the tenant
+        :param redirect_uri: redirect url
+        :param idp_alias: idp to which redirection has to be made
+        :return:authorization_url, redirect_uri, state
+        """
+        redirect_uri += '?idp_alias=' + quote(idp_alias)
+        base_authorize_url = self.keycloak_settings.KEYCLOAK_AUTHORIZE_URL
+        oauth2_session = OAuth2Session(client_id, scope='openid', redirect_uri=redirect_uri)
+        authorization_url, state = oauth2_session.authorization_url(base_authorize_url)
+        authorization_url += '&kc_idp_hint=' + quote(idp_alias)
+        return authorization_url, redirect_uri, state
+
     def get_authorization_token(self, client_credentials, tenant_id, username=None):
         """
         This method created a authorization token for the user or a service account
@@ -83,7 +99,7 @@ class KeycloakBackend(object):
         client = BackendApplicationClient(client_id=client_credentials.client_id)
         oauth = OAuth2Session(client=client)
         token = oauth.fetch_token(
-            token_url=self.keycloak_settings.token_url,
+            token_url=self.keycloak_settings.KEYCLOAK_TOKEN_URL,
             client_id=client_credentials.client_id,
             client_secret=client_credentials.client_secret,
             verify=client_credentials.verify_ssl)
@@ -93,7 +109,6 @@ class KeycloakBackend(object):
             accessToken=access_token,
             claimsMap={'gatewayID': tenant_id, 'userName': username})
 
-
     def _get_token_and_user_info_password_flow(self, client_credentials):
 
         oauth2_session = OAuth2Session(client=LegacyApplicationClient(client_id=client_credentials.client_id))
@@ -104,7 +119,7 @@ class KeycloakBackend(object):
                                            client_secret=client_credentials.client_secret,
                                            verify=self.keycloak_settings.VERIFY_SSL)
         user_info = oauth2_session.get(self.keycloak_settings.KEYCLOAK_USERINFO_URL).json()
-        return self._process_token(token), self._process_userinfo(user_info)
+        return token, user_info
 
     def _get_token_and_user_info_redirect_flow(self, client_credentials):
         oauth2_session = OAuth2Session(client_credentials.client_id,
@@ -116,7 +131,7 @@ class KeycloakBackend(object):
                                            authorization_response=client_credentials.authorization_code_url,
                                            verify=self.keycloak_settings.VERIFY_SSL)
         user_info = oauth2_session.get(self.keycloak_settings.KEYCLOAK_USERINFO_URL).json()
-        return self._process_token(token), self._process_userinfo(user_info)
+        return token, user_info
 
     def _get_token_from_refresh_token(self, client_credentials, refresh_token):
 
@@ -126,26 +141,7 @@ class KeycloakBackend(object):
                                              refresh_token=refresh_token,
                                              auth=auth,
                                              verify=self.keycloak_settings.VERIFY_SSL)
-        return self._process_token(token)
-
-    @classmethod
-    def _process_token(cls, token):
-
-        now = time.time()
-        access_token = token['access_token']
-        access_token_expires_at = now + token['expires_in']
-        refresh_token = token['refresh_token']
-        refresh_token_expires_at = now + token['refresh_expires_in']
-        return Token(access_token, access_token_expires_at, refresh_token, refresh_token_expires_at)
-
-    @classmethod
-    def _process_userinfo(cls, userinfo):
-
-        username = userinfo['preferred_username']
-        email = userinfo['email']
-        first_name = userinfo['given_name']
-        last_name = userinfo['family_name']
-        return UserInfo(username, email, first_name, last_name)
+        return token
 
     def _load_settings(self, configuration_file_location):
         config = configparser.ConfigParser()
@@ -157,20 +153,3 @@ class KeycloakBackend(object):
         self.keycloak_settings.KEYCLOAK_USERINFO_URL = settings['KEYCLOAK_USERINFO_URL']
         self.keycloak_settings.VERIFY_SSL = settings.getboolean('VERIFY_SSL')
 
-
-class Token(object):
-
-    def __init__(self, access_token, access_token_expires_at, refresh_token, refresh_token_expires_at):
-        self.access_token = access_token
-        self.access_token_expires_at = access_token_expires_at
-        self.refresh_token = refresh_token
-        self.refresh_token_expires_at = refresh_token_expires_at
-
-
-class UserInfo(object):
-
-    def __init__(self, username, email, first_name, last_name):
-        self.username = username
-        self.email = email
-        self.first_name = first_name
-        self.last_name = last_name


[airavata-custos] 15/24: Merge pull request #7 from apache/python-lib

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit 841c5cd35b592dfe8c34bdab8e391b102cb16034
Merge: 742535b e56d294
Author: Suresh Marru <sm...@apache.org>
AuthorDate: Mon Sep 9 15:17:33 2019 -0400

    Merge pull request #7 from apache/python-lib
    
    Merging the PR for aarushi to build on this library

 .gitignore                                    |   1 +
 clients/python/.gitignore                     |   4 +
 clients/python/LICENSE                        | 201 ++++++++++++++++++++++++++
 clients/python/README.md                      |   1 +
 clients/python/airavata_custos/__init__.py    |  20 +++
 clients/python/airavata_custos/well_knowns.py |  30 ++++
 clients/python/requirements_dev.txt           |  13 ++
 clients/python/setup.cfg                      |  26 ++++
 clients/python/setup.py                       |  26 ++++
 clients/python/tests/__init__.py              |   0
 clients/python/tests/test_well_knowns.py      |  38 +++++
 clients/python/tox.ini                        |  22 +++
 12 files changed, 382 insertions(+)


[airavata-custos] 05/24: Dozer mapping to map between credential entity and resources

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit 1af4ad183c66596ee73428a98b5fcf1663734305
Author: Dimuthu Wannipurage <di...@gmail.com>
AuthorDate: Tue Jul 9 01:48:59 2019 -0400

    Dozer mapping to map between credential entity and resources
---
 credential-store/credential-api/pom.xml                      |  8 +++++++-
 .../credential/api/controllers/SSHCredentialsController.java | 12 +++++-------
 2 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/credential-store/credential-api/pom.xml b/credential-store/credential-api/pom.xml
index 024f783..817ef74 100644
--- a/credential-store/credential-api/pom.xml
+++ b/credential-store/credential-api/pom.xml
@@ -19,7 +19,13 @@
         <dependency>
             <groupId>net.sf.dozer</groupId>
             <artifactId>dozer</artifactId>
-            <version>5.5.1</version>
+            <version>5.4.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-log4j12</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
         <dependency>
             <groupId>org.apache.airavata.custos</groupId>
diff --git a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
index e78a489..b949526 100644
--- a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
+++ b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
@@ -3,10 +3,11 @@ package org.apache.custos.credential.api.controllers;
 import org.apache.airavata.custos.credentials.ssh.SSHCredentialEntity;
 import org.apache.airavata.custos.vault.VaultManager;
 import org.apache.custos.credential.api.resources.SSHCredential;
+import org.dozer.DozerBeanMapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RestController;
 
 @RestController
@@ -15,14 +16,11 @@ public class SSHCredentialsController {
     @Autowired
     private VaultManager vaultManager;
 
+    final private DozerBeanMapper mapper = new DozerBeanMapper();
 
-    @RequestMapping("/ssh/{gateway}/{token}")
+    @RequestMapping(value = "/ssh/{gateway}/{token}", method = RequestMethod.GET)
     public SSHCredential getSSHCredential(@PathVariable("gateway") String gateway, @PathVariable("token") String token) throws Exception {
         SSHCredentialEntity credentialEntity = vaultManager.getCredentialEntity(SSHCredentialEntity.class, token, gateway);
-        SSHCredential resource = new SSHCredential();
-        resource.setPassphrase(credentialEntity.getPassphrase());
-        resource.setPrivateKey(credentialEntity.getPrivateKey());
-        resource.setPublicKey(credentialEntity.getPublicKey());
-        return resource;
+        return mapper.map(credentialEntity, SSHCredential.class);
     }
 }


[airavata-custos] 04/24: Fixing the issue in VaultManager to retrieve correct credential

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit 0c1750cbaefada3d107cae7ff74faf9d37a51cb1
Author: Dimuthu Wannipurage <di...@gmail.com>
AuthorDate: Tue Jul 9 01:48:23 2019 -0400

    Fixing the issue in VaultManager to retrieve correct credential
---
 .../java/org/apache/airavata/custos/vault/VaultManager.java | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java
index 4e4baf4..4e333c0 100644
--- a/credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java
+++ b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java
@@ -10,6 +10,7 @@ import org.apache.airavata.custos.vault.annotations.VaultPath;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.PostConstruct;
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.util.*;
 
@@ -27,25 +28,29 @@ public class VaultManager {
         vault = new Vault(config);
     }
 
-    public <T extends BaseCredentialEntity> T getCredentialEntity(Class<T> calzz, final String token, final String gateway) throws Exception {
+    public <T extends BaseCredentialEntity> T getCredentialEntity(Class<T> clazz, final String token, final String gateway) throws Exception {
 
         Map<String, String> params = new HashMap<String, String>() {{
             put("token", token);
             put("gateway", gateway);
         }};
 
-        for (Class<?> c = calzz; c != null; c = c.getSuperclass()) {
+        Constructor<T> ctor = clazz.getConstructor();
+        T obj = ctor.newInstance();
+
+        for (Class<?> c = clazz; c != null; c = c.getSuperclass()) {
             Field[] fields = c.getDeclaredFields();
             for (Field field : fields) {
                 VaultPath vaultPathAnnotation = field.getAnnotation(VaultPath.class);
                 if (vaultPathAnnotation != null) {
                     String path = populatePathWithParams(vaultPathAnnotation.path(), params);
                     Map<String,String> data = vault.logical().read(path).getData();
-                    System.out.println("Value for key " + vaultPathAnnotation.name() + " " + data.get(vaultPathAnnotation.name()));
+                    field.setAccessible(true);
+                    field.set(obj, data.get(vaultPathAnnotation.name()));
                 }
             }
         }
-        return null;
+        return obj;
     }
 
     public <T extends BaseCredentialEntity> String saveCredentialEntity(final T credentialEntity, final String gateway) throws Exception {


[airavata-custos] 03/24: Fixing a typo in module naming

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit 082aec18edc54c49bbecaeb89ea6fccbc3ef3bf9
Author: Dimuthu Wannipurage <di...@gmail.com>
AuthorDate: Tue Jul 9 01:19:16 2019 -0400

    Fixing a typo in module naming
---
 {credntial-store => credential-store}/credential-api/pom.xml          | 4 ++--
 .../src/main/java/org/apache/custos/credential/api/Application.java   | 0
 .../custos/credential/api/controllers/SSHCredentialsController.java   | 1 +
 .../org/apache/custos/credential/api/resources/SSHCredential.java     | 0
 {credntial-store => credential-store}/credential-core/pom.xml         | 4 ++--
 .../org/apache/airavata/custos/credentials/BaseCredentialEntity.java  | 0
 .../apache/airavata/custos/credentials/ssh/SSHCredentialEntity.java   | 0
 .../src/main/java/org/apache/airavata/custos/vault/VaultManager.java  | 0
 .../java/org/apache/airavata/custos/vault/annotations/VaultPath.java  | 0
 {credntial-store => credential-store}/pom.xml                         | 4 ++--
 pom.xml                                                               | 2 +-
 11 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/credntial-store/credential-api/pom.xml b/credential-store/credential-api/pom.xml
similarity index 95%
rename from credntial-store/credential-api/pom.xml
rename to credential-store/credential-api/pom.xml
index f559740..024f783 100644
--- a/credntial-store/credential-api/pom.xml
+++ b/credential-store/credential-api/pom.xml
@@ -3,7 +3,7 @@
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <artifactId>credntial-store</artifactId>
+        <artifactId>credential-store</artifactId>
         <groupId>org.apache.airavata.custos</groupId>
         <version>0.01-SNAPSHOT</version>
     </parent>
@@ -37,4 +37,4 @@
         </plugins>
     </build>
 
-</project>
\ No newline at end of file
+</project>
diff --git a/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/Application.java b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/Application.java
similarity index 100%
rename from credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/Application.java
rename to credential-store/credential-api/src/main/java/org/apache/custos/credential/api/Application.java
diff --git a/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
similarity index 99%
rename from credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
rename to credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
index cc5094f..e78a489 100644
--- a/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
+++ b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
@@ -15,6 +15,7 @@ public class SSHCredentialsController {
     @Autowired
     private VaultManager vaultManager;
 
+
     @RequestMapping("/ssh/{gateway}/{token}")
     public SSHCredential getSSHCredential(@PathVariable("gateway") String gateway, @PathVariable("token") String token) throws Exception {
         SSHCredentialEntity credentialEntity = vaultManager.getCredentialEntity(SSHCredentialEntity.class, token, gateway);
diff --git a/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/SSHCredential.java b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/SSHCredential.java
similarity index 100%
rename from credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/SSHCredential.java
rename to credential-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/SSHCredential.java
diff --git a/credntial-store/credential-core/pom.xml b/credential-store/credential-core/pom.xml
similarity index 94%
rename from credntial-store/credential-core/pom.xml
rename to credential-store/credential-core/pom.xml
index b70ef4b..090c1bf 100644
--- a/credntial-store/credential-core/pom.xml
+++ b/credential-store/credential-core/pom.xml
@@ -3,7 +3,7 @@
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <artifactId>credntial-store</artifactId>
+        <artifactId>credential-store</artifactId>
         <groupId>org.apache.airavata.custos</groupId>
         <version>0.01-SNAPSHOT</version>
     </parent>
@@ -30,4 +30,4 @@
         </dependency>
     </dependencies>
 
-</project>
\ No newline at end of file
+</project>
diff --git a/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/BaseCredentialEntity.java b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/BaseCredentialEntity.java
similarity index 100%
rename from credntial-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/BaseCredentialEntity.java
rename to credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/BaseCredentialEntity.java
diff --git a/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/ssh/SSHCredentialEntity.java b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/ssh/SSHCredentialEntity.java
similarity index 100%
rename from credntial-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/ssh/SSHCredentialEntity.java
rename to credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/ssh/SSHCredentialEntity.java
diff --git a/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java
similarity index 100%
rename from credntial-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java
rename to credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java
diff --git a/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/vault/annotations/VaultPath.java b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/annotations/VaultPath.java
similarity index 100%
rename from credntial-store/credential-core/src/main/java/org/apache/airavata/custos/vault/annotations/VaultPath.java
rename to credential-store/credential-core/src/main/java/org/apache/airavata/custos/vault/annotations/VaultPath.java
diff --git a/credntial-store/pom.xml b/credential-store/pom.xml
similarity index 93%
rename from credntial-store/pom.xml
rename to credential-store/pom.xml
index 52eb721..c64d1d2 100644
--- a/credntial-store/pom.xml
+++ b/credential-store/pom.xml
@@ -5,7 +5,7 @@
     <modelVersion>4.0.0</modelVersion>
 
     <groupId>org.apache.airavata.custos</groupId>
-    <artifactId>credntial-store</artifactId>
+    <artifactId>credential-store</artifactId>
     <packaging>pom</packaging>
     <version>0.01-SNAPSHOT</version>
     <name>Custos Credential Store</name>
@@ -22,4 +22,4 @@
             <version>2.1.6.RELEASE</version>
         </dependency>
     </dependencies>
-</project>
\ No newline at end of file
+</project>
diff --git a/pom.xml b/pom.xml
index a141d16..5f650ea 100644
--- a/pom.xml
+++ b/pom.xml
@@ -87,7 +87,7 @@
                 <activeByDefault>true</activeByDefault>
             </activation>
             <modules>
-                <module>credntial-store</module>
+                <module>credential-store</module>
             </modules>
         </profile>
     </profiles>


[airavata-custos] 06/24: Using dependency injection for DozerBeanMapper

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit fa997dfbc228435d6a0b7ffccaff34c78132bdd0
Author: Dimuthu Wannipurage <di...@gmail.com>
AuthorDate: Tue Jul 9 01:59:20 2019 -0400

    Using dependency injection for DozerBeanMapper
---
 .../java/org/apache/custos/credential/api/AppConfig.java  | 15 +++++++++++++++
 .../api/controllers/SSHCredentialsController.java         |  3 ++-
 2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/AppConfig.java b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/AppConfig.java
new file mode 100644
index 0000000..8a2ee86
--- /dev/null
+++ b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/AppConfig.java
@@ -0,0 +1,15 @@
+package org.apache.custos.credential.api;
+
+import org.dozer.DozerBeanMapper;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class AppConfig {
+
+    // Use for future custom mapping scenarios https://www.java-success.com/dozer-with-spring-maven-tutorial/
+    @Bean
+    public DozerBeanMapper dozerBeanMapper() {
+        return new DozerBeanMapper();
+    }
+}
diff --git a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
index b949526..a6063e5 100644
--- a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
+++ b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
@@ -16,7 +16,8 @@ public class SSHCredentialsController {
     @Autowired
     private VaultManager vaultManager;
 
-    final private DozerBeanMapper mapper = new DozerBeanMapper();
+    @Autowired
+    private DozerBeanMapper mapper;
 
     @RequestMapping(value = "/ssh/{gateway}/{token}", method = RequestMethod.GET)
     public SSHCredential getSSHCredential(@PathVariable("gateway") String gateway, @PathVariable("token") String token) throws Exception {


[airavata-custos] 19/24: adding sample settings file

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit f1b591f4d4b90c8cf9a933925e9a4b12cfba543a
Author: Aarushi <aa...@gmail.com>
AuthorDate: Wed Sep 25 11:21:20 2019 -0400

    adding sample settings file
---
 clients/python/README.md                           | 49 +++++++++++++++++++--
 clients/python/airavata_custos/sample_settings.ini |  4 +-
 .../security/keycloak_connectors.py                | 51 +++++++++++-----------
 3 files changed, 74 insertions(+), 30 deletions(-)

diff --git a/clients/python/README.md b/clients/python/README.md
index 907817d..dd70088 100644
--- a/clients/python/README.md
+++ b/clients/python/README.md
@@ -1,10 +1,53 @@
 # Airavata Custos Python SDK
 
+Folder structure 
+
+- airavata_custos : client
+    - admin
+    - security
+    
+- custos: thrift generated service APIs and models
+- tests: test cases
+
 Create a virtual environment
-- python3 -m venv venv
+
+    python3 -m venv venv
 
 Activate the virtual environment
-- source venv/bin/activate
+    
+    source venv/bin/activate
 
 Install dependencies
-- pip install -r requirements_dev.txt
\ No newline at end of file
+    
+    pip install -r requirements_dev.txt
+
+Server configuration should be kept in a INI file in the following format. For more information refer to sample_settings.ini file  
+    
+    [IAMSeverSettings]
+    KEYCLOAK_AUTHORIZE_URL =
+    KEYCLOAK_TOKEN_URL = 
+    KEYCLOAK_USERINFO_URL = 
+    KEYCLOAK_LOGOUT_URL = 
+    VERIFY_SSL = 
+    
+    [ProfileServerSettings]
+    PROFILE_SERVICE_HOST = 
+    PROFILE_SERVICE_PORT =
+    
+Keycloak connections
+
+    - authenticate_user
+    - authenticate_account
+    - authenticate_using_refresh_token
+
+Admin operations
+    
+    - is_username_available
+    - register_user
+    - is_user_enabled
+    - enable_user
+    - delete_user
+    - is_user_exist
+    - get_user
+    - get_users
+    - reset_user_password
\ No newline at end of file
diff --git a/clients/python/airavata_custos/sample_settings.ini b/clients/python/airavata_custos/sample_settings.ini
index c9a1e1c..18eb04b 100644
--- a/clients/python/airavata_custos/sample_settings.ini
+++ b/clients/python/airavata_custos/sample_settings.ini
@@ -6,5 +6,5 @@ KEYCLOAK_LOGOUT_URL = https://localhost:8443/auth/realms/default/protocol/openid
 VERIFY_SSL = no
 
 [ProfileServerSettings]
-PROFILE_SERVICE_HOST = '0.0.0.0'
-PROFILE_SERVICE_PORT = '8081'
\ No newline at end of file
+PROFILE_SERVICE_HOST = 0.0.0.0
+PROFILE_SERVICE_PORT = 8081
\ No newline at end of file
diff --git a/clients/python/airavata_custos/security/keycloak_connectors.py b/clients/python/airavata_custos/security/keycloak_connectors.py
index 05816c2..07e8dd8 100644
--- a/clients/python/airavata_custos/security/keycloak_connectors.py
+++ b/clients/python/airavata_custos/security/keycloak_connectors.py
@@ -50,11 +50,11 @@ class KeycloakBackend(object):
         """
 
         :param account_credentials: object of AccountCredentials class
-        :return: Token object, UserInfo object
+        :return: Token object, Account info object
         """
         try:
-            token, user_info = self._get_token_and_user_info_redirect_flow(account_credentials)
-            return token, user_info
+            token, account_info = self._get_token_and_user_info_redirect_flow(account_credentials)
+            return token, account_info
         except Exception as e:
             return None
 
@@ -71,6 +71,29 @@ class KeycloakBackend(object):
         except Exception as e:
             return None
 
+    def get_authorization_token(self, client_credentials, tenant_id, username=None):
+        """
+        This method created a authorization token for the user or a service account
+        In case of a service account username will be null
+        :param client_credentials: object of class client_credentials
+        :param tenant_id: gateway id of the client
+        :param username: username of the user for which authorization token is being created
+        :return: AuthzToken
+        """
+        client = BackendApplicationClient(client_id=client_credentials.client_id)
+        oauth = OAuth2Session(client=client)
+        token = oauth.fetch_token(
+            token_url=self.keycloak_settings.token_url,
+            client_id=client_credentials.client_id,
+            client_secret=client_credentials.client_secret,
+            verify=client_credentials.verify_ssl)
+
+        access_token = token.get('access_token')
+        return AuthzToken(
+            accessToken=access_token,
+            claimsMap={'gatewayID': tenant_id, 'userName': username})
+
+
     def _get_token_and_user_info_password_flow(self, client_credentials):
 
         oauth2_session = OAuth2Session(client=LegacyApplicationClient(client_id=client_credentials.client_id))
@@ -105,28 +128,6 @@ class KeycloakBackend(object):
                                              verify=self.keycloak_settings.VERIFY_SSL)
         return self._process_token(token)
 
-    def get_authorization_token(self, client_credentials, tenant_id, username=None):
-        """
-        This method created a authorization token for the user or a service account
-        In case of a service account username will be null
-        :param client_credentials: object of class client_credentials
-        :param tenant_id: gateway id of the client
-        :param username: username of the user for which authorization token is being created
-        :return: AuthzToken
-        """
-        client = BackendApplicationClient(client_id=client_credentials.client_id)
-        oauth = OAuth2Session(client=client)
-        token = oauth.fetch_token(
-            token_url=self.keycloak_settings.token_url,
-            client_id=client_credentials.client_id,
-            client_secret=client_credentials.client_secret,
-            verify=client_credentials.verify_ssl)
-
-        access_token = token.get('access_token')
-        return AuthzToken(
-            accessToken=access_token,
-            claimsMap={'gatewayID': tenant_id, 'userName': username})
-
     @classmethod
     def _process_token(cls, token):
 


[airavata-custos] 13/24: Merge pull request #3 from DImuthuUpe/master

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit 742535b2fd833860cca247b86b62823bc486cf39
Merge: 3802acd 67dce3f
Author: Dimuthu Wannipurage <di...@gmail.com>
AuthorDate: Thu Aug 22 12:45:13 2019 -0400

    Merge pull request #3 from DImuthuUpe/master
    
    Initial vault based credential store + Rest API framework with SSH and AWS credential support

 .gitignore                                         |   3 +
 LICENSE                                            | 201 +++++++++++++++++++++
 credential-store/credential-api/pom.xml            |  66 +++++++
 .../apache/custos/credential/api/AppConfig.java    |  34 ++++
 .../apache/custos/credential/api/Application.java  |  33 ++++
 .../api/controllers/AWSCredentialController.java   |  52 ++++++
 .../api/controllers/SSHCredentialsController.java  |  51 ++++++
 .../custos/credential/api/filters/AuthFilter.java  |  59 ++++++
 .../credential/api/resources/AWSCredntial.java     |  41 +++++
 .../credential/api/resources/SSHCredential.java    |  51 ++++++
 credential-store/credential-core/pom.xml           |  54 ++++++
 .../custos/credentials/BaseCredentialEntity.java   |  41 +++++
 .../credentials/aws/AWSCredentialEntity.java       |  48 +++++
 .../credentials/ssh/SSHCredentialEntity.java       |  58 ++++++
 .../apache/airavata/custos/vault/VaultManager.java | 180 ++++++++++++++++++
 .../custos/vault/annotations/VaultPath.java        |  33 ++++
 credential-store/pom.xml                           |  46 +++++
 pom.xml                                            | 111 ++++++++++++
 18 files changed, 1162 insertions(+)


[airavata-custos] 22/24: refactored the code to improve usability

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit 4844ef61bfee0ad587f440b189ea798671d6eae3
Author: Aarushi <aa...@gmail.com>
AuthorDate: Sun Sep 29 15:26:37 2019 -0400

    refactored the code to improve usability
---
 .../airavata_custos/security/client_credentials.py |  2 +-
 .../security/keycloak_connectors.py                | 59 ++++++++++++++--------
 2 files changed, 38 insertions(+), 23 deletions(-)

diff --git a/clients/python/airavata_custos/security/client_credentials.py b/clients/python/airavata_custos/security/client_credentials.py
index a0934d1..8e2899f 100644
--- a/clients/python/airavata_custos/security/client_credentials.py
+++ b/clients/python/airavata_custos/security/client_credentials.py
@@ -48,7 +48,7 @@ class UserCredentials(ClientCredentials):
         self.password = password
 
 
-class AccountCredentials(ClientCredentials):
+class IdpCredentials(ClientCredentials):
     """
     This class inherits from ClientCredentials class. Used for passing parameters required to authenticate service
     account with keycloak
diff --git a/clients/python/airavata_custos/security/keycloak_connectors.py b/clients/python/airavata_custos/security/keycloak_connectors.py
index a99c3f0..cf105f7 100644
--- a/clients/python/airavata_custos/security/keycloak_connectors.py
+++ b/clients/python/airavata_custos/security/keycloak_connectors.py
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-import time
+
 from oauthlib.oauth2 import LegacyApplicationClient
 import requests
 import configparser
@@ -23,6 +23,7 @@ from oauthlib.oauth2 import BackendApplicationClient
 from requests_oauthlib import OAuth2Session
 from custos.commons.model.security.ttypes import AuthzToken
 from urllib.parse import quote
+from airavata_custos.security.client_credentials import IdpCredentials, UserCredentials, ClientCredentials
 
 
 class KeycloakBackend(object):
@@ -35,10 +36,10 @@ class KeycloakBackend(object):
         self.keycloak_settings = IAMSettings()
         self._load_settings(configuration_file_location)
 
-    def authenticate_user(self, user_credentials):
+    def authenticate_using_user_details(self, user_credentials):
         """
         Method to authenticate a gateway user with keycloak
-        :param user_credentials: object of UserCredentials class
+        :param user_credentials: object of UserCredentials class. To get instance of this class use prepare_user_credentials
         :return: openid token, openid user information
         """
         try:
@@ -47,45 +48,59 @@ class KeycloakBackend(object):
         except Exception as e:
             return None
 
-    def authenticate_account(self, account_credentials):
+    def prepare_user_credentials(self, client_id, client_secret, username, password):
         """
 
-        :param account_credentials: object of AccountCredentials class
-        :return: openid token, openid user information
+        :param client_id: client identifier received after registering the tenant
+        :param client_secret: client password received after registering the tenant
+        :param username: username of the user which needs to be authenticated
+        :param password: password of the user which needs to be authenticated
+        :return: UserCredentials object
         """
-        try:
-            token, account_info = self._get_token_and_user_info_redirect_flow(account_credentials)
-            return token, account_info
-        except Exception as e:
-            return None
+        return UserCredentials(client_id, client_secret, username, password)
 
-    def authenticate_using_refresh_token(self, client_credentials, refresh_token):
+    def authenticate_using_idp(self, idp_credentials):
         """
 
-        :param client_credentials: object of ClientCredentials class
-        :param refresh_token: openid connect refresh token
-        :return: openid token
+        :param idp_credentials: object of IdpCredentials class. To get an instance of this class use prepare_idp_credentials
+        :return: openid token, openid user information
         """
         try:
-            token = self._get_token_from_refresh_token(client_credentials, refresh_token)
-            return token
+            token, user_info = self._get_token_and_user_info_redirect_flow(idp_credentials)
+            return token, user_info
         except Exception as e:
             return None
 
-    def idp_login(self, client_id, redirect_uri, idp_alias):
+    def prepare_idp_credentials(self, client_id, client_secret, redirect_uri, idp_alias):
         """
 
         :param client_id: client identifier received after registering the tenant
-        :param redirect_uri: redirect url
-        :param idp_alias: idp to which redirection has to be made
-        :return:authorization_url, redirect_uri, state
+        :param client_secret: client password received after registering the tenant
+        :param redirect_uri: URI for the callback entry point of the client
+        :param idp_alias: name of the idp
+        :return: object of class IdpCredentials
         """
         redirect_uri += '?idp_alias=' + quote(idp_alias)
         base_authorize_url = self.keycloak_settings.KEYCLOAK_AUTHORIZE_URL
         oauth2_session = OAuth2Session(client_id, scope='openid', redirect_uri=redirect_uri)
         authorization_url, state = oauth2_session.authorization_url(base_authorize_url)
         authorization_url += '&kc_idp_hint=' + quote(idp_alias)
-        return authorization_url, redirect_uri, state
+        return IdpCredentials(client_id, client_secret, authorization_url, state,
+                              redirect_uri)
+
+    def authenticate_using_refresh_token(self, client_id, client_secret, refresh_token):
+        """
+
+        :param client_id: client identifier received after registering the tenant
+        :param client_secret: client password received after registering the tenant
+        :param refresh_token: openid connect refresh token
+        :return: openid token
+        """
+        try:
+            token = self._get_token_from_refresh_token(ClientCredentials(client_id, client_secret), refresh_token)
+            return token
+        except Exception as e:
+            return None
 
     def get_authorization_token(self, client_credentials, tenant_id, username=None):
         """


[airavata-custos] 07/24: API to add SSH credentials

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit e4021296257df31e5c8a56e8a7ab821f5800a10b
Author: Dimuthu Wannipurage <di...@gmail.com>
AuthorDate: Tue Jul 9 02:12:13 2019 -0400

    API to add SSH credentials
---
 .../credential/api/controllers/SSHCredentialsController.java | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
index a6063e5..8250687 100644
--- a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
+++ b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
@@ -5,10 +5,7 @@ import org.apache.airavata.custos.vault.VaultManager;
 import org.apache.custos.credential.api.resources.SSHCredential;
 import org.dozer.DozerBeanMapper;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 @RestController
 public class SSHCredentialsController {
@@ -24,4 +21,11 @@ public class SSHCredentialsController {
         SSHCredentialEntity credentialEntity = vaultManager.getCredentialEntity(SSHCredentialEntity.class, token, gateway);
         return mapper.map(credentialEntity, SSHCredential.class);
     }
+
+    @RequestMapping(value = "/ssh/{gateway}", method = RequestMethod.POST)
+    public String createSSHCredential(@RequestBody SSHCredential sshCredential, @PathVariable("gateway") String gateway) throws Exception {
+        SSHCredentialEntity credentialEntity = mapper.map(sshCredential, SSHCredentialEntity.class);
+        String token = vaultManager.saveCredentialEntity(credentialEntity, gateway);
+        return token;
+    }
 }


[airavata-custos] 14/24: initial python custos library

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit e56d294223fb10e1ddd3893d93b596e294d637b2
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Fri Sep 6 09:06:32 2019 -0400

    initial python custos library
---
 .gitignore                                    |   1 +
 clients/python/.gitignore                     |   4 +
 clients/python/LICENSE                        | 201 ++++++++++++++++++++++++++
 clients/python/README.md                      |   1 +
 clients/python/airavata_custos/__init__.py    |  20 +++
 clients/python/airavata_custos/well_knowns.py |  30 ++++
 clients/python/requirements_dev.txt           |  13 ++
 clients/python/setup.cfg                      |  26 ++++
 clients/python/setup.py                       |  26 ++++
 clients/python/tests/__init__.py              |   0
 clients/python/tests/test_well_knowns.py      |  38 +++++
 clients/python/tox.ini                        |  22 +++
 12 files changed, 382 insertions(+)

diff --git a/.gitignore b/.gitignore
index 6366460..d0ada35 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
 .idea/*
 *.iml
 target/*
+.vscode
diff --git a/clients/python/.gitignore b/clients/python/.gitignore
new file mode 100644
index 0000000..836c896
--- /dev/null
+++ b/clients/python/.gitignore
@@ -0,0 +1,4 @@
+venv
+airavata_custos.egg-info
+*.pyc
+.tox
diff --git a/clients/python/LICENSE b/clients/python/LICENSE
new file mode 100644
index 0000000..f49a4e1
--- /dev/null
+++ b/clients/python/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
\ No newline at end of file
diff --git a/clients/python/README.md b/clients/python/README.md
new file mode 100644
index 0000000..5c4de03
--- /dev/null
+++ b/clients/python/README.md
@@ -0,0 +1 @@
+# Airavata Custos Python SDK
diff --git a/clients/python/airavata_custos/__init__.py b/clients/python/airavata_custos/__init__.py
new file mode 100644
index 0000000..227c90f
--- /dev/null
+++ b/clients/python/airavata_custos/__init__.py
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+
+"""
+airavata_custos is the Python API for Airavata Custos.
+"""
diff --git a/clients/python/airavata_custos/well_knowns.py b/clients/python/airavata_custos/well_knowns.py
new file mode 100644
index 0000000..45e6ed6
--- /dev/null
+++ b/clients/python/airavata_custos/well_knowns.py
@@ -0,0 +1,30 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+
+"""
+Define URIs needed for configuring OIDC clients.
+"""
+
+
+def get_well_known_oidc_uri_for_realm(realm):
+    return ("https://iam.scigap.org/auth/realms/{}/.well-known/"
+            "openid-configuration".format(realm))
+
+
+def get_dev_well_known_oidc_uri_for_realm(realm):
+    return ("https://iamdev.scigap.org/auth/realms/{}/.well-known/"
+            "openid-configuration".format(realm))
diff --git a/clients/python/requirements_dev.txt b/clients/python/requirements_dev.txt
new file mode 100644
index 0000000..a1d25f4
--- /dev/null
+++ b/clients/python/requirements_dev.txt
@@ -0,0 +1,13 @@
+atomicwrites==1.3.0
+attrs==19.1.0
+autopep8==1.4.3
+entrypoints==0.3
+flake8==3.7.7
+mccabe==0.6.1
+pluggy==0.9.0
+py==1.8.0
+pycodestyle==2.5.0
+pyflakes==2.1.1
+pytest==4.3.0
+pytest-runner==4.4
+six==1.12.0
diff --git a/clients/python/setup.cfg b/clients/python/setup.cfg
new file mode 100644
index 0000000..4101d6d
--- /dev/null
+++ b/clients/python/setup.cfg
@@ -0,0 +1,26 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+
+[bdist_wheel]
+universal = 1
+
+[metadata]
+description-file = README.md
+license_file = LICENSE
+
+[aliases]
+test = pytest
diff --git a/clients/python/setup.py b/clients/python/setup.py
new file mode 100644
index 0000000..e56024b
--- /dev/null
+++ b/clients/python/setup.py
@@ -0,0 +1,26 @@
+
+from setuptools import setup, find_packages
+
+setup_requirements = ['pytest-runner', ]
+
+test_requirements = ['pytest', ]
+
+setup(
+    name='airavata_custos',
+    version="0.0.1",
+    description='Apache Airavata Custos Python API',
+    author='Airavata Custos Developers',
+    author_email='custos@airavata.apache.org',
+    packages=find_packages(include=['airavata_custos']),
+    license='Apache License 2.0',
+    setup_requires=setup_requirements,
+    test_suite='tests',
+    tests_require=test_requirements,
+    classifiers=[
+        'Development Status :: 5 - Production/Stable',
+        'License :: OSI Approved :: Apache Software License',
+        'Programming Language :: Python :: 2.7',
+        'Programming Language :: Python :: 3',
+        'Programming Language :: Python :: 3.6'
+    ]
+)
diff --git a/clients/python/tests/__init__.py b/clients/python/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/clients/python/tests/test_well_knowns.py b/clients/python/tests/test_well_knowns.py
new file mode 100644
index 0000000..8be65b2
--- /dev/null
+++ b/clients/python/tests/test_well_knowns.py
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+
+"""
+Tests for the well_knowns module.
+"""
+
+from airavata_custos import well_knowns
+
+
+def test_get_well_known_oidc_uri_for_realm():
+    well_known_oidc_uri = well_knowns.get_well_known_oidc_uri_for_realm(
+        "test-realm")
+    assert (well_known_oidc_uri ==
+            "https://iam.scigap.org/auth/realms/"
+            "test-realm/.well-known/openid-configuration")
+
+
+def test_get_dev_well_known_oidc_uri_for_realm():
+    well_known_oidc_uri = well_knowns.get_dev_well_known_oidc_uri_for_realm(
+        "test-realm")
+    assert (well_known_oidc_uri ==
+            "https://iamdev.scigap.org/auth/realms/"
+            "test-realm/.well-known/openid-configuration")
diff --git a/clients/python/tox.ini b/clients/python/tox.ini
new file mode 100644
index 0000000..d6e2cc2
--- /dev/null
+++ b/clients/python/tox.ini
@@ -0,0 +1,22 @@
+
+[tox]
+envlist = py27, py36, flake8
+
+[testenv:flake8]
+basepython = python
+deps = flake8
+commands = flake8 airavata_custos
+
+[testenv]
+setenv =
+    PYTHONPATH = {toxinidir}
+deps =
+    -r{toxinidir}/requirements_dev.txt
+; If you want to make tox run the tests with the same versions, create a
+; requirements.txt with the pinned versions and uncomment the following line:
+;     -r{toxinidir}/requirements.txt
+commands =
+    pip install -U pip
+    py.test --basetemp={envtmpdir}
+
+


[airavata-custos] 08/24: Adding AWS key store feature

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit 05361dbdc29b85584d1f8a9708c06c605f4386c0
Author: Dimuthu Wannipurage <di...@gmail.com>
AuthorDate: Tue Jul 9 16:37:28 2019 -0400

    Adding AWS key store feature
---
 .../api/controllers/AWSCredentialController.java   | 33 ++++++++++++++++++++++
 .../api/controllers/SSHCredentialsController.java  |  5 ++--
 .../credential/api/resources/AWSCredntial.java     | 22 +++++++++++++++
 .../credentials/aws/AWSCredentialEntity.java       | 29 +++++++++++++++++++
 4 files changed, 87 insertions(+), 2 deletions(-)

diff --git a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/AWSCredentialController.java b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/AWSCredentialController.java
new file mode 100644
index 0000000..1b757be
--- /dev/null
+++ b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/AWSCredentialController.java
@@ -0,0 +1,33 @@
+package org.apache.custos.credential.api.controllers;
+
+import org.apache.airavata.custos.credentials.aws.AWSCredentialEntity;
+import org.apache.airavata.custos.credentials.ssh.SSHCredentialEntity;
+import org.apache.airavata.custos.vault.VaultManager;
+import org.apache.custos.credential.api.resources.AWSCredntial;
+import org.apache.custos.credential.api.resources.SSHCredential;
+import org.dozer.DozerBeanMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/aws")
+public class AWSCredentialController {
+    @Autowired
+    private VaultManager vaultManager;
+
+    @Autowired
+    private DozerBeanMapper mapper;
+
+    @RequestMapping(value = "/{gateway}/{token}", method = RequestMethod.GET)
+    public AWSCredntial getAWSCredential(@PathVariable("gateway") String gateway, @PathVariable("token") String token) throws Exception {
+        AWSCredentialEntity credentialEntity = vaultManager.getCredentialEntity(AWSCredentialEntity.class, token, gateway);
+        return mapper.map(credentialEntity, AWSCredntial.class);
+    }
+
+    @RequestMapping(value = "/{gateway}", method = RequestMethod.POST)
+    public String createAWSCredential(@RequestBody AWSCredntial credntial, @PathVariable("gateway") String gateway) throws Exception {
+        AWSCredentialEntity credentialEntity = mapper.map(credntial, AWSCredentialEntity.class);
+        String token = vaultManager.saveCredentialEntity(credentialEntity, gateway);
+        return token;
+    }
+}
diff --git a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
index 8250687..dbd8c94 100644
--- a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
+++ b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
@@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 @RestController
+@RequestMapping("/ssh")
 public class SSHCredentialsController {
 
     @Autowired
@@ -16,13 +17,13 @@ public class SSHCredentialsController {
     @Autowired
     private DozerBeanMapper mapper;
 
-    @RequestMapping(value = "/ssh/{gateway}/{token}", method = RequestMethod.GET)
+    @RequestMapping(value = "/{gateway}/{token}", method = RequestMethod.GET)
     public SSHCredential getSSHCredential(@PathVariable("gateway") String gateway, @PathVariable("token") String token) throws Exception {
         SSHCredentialEntity credentialEntity = vaultManager.getCredentialEntity(SSHCredentialEntity.class, token, gateway);
         return mapper.map(credentialEntity, SSHCredential.class);
     }
 
-    @RequestMapping(value = "/ssh/{gateway}", method = RequestMethod.POST)
+    @RequestMapping(value = "/{gateway}", method = RequestMethod.POST)
     public String createSSHCredential(@RequestBody SSHCredential sshCredential, @PathVariable("gateway") String gateway) throws Exception {
         SSHCredentialEntity credentialEntity = mapper.map(sshCredential, SSHCredentialEntity.class);
         String token = vaultManager.saveCredentialEntity(credentialEntity, gateway);
diff --git a/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/AWSCredntial.java b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/AWSCredntial.java
new file mode 100644
index 0000000..befab46
--- /dev/null
+++ b/credential-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/AWSCredntial.java
@@ -0,0 +1,22 @@
+package org.apache.custos.credential.api.resources;
+
+public class AWSCredntial {
+    private String accessKey;
+    private String secretKey;
+
+    public String getAccessKey() {
+        return accessKey;
+    }
+
+    public void setAccessKey(String accessKey) {
+        this.accessKey = accessKey;
+    }
+
+    public String getSecretKey() {
+        return secretKey;
+    }
+
+    public void setSecretKey(String secretKey) {
+        this.secretKey = secretKey;
+    }
+}
diff --git a/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/aws/AWSCredentialEntity.java b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/aws/AWSCredentialEntity.java
new file mode 100644
index 0000000..5991264
--- /dev/null
+++ b/credential-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/aws/AWSCredentialEntity.java
@@ -0,0 +1,29 @@
+package org.apache.airavata.custos.credentials.aws;
+
+import org.apache.airavata.custos.credentials.BaseCredentialEntity;
+import org.apache.airavata.custos.vault.annotations.VaultPath;
+
+public class AWSCredentialEntity extends BaseCredentialEntity {
+
+    @VaultPath(path = "secret/aws/{gateway}/{token}", name = "access_key_id", required = true)
+    private String accessKey;
+
+    @VaultPath(path = "secret/aws/{gateway}/{token}", name = "secret_access_key")
+    private String secretKey;
+
+    public String getAccessKey() {
+        return accessKey;
+    }
+
+    public void setAccessKey(String accessKey) {
+        this.accessKey = accessKey;
+    }
+
+    public String getSecretKey() {
+        return secretKey;
+    }
+
+    public void setSecretKey(String secretKey) {
+        this.secretKey = secretKey;
+    }
+}


[airavata-custos] 21/24: refactored the name

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit 6b8da39acb1289b66657c0c54707458920564652
Author: Aarushi <aa...@gmail.com>
AuthorDate: Sun Sep 29 14:18:19 2019 -0400

    refactored the name
---
 clients/python/airavata_custos/security/keycloak_connectors.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clients/python/airavata_custos/security/keycloak_connectors.py b/clients/python/airavata_custos/security/keycloak_connectors.py
index 99d4c72..a99c3f0 100644
--- a/clients/python/airavata_custos/security/keycloak_connectors.py
+++ b/clients/python/airavata_custos/security/keycloak_connectors.py
@@ -72,7 +72,7 @@ class KeycloakBackend(object):
         except Exception as e:
             return None
 
-    def redirect_login(self, client_id, redirect_uri, idp_alias):
+    def idp_login(self, client_id, redirect_uri, idp_alias):
         """
 
         :param client_id: client identifier received after registering the tenant


[airavata-custos] 02/24: Initial credential store framework

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit d20c5fc1f758e526606b321ab2cdbe738642bae3
Author: Dimuthu Wannipurage <di...@gmail.com>
AuthorDate: Tue Jul 9 01:10:53 2019 -0400

    Initial credential store framework
---
 .gitignore                                         |   3 +
 credntial-store/credential-api/pom.xml             |  40 ++++++
 .../apache/custos/credential/api/Application.java  |  14 ++
 .../api/controllers/SSHCredentialsController.java  |  27 ++++
 .../credential/api/resources/SSHCredential.java    |  32 +++++
 credntial-store/credential-core/pom.xml            |  33 +++++
 .../custos/credentials/BaseCredentialEntity.java   |  22 +++
 .../credentials/ssh/SSHCredentialEntity.java       |  39 ++++++
 .../apache/airavata/custos/vault/VaultManager.java | 156 +++++++++++++++++++++
 .../custos/vault/annotations/VaultPath.java        |  14 ++
 credntial-store/pom.xml                            |  25 ++++
 pom.xml                                            | 111 +++++++++++++++
 12 files changed, 516 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6366460
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.idea/*
+*.iml
+target/*
diff --git a/credntial-store/credential-api/pom.xml b/credntial-store/credential-api/pom.xml
new file mode 100644
index 0000000..f559740
--- /dev/null
+++ b/credntial-store/credential-api/pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>credntial-store</artifactId>
+        <groupId>org.apache.airavata.custos</groupId>
+        <version>0.01-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>credential-api</artifactId>
+
+    <properties>
+        <java.version>1.8</java.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>net.sf.dozer</groupId>
+            <artifactId>dozer</artifactId>
+            <version>5.5.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.airavata.custos</groupId>
+            <artifactId>credential-core</artifactId>
+            <version>0.01-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/Application.java b/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/Application.java
new file mode 100644
index 0000000..d4f7ce4
--- /dev/null
+++ b/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/Application.java
@@ -0,0 +1,14 @@
+package org.apache.custos.credential.api;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
+
+@SpringBootApplication
+@ComponentScan(basePackages = {"org.apache.custos", "org.apache.airavata"})
+public class Application {
+
+    public static void main(String[] args) {
+        SpringApplication.run(Application.class, args);
+    }
+}
\ No newline at end of file
diff --git a/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java b/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
new file mode 100644
index 0000000..cc5094f
--- /dev/null
+++ b/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
@@ -0,0 +1,27 @@
+package org.apache.custos.credential.api.controllers;
+
+import org.apache.airavata.custos.credentials.ssh.SSHCredentialEntity;
+import org.apache.airavata.custos.vault.VaultManager;
+import org.apache.custos.credential.api.resources.SSHCredential;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class SSHCredentialsController {
+
+    @Autowired
+    private VaultManager vaultManager;
+
+    @RequestMapping("/ssh/{gateway}/{token}")
+    public SSHCredential getSSHCredential(@PathVariable("gateway") String gateway, @PathVariable("token") String token) throws Exception {
+        SSHCredentialEntity credentialEntity = vaultManager.getCredentialEntity(SSHCredentialEntity.class, token, gateway);
+        SSHCredential resource = new SSHCredential();
+        resource.setPassphrase(credentialEntity.getPassphrase());
+        resource.setPrivateKey(credentialEntity.getPrivateKey());
+        resource.setPublicKey(credentialEntity.getPublicKey());
+        return resource;
+    }
+}
diff --git a/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/SSHCredential.java b/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/SSHCredential.java
new file mode 100644
index 0000000..e80e77a
--- /dev/null
+++ b/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/SSHCredential.java
@@ -0,0 +1,32 @@
+package org.apache.custos.credential.api.resources;
+
+public class SSHCredential {
+
+    private String privateKey;
+    private String publicKey;
+    private String passphrase;
+
+    public String getPrivateKey() {
+        return privateKey;
+    }
+
+    public void setPrivateKey(String privateKey) {
+        this.privateKey = privateKey;
+    }
+
+    public String getPublicKey() {
+        return publicKey;
+    }
+
+    public void setPublicKey(String publicKey) {
+        this.publicKey = publicKey;
+    }
+
+    public String getPassphrase() {
+        return passphrase;
+    }
+
+    public void setPassphrase(String passphrase) {
+        this.passphrase = passphrase;
+    }
+}
diff --git a/credntial-store/credential-core/pom.xml b/credntial-store/credential-core/pom.xml
new file mode 100644
index 0000000..b70ef4b
--- /dev/null
+++ b/credntial-store/credential-core/pom.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>credntial-store</artifactId>
+        <groupId>org.apache.airavata.custos</groupId>
+        <version>0.01-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>credential-core</artifactId>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>com.bettercloud</groupId>
+            <artifactId>vault-java-driver</artifactId>
+            <version>4.0.0</version>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/BaseCredentialEntity.java b/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/BaseCredentialEntity.java
new file mode 100644
index 0000000..45ba2aa
--- /dev/null
+++ b/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/BaseCredentialEntity.java
@@ -0,0 +1,22 @@
+package org.apache.airavata.custos.credentials;
+
+public class BaseCredentialEntity {
+    private String token;
+    private String gateway;
+
+    public String getToken() {
+        return token;
+    }
+
+    public void setToken(String token) {
+        this.token = token;
+    }
+
+    public String getGateway() {
+        return gateway;
+    }
+
+    public void setGateway(String gateway) {
+        this.gateway = gateway;
+    }
+}
diff --git a/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/ssh/SSHCredentialEntity.java b/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/ssh/SSHCredentialEntity.java
new file mode 100644
index 0000000..1e8ddcf
--- /dev/null
+++ b/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/ssh/SSHCredentialEntity.java
@@ -0,0 +1,39 @@
+package org.apache.airavata.custos.credentials.ssh;
+
+import org.apache.airavata.custos.credentials.BaseCredentialEntity;
+import org.apache.airavata.custos.vault.annotations.VaultPath;
+
+public class SSHCredentialEntity extends BaseCredentialEntity {
+    @VaultPath(path = "secret/ssh/{gateway}/{token}", name = "private", required = true)
+    private String privateKey;
+
+    @VaultPath(path = "secret/ssh/{gateway}/{token}", name = "passphrase")
+    private String passphrase;
+
+    @VaultPath(path = "secret/ssh/{gateway}/{token}", name = "public", required = true)
+    private String publicKey;
+
+    public String getPrivateKey() {
+        return privateKey;
+    }
+
+    public void setPrivateKey(String privateKey) {
+        this.privateKey = privateKey;
+    }
+
+    public String getPassphrase() {
+        return passphrase;
+    }
+
+    public void setPassphrase(String passphrase) {
+        this.passphrase = passphrase;
+    }
+
+    public String getPublicKey() {
+        return publicKey;
+    }
+
+    public void setPublicKey(String publicKey) {
+        this.publicKey = publicKey;
+    }
+}
diff --git a/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java b/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java
new file mode 100644
index 0000000..4e4baf4
--- /dev/null
+++ b/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java
@@ -0,0 +1,156 @@
+package org.apache.airavata.custos.vault;
+
+import com.bettercloud.vault.Vault;
+import com.bettercloud.vault.VaultConfig;
+import com.bettercloud.vault.VaultException;
+import com.bettercloud.vault.response.LogicalResponse;
+import org.apache.airavata.custos.credentials.BaseCredentialEntity;
+import org.apache.airavata.custos.credentials.ssh.SSHCredentialEntity;
+import org.apache.airavata.custos.vault.annotations.VaultPath;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import java.lang.reflect.Field;
+import java.util.*;
+
+@Service
+public class VaultManager {
+
+    private String vaultAddress = "http://127.0.0.1:8200";
+    private String vaultToken = "s.PFc3SbOz1N2wSpV5hWZ56yVI";
+
+    private Vault vault;
+
+    @PostConstruct
+    public void init() throws VaultException {
+        final VaultConfig config = new VaultConfig().address(vaultAddress).token(vaultToken).build();
+        vault = new Vault(config);
+    }
+
+    public <T extends BaseCredentialEntity> T getCredentialEntity(Class<T> calzz, final String token, final String gateway) throws Exception {
+
+        Map<String, String> params = new HashMap<String, String>() {{
+            put("token", token);
+            put("gateway", gateway);
+        }};
+
+        for (Class<?> c = calzz; c != null; c = c.getSuperclass()) {
+            Field[] fields = c.getDeclaredFields();
+            for (Field field : fields) {
+                VaultPath vaultPathAnnotation = field.getAnnotation(VaultPath.class);
+                if (vaultPathAnnotation != null) {
+                    String path = populatePathWithParams(vaultPathAnnotation.path(), params);
+                    Map<String,String> data = vault.logical().read(path).getData();
+                    System.out.println("Value for key " + vaultPathAnnotation.name() + " " + data.get(vaultPathAnnotation.name()));
+                }
+            }
+        }
+        return null;
+    }
+
+    public <T extends BaseCredentialEntity> String saveCredentialEntity(final T credentialEntity, final String gateway) throws Exception {
+        final String token = UUID.randomUUID().toString();
+
+        credentialEntity.setGateway(gateway);
+        credentialEntity.setToken(token);
+
+        Map<String, String> params = new HashMap<String, String>() {{
+            put("token", token);
+            put("gateway", gateway);
+        }};
+
+        Map<String, Map<String, Object>> summary = new HashMap<>();
+
+        for (Class<?> c = credentialEntity.getClass(); c != null; c = c.getSuperclass()) {
+            Field[] fields = c.getDeclaredFields();
+            for (Field field : fields) {
+                VaultPath vaultPathAnnotation = field.getAnnotation(VaultPath.class);
+                if (vaultPathAnnotation != null) {
+                    field.setAccessible(true);
+                    String vaultPathValue = (String ) field.get(credentialEntity);
+                    String path = populatePathWithParams(vaultPathAnnotation.path(), params);
+                    Map map = summary.computeIfAbsent(path, (k) -> new HashMap());
+                    map.put(vaultPathAnnotation.name(), vaultPathValue);
+                }
+            }
+        }
+
+        for (Map.Entry<String, Map<String, Object>> entry : summary.entrySet()) {
+            vault.logical().write(entry.getKey(), entry.getValue());
+        }
+        return token;
+    }
+
+    /**
+     * This will resolve parameterized path strings with the provided param map. Example path is like secret/{gateway}/{token}/value.
+     * Params map should provide the values for gateway and token parameters.
+     *
+     * @param path Path with parameters
+     * @param params Parameter map
+     * @return Resolved path
+     * @throws Exception if the path is not in correct format or required parameters are not found in the params map
+     */
+    private String populatePathWithParams(String path, Map<String, String> params) throws Exception {
+        String newPath = "";
+        int begin = 0;
+        while (true) {
+            int startPos = path.indexOf("{", begin);
+            if (startPos != -1) {
+                int endPos = path.indexOf("}" , begin);
+                if (endPos == -1) {
+                    throw new Exception("Path " + path + " is not in the correct format");
+                } else {
+                    newPath += path.substring(begin, startPos);
+                    String paramName = path.substring(startPos + 1, endPos);
+                    String paramValue = params.get(paramName);
+                    if (paramValue == null) {
+                        throw new Exception("Parameter can not be found for name " + paramName + " in path " + path);
+                    }
+                    newPath += paramValue;
+                    begin = endPos + 1;
+                }
+            } else {
+                return begin == 0? path: newPath + path.substring(begin);
+            }
+        }
+    }
+
+    public String getVaultAddress() {
+        return vaultAddress;
+    }
+
+    public void setVaultAddress(String vaultAddress) {
+        this.vaultAddress = vaultAddress;
+    }
+
+    public String getVaultToken() {
+        return vaultToken;
+    }
+
+    public void setVaultToken(String vaultToken) {
+        this.vaultToken = vaultToken;
+    }
+
+    public static void main(String args[]) throws Exception {
+        final VaultConfig config = new VaultConfig()
+                .address("http://127.0.0.1:8200")
+                .token("s.PFc3SbOz1N2wSpV5hWZ56yVI")
+                .build();
+
+        final Vault vault = new Vault(config);
+
+        Map<String, String> data = vault.logical().read("secret/hello").getData();
+        //System.out.println(data);
+
+        VaultManager manager = new VaultManager();
+        manager.init();
+
+        SSHCredentialEntity credentialEntity = new SSHCredentialEntity();
+        credentialEntity.setPrivateKey("def");
+        credentialEntity.setPublicKey("ddfdfef");
+        credentialEntity.setPassphrase("11111");
+        String token = manager.saveCredentialEntity(credentialEntity, "seagrid");
+        //System.out.println(token);
+        SSHCredentialEntity c = manager.getCredentialEntity(SSHCredentialEntity.class, "1a5a8ba8-384b-40ab-8be4-577a1cdb02c3", "seagrid");
+    }
+}
diff --git a/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/vault/annotations/VaultPath.java b/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/vault/annotations/VaultPath.java
new file mode 100644
index 0000000..b36d4bb
--- /dev/null
+++ b/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/vault/annotations/VaultPath.java
@@ -0,0 +1,14 @@
+package org.apache.airavata.custos.vault.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface VaultPath {
+    public String name();
+    public String path();
+    public boolean required() default false;
+}
diff --git a/credntial-store/pom.xml b/credntial-store/pom.xml
new file mode 100644
index 0000000..52eb721
--- /dev/null
+++ b/credntial-store/pom.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.apache.airavata.custos</groupId>
+    <artifactId>credntial-store</artifactId>
+    <packaging>pom</packaging>
+    <version>0.01-SNAPSHOT</version>
+    <name>Custos Credential Store</name>
+
+    <modules>
+        <module>credential-core</module>
+        <module>credential-api</module>
+    </modules>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <version>2.1.6.RELEASE</version>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..a141d16
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <prerequisites>
+        <maven>3.0</maven>
+    </prerequisites>
+
+    <parent>
+        <groupId>org.apache</groupId>
+        <artifactId>apache</artifactId>
+        <version>21</version>
+    </parent>
+
+    <groupId>org.apache.airavata</groupId>
+    <artifactId>airavata-custos</artifactId>
+    <packaging>pom</packaging>
+    <name>Airavata Custos</name>
+    <version>0.01-SNAPSHOT</version>
+
+    <url>http://airavata.apache.org/</url>
+    <inceptionYear>2011</inceptionYear>
+
+    <scm>
+        <connection>scm:git:https://github.com/apache/airavata.git</connection>
+        <developerConnection>scm:git:https://github.com/apache/airavata.git</developerConnection>
+        <url>https://github.com/apache/airavata</url>
+        <tag>HEAD</tag>
+    </scm>
+
+    <mailingLists>
+
+        <mailingList>
+            <name>Airavata Custos Developer List</name>
+            <subscribe>custos-subscribe@airavata.apache.org</subscribe>
+            <unsubscribe>custos-unsubscribe@airavata.apache.org</unsubscribe>
+            <post>mailto:custos@airavata.apache.org</post>
+            <archive>http://mail-archives.apache.org/mod_mbox/airavata-custos/</archive>
+        </mailingList>
+
+    </mailingLists>
+
+    <dependencyManagement>
+        <dependencies>
+        </dependencies>
+    </dependencyManagement>
+
+    <profiles>
+        <profile>
+            <id>default</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-compiler-plugin</artifactId>
+                        <version>3.6.1</version>
+                        <configuration>
+                            <source>1.8</source>
+                            <target>1.8</target>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <modules>
+                <module>credntial-store</module>
+            </modules>
+        </profile>
+    </profiles>
+
+    <reporting>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>2.9.1</version>
+                <configuration>
+                    <aggregate>true</aggregate>
+                    <quiet>true</quiet>
+                    <minmemory>256m</minmemory>
+                    <maxmemory>2g</maxmemory>
+                </configuration>
+            </plugin>
+        </plugins>
+    </reporting>
+
+</project>