# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2012 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 keystone.common import kvs from keystone.common import utils from keystone import exception from keystone import identity class Identity(kvs.Base, identity.Driver): def __init__(self): super(Identity, self).__init__() def default_assignment_driver(self): return "keystone.assignment.backends.kvs.Assignment" def is_domain_aware(self): return True # Public interface def authenticate(self, user_id, password): user_ref = None try: user_ref = self._get_user(user_id) except exception.UserNotFound: raise AssertionError('Invalid user / password') if not utils.check_password(password, user_ref.get('password')): raise AssertionError('Invalid user / password') return identity.filter_user(user_ref) def _get_user(self, user_id): try: return self.db.get('user-%s' % user_id) except exception.NotFound: raise exception.UserNotFound(user_id=user_id) def _get_user_by_name(self, user_name, domain_id): try: return self.db.get('user_name-%s' % user_name) except exception.NotFound: raise exception.UserNotFound(user_id=user_name) def get_user(self, user_id): return identity.filter_user(self._get_user(user_id)) def get_user_by_name(self, user_name, domain_id): return identity.filter_user( self._get_user_by_name(user_name, domain_id)) def list_users(self): user_ids = self.db.get('user_list', []) return [self.get_user(x) for x in user_ids] # CRUD def create_user(self, user_id, user): try: self.get_user(user_id) except exception.UserNotFound: pass else: msg = 'Duplicate ID, %s.' % user_id raise exception.Conflict(type='user', details=msg) try: self.get_user_by_name(user['name'], user['domain_id']) except exception.UserNotFound: pass else: msg = 'Duplicate name, %s.' % user['name'] raise exception.Conflict(type='user', details=msg) user = utils.hash_user_password(user) new_user = user.copy() new_user.setdefault('groups', []) self.db.set('user-%s' % user_id, new_user) self.db.set('user_name-%s' % new_user['name'], new_user) user_list = set(self.db.get('user_list', [])) user_list.add(user_id) self.db.set('user_list', list(user_list)) return identity.filter_user(new_user) def update_user(self, user_id, user): if 'name' in user: existing = self.db.get('user_name-%s' % user['name']) if existing and user_id != existing['id']: msg = 'Duplicate name, %s.' % user['name'] raise exception.Conflict(type='user', details=msg) # get the old name and delete it too try: old_user = self.db.get('user-%s' % user_id) except exception.NotFound: raise exception.UserNotFound(user_id=user_id) new_user = old_user.copy() user = utils.hash_user_password(user) new_user.update(user) if new_user['id'] != user_id: raise exception.ValidationError('Cannot change user ID') self.db.delete('user_name-%s' % old_user['name']) self.db.set('user-%s' % user_id, new_user) self.db.set('user_name-%s' % new_user['name'], new_user) return new_user def add_user_to_group(self, user_id, group_id): self.get_group(group_id) user_ref = self._get_user(user_id) groups = set(user_ref.get('groups', [])) groups.add(group_id) self.update_user(user_id, {'groups': list(groups)}) def check_user_in_group(self, user_id, group_id): self.get_group(group_id) user_ref = self._get_user(user_id) if group_id not in set(user_ref.get('groups', [])): raise exception.NotFound(_('User not found in group')) def remove_user_from_group(self, user_id, group_id): self.get_group(group_id) user_ref = self._get_user(user_id) groups = set(user_ref.get('groups', [])) try: groups.remove(group_id) except KeyError: raise exception.NotFound(_('User not found in group')) self.update_user(user_id, {'groups': list(groups)}) def list_users_in_group(self, group_id): self.get_group(group_id) user_keys = filter(lambda x: x.startswith("user-"), self.db.keys()) user_refs = [self.db.get(key) for key in user_keys] user_refs_for_group = filter(lambda x: group_id in x['groups'], user_refs) return [identity.filter_user(x) for x in user_refs_for_group] def list_groups_for_user(self, user_id): user_ref = self._get_user(user_id) group_ids = user_ref.get('groups', []) return [self.get_group(x) for x in group_ids] def delete_user(self, user_id): try: old_user = self.db.get('user-%s' % user_id) except exception.NotFound: raise exception.UserNotFound(user_id=user_id) self.db.delete('user_name-%s' % old_user['name']) self.db.delete('user-%s' % user_id) user_list = set(self.db.get('user_list', [])) user_list.remove(user_id) self.db.set('user_list', list(user_list)) # group crud def create_group(self, group_id, group): try: return self.db.get('group-%s' % group_id) except exception.NotFound: pass else: msg = _('Duplicate ID, %s.') % group_id raise exception.Conflict(type='group', details=msg) try: self.db.get('group_name-%s' % group['name']) except exception.NotFound: pass else: msg = _('Duplicate name, %s.') % group['name'] raise exception.Conflict(type='group', details=msg) self.db.set('group-%s' % group_id, group) self.db.set('group_name-%s' % group['name'], group) group_list = set(self.db.get('group_list', [])) group_list.add(group_id) self.db.set('group_list', list(group_list)) return group def list_groups(self): group_ids = self.db.get('group_list', []) return [self.get_group(x) for x in group_ids] def get_group(self, group_id): try: return self.db.get('group-%s' % group_id) except exception.NotFound: raise exception.GroupNotFound(group_id=group_id) def update_group(self, group_id, group): # First, make sure we are not trying to change the # name to one that is already in use try: self.db.get('group_name-%s' % group['name']) except exception.NotFound: pass else: msg = _('Duplicate name, %s.') % group['name'] raise exception.Conflict(type='group', details=msg) # Now, get the old name and delete it try: old_group = self.db.get('group-%s' % group_id) except exception.NotFound: raise exception.GroupNotFound(group_id=group_id) self.db.delete('group_name-%s' % old_group['name']) # Finally, actually do the update self.db.set('group-%s' % group_id, group) self.db.set('group_name-%s' % group['name'], group) return group def delete_group(self, group_id): try: group = self.db.get('group-%s' % group_id) except exception.NotFound: raise exception.GroupNotFound(group_id=group_id) # Delete any entries in the group lists of all users user_keys = filter(lambda x: x.startswith("user-"), self.db.keys()) user_refs = [self.db.get(key) for key in user_keys] for user_ref in user_refs: groups = set(user_ref.get('groups', [])) if group_id in groups: groups.remove(group_id) self.update_user(user_ref['id'], {'groups': list(groups)}) # Now delete the group itself self.db.delete('group-%s' % group_id) self.db.delete('group_name-%s' % group['name']) group_list = set(self.db.get('group_list', [])) group_list.remove(group_id) self.db.set('group_list', list(group_list))