# -*- coding: utf-8 -*- # Copyright (C) 2010-2017 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 # (only), as published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # # This program is dual-licensed. If you wish to learn more about the # RhodeCode Enterprise Edition, including its added features, Support services, # and proprietary license terms, please see https://rhodecode.com/licenses/ """ permissions model for RhodeCode """ import logging import traceback from sqlalchemy.exc import DatabaseError from rhodecode.model import BaseModel from rhodecode.model.db import ( User, Permission, UserToPerm, UserRepoToPerm, UserRepoGroupToPerm, UserUserGroupToPerm, UserGroup, UserGroupToPerm) from rhodecode.lib.utils2 import str2bool, safe_int log = logging.getLogger(__name__) class PermissionModel(BaseModel): """ Permissions model for RhodeCode """ cls = Permission global_perms = { 'default_repo_create': None, # special case for create repos on write access to group 'default_repo_create_on_write': None, 'default_repo_group_create': None, 'default_user_group_create': None, 'default_fork_create': None, 'default_inherit_default_permissions': None, 'default_register': None, 'default_password_reset': None, 'default_extern_activate': None, # object permissions below 'default_repo_perm': None, 'default_group_perm': None, 'default_user_group_perm': None, } def set_global_permission_choices(self, c_obj, gettext_translator): c_obj.repo_perms_choices = [ ('repository.none', gettext_translator('None'),), ('repository.read', gettext_translator('Read'),), ('repository.write', gettext_translator('Write'),), ('repository.admin', gettext_translator('Admin'),)] c_obj.group_perms_choices = [ ('group.none', gettext_translator('None'),), ('group.read', gettext_translator('Read'),), ('group.write', gettext_translator('Write'),), ('group.admin', gettext_translator('Admin'),)] c_obj.user_group_perms_choices = [ ('usergroup.none', gettext_translator('None'),), ('usergroup.read', gettext_translator('Read'),), ('usergroup.write', gettext_translator('Write'),), ('usergroup.admin', gettext_translator('Admin'),)] c_obj.register_choices = [ ('hg.register.none', gettext_translator('Disabled')), ('hg.register.manual_activate', gettext_translator('Allowed with manual account activation')), ('hg.register.auto_activate', gettext_translator('Allowed with automatic account activation')),] c_obj.password_reset_choices = [ ('hg.password_reset.enabled', gettext_translator('Allow password recovery')), ('hg.password_reset.hidden', gettext_translator('Hide password recovery link')), ('hg.password_reset.disabled', gettext_translator('Disable password recovery')),] c_obj.extern_activate_choices = [ ('hg.extern_activate.manual', gettext_translator('Manual activation of external account')), ('hg.extern_activate.auto', gettext_translator('Automatic activation of external account')),] c_obj.repo_create_choices = [ ('hg.create.none', gettext_translator('Disabled')), ('hg.create.repository', gettext_translator('Enabled'))] c_obj.repo_create_on_write_choices = [ ('hg.create.write_on_repogroup.false', gettext_translator('Disabled')), ('hg.create.write_on_repogroup.true', gettext_translator('Enabled'))] c_obj.user_group_create_choices = [ ('hg.usergroup.create.false', gettext_translator('Disabled')), ('hg.usergroup.create.true', gettext_translator('Enabled'))] c_obj.repo_group_create_choices = [ ('hg.repogroup.create.false', gettext_translator('Disabled')), ('hg.repogroup.create.true', gettext_translator('Enabled'))] c_obj.fork_choices = [ ('hg.fork.none', gettext_translator('Disabled')), ('hg.fork.repository', gettext_translator('Enabled'))] c_obj.inherit_default_permission_choices = [ ('hg.inherit_default_perms.false', gettext_translator('Disabled')), ('hg.inherit_default_perms.true', gettext_translator('Enabled'))] def get_default_perms(self, object_perms, suffix): defaults = {} for perm in object_perms: # perms if perm.permission.permission_name.startswith('repository.'): defaults['default_repo_perm' + suffix] = perm.permission.permission_name if perm.permission.permission_name.startswith('group.'): defaults['default_group_perm' + suffix] = perm.permission.permission_name if perm.permission.permission_name.startswith('usergroup.'): defaults['default_user_group_perm' + suffix] = perm.permission.permission_name # creation of objects if perm.permission.permission_name.startswith('hg.create.write_on_repogroup'): defaults['default_repo_create_on_write' + suffix] = perm.permission.permission_name elif perm.permission.permission_name.startswith('hg.create.'): defaults['default_repo_create' + suffix] = perm.permission.permission_name if perm.permission.permission_name.startswith('hg.fork.'): defaults['default_fork_create' + suffix] = perm.permission.permission_name if perm.permission.permission_name.startswith('hg.inherit_default_perms.'): defaults['default_inherit_default_permissions' + suffix] = perm.permission.permission_name if perm.permission.permission_name.startswith('hg.repogroup.'): defaults['default_repo_group_create' + suffix] = perm.permission.permission_name if perm.permission.permission_name.startswith('hg.usergroup.'): defaults['default_user_group_create' + suffix] = perm.permission.permission_name # registration and external account activation if perm.permission.permission_name.startswith('hg.register.'): defaults['default_register' + suffix] = perm.permission.permission_name if perm.permission.permission_name.startswith('hg.password_reset.'): defaults['default_password_reset' + suffix] = perm.permission.permission_name if perm.permission.permission_name.startswith('hg.extern_activate.'): defaults['default_extern_activate' + suffix] = perm.permission.permission_name return defaults def _make_new_user_perm(self, user, perm_name): log.debug('Creating new user permission:%s', perm_name) new = UserToPerm() new.user = user new.permission = Permission.get_by_key(perm_name) return new def _make_new_user_group_perm(self, user_group, perm_name): log.debug('Creating new user group permission:%s', perm_name) new = UserGroupToPerm() new.users_group = user_group new.permission = Permission.get_by_key(perm_name) return new def _keep_perm(self, perm_name, keep_fields): def get_pat(field_name): return { # global perms 'default_repo_create': 'hg.create.', # special case for create repos on write access to group 'default_repo_create_on_write': 'hg.create.write_on_repogroup.', 'default_repo_group_create': 'hg.repogroup.create.', 'default_user_group_create': 'hg.usergroup.create.', 'default_fork_create': 'hg.fork.', 'default_inherit_default_permissions': 'hg.inherit_default_perms.', # application perms 'default_register': 'hg.register.', 'default_password_reset': 'hg.password_reset.', 'default_extern_activate': 'hg.extern_activate.', # object permissions below 'default_repo_perm': 'repository.', 'default_group_perm': 'group.', 'default_user_group_perm': 'usergroup.', }[field_name] for field in keep_fields: pat = get_pat(field) if perm_name.startswith(pat): return True return False def _clear_object_perm(self, object_perms, preserve=None): preserve = preserve or [] _deleted = [] for perm in object_perms: perm_name = perm.permission.permission_name if not self._keep_perm(perm_name, keep_fields=preserve): _deleted.append(perm_name) self.sa.delete(perm) return _deleted def _clear_user_perms(self, user_id, preserve=None): perms = self.sa.query(UserToPerm)\ .filter(UserToPerm.user_id == user_id)\ .all() return self._clear_object_perm(perms, preserve=preserve) def _clear_user_group_perms(self, user_group_id, preserve=None): perms = self.sa.query(UserGroupToPerm)\ .filter(UserGroupToPerm.users_group_id == user_group_id)\ .all() return self._clear_object_perm(perms, preserve=preserve) def _set_new_object_perms(self, obj_type, object, form_result, preserve=None): # clear current entries, to make this function idempotent # it will fix even if we define more permissions or permissions # are somehow missing preserve = preserve or [] _global_perms = self.global_perms.copy() if obj_type not in ['user', 'user_group']: raise ValueError("obj_type must be on of 'user' or 'user_group'") if len(_global_perms) != len(Permission.DEFAULT_USER_PERMISSIONS): raise Exception('Inconsistent permissions definition') if obj_type == 'user': self._clear_user_perms(object.user_id, preserve) if obj_type == 'user_group': self._clear_user_group_perms(object.users_group_id, preserve) # now kill the keys that we want to preserve from the form. for key in preserve: del _global_perms[key] for k in _global_perms.copy(): _global_perms[k] = form_result[k] # at that stage we validate all are passed inside form_result for _perm_key, perm_value in _global_perms.items(): if perm_value is None: raise ValueError('Missing permission for %s' % (_perm_key,)) if obj_type == 'user': p = self._make_new_user_perm(object, perm_value) self.sa.add(p) if obj_type == 'user_group': p = self._make_new_user_group_perm(object, perm_value) self.sa.add(p) def _set_new_user_perms(self, user, form_result, preserve=None): return self._set_new_object_perms( 'user', user, form_result, preserve) def _set_new_user_group_perms(self, user_group, form_result, preserve=None): return self._set_new_object_perms( 'user_group', user_group, form_result, preserve) def set_new_user_perms(self, user, form_result): # calculate what to preserve from what is given in form_result preserve = set(self.global_perms.keys()).difference(set(form_result.keys())) return self._set_new_user_perms(user, form_result, preserve) def set_new_user_group_perms(self, user_group, form_result): # calculate what to preserve from what is given in form_result preserve = set(self.global_perms.keys()).difference(set(form_result.keys())) return self._set_new_user_group_perms(user_group, form_result, preserve) def create_permissions(self): """ Create permissions for whole system """ for p in Permission.PERMS: if not Permission.get_by_key(p[0]): new_perm = Permission() new_perm.permission_name = p[0] new_perm.permission_longname = p[0] # translation err with p[1] self.sa.add(new_perm) def _create_default_object_permission(self, obj_type, obj, obj_perms, force=False): if obj_type not in ['user', 'user_group']: raise ValueError("obj_type must be on of 'user' or 'user_group'") def _get_group(perm_name): return '.'.join(perm_name.split('.')[:1]) defined_perms_groups = map( _get_group, (x.permission.permission_name for x in obj_perms)) log.debug('GOT ALREADY DEFINED:%s', obj_perms) if force: self._clear_object_perm(obj_perms) self.sa.commit() defined_perms_groups = [] # for every default permission that needs to be created, we check if # it's group is already defined, if it's not we create default perm for perm_name in Permission.DEFAULT_USER_PERMISSIONS: gr = _get_group(perm_name) if gr not in defined_perms_groups: log.debug('GR:%s not found, creating permission %s', gr, perm_name) if obj_type == 'user': new_perm = self._make_new_user_perm(obj, perm_name) self.sa.add(new_perm) if obj_type == 'user_group': new_perm = self._make_new_user_group_perm(obj, perm_name) self.sa.add(new_perm) def create_default_user_permissions(self, user, force=False): """ Creates only missing default permissions for user, if force is set it resets the default permissions for that user :param user: :param force: """ user = self._get_user(user) obj_perms = UserToPerm.query().filter(UserToPerm.user == user).all() return self._create_default_object_permission( 'user', user, obj_perms, force) def create_default_user_group_permissions(self, user_group, force=False): """ Creates only missing default permissions for user group, if force is set it resets the default permissions for that user group :param user_group: :param force: """ user_group = self._get_user_group(user_group) obj_perms = UserToPerm.query().filter(UserGroupToPerm.users_group == user_group).all() return self._create_default_object_permission( 'user_group', user_group, obj_perms, force) def update_application_permissions(self, form_result): if 'perm_user_id' in form_result: perm_user = User.get(safe_int(form_result['perm_user_id'])) else: # used mostly to do lookup for default user perm_user = User.get_by_username(form_result['perm_user_name']) try: # stage 1 set anonymous access if perm_user.username == User.DEFAULT_USER: perm_user.active = str2bool(form_result['anonymous']) self.sa.add(perm_user) # stage 2 reset defaults and set them from form data self._set_new_user_perms(perm_user, form_result, preserve=[ 'default_repo_perm', 'default_group_perm', 'default_user_group_perm', 'default_repo_group_create', 'default_user_group_create', 'default_repo_create_on_write', 'default_repo_create', 'default_fork_create', 'default_inherit_default_permissions',]) self.sa.commit() except (DatabaseError,): log.error(traceback.format_exc()) self.sa.rollback() raise def update_user_permissions(self, form_result): if 'perm_user_id' in form_result: perm_user = User.get(safe_int(form_result['perm_user_id'])) else: # used mostly to do lookup for default user perm_user = User.get_by_username(form_result['perm_user_name']) try: # stage 2 reset defaults and set them from form data self._set_new_user_perms(perm_user, form_result, preserve=[ 'default_repo_perm', 'default_group_perm', 'default_user_group_perm', 'default_register', 'default_password_reset', 'default_extern_activate']) self.sa.commit() except (DatabaseError,): log.error(traceback.format_exc()) self.sa.rollback() raise def update_user_group_permissions(self, form_result): if 'perm_user_group_id' in form_result: perm_user_group = UserGroup.get(safe_int(form_result['perm_user_group_id'])) else: # used mostly to do lookup for default user perm_user_group = UserGroup.get_by_group_name(form_result['perm_user_group_name']) try: # stage 2 reset defaults and set them from form data self._set_new_user_group_perms(perm_user_group, form_result, preserve=[ 'default_repo_perm', 'default_group_perm', 'default_user_group_perm', 'default_register', 'default_password_reset', 'default_extern_activate']) self.sa.commit() except (DatabaseError,): log.error(traceback.format_exc()) self.sa.rollback() raise def update_object_permissions(self, form_result): if 'perm_user_id' in form_result: perm_user = User.get(safe_int(form_result['perm_user_id'])) else: # used mostly to do lookup for default user perm_user = User.get_by_username(form_result['perm_user_name']) try: # stage 2 reset defaults and set them from form data self._set_new_user_perms(perm_user, form_result, preserve=[ 'default_repo_group_create', 'default_user_group_create', 'default_repo_create_on_write', 'default_repo_create', 'default_fork_create', 'default_inherit_default_permissions', 'default_register', 'default_password_reset', 'default_extern_activate']) # overwrite default repo permissions if form_result['overwrite_default_repo']: _def_name = form_result['default_repo_perm'].split('repository.')[-1] _def = Permission.get_by_key('repository.' + _def_name) for r2p in self.sa.query(UserRepoToPerm)\ .filter(UserRepoToPerm.user == perm_user)\ .all(): # don't reset PRIVATE repositories if not r2p.repository.private: r2p.permission = _def self.sa.add(r2p) # overwrite default repo group permissions if form_result['overwrite_default_group']: _def_name = form_result['default_group_perm'].split('group.')[-1] _def = Permission.get_by_key('group.' + _def_name) for g2p in self.sa.query(UserRepoGroupToPerm)\ .filter(UserRepoGroupToPerm.user == perm_user)\ .all(): g2p.permission = _def self.sa.add(g2p) # overwrite default user group permissions if form_result['overwrite_default_user_group']: _def_name = form_result['default_user_group_perm'].split('usergroup.')[-1] # user groups _def = Permission.get_by_key('usergroup.' + _def_name) for g2p in self.sa.query(UserUserGroupToPerm)\ .filter(UserUserGroupToPerm.user == perm_user)\ .all(): g2p.permission = _def self.sa.add(g2p) self.sa.commit() except (DatabaseError,): log.exception('Failed to set default object permissions') self.sa.rollback() raise