Пример #1
0
    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']]
        })
Пример #2
0
    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
Пример #3
0
    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']]
        })
Пример #4
0
    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
Пример #5
0
    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'])
            user_gid = user_gid['gid'] if user_gid else 0

            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.run_subtask_sync(
                        '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.secret),
                    'nthash': nt_password(password.secret),
                    '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']]
        })
Пример #6
0
    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.secret),
                'nthash': nt_password(password.secret),
                '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
Пример #7
0
    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