def run(self, id, updated_fields): normalize_name(updated_fields, 'username') try: user = self.datastore.get_by_id('users', id) if not user: raise TaskException(errno.ENOENT, 'User {0} not found'.format(id)) self.original_user = copy.deepcopy(user) home_before = user.get('home') user.update(updated_fields) password = user.pop('password', None) if password: user.update({ 'unixhash': crypted_password(password), 'nthash': nt_password(password), 'password_changed_at': datetime.utcnow() }) self.datastore.update('users', user['id'], user) self.dispatcher.call_sync('etcd.generation.generate_group', 'accounts') except SubprocessException as e: raise TaskException( errno.ENXIO, 'Could not generate samba password. stdout: {0}\nstderr: {1}'.format(e.out, e.err)) except DatastoreException as e: raise TaskException(errno.EBADMSG, 'Cannot update user: {0}'.format(str(e))) except RpcException as e: raise TaskException(e.code, 'Cannot regenerate users file: {0}'.format(e.message)) group = self.datastore.get_by_id('groups', user['group']) if user['home'] not in (None, '/nonexistent') and not user['builtin']: user['home'] = os.path.normpath(user['home']) if not os.path.exists(user['home']): try: self.dispatcher.call_sync('volume.decode_path', user['home']) except RpcException as err: raise TaskException(err.code, err.message) if (home_before != '/nonexistent' and home_before != user['home'] and os.path.exists(home_before)): system('mv', home_before, user['home']) else: os.makedirs(user['home']) os.chown(user['home'], user['uid'], group['gid']) os.chmod(user['home'], 0o755) elif user['home'] != home_before: os.chown(user['home'], user['uid'], group['gid']) os.chmod(user['home'], 0o755) self.dispatcher.dispatch_event('user.changed', { 'operation': 'update', 'ids': [user['id']] })
def change_password(self, username, password): try: with open(self.passwd_filename, 'r') as f: passwd = load(f) user = first_or_default(lambda u: u['username'] == username, passwd) if not user: raise OSError(errno.ENOENT, os.strerror(errno.ENOENT)) user.update({ 'unixhash': crypted_password(password), 'smbhash': nt_password(password), 'password_changed_at': datetime.datetime.utcnow() }) with open(self.passwd_filename + '.tmp', 'w') as f: dump(passwd, f, indent=4) os.rename(self.passwd_filename + '.tmp', self.passwd_filename) shutil.copy(self.passwd_filename, os.path.join('/conf/base', self.passwd_filename[1:])) self.__load() except (IOError, ValueError) as err: logger.warn('Cannot change password: {0}'.format(str(err))) raise
def run(self, id, updated_fields): normalize_name(updated_fields, 'username') user = self.datastore.get_by_id('users', id) self.original_user = copy.deepcopy(user) if user is None: raise TaskException( errno.ENOENT, "User with id: {0} does not exist".format(id) ) if user.get('builtin'): if 'home' in updated_fields: raise TaskException( errno.EPERM, "Cannot change builtin user's home directory" ) # Similarly ignore uid changes for builtin users if 'uid' in updated_fields: raise TaskException(errno.EPERM, "Cannot change builtin user's UID") if 'username' in updated_fields: raise TaskException( errno.EPERM, "Cannot change builtin user's username" ) if 'locked' in updated_fields: raise TaskException( errno.EPERM, "Cannot change builtin user's locked flag" ) if not user: raise TaskException(errno.ENOENT, 'User {0} not found'.format(id)) if 'home' in updated_fields and updated_fields['home'] is None: updated_fields['home'] = '/nonexistent' if 'home' in updated_fields and updated_fields['home'] != '/nonexistent': updated_fields['home'] = os.path.normpath(updated_fields['home']) zfs_dataset_mountpoints = list(self.dispatcher.call_sync('volume.dataset.query', [], {'select': 'mountpoint'})) zfs_pool_mountpoints = list(self.dispatcher.call_sync('volume.query', [], {'select': 'mountpoint'})) homedir_occurrence = self.dispatcher.call_sync( 'user.query', [('home', '=', updated_fields['home'])], {'single': True} ) homedir_mount_path = os.path.join('/', *(updated_fields['home'].split(os.path.sep)[:-1])) user_gid = self.datastore.get_by_id('groups', user['group'])['gid'] if user['home'] != updated_fields['home']: if updated_fields['home'] in zfs_pool_mountpoints: raise TaskException( errno.ENXIO, 'Volume mountpoint cannot be set as the home directory.' ) if not any(homedir_mount_path == dataset_mountpoint and os.path.ismount(dataset_mountpoint) for dataset_mountpoint in zfs_dataset_mountpoints): raise TaskException( errno.ENXIO, 'Home directory has to reside in zfs pool or dataset.' + ' Provide a path which starts with valid, mounted zfs pool or dataset location.' ) if homedir_occurrence: raise TaskException( errno.ENXIO, '{} is already assigned to another user.'.format(updated_fields['home']) + ' Multiple users cannot share the same home directory.' ) if not os.path.exists(updated_fields['home']): parent_dataset = self.dispatcher.call_sync( 'volume.dataset.query', [('mountpoint', '=', homedir_mount_path)], {'single': True} ) homedir_dataset_id = os.path.join( parent_dataset['id'], updated_fields['home'].split(os.path.sep)[-1] ) self.join_subtasks(self.run_subtask( 'volume.dataset.create', {'id': homedir_dataset_id, 'type': 'FILESYSTEM', 'volume': parent_dataset['volume']} )) os.chmod(updated_fields['home'], 0o755) else: os.chmod(updated_fields['home'], 0o755) for file in os.listdir(SKEL_PATH): if file.startswith('dot'): dest_file = os.path.join(updated_fields['home'], file[3:]) if not os.path.exists(dest_file): shutil.copyfile(os.path.join(SKEL_PATH, file), dest_file) os.chown(dest_file, user['uid'], user_gid) else: dest_file = os.path.join(updated_fields['home'], file) if not os.path.exists(dest_file): shutil.copyfile(os.path.join(SKEL_PATH, file), dest_file) os.chown(dest_file, user['uid'], user_gid) os.chown(updated_fields['home'], user['uid'], user_gid) user.update(updated_fields) try: password = user.pop('password', None) if password: user.update({ 'unixhash': crypted_password(password), 'nthash': nt_password(password), 'password_changed_at': datetime.utcnow() }) self.datastore.update('users', user['id'], user) self.dispatcher.call_sync('etcd.generation.generate_group', 'accounts') except SubprocessException as e: raise TaskException( errno.ENXIO, 'Could not generate samba password. stdout: {0}\nstderr: {1}'.format(e.out, e.err)) except DatastoreException as e: raise TaskException(errno.EBADMSG, 'Cannot update user: {0}'.format(str(e))) except RpcException as e: raise TaskException(e.code, 'Cannot regenerate users file: {0}'.format(e.message)) self.dispatcher.dispatch_event('user.changed', { 'operation': 'update', 'ids': [user['id']] })
def run(self, user): if self.datastore.exists('users', ('username', '=', user['username'])): raise TaskException(errno.EEXIST, 'User with given name already exists') if 'uid' not in user: # Need to get next free UID uid = self.dispatcher.call_sync('user.next_uid', user.get('group') is None) else: uid = user.pop('uid') normalize_name(user, 'username') normalize(user, { 'builtin': False, 'unixhash': None, 'nthash': None, 'password_changed_at': None, 'full_name': 'User &', 'shell': '/bin/sh', 'home': '/nonexistent', 'groups': [], 'uid': uid, 'sudo': False, 'attributes': {} }) if user['home'] is None: user['home'] = '/nonexistent' if user['home'] != '/nonexistent': user['home'] = os.path.normpath(user['home']) zfs_dataset_mountpoints = list(self.dispatcher.call_sync('volume.dataset.query', [], {'select': 'mountpoint'})) zfs_pool_mountpoints = list(self.dispatcher.call_sync('volume.query', [], {'select': 'mountpoint'})) homedir_occurrence = self.dispatcher.call_sync( 'user.query', [('home', '=', user['home'])], {'single': True} ) if user['home'] in zfs_pool_mountpoints: raise TaskException( errno.ENXIO, 'ZFS pool mountpoint cannot be set as the home directory.' ) homedir_mount_path = os.path.join('/', *(user['home'].split(os.path.sep)[:-1])) if not any(homedir_mount_path == dataset_mountpoint and os.path.ismount(dataset_mountpoint) for dataset_mountpoint in zfs_dataset_mountpoints): raise TaskException( errno.ENXIO, 'Home directory has to reside in zfs pool or dataset.' + ' Provide a path which starts with valid, mounted zfs pool or dataset location.' ) if homedir_occurrence: raise TaskException( errno.ENXIO, '{} is already assigned to another user.'.format(user['home']) + ' Multiple users cannot share the same home directory.' ) password = user.pop('password', None) if password: user.update({ 'unixhash': crypted_password(password), 'nthash': nt_password(password), 'password_changed_at': datetime.utcnow() }) if user.get('group') is None: try: result = self.join_subtasks(self.run_subtask('group.create', { 'gid': uid, 'name': user['username'] })) except RpcException as err: raise err user['group'] = result[0] self.created_group = result[0] try: id = self.datastore.insert('users', user) self.id = id self.dispatcher.call_sync('etcd.generation.generate_group', 'accounts') except SubprocessException as e: raise TaskException( errno.ENXIO, 'Could not generate samba password. stdout: {0}\nstderr: {1}'.format(e.out, e.err) ) except DuplicateKeyException as e: raise TaskException(errno.EBADMSG, 'Cannot add user: {0}'.format(str(e))) except RpcException as e: raise TaskException( errno.ENXIO, 'Cannot regenerate users file: {0}'.format(e) ) if user['home'] != '/nonexistent': group = self.dispatcher.call_sync('group.query', [('id', '=', user['group'])], {'single': True}) if not group: raise TaskException(errno.ENOENT, 'Group {0} not found'.format(user['group'])) user_gid = group['gid'] if not os.path.exists(user['home']): parent_dataset = self.dispatcher.call_sync( 'volume.dataset.query', [('mountpoint', '=', homedir_mount_path)], {'single': True} ) homedir_dataset_id = os.path.join(parent_dataset['id'], user['home'].split(os.path.sep)[-1]) self.join_subtasks(self.run_subtask( 'volume.dataset.create', {'id': homedir_dataset_id, 'type': 'FILESYSTEM', 'volume': parent_dataset['volume']} )) os.chmod(user['home'], 0o755) else: os.chmod(user['home'], 0o755) for file in os.listdir(SKEL_PATH): if file.startswith('dot'): dest_file = os.path.join(user['home'], file[3:]) if not os.path.exists(dest_file): shutil.copyfile(os.path.join(SKEL_PATH, file), dest_file) os.chown(dest_file, uid, user_gid) else: dest_file = os.path.join(user['home'], file) if not os.path.exists(dest_file): shutil.copyfile(os.path.join(SKEL_PATH, file), dest_file) os.chown(dest_file, uid, user_gid) os.chown(user['home'], uid, user_gid) self.dispatcher.dispatch_event('user.changed', { 'operation': 'create', 'ids': [id] }) return id
def run(self, user): if 'uid' not in user: # Need to get next free UID uid = self.dispatcher.call_sync('user.next_uid', user.get('group') is None) else: uid = user.pop('uid') normalize_name(user, 'username') normalize(user, { 'builtin': False, 'unixhash': None, 'nthash': None, 'password_changed_at': None, 'full_name': 'User &', 'shell': '/bin/sh', 'home': '/nonexistent', 'groups': [], 'uid': uid, 'attributes': {} }) password = user.pop('password', None) if password: user.update({ 'unixhash': crypted_password(password), 'nthash': nt_password(password), 'password_changed_at': datetime.utcnow() }) if user.get('group') is None: try: result = self.join_subtasks(self.run_subtask('group.create', { 'gid': uid, 'name': user['username'] })) except RpcException as err: raise err user['group'] = result[0] self.created_group = result[0] try: id = self.datastore.insert('users', user) self.id = id self.dispatcher.call_sync('etcd.generation.generate_group', 'accounts') except SubprocessException as e: raise TaskException( errno.ENXIO, 'Could not generate samba password. stdout: {0}\nstderr: {1}'.format(e.out, e.err) ) except DuplicateKeyException as e: raise TaskException(errno.EBADMSG, 'Cannot add user: {0}'.format(str(e))) except RpcException as e: raise TaskException( errno.ENXIO, 'Cannot regenerate users file: {0}'.format(e) ) volumes_root = self.dispatcher.call_sync('volume.get_volumes_root') if user['home'].startswith(volumes_root): if not os.path.exists(user['home']): try: self.dispatcher.call_sync('volume.decode_path', user['home']) except RpcException as err: raise TaskException(err.code, err.message) os.makedirs(user['home']) group = self.datastore.get_by_id('groups', user['group']) os.chown(user['home'], uid, group['gid']) os.chmod(user['home'], 0o755) elif not user['builtin'] and user['home'] not in (None, '/nonexistent'): raise TaskException( errno.ENOENT, "Invalid mountpoint specified for home directory: {0}.".format(user['home']) + " Use '{0}' instead as the mountpoint".format(volumes_root) ) self.dispatcher.dispatch_event('user.changed', { 'operation': 'create', 'ids': [id] }) return uid
def run(self, user): if self.datastore.exists('users', ('username', '=', user['username'])): raise TaskException(errno.EEXIST, 'User with given name already exists') if 'uid' not in user: # Need to get next free UID uid = self.dispatcher.call_sync('user.next_uid', user.get('group') is None) else: uid = user.pop('uid') normalize_name(user, 'username') normalize( user, { 'builtin': False, 'unixhash': None, 'nthash': None, 'password_changed_at': None, 'full_name': 'User &', 'shell': '/bin/sh', 'home': '/nonexistent', 'groups': [], 'uid': uid, 'sudo': False, 'attributes': {} }) if user['home'] is None: user['home'] = '/nonexistent' if user['home'] != '/nonexistent': user['home'] = os.path.normpath(user['home']) zfs_dataset_mountpoints = list( self.dispatcher.call_sync('volume.dataset.query', [], {'select': 'mountpoint'})) zfs_pool_mountpoints = list( self.dispatcher.call_sync('volume.query', [], {'select': 'mountpoint'})) homedir_occurrence = self.dispatcher.call_sync( 'user.query', [('home', '=', user['home'])], {'single': True}) if user['home'] in zfs_pool_mountpoints: raise TaskException( errno.ENXIO, 'ZFS pool mountpoint cannot be set as the home directory.') homedir_mount_path = os.path.join( '/', *(user['home'].split(os.path.sep)[:-1])) if not any(homedir_mount_path == dataset_mountpoint and os.path.ismount(dataset_mountpoint) for dataset_mountpoint in zfs_dataset_mountpoints): raise TaskException( errno.ENXIO, 'Home directory has to reside in zfs pool or dataset.' + ' Provide a path which starts with valid, mounted zfs pool or dataset location.' ) if homedir_occurrence: raise TaskException( errno.ENXIO, '{} is already assigned to another user.'.format( user['home']) + ' Multiple users cannot share the same home directory.') password = user.pop('password', None) if password: user.update({ 'unixhash': crypted_password(password), 'nthash': nt_password(password), 'password_changed_at': datetime.utcnow() }) if user.get('group') is None: try: result = self.run_subtask_sync('group.create', { 'gid': uid, 'name': user['username'] }) except RpcException as err: raise err user['group'] = result self.created_group = result try: id = self.datastore.insert('users', user) self.id = id self.dispatcher.call_sync('etcd.generation.generate_group', 'accounts') except SubprocessException as e: raise TaskException( errno.ENXIO, 'Could not generate samba password. stdout: {0}\nstderr: {1}'. format(e.out, e.err)) except DuplicateKeyException as e: raise TaskException(errno.EBADMSG, 'Cannot add user: {0}'.format(str(e))) except RpcException as e: raise TaskException(errno.ENXIO, 'Cannot regenerate users file: {0}'.format(e)) if user['home'] != '/nonexistent': group = self.dispatcher.call_sync('group.query', [('id', '=', user['group'])], {'single': True}) if not group: raise TaskException( errno.ENOENT, 'Group {0} not found'.format(user['group'])) user_gid = group['gid'] if not os.path.exists(user['home']): parent_dataset = self.dispatcher.call_sync( 'volume.dataset.query', [('mountpoint', '=', homedir_mount_path)], {'single': True}) homedir_dataset_id = os.path.join( parent_dataset['id'], user['home'].split(os.path.sep)[-1]) self.run_subtask_sync( 'volume.dataset.create', { 'id': homedir_dataset_id, 'type': 'FILESYSTEM', 'volume': parent_dataset['volume'] }) os.chmod(user['home'], 0o755) else: os.chmod(user['home'], 0o755) for file in os.listdir(SKEL_PATH): if file.startswith('dot'): dest_file = os.path.join(user['home'], file[3:]) if not os.path.exists(dest_file): shutil.copyfile(os.path.join(SKEL_PATH, file), dest_file) os.chown(dest_file, uid, user_gid) else: dest_file = os.path.join(user['home'], file) if not os.path.exists(dest_file): shutil.copyfile(os.path.join(SKEL_PATH, file), dest_file) os.chown(dest_file, uid, user_gid) os.chown(user['home'], uid, user_gid) self.dispatcher.dispatch_event('user.changed', { 'operation': 'create', 'ids': [id] }) return id