<commit_before># Copyright (c) 2010-2011 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from datetime import datetime
from datetime import timedelta
import uuid

import keystone.logic.types.auth as auth
import keystone.logic.types.atom as atom
import keystone.db.sqlalchemy.api as db_api
import keystone.db.sqlalchemy.models as db_models
import keystone.logic.types.fault as fault
import keystone.logic.types.tenant as tenants
import keystone.logic.types.role as roles
import keystone.logic.types.user as users


class IdentityService(object):
    "This is the logical implemenation of the Identity service"

    #
    #  Token Operations
    #

    def authenticate(self, credentials):
        # Check credentials
        if not isinstance(credentials, auth.PasswordCredentials):
            raise fault.BadRequestFault("Expecting Password Credentials!")

        if not credentials.tenant_id:
            duser = db_api.user_get(credentials.username)
            if duser == None:
                raise fault.UnauthorizedFault("Unauthorized")
        else:
            duser = db_api.user_get_by_tenant(credentials.username,
                                              credentials.tenant_id)
            if duser == None:
                raise fault.UnauthorizedFault("Unauthorized on this tenant")

        if not duser.enabled:
            raise fault.UserDisabledFault("Your account has been disabled")
        if duser.password != <PASSWORD>:
            raise fault.UnauthorizedFault("Unauthorized")

        #
        # Look for an existing token, or create one,
        # TODO: Handle tenant/token search
        #
        if not credentials.tenant_id:
            dtoken = db_api.token_for_user(duser.id)
        else:
            dtoken = db_api.token_for_user_tenant(duser.id,
                                                  credentials.tenant_id)
        if not dtoken or dtoken.expires < datetime.now():
            # Create new token
            dtoken = db_models.Token()
            dtoken.token_id = str(uuid.uuid4())
            dtoken.user_id = duser.id
            if credentials.tenant_id:
                dtoken.tenant_id = credentials.tenant_id
            dtoken.expires = datetime.now() + timedelta(days=1)
            db_api.token_create(dtoken)

        return self.__get_auth_data(dtoken, duser)

    def validate_token(self, admin_token, token_id, belongs_to=None):
        self.__validate_token(admin_token)
        if not token_id:
            raise fault.UnauthorizedFault("Missing token")
        (token, user) = self.__get_dauth_data(token_id)

        if not token:
            raise fault.UnauthorizedFault("Bad token, please reauthenticate")
        if token.expires < datetime.now():
            raise fault.ForbiddenFault("Token expired, please renew")
        if not user.enabled:
            raise fault.UserDisabledFault("The user %s has been disabled!"
                                          % user.id)
        return self.__get_auth_data(token, user)

    def revoke_token(self, admin_token, token_id):
        self.__validate_token(admin_token)

        dtoken = db_api.token_get(token_id)
        if not dtoken:
            raise fault.ItemNotFoundFault("Token not found")

        db_api.token_delete(token_id)

    #
    #   Tenant Operations
    #

    def create_tenant(self, admin_token, tenant):
        self.__validate_token(admin_token)

        if not isinstance(tenant, tenants.Tenant):
            raise fault.BadRequestFault("Expecting a Tenant")

        if tenant.tenant_id == None:
            raise fault.BadRequestFault("Expecting a unique Tenant Id")

        if db_api.tenant_get(tenant.tenant_id) != None:
            raise fault.TenantConflictFault(
                "A tenant with that id already exists")

        dtenant = db_models.Tenant()
        dtenant.id = tenant.tenant_id
        dtenant.desc = tenant.description
        dtenant.enabled = tenant.enabled

        db_api.tenant_create(dtenant)
        return tenant

    ##
    ##    GET Tenants with Pagination
    ##
    def get_tenants(self, admin_token, marker, limit, url):
        self.__validate_token(admin_token)

        ts = []
        dtenants = db_api.tenant_get_page(marker, limit)
        for dtenant in dtenants:
            ts.append(tenants.Tenant(dtenant.id,
                                     dtenant.desc, dtenant.enabled))
        prev, next = db_api.tenant_get_page_markers(marker, limit)
        links = []
        if prev:
            links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'" \
                                                % (url, prev, limit)))
        if next:
            links.append(atom.Link('next', "%s?'marker=%s&limit=%s'" \
                                                % (url, next, limit)))
        return tenants.Tenants(ts, links)

    def get_tenant(self, admin_token, tenant_id):
        self.__validate_token(admin_token)

        dtenant = db_api.tenant_get(tenant_id)
        if not dtenant:
            raise fault.ItemNotFoundFault("The tenant could not be found")
        return tenants.Tenant(dtenant.id, dtenant.desc, dtenant.enabled)

    def update_tenant(self, admin_token, tenant_id, tenant):
        self.__validate_token(admin_token)

        if not isinstance(tenant, tenants.Tenant):
            raise fault.BadRequestFault("Expecting a Tenant")

        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.ItemNotFoundFault("The tenant cloud not be found")
        values = {'desc': tenant.description, 'enabled': tenant.enabled}
        db_api.tenant_update(tenant_id, values)
        return tenants.Tenant(dtenant.id, tenant.description, tenant.enabled)

    def delete_tenant(self, admin_token, tenant_id):
        self.__validate_token(admin_token)

        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.ItemNotFoundFault("The tenant cloud not be found")

        if not db_api.tenant_is_empty(tenant_id):
            raise fault.ForbiddenFault("You may not delete a tenant that "
                                       "contains users or groups")

        db_api.tenant_delete(dtenant.id)
        return None

    #
    #   Tenant Group Operations
    #

    def create_tenant_group(self, admin_token, tenant, group):
        self.__validate_token(admin_token)

        if not isinstance(group, tenants.Group):
            raise fault.BadRequestFault("Expecting a Group")

        if tenant == None:
            raise fault.BadRequestFault("Expecting a Tenant Id")

        dtenant = db_api.tenant_get(tenant)
        if dtenant == None:
            raise fault.ItemNotFoundFault("The tenant not found")

        if group.group_id == None:
            raise fault.BadRequestFault("Expecting a Group Id")

        if db_api.group_get(group.group_id) != None:
            raise fault.TenantGroupConflictFault(
                "A tenant group with that id already exists")

        dtenant = db_models.Group()
        dtenant.id = group.group_id
        dtenant.desc = group.description
        dtenant.tenant_id = tenant
        db_api.tenant_group_create(dtenant)
        return tenants.Group(dtenant.id, dtenant.desc, dtenant.tenant_id)

    def get_tenant_groups(self, admin_token, tenant_id, marker, limit, url):
        self.__validate_token(admin_token)
        if tenant_id == None:
            raise fault.BadRequestFault("Expecting a Tenant Id")

        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.ItemNotFoundFault("The tenant not found")

        ts = []
        dtenantgroups = db_api.tenant_group_get_page(tenant_id, marker, limit)

        for dtenantgroup in dtenantgroups:
            ts.append(tenants.Group(dtenantgroup.id,
                                     dtenantgroup.desc,
                                     dtenantgroup.tenant_id))
        prev, next = db_api.tenant_group_get_page_markers(tenant_id, marker,
                                                          limit)
        links = []
        if prev:
            links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'" \
                                    % (url, prev, limit)))
        if next:
            links.append(atom.Link('next', "%s?'marker=%s&limit=%s'"\
                                    % (url, next, limit)))

        return tenants.Groups(ts, links)

    def get_tenant_group(self, admin_token, tenant_id, group_id):
        self.__validate_token(admin_token)

        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.ItemNotFoundFault("The tenant not found")

        dtenant = db_api.tenant_group_get(group_id, tenant_id)
        if not dtenant:
            raise fault.ItemNotFoundFault("The tenant group not found")

        return tenants.Group(dtenant.id, dtenant.desc, dtenant.tenant_id)

    def update_tenant_group(self, admin_token, tenant_id, group_id, group):
        self.__validate_token(admin_token)

        if not isinstance(group, tenants.Group):
            raise fault.BadRequestFault("Expecting a Group")
        True

        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.ItemNotFoundFault("The tenant not found")

        dtenant = db_api.tenant_group_get(group_id, tenant_id)
        if not dtenant:
            raise fault.ItemNotFoundFault("The tenant group not found")

        if group_id != group.group_id:
            raise fault.BadRequestFault("Wrong Data Provided,\
                                            Group id not matching")

        if str(tenant_id) != str(group.tenant_id):
            raise fault.BadRequestFault("Wrong Data Provided,\
                                            Tenant id not matching ")

        values = {'desc': group.description}

        db_api.tenant_group_update(group_id, tenant_id, values)

        return tenants.Group(group_id, group.description, tenant_id)

    def delete_tenant_group(self, admin_token, tenant_id, group_id):
        self.__validate_token(admin_token)

        dtenant = db_api.tenant_get(tenant_id)

        if dtenant == None:
            raise fault.ItemNotFoundFault("The tenant not found")

        dtenant = db_api.tenant_group_get(group_id, tenant_id)
        if not dtenant:
            raise fault.ItemNotFoundFault("The tenant group not found")

        if not db_api.tenant_group_is_empty(group_id):
            raise fault.ForbiddenFault("You may not delete a tenant that "
                                       "contains users or groups")

        db_api.tenant_group_delete(group_id, tenant_id)
        return None

    def get_users_tenant_group(self, admin_token, tenantId, groupId, marker,
                               limit, url):
        self.__validate_token(admin_token)
        if tenantId == None:
            raise fault.BadRequestFault("Expecting a Tenant Id")

        if db_api.tenant_get(tenantId) == None:
            raise fault.ItemNotFoundFault("The tenant not found")

        if db_api.tenant_group_get(groupId, tenantId) == None:
            raise fault.ItemNotFoundFault(
                "A tenant group with that id not found")
        ts = []
        dgroupusers = db_api.users_tenant_group_get_page(groupId, marker,
                                                          limit)
        for dgroupuser, dgroupuserAsso in dgroupusers:

            ts.append(tenants.User(dgroupuser.id,
                                   dgroupuser.email, dgroupuser.enabled,
                                   tenantId, None))
        links = []
        if ts.__len__():
            prev, next = db_api.users_tenant_group_get_page_markers(groupId,
                                                             marker, limit)
            if prev:
                links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'" %
                                      (url, prev, limit)))
            if next:
                links.append(atom.Link('next', "%s?'marker=%s&limit=%s'" %
                                      (url, next, limit)))
        return tenants.Users(ts, links)

    def add_user_tenant_group(self, admin_token, tenant, group, user):
        self.__validate_token(admin_token)

        if db_api.tenant_get(tenant) == None:
            raise fault.ItemNotFoundFault("The Tenant not found")

        if db_api.group_get(group) == None:
            raise fault.ItemNotFoundFault("The Group not found")
        duser = db_api.user_get(user)
        if duser == None:
            raise fault.ItemNotFoundFault("The User not found")

        if db_api.tenant_group_get(group, tenant) == None:
            raise fault.ItemNotFoundFault("A tenant group with"
                                           " that id not found")

        if db_api.get_user_by_group(user, group) != None:
            raise fault.UserGroupConflictFault(
                "A user with that id already exists in group")

        dusergroup = db_models.UserGroupAssociation()
        dusergroup.user_id = user
        dusergroup.group_id = group
        db_api.user_tenant_group(dusergroup)

        return tenants.User(duser.id, duser.email, duser.enabled,
                            tenant, group)

    def delete_user_tenant_group(self, admin_token, tenant, group, user):
        self.__validate_token(admin_token)

        if db_api.tenant_get(tenant) == None:
            raise fault.ItemNotFoundFault("The Tenant not found")

        if db_api.group_get(group) == None:
            raise fault.ItemNotFoundFault("The Group not found")
        duser = db_api.user_get(user)
        if duser == None:
            raise fault.ItemNotFoundFault("The User not found")

        if db_api.tenant_group_get(group, tenant) == None:
            raise fault.ItemNotFoundFault("A tenant group with"
                                          " that id not found")

        if db_api.get_user_by_group(user, group) == None:
            raise fault.ItemNotFoundFault("A user with that id "
                                          "in a group not found")

        db_api.user_tenant_group_delete(user, group)
        return None


    #
    # Private Operations
    #
    def __get_dauth_data(self, token_id):
        """return token and user object for a token_id"""

        token = None
        user = None
        if token_id:
            token = db_api.token_get(token_id)
            if token:
                user = db_api.user_get(token.user_id)
        return (token, user)


    #
    #   User Operations
    #
    def create_user(self, admin_token, tenant_id, user):
        self.__validate_token(admin_token)

        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.UnauthorizedFault("Unauthorized")
        if not dtenant.enabled:
            raise fault.TenantDisabledFault("Your account has been disabled")

        if not isinstance(user, users.User):
            raise fault.BadRequestFault("Expecting a User")

        if user.user_id == None:
            raise fault.BadRequestFault("Expecting a unique User Id")

        if db_api.user_get(user.user_id) != None:
            raise fault.UserConflictFault(
                "An user with that id already exists")

        if db_api.user_get_email(user.email) != None:
            raise fault.EmailConflictFault(
                "Email already exists")

        duser_tenant = db_models.UserTenantAssociation()
        duser_tenant.user_id = user.user_id
        duser_tenant.tenant_id = tenant_id
        db_api.user_tenant_create(duser_tenant)

        duser = db_models.User()
        duser.id = user.user_id
        duser.password = <PASSWORD>
        duser.email = user.email
        duser.enabled = user.enabled
        db_api.user_create(duser)

        return user


    def get_tenant_users(self, admin_token, tenant_id, marker, limit, url):
        self.__validate_token(admin_token)

        if tenant_id == None:
            raise fault.BadRequestFault("Expecting a Tenant Id")
        dtenant = db_api.tenant_get(tenant_id)
        if dtenant is  None:
            raise fault.ItemNotFoundFault("The tenant not found")
        if not dtenant.enabled:
            raise fault.TenantDisabledFault("Your account has been disabled")
        ts = []
        dtenantusers = db_api.users_get_by_tenant_get_page(tenant_id, marker,
                                                          limit)
        for dtenantuser, dtenantuserAsso in dtenantusers:
            ts.append(users.User(None, dtenantuser.id, tenant_id,
                                   dtenantuser.email, dtenantuser.enabled))
        links = []
        if ts.__len__():
            prev, next = db_api.users_get_by_tenant_get_page_markers(tenant_id,
                                                             marker, limit)
            if prev:
                links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'" %
                                      (url, prev, limit)))
            if next:
                links.append(atom.Link('next', "%s?'marker=%s&limit=%s'" %
                                      (url, next, limit)))
        return users.Users(ts, links)

    def get_user(self, admin_token, tenant_id, user_id):
        self.__validate_token(admin_token)
        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.UnauthorizedFault("Unauthorized")
        if not dtenant.enabled:
            raise fault.TenantDisabledFault("Your account has been disabled")

        duser = db_api.user_get(user_id)
        if not duser:
            raise fault.ItemNotFoundFault("The user could not be found")

        if not duser.enabled:
            raise fault.UserDisabledFault("User has been disabled")

        if len(duser.tenants) > 0:
            tenant_user = duser.tenants[0].tenant_id
        else:
            tenant_user = tenant_id

        ts = []
        dusergroups = db_api.user_groups_get_all(user_id)

        for dusergroup, dusergroupAsso in dusergroups:
            ts.append(tenants.Group(dusergroup.id, dusergroup.tenant_id, None))

        return users.User_Update(None, duser.id, tenant_user, duser.email,
                                 duser.enabled, ts)

    def update_user(self, admin_token, user_id, user, tenant_id):
        self.__validate_token(admin_token)

        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.UnauthorizedFault("Unauthorized")
        if not dtenant.enabled:
            raise fault.TenantDisabledFault("Your account has been disabled")

        duser = db_api.user_get(user_id)

        if not duser:
            raise fault.ItemNotFoundFault("The user could not be found")

        if not duser.enabled:
            raise fault.UserDisabledFault("User has been disabled")

        if not isinstance(user, users.User):
            raise fault.BadRequestFault("Expecting a User")

        if db_api.user_get_email(user.email) is not None:
            raise fault.EmailConflictFault(
                "Email already exists")

        values = {'email': user.email}

        db_api.user_update(user_id, values)
        duser = db_api.user_get_update(user_id)
        return users.User(duser.password, duser.id, tenant_id, duser.email,
                          duser.enabled)

    def set_user_password(self, admin_token, user_id, user, tenant_id):
        self.__validate_token(admin_token)

        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.UnauthorizedFault("Unauthorized")
        if not dtenant.enabled:
            raise fault.TenantDisabledFault("Your account has been disabled")

        duser = db_api.user_get(user_id)
        if not duser:
            raise fault.ItemNotFoundFault("The user could not be found")

        if not duser.enabled:
            raise fault.UserDisabledFault("User has been disabled")

        if not isinstance(user, users.User):
            raise fault.BadRequestFault("Expecting a User")

        duser = db_api.user_get(user_id)
        if duser == None:
            raise fault.ItemNotFoundFault("The user could not be found")

        values = {'password': <PASSWORD>}

        db_api.user_update(user_id, values)

        return users.User_Update(user.password, None, None, None, None, None)

    def enable_disable_user(self, admin_token, user_id, user, tenant_id):
        self.__validate_token(admin_token)
        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.UnauthorizedFault("Unauthorized")
        if not dtenant.enabled:
            raise fault.TenantDisabledFault("Your account has been disabled")

        duser = db_api.user_get(user_id)
        if not duser:
            raise fault.ItemNotFoundFault("The user could not be found")
        if not isinstance(user, users.User):
            raise fault.BadRequestFault("Expecting a User")

        duser = db_api.user_get(user_id)
        if duser == None:
            raise fault.ItemNotFoundFault("The user could not be found")

        values = {'enabled': user.enabled}

        db_api.user_update(user_id, values)

        return users.User_Update(None, None, None, None, user.enabled, None)

    def delete_user(self, admin_token, user_id, tenant_id):
        self.__validate_token(admin_token)
        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.UnauthorizedFault("Unauthorized")
        if not dtenant.enabled:
            raise fault.TenantDisabledFault("Your account has been disabled")

        duser = db_api.user_get(user_id)
        if not duser:
            raise fault.ItemNotFoundFault("The user could not be found")
        duser = db_api.user_get_by_tenant(user_id, tenant_id)
        if not duser:
            raise fault.ItemNotFoundFault("The user could not be "
                                        "found under given tenant")

        db_api.user_delete_tenant(user_id, tenant_id)
        return None

    def get_user_groups(self, admin_token, tenant_id, user_id, marker, limit,
                        url):
        self.__validate_token(admin_token)

        if tenant_id == None:
            raise fault.BadRequestFault("Expecting a Tenant Id")

        if db_api.tenant_get(tenant_id) == None:
            raise fault.ItemNotFoundFault("The tenant not found")

        if not db_api.tenant_get(tenant_id).enabled:
            raise fault.TenantDisabledFault("Your account has been disabled")

        ts = []
        dusergroups = db_api.groups_get_by_user_get_page(user_id, marker,
                                                          limit)

        for dusergroup, dusergroupAsso in dusergroups:
            ts.append(tenants.Group(dusergroup.id, dusergroup.desc,
                                    dusergroup.tenant_id))
        links = []
        if ts.__len__():
            prev, next = db_api.groups_get_by_user_get_page_markers(user_id,
                                                        marker, limit)
            if prev:
                links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'" %
                                      (url, prev, limit)))
            if next:
                links.append(atom.Link('next', "%s?'marker=%s&limit=%s'" %
                                      (url, next, limit)))
        return tenants.Groups(ts, links)

    #
    # Global Group Operations
    # TODO:(India Team) Rename functions
    #       and to maintain consistency
    #       with server.py
    def __check_create_global_tenant(self):

        dtenant = db_api.tenant_get('GlobalTenant')

        if dtenant is None:
            dtenant = db_models.Tenant()
            dtenant.id = 'GlobalTenant'
            dtenant.desc = 'GlobalTenant is Default tenant for global groups'
            dtenant.enabled = True
            db_api.tenant_create(dtenant)
        return dtenant

    def create_global_group(self, admin_token, group):
        self.__validate_token(admin_token)

        if not isinstance(group, tenants.GlobalGroup):
            raise fault.BadRequestFault("Expecting a Group")

        if group.group_id == None:
            raise fault.BadRequestFault("Expecting a Group Id")

        if db_api.group_get(group.group_id) != None:
            raise fault.TenantGroupConflictFault(
                "A tenant group with that id already exists")
        gtenant = self.__check_create_global_tenant()
        dtenant = db_models.Group()
        dtenant.id = group.group_id
        dtenant.desc = group.description
        dtenant.tenant_id = gtenant.id
        db_api.tenant_group_create(dtenant)
        return tenants.GlobalGroup(dtenant.id, dtenant.desc, None)

    def get_global_groups(self, admin_token, marker, limit, url):
        self.__validate_token(admin_token)
        gtenant = self.__check_create_global_tenant()
        ts = []
        dtenantgroups = db_api.tenant_group_get_page(gtenant.id, \
                                                      marker, limit)
        for dtenantgroup in dtenantgroups:
            ts.append(tenants.GlobalGroup(dtenantgroup.id,
                                     dtenantgroup.desc))
        prev, next = db_api.tenant_group_get_page_markers(gtenant.id,
                                                       marker, limit)
        links = []
        if prev:
            links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'" %
                                  (url, prev, limit)))
        if next:
            links.append(atom.Link('next', "%s?'marker=%s&limit=%s'" %
                                  (url, next, limit)))
        return tenants.GlobalGroups(ts, links)

    def get_global_group(self, admin_token, group_id):
        self.__validate_token(admin_token)
        gtenant = self.__check_create_global_tenant()
        dtenant = db_api.tenant_get(gtenant.id)
        if dtenant == None:
            raise fault.ItemNotFoundFault("The Global tenant not found")

        dtenant = db_api.tenant_group_get(group_id, gtenant.id)

        if not dtenant:
            raise fault.ItemNotFoundFault("The Global tenant group not found")
        return tenants.GlobalGroup(dtenant.id, dtenant.desc)

    def update_global_group(self, admin_token, group_id, group):
        self.__validate_token(admin_token)
        gtenant = self.__check_create_global_tenant()
        if not isinstance(group, tenants.GlobalGroup):
            raise fault.BadRequestFault("Expecting a Group")

        dtenant = db_api.tenant_get(gtenant.id)
        if dtenant == None:
            raise fault.ItemNotFoundFault("The global tenant not found")

        dtenant = db_api.tenant_group_get(group_id, gtenant.id)
        if not dtenant:
            raise fault.ItemNotFoundFault("The Global tenant group not found")
        if group_id != group.group_id:
            raise fault.BadRequestFault("Wrong Data Provided,"
                                            "Group id not matching")

        values = {'desc': group.description}
        db_api.tenant_group_update(group_id, gtenant.id, values)
        return tenants.GlobalGroup(group_id, group.description, gtenant.id)

    def delete_global_group(self, admin_token, group_id):
        self.__validate_token(admin_token)
        gtenant = self.__check_create_global_tenant()
        dtenant = db_api.tenant_get(gtenant.id)

        if dtenant == None:
            raise fault.ItemNotFoundFault("The global tenant not found")

        dtenant = db_api.tenant_group_get(group_id, dtenant.id)
        if not dtenant:
            raise fault.ItemNotFoundFault("The global tenant group not found")

        if not db_api.tenant_group_is_empty(group_id):
            raise fault.ForbiddenFault("You may not delete a group that "
                                       "contains users")

        db_api.tenant_group_delete(group_id, gtenant.id)
        return None

    def get_users_global_group(self, admin_token, groupId, marker, limit, url):
        self.__validate_token(admin_token)

        gtenant = self.__check_create_global_tenant()
        if gtenant.id == None:
            raise fault.BadRequestFault("Expecting a global Tenant")

        if db_api.tenant_get(gtenant.id) == None:
            raise fault.ItemNotFoundFault("The global tenant not found")

        if db_api.tenant_group_get(groupId, gtenant.id) == None:
            raise fault.ItemNotFoundFault(
                "A global tenant group with that id not found")
        ts = []
        dgroupusers = db_api.users_tenant_group_get_page(groupId, marker,
                                                         limit)
        for dgroupuser, dgroupuserassoc in dgroupusers:
            ts.append(tenants.User(dgroupuser.id, dgroupuser.email,
                                   dgroupuser.enabled))
        links = []
        if ts.__len__():
            prev, next = db_api.users_tenant_group_get_page_markers(groupId,
                                                                marker, limit)
            if prev:
                links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'"
                                       % (url, prev, limit)))
            if next:
                links.append(atom.Link('next', "%s?'marker=%s&limit=%s'"
                                       % (url, next, limit)))
        return tenants.Users(ts, links)

    def add_user_global_group(self, admin_token, group, user):
        self.__validate_token(admin_token)
        gtenant = self.__check_create_global_tenant()

        if db_api.tenant_get(gtenant.id) == None:
            raise fault.ItemNotFoundFault("The Global Tenant not found")

        if db_api.group_get(group) == None:
            raise fault.ItemNotFoundFault("The Group not found")
        duser = db_api.user_get(user)
        if duser == None:
            raise fault.ItemNotFoundFault("The User not found")

        if db_api.tenant_group_get(group, gtenant.id) == None:
            raise fault.ItemNotFoundFault("A global tenant group with"
                                          " that id not found")

        if db_api.get_user_by_group(user, group) != None:
            raise fault.UserGroupConflictFault(
                "A user with that id already exists in group")

        dusergroup = db_models.UserGroupAssociation()
        dusergroup.user_id = user
        dusergroup.group_id = group
        db_api.user_tenant_group(dusergroup)

        return tenants.User(duser.id, duser.email, duser.enabled,
                           group_id=group)

    def delete_user_global_group(self, admin_token, group, user):
        self.__validate_token(admin_token)
        gtenant = self.__check_create_global_tenant()

        if db_api.tenant_get(gtenant.id) == None:
            raise fault.ItemNotFoundFault("The Global Tenant not found")

        if db_api.group_get(group) == None:
            raise fault.ItemNotFoundFault("The Group not found")
        duser = db_api.user_get(user)
        if duser == None:
            raise fault.ItemNotFoundFault("The User not found")

        if db_api.tenant_group_get(group, gtenant.id) == None:
            raise fault.ItemNotFoundFault("A global tenant group with "
                                          "that id not found")

        if db_api.get_user_by_group(user, group) == None:
            raise fault.ItemNotFoundFault("A user with that id in a "
                                          "group not found")

        db_api.user_tenant_group_delete(user, group)
        return None

    #

    def __get_auth_data(self, dtoken, duser):
        """return AuthData object for a token/user pair"""

        token = auth.Token(dtoken.expires, dtoken.token_id, dtoken.tenant_id)

        """gs = []
        for ug in duser.groups:
            dgroup = db_api.group_get(ug.group_id)
            if dtoken.tenant_id:
                if dgroup.tenant_id == dtoken.tenant_id:
                    gs.append(auth.Group(dgroup.id, dgroup.tenant_id))
            else:
                if dgroup.tenant_id == None:
                    gs.append(auth.Group(dgroup.id))
        user = auth.User(duser.id, dtoken.tenant_id, gs)
        """
        user = auth.User(duser.id, dtoken.tenant_id, None)
        return auth.AuthData(token, user)

    def __validate_token(self, token_id, admin=True):
        if not token_id:
            raise fault.UnauthorizedFault("Missing token")
        (token, user) = self.__get_dauth_data(token_id)

        if not token:<commit_msg>Fixed issue where user tenant not returned in GET /token - related to issue #49<commit_after># Copyright (c) 2010-2011 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from datetime import datetime
from datetime import timedelta
import uuid

import keystone.logic.types.auth as auth
import keystone.logic.types.atom as atom
import keystone.db.sqlalchemy.api as db_api
import keystone.db.sqlalchemy.models as db_models
import keystone.logic.types.fault as fault
import keystone.logic.types.tenant as tenants
import keystone.logic.types.role as roles
import keystone.logic.types.user as users


class IdentityService(object):
    "This is the logical implemenation of the Identity service"

    #
    #  Token Operations
    #

    def authenticate(self, credentials):
        # Check credentials
        if not isinstance(credentials, auth.PasswordCredentials):
            raise fault.BadRequestFault("Expecting Password Credentials!")

        if not credentials.tenant_id:
            duser = db_api.user_get(credentials.username)
            if duser == None:
                raise fault.UnauthorizedFault("Unauthorized")
        else:
            duser = db_api.user_get_by_tenant(credentials.username,
                                              credentials.tenant_id)
            if duser == None:
                raise fault.UnauthorizedFault("Unauthorized on this tenant")

        if not duser.enabled:
            raise fault.UserDisabledFault("Your account has been disabled")
        if duser.password != <PASSWORD>:
            raise fault.UnauthorizedFault("Unauthorized")

        #
        # Look for an existing token, or create one,
        # TODO: Handle tenant/token search
        #
        if not credentials.tenant_id:
            dtoken = db_api.token_for_user(duser.id)
        else:
            dtoken = db_api.token_for_user_tenant(duser.id,
                                                  credentials.tenant_id)
        if not dtoken or dtoken.expires < datetime.now():
            # Create new token
            dtoken = db_models.Token()
            dtoken.token_id = str(uuid.uuid4())
            dtoken.user_id = duser.id
            if credentials.tenant_id:
                dtoken.tenant_id = credentials.tenant_id
            dtoken.expires = datetime.now() + timedelta(days=1)
            db_api.token_create(dtoken)

        return self.__get_auth_data(dtoken, duser)

    def validate_token(self, admin_token, token_id, belongs_to=None):
        self.__validate_token(admin_token)
        if not token_id:
            raise fault.UnauthorizedFault("Missing token")
        (token, user) = self.__get_dauth_data(token_id)

        if not token:
            raise fault.UnauthorizedFault("Bad token, please reauthenticate")
        if token.expires < datetime.now():
            raise fault.ForbiddenFault("Token expired, please renew")
        if not user.enabled:
            raise fault.UserDisabledFault("The user %s has been disabled!"
                                          % user.id)
        return self.__get_auth_data(token, user)

    def revoke_token(self, admin_token, token_id):
        self.__validate_token(admin_token)

        dtoken = db_api.token_get(token_id)
        if not dtoken:
            raise fault.ItemNotFoundFault("Token not found")

        db_api.token_delete(token_id)

    #
    #   Tenant Operations
    #

    def create_tenant(self, admin_token, tenant):
        self.__validate_token(admin_token)

        if not isinstance(tenant, tenants.Tenant):
            raise fault.BadRequestFault("Expecting a Tenant")

        if tenant.tenant_id == None:
            raise fault.BadRequestFault("Expecting a unique Tenant Id")

        if db_api.tenant_get(tenant.tenant_id) != None:
            raise fault.TenantConflictFault(
                "A tenant with that id already exists")

        dtenant = db_models.Tenant()
        dtenant.id = tenant.tenant_id
        dtenant.desc = tenant.description
        dtenant.enabled = tenant.enabled

        db_api.tenant_create(dtenant)
        return tenant

    ##
    ##    GET Tenants with Pagination
    ##
    def get_tenants(self, admin_token, marker, limit, url):
        self.__validate_token(admin_token)

        ts = []
        dtenants = db_api.tenant_get_page(marker, limit)
        for dtenant in dtenants:
            ts.append(tenants.Tenant(dtenant.id,
                                     dtenant.desc, dtenant.enabled))
        prev, next = db_api.tenant_get_page_markers(marker, limit)
        links = []
        if prev:
            links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'" \
                                                % (url, prev, limit)))
        if next:
            links.append(atom.Link('next', "%s?'marker=%s&limit=%s'" \
                                                % (url, next, limit)))
        return tenants.Tenants(ts, links)

    def get_tenant(self, admin_token, tenant_id):
        self.__validate_token(admin_token)

        dtenant = db_api.tenant_get(tenant_id)
        if not dtenant:
            raise fault.ItemNotFoundFault("The tenant could not be found")
        return tenants.Tenant(dtenant.id, dtenant.desc, dtenant.enabled)

    def update_tenant(self, admin_token, tenant_id, tenant):
        self.__validate_token(admin_token)

        if not isinstance(tenant, tenants.Tenant):
            raise fault.BadRequestFault("Expecting a Tenant")

        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.ItemNotFoundFault("The tenant cloud not be found")
        values = {'desc': tenant.description, 'enabled': tenant.enabled}
        db_api.tenant_update(tenant_id, values)
        return tenants.Tenant(dtenant.id, tenant.description, tenant.enabled)

    def delete_tenant(self, admin_token, tenant_id):
        self.__validate_token(admin_token)

        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.ItemNotFoundFault("The tenant cloud not be found")

        if not db_api.tenant_is_empty(tenant_id):
            raise fault.ForbiddenFault("You may not delete a tenant that "
                                       "contains users or groups")

        db_api.tenant_delete(dtenant.id)
        return None

    #
    #   Tenant Group Operations
    #

    def create_tenant_group(self, admin_token, tenant, group):
        self.__validate_token(admin_token)

        if not isinstance(group, tenants.Group):
            raise fault.BadRequestFault("Expecting a Group")

        if tenant == None:
            raise fault.BadRequestFault("Expecting a Tenant Id")

        dtenant = db_api.tenant_get(tenant)
        if dtenant == None:
            raise fault.ItemNotFoundFault("The tenant not found")

        if group.group_id == None:
            raise fault.BadRequestFault("Expecting a Group Id")

        if db_api.group_get(group.group_id) != None:
            raise fault.TenantGroupConflictFault(
                "A tenant group with that id already exists")

        dtenant = db_models.Group()
        dtenant.id = group.group_id
        dtenant.desc = group.description
        dtenant.tenant_id = tenant
        db_api.tenant_group_create(dtenant)
        return tenants.Group(dtenant.id, dtenant.desc, dtenant.tenant_id)

    def get_tenant_groups(self, admin_token, tenant_id, marker, limit, url):
        self.__validate_token(admin_token)
        if tenant_id == None:
            raise fault.BadRequestFault("Expecting a Tenant Id")

        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.ItemNotFoundFault("The tenant not found")

        ts = []
        dtenantgroups = db_api.tenant_group_get_page(tenant_id, marker, limit)

        for dtenantgroup in dtenantgroups:
            ts.append(tenants.Group(dtenantgroup.id,
                                     dtenantgroup.desc,
                                     dtenantgroup.tenant_id))
        prev, next = db_api.tenant_group_get_page_markers(tenant_id, marker,
                                                          limit)
        links = []
        if prev:
            links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'" \
                                    % (url, prev, limit)))
        if next:
            links.append(atom.Link('next', "%s?'marker=%s&limit=%s'"\
                                    % (url, next, limit)))

        return tenants.Groups(ts, links)

    def get_tenant_group(self, admin_token, tenant_id, group_id):
        self.__validate_token(admin_token)

        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.ItemNotFoundFault("The tenant not found")

        dtenant = db_api.tenant_group_get(group_id, tenant_id)
        if not dtenant:
            raise fault.ItemNotFoundFault("The tenant group not found")

        return tenants.Group(dtenant.id, dtenant.desc, dtenant.tenant_id)

    def update_tenant_group(self, admin_token, tenant_id, group_id, group):
        self.__validate_token(admin_token)

        if not isinstance(group, tenants.Group):
            raise fault.BadRequestFault("Expecting a Group")
        True

        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.ItemNotFoundFault("The tenant not found")

        dtenant = db_api.tenant_group_get(group_id, tenant_id)
        if not dtenant:
            raise fault.ItemNotFoundFault("The tenant group not found")

        if group_id != group.group_id:
            raise fault.BadRequestFault("Wrong Data Provided,\
                                            Group id not matching")

        if str(tenant_id) != str(group.tenant_id):
            raise fault.BadRequestFault("Wrong Data Provided,\
                                            Tenant id not matching ")

        values = {'desc': group.description}

        db_api.tenant_group_update(group_id, tenant_id, values)

        return tenants.Group(group_id, group.description, tenant_id)

    def delete_tenant_group(self, admin_token, tenant_id, group_id):
        self.__validate_token(admin_token)

        dtenant = db_api.tenant_get(tenant_id)

        if dtenant == None:
            raise fault.ItemNotFoundFault("The tenant not found")

        dtenant = db_api.tenant_group_get(group_id, tenant_id)
        if not dtenant:
            raise fault.ItemNotFoundFault("The tenant group not found")

        if not db_api.tenant_group_is_empty(group_id):
            raise fault.ForbiddenFault("You may not delete a tenant that "
                                       "contains users or groups")

        db_api.tenant_group_delete(group_id, tenant_id)
        return None

    def get_users_tenant_group(self, admin_token, tenantId, groupId, marker,
                               limit, url):
        self.__validate_token(admin_token)
        if tenantId == None:
            raise fault.BadRequestFault("Expecting a Tenant Id")

        if db_api.tenant_get(tenantId) == None:
            raise fault.ItemNotFoundFault("The tenant not found")

        if db_api.tenant_group_get(groupId, tenantId) == None:
            raise fault.ItemNotFoundFault(
                "A tenant group with that id not found")
        ts = []
        dgroupusers = db_api.users_tenant_group_get_page(groupId, marker,
                                                          limit)
        for dgroupuser, dgroupuserAsso in dgroupusers:

            ts.append(tenants.User(dgroupuser.id,
                                   dgroupuser.email, dgroupuser.enabled,
                                   tenantId, None))
        links = []
        if ts.__len__():
            prev, next = db_api.users_tenant_group_get_page_markers(groupId,
                                                             marker, limit)
            if prev:
                links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'" %
                                      (url, prev, limit)))
            if next:
                links.append(atom.Link('next', "%s?'marker=%s&limit=%s'" %
                                      (url, next, limit)))
        return tenants.Users(ts, links)

    def add_user_tenant_group(self, admin_token, tenant, group, user):
        self.__validate_token(admin_token)

        if db_api.tenant_get(tenant) == None:
            raise fault.ItemNotFoundFault("The Tenant not found")

        if db_api.group_get(group) == None:
            raise fault.ItemNotFoundFault("The Group not found")
        duser = db_api.user_get(user)
        if duser == None:
            raise fault.ItemNotFoundFault("The User not found")

        if db_api.tenant_group_get(group, tenant) == None:
            raise fault.ItemNotFoundFault("A tenant group with"
                                           " that id not found")

        if db_api.get_user_by_group(user, group) != None:
            raise fault.UserGroupConflictFault(
                "A user with that id already exists in group")

        dusergroup = db_models.UserGroupAssociation()
        dusergroup.user_id = user
        dusergroup.group_id = group
        db_api.user_tenant_group(dusergroup)

        return tenants.User(duser.id, duser.email, duser.enabled,
                            tenant, group)

    def delete_user_tenant_group(self, admin_token, tenant, group, user):
        self.__validate_token(admin_token)

        if db_api.tenant_get(tenant) == None:
            raise fault.ItemNotFoundFault("The Tenant not found")

        if db_api.group_get(group) == None:
            raise fault.ItemNotFoundFault("The Group not found")
        duser = db_api.user_get(user)
        if duser == None:
            raise fault.ItemNotFoundFault("The User not found")

        if db_api.tenant_group_get(group, tenant) == None:
            raise fault.ItemNotFoundFault("A tenant group with"
                                          " that id not found")

        if db_api.get_user_by_group(user, group) == None:
            raise fault.ItemNotFoundFault("A user with that id "
                                          "in a group not found")

        db_api.user_tenant_group_delete(user, group)
        return None


    #
    # Private Operations
    #
    def __get_dauth_data(self, token_id):
        """return token and user object for a token_id"""

        token = None
        user = None
        if token_id:
            token = db_api.token_get(token_id)
            if token:
                user = db_api.user_get(token.user_id)
        return (token, user)


    #
    #   User Operations
    #
    def create_user(self, admin_token, tenant_id, user):
        self.__validate_token(admin_token)

        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.UnauthorizedFault("Unauthorized")
        if not dtenant.enabled:
            raise fault.TenantDisabledFault("Your account has been disabled")

        if not isinstance(user, users.User):
            raise fault.BadRequestFault("Expecting a User")

        if user.user_id == None:
            raise fault.BadRequestFault("Expecting a unique User Id")

        if db_api.user_get(user.user_id) != None:
            raise fault.UserConflictFault(
                "An user with that id already exists")

        if db_api.user_get_email(user.email) != None:
            raise fault.EmailConflictFault(
                "Email already exists")

        duser_tenant = db_models.UserTenantAssociation()
        duser_tenant.user_id = user.user_id
        duser_tenant.tenant_id = tenant_id
        db_api.user_tenant_create(duser_tenant)

        duser = db_models.User()
        duser.id = user.user_id
        duser.password = <PASSWORD>
        duser.email = user.email
        duser.enabled = user.enabled
        db_api.user_create(duser)

        return user


    def get_tenant_users(self, admin_token, tenant_id, marker, limit, url):
        self.__validate_token(admin_token)

        if tenant_id == None:
            raise fault.BadRequestFault("Expecting a Tenant Id")
        dtenant = db_api.tenant_get(tenant_id)
        if dtenant is  None:
            raise fault.ItemNotFoundFault("The tenant not found")
        if not dtenant.enabled:
            raise fault.TenantDisabledFault("Your account has been disabled")
        ts = []
        dtenantusers = db_api.users_get_by_tenant_get_page(tenant_id, marker,
                                                          limit)
        for dtenantuser, dtenantuserAsso in dtenantusers:
            ts.append(users.User(None, dtenantuser.id, tenant_id,
                                   dtenantuser.email, dtenantuser.enabled))
        links = []
        if ts.__len__():
            prev, next = db_api.users_get_by_tenant_get_page_markers(tenant_id,
                                                             marker, limit)
            if prev:
                links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'" %
                                      (url, prev, limit)))
            if next:
                links.append(atom.Link('next', "%s?'marker=%s&limit=%s'" %
                                      (url, next, limit)))
        return users.Users(ts, links)

    def get_user(self, admin_token, tenant_id, user_id):
        self.__validate_token(admin_token)
        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.UnauthorizedFault("Unauthorized")
        if not dtenant.enabled:
            raise fault.TenantDisabledFault("Your account has been disabled")

        duser = db_api.user_get(user_id)
        if not duser:
            raise fault.ItemNotFoundFault("The user could not be found")

        if not duser.enabled:
            raise fault.UserDisabledFault("User has been disabled")

        if len(duser.tenants) > 0:
            tenant_user = duser.tenants[0].tenant_id
        else:
            tenant_user = tenant_id

        ts = []
        dusergroups = db_api.user_groups_get_all(user_id)

        for dusergroup, dusergroupAsso in dusergroups:
            ts.append(tenants.Group(dusergroup.id, dusergroup.tenant_id, None))

        return users.User_Update(None, duser.id, tenant_user, duser.email,
                                 duser.enabled, ts)

    def update_user(self, admin_token, user_id, user, tenant_id):
        self.__validate_token(admin_token)

        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.UnauthorizedFault("Unauthorized")
        if not dtenant.enabled:
            raise fault.TenantDisabledFault("Your account has been disabled")

        duser = db_api.user_get(user_id)

        if not duser:
            raise fault.ItemNotFoundFault("The user could not be found")

        if not duser.enabled:
            raise fault.UserDisabledFault("User has been disabled")

        if not isinstance(user, users.User):
            raise fault.BadRequestFault("Expecting a User")

        if db_api.user_get_email(user.email) is not None:
            raise fault.EmailConflictFault(
                "Email already exists")

        values = {'email': user.email}

        db_api.user_update(user_id, values)
        duser = db_api.user_get_update(user_id)
        return users.User(duser.password, duser.id, tenant_id, duser.email,
                          duser.enabled)

    def set_user_password(self, admin_token, user_id, user, tenant_id):
        self.__validate_token(admin_token)

        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.UnauthorizedFault("Unauthorized")
        if not dtenant.enabled:
            raise fault.TenantDisabledFault("Your account has been disabled")

        duser = db_api.user_get(user_id)
        if not duser:
            raise fault.ItemNotFoundFault("The user could not be found")

        if not duser.enabled:
            raise fault.UserDisabledFault("User has been disabled")

        if not isinstance(user, users.User):
            raise fault.BadRequestFault("Expecting a User")

        duser = db_api.user_get(user_id)
        if duser == None:
            raise fault.ItemNotFoundFault("The user could not be found")

        values = {'password': user.password}

        db_api.user_update(user_id, values)

        return users.User_Update(user.password, None, None, None, None, None)

    def enable_disable_user(self, admin_token, user_id, user, tenant_id):
        self.__validate_token(admin_token)
        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.UnauthorizedFault("Unauthorized")
        if not dtenant.enabled:
            raise fault.TenantDisabledFault("Your account has been disabled")

        duser = db_api.user_get(user_id)
        if not duser:
            raise fault.ItemNotFoundFault("The user could not be found")
        if not isinstance(user, users.User):
            raise fault.BadRequestFault("Expecting a User")

        duser = db_api.user_get(user_id)
        if duser == None:
            raise fault.ItemNotFoundFault("The user could not be found")

        values = {'enabled': user.enabled}

        db_api.user_update(user_id, values)

        return users.User_Update(None, None, None, None, user.enabled, None)

    def delete_user(self, admin_token, user_id, tenant_id):
        self.__validate_token(admin_token)
        dtenant = db_api.tenant_get(tenant_id)
        if dtenant == None:
            raise fault.UnauthorizedFault("Unauthorized")
        if not dtenant.enabled:
            raise fault.TenantDisabledFault("Your account has been disabled")

        duser = db_api.user_get(user_id)
        if not duser:
            raise fault.ItemNotFoundFault("The user could not be found")
        duser = db_api.user_get_by_tenant(user_id, tenant_id)
        if not duser:
            raise fault.ItemNotFoundFault("The user could not be "
                                        "found under given tenant")

        db_api.user_delete_tenant(user_id, tenant_id)
        return None

    def get_user_groups(self, admin_token, tenant_id, user_id, marker, limit,
                        url):
        self.__validate_token(admin_token)

        if tenant_id == None:
            raise fault.BadRequestFault("Expecting a Tenant Id")

        if db_api.tenant_get(tenant_id) == None:
            raise fault.ItemNotFoundFault("The tenant not found")

        if not db_api.tenant_get(tenant_id).enabled:
            raise fault.TenantDisabledFault("Your account has been disabled")

        ts = []
        dusergroups = db_api.groups_get_by_user_get_page(user_id, marker,
                                                          limit)

        for dusergroup, dusergroupAsso in dusergroups:
            ts.append(tenants.Group(dusergroup.id, dusergroup.desc,
                                    dusergroup.tenant_id))
        links = []
        if ts.__len__():
            prev, next = db_api.groups_get_by_user_get_page_markers(user_id,
                                                        marker, limit)
            if prev:
                links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'" %
                                      (url, prev, limit)))
            if next:
                links.append(atom.Link('next', "%s?'marker=%s&limit=%s'" %
                                      (url, next, limit)))
        return tenants.Groups(ts, links)

    #
    # Global Group Operations
    # TODO:(India Team) Rename functions
    #       and to maintain consistency
    #       with server.py
    def __check_create_global_tenant(self):

        dtenant = db_api.tenant_get('GlobalTenant')

        if dtenant is None:
            dtenant = db_models.Tenant()
            dtenant.id = 'GlobalTenant'
            dtenant.desc = 'GlobalTenant is Default tenant for global groups'
            dtenant.enabled = True
            db_api.tenant_create(dtenant)
        return dtenant

    def create_global_group(self, admin_token, group):
        self.__validate_token(admin_token)

        if not isinstance(group, tenants.GlobalGroup):
            raise fault.BadRequestFault("Expecting a Group")

        if group.group_id == None:
            raise fault.BadRequestFault("Expecting a Group Id")

        if db_api.group_get(group.group_id) != None:
            raise fault.TenantGroupConflictFault(
                "A tenant group with that id already exists")
        gtenant = self.__check_create_global_tenant()
        dtenant = db_models.Group()
        dtenant.id = group.group_id
        dtenant.desc = group.description
        dtenant.tenant_id = gtenant.id
        db_api.tenant_group_create(dtenant)
        return tenants.GlobalGroup(dtenant.id, dtenant.desc, None)

    def get_global_groups(self, admin_token, marker, limit, url):
        self.__validate_token(admin_token)
        gtenant = self.__check_create_global_tenant()
        ts = []
        dtenantgroups = db_api.tenant_group_get_page(gtenant.id, \
                                                      marker, limit)
        for dtenantgroup in dtenantgroups:
            ts.append(tenants.GlobalGroup(dtenantgroup.id,
                                     dtenantgroup.desc))
        prev, next = db_api.tenant_group_get_page_markers(gtenant.id,
                                                       marker, limit)
        links = []
        if prev:
            links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'" %
                                  (url, prev, limit)))
        if next:
            links.append(atom.Link('next', "%s?'marker=%s&limit=%s'" %
                                  (url, next, limit)))
        return tenants.GlobalGroups(ts, links)

    def get_global_group(self, admin_token, group_id):
        self.__validate_token(admin_token)
        gtenant = self.__check_create_global_tenant()
        dtenant = db_api.tenant_get(gtenant.id)
        if dtenant == None:
            raise fault.ItemNotFoundFault("The Global tenant not found")

        dtenant = db_api.tenant_group_get(group_id, gtenant.id)

        if not dtenant:
            raise fault.ItemNotFoundFault("The Global tenant group not found")
        return tenants.GlobalGroup(dtenant.id, dtenant.desc)

    def update_global_group(self, admin_token, group_id, group):
        self.__validate_token(admin_token)
        gtenant = self.__check_create_global_tenant()
        if not isinstance(group, tenants.GlobalGroup):
            raise fault.BadRequestFault("Expecting a Group")

        dtenant = db_api.tenant_get(gtenant.id)
        if dtenant == None:
            raise fault.ItemNotFoundFault("The global tenant not found")

        dtenant = db_api.tenant_group_get(group_id, gtenant.id)
        if not dtenant:
            raise fault.ItemNotFoundFault("The Global tenant group not found")
        if group_id != group.group_id:
            raise fault.BadRequestFault("Wrong Data Provided,"
                                            "Group id not matching")

        values = {'desc': group.description}
        db_api.tenant_group_update(group_id, gtenant.id, values)
        return tenants.GlobalGroup(group_id, group.description, gtenant.id)

    def delete_global_group(self, admin_token, group_id):
        self.__validate_token(admin_token)
        gtenant = self.__check_create_global_tenant()
        dtenant = db_api.tenant_get(gtenant.id)

        if dtenant == None:
            raise fault.ItemNotFoundFault("The global tenant not found")

        dtenant = db_api.tenant_group_get(group_id, dtenant.id)
        if not dtenant:
            raise fault.ItemNotFoundFault("The global tenant group not found")

        if not db_api.tenant_group_is_empty(group_id):
            raise fault.ForbiddenFault("You may not delete a group that "
                                       "contains users")

        db_api.tenant_group_delete(group_id, gtenant.id)
        return None

    def get_users_global_group(self, admin_token, groupId, marker, limit, url):
        self.__validate_token(admin_token)

        gtenant = self.__check_create_global_tenant()
        if gtenant.id == None:
            raise fault.BadRequestFault("Expecting a global Tenant")

        if db_api.tenant_get(gtenant.id) == None:
            raise fault.ItemNotFoundFault("The global tenant not found")

        if db_api.tenant_group_get(groupId, gtenant.id) == None:
            raise fault.ItemNotFoundFault(
                "A global tenant group with that id not found")
        ts = []
        dgroupusers = db_api.users_tenant_group_get_page(groupId, marker,
                                                         limit)
        for dgroupuser, dgroupuserassoc in dgroupusers:
            ts.append(tenants.User(dgroupuser.id, dgroupuser.email,
                                   dgroupuser.enabled))
        links = []
        if ts.__len__():
            prev, next = db_api.users_tenant_group_get_page_markers(groupId,
                                                                marker, limit)
            if prev:
                links.append(atom.Link('prev', "%s?'marker=%s&limit=%s'"
                                       % (url, prev, limit)))
            if next:
                links.append(atom.Link('next', "%s?'marker=%s&limit=%s'"
                                       % (url, next, limit)))
        return tenants.Users(ts, links)

    def add_user_global_group(self, admin_token, group, user):
        self.__validate_token(admin_token)
        gtenant = self.__check_create_global_tenant()

        if db_api.tenant_get(gtenant.id) == None:
            raise fault.ItemNotFoundFault("The Global Tenant not found")

        if db_api.group_get(group) == None:
            raise fault.ItemNotFoundFault("The Group not found")
        duser = db_api.user_get(user)
        if duser == None:
            raise fault.ItemNotFoundFault("The User not found")

        if db_api.tenant_group_get(group, gtenant.id) == None:
            raise fault.ItemNotFoundFault("A global tenant group with"
                                          " that id not found")

        if db_api.get_user_by_group(user, group) != None:
            raise fault.UserGroupConflictFault(
                "A user with that id already exists in group")

        dusergroup = db_models.UserGroupAssociation()
        dusergroup.user_id = user
        dusergroup.group_id = group
        db_api.user_tenant_group(dusergroup)

        return tenants.User(duser.id, duser.email, duser.enabled,
                           group_id=group)

    def delete_user_global_group(self, admin_token, group, user):
        self.__validate_token(admin_token)
        gtenant = self.__check_create_global_tenant()

        if db_api.tenant_get(gtenant.id) == None:
            raise fault.ItemNotFoundFault("The Global Tenant not found")

        if db_api.group_get(group) == None:
            raise fault.ItemNotFoundFault("The Group not found")
        duser = db_api.user_get(user)
        if duser == None:
            raise fault.ItemNotFoundFault("The User not found")

        if db_api.tenant_group_get(group, gtenant.id) == None:
            raise fault.ItemNotFoundFault("A global tenant group with "
                                          "that id not found")

        if db_api.get_user_by_group(user, group) == None:
            raise fault.ItemNotFoundFault("A user with that id in a "
                                          "group not found")

        db_api.user_tenant_group_delete(user, group)
        return None

    #

    def __get_auth_data(self, dtoken, duser):
        """return AuthData object for a token/user pair"""

        token = auth.Token(dtoken.expires, dtoken.token_id, dtoken.tenant_id)

        """gs = []
        for ug in duser.groups:
            dgroup = db_api.group_get(ug.group_id)
            if dtoken.tenant_id:
                if dgroup.tenant_id == dtoken.tenant_id:
                    gs.append(auth.Group(dgroup.id, dgroup.tenant_id))
            else:
                if dgroup.tenant_id == None:
                    gs.append(auth.Group(dgroup.id))
        user = auth.User(duser.id, dtoken.tenant_id, gs)
        """
        user = auth.User(duser.id, duser.tenant_id, None)
        return auth.AuthData(token, user)

    def __validate_token(self, token_id, admin=True):
        if not token_id:
            raise fault.UnauthorizedFault("Missing token")
        (token, user) = self.__get_dauth_data(token_id)

        if not token:
{'fraction_non_alphanumeric': 0.08625744250007555, 'fraction_numerical': 0.0005137969595309336, 'mean_word_length': 2.0250045712196014, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 10, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 156, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3187753', 'n_tokens_mistral': 18685, 'n_tokens_neox': 18689, 'n_words': 4270}