def test_init_user_attributes_from_ldap(monkeypatch, arrange_ldap_auth): """Authenticate unknown user with mocked LDAP, verify user is created. """ # Arrange test user. uniqifier = uuid.uuid4() username = '******'.format(uniqifier) assert User.get_by_username(username) is None # Arrange LDAP auth. monkeypatch.setattr(auth_ldap, 'AuthLdap', _AuthLdapMock) # Authenticate with LDAP. user_data = authenticate(username, 'password') # Verify that authenication succeeded and retrieved correct attributes # from LDAP. assert user_data is not None assert user_data.get('firstname') == u'spam ldap first name' assert user_data.get('lastname') == u'spam ldap last name' assert user_data.get('email') == 'spam ldap email' # Verify that authentication created new user with attributes # retrieved from LDAP. new_user = User.get_by_username(username) assert new_user is not None assert new_user.firstname == u'spam ldap first name' assert new_user.lastname == u'spam ldap last name' assert new_user.email == 'spam ldap email'
def test_enforce_groups(self, pre_existing, regular_should_be, external_should_be, groups, expected): # delete all groups for gr in UserGroupModel.get_all(): fixture.destroy_user_group(gr) Session().commit() user = User.get_by_username(TEST_USER_REGULAR_LOGIN) for gr in pre_existing: gr = fixture.create_user_group(gr) Session().commit() # make sure use is just in those groups for gr in regular_should_be: gr = fixture.create_user_group(gr) Session().commit() UserGroupModel().add_user_to_group(gr, user) Session().commit() # now special external groups created by auth plugins for gr in external_should_be: gr = fixture.create_user_group(gr, user_group_data={'extern_type': 'container'}) Session().commit() UserGroupModel().add_user_to_group(gr, user) Session().commit() UserGroupModel().enforce_groups(user, groups, 'container') Session().commit() user = User.get_by_username(TEST_USER_REGULAR_LOGIN) in_groups = user.group_member self.assertEqual(expected, [x.users_group.users_group_name for x in in_groups])
def test_create_and_remove(self): usr = UserModel().create_or_update(username=u'test_user', password=u'qweqwe', email=u'*****@*****.**', firstname=u'u1', lastname=u'u1') Session().commit() assert User.get_by_username(u'test_user') == usr assert User.get_by_username(u'test_USER', case_insensitive=True) == usr # User.get_by_username without explicit request for case insensitivty # will use database case sensitivity. The following will thus return # None on for example PostgreSQL but find test_user on MySQL - we are # fine with leaving that as undefined as long as it doesn't crash. User.get_by_username(u'test_USER', case_insensitive=False) # make user group user_group = fixture.create_user_group(u'some_example_group') Session().commit() UserGroupModel().add_user_to_group(user_group, usr) Session().commit() assert UserGroup.get(user_group.users_group_id) == user_group assert UserGroupMember.query().count() == 1 UserModel().delete(usr.user_id) Session().commit() assert UserGroupMember.query().all() == []
def set_anonymous_access(enable=True): user = User.get_by_username(User.DEFAULT_USER) user.active = enable Session().add(user) Session().commit() print '\tanonymous access is now:', enable if enable != User.get_by_username(User.DEFAULT_USER).active: raise Exception('Cannot set anonymous access')
def mention_from_description(self, pr, old_description=''): mention_recipients = set(User.get_by_username(username, case_insensitive=True) for username in extract_mentioned_users(pr.description)) mention_recipients.difference_update(User.get_by_username(username, case_insensitive=True) for username in extract_mentioned_users(old_description)) log.debug("Mentioning %s" % mention_recipients) self.__add_reviewers(pr, [], mention_recipients)
def create_user(self, name, **kwargs): if 'skip_if_exists' in kwargs: del kwargs['skip_if_exists'] user = User.get_by_username(name) if user: return user form_data = self._get_user_create_params(name, **kwargs) user = UserModel().create(form_data) Session().commit() user = User.get_by_username(user.username) return user
def create_pullrequest(self, testcontroller, repo_name, pr_src_rev, pr_dst_rev, title=u'title'): org_ref = 'branch:stable:%s' % pr_src_rev other_ref = 'branch:default:%s' % pr_dst_rev with test_context(testcontroller.app): # needed to be able to mock request user org_repo = other_repo = Repository.get_by_repo_name(repo_name) owner_user = User.get_by_username(TEST_USER_ADMIN_LOGIN) reviewers = [User.get_by_username(TEST_USER_REGULAR_LOGIN)] request.authuser = request.user = AuthUser(dbuser=owner_user) # creating a PR sends a message with an absolute URL - without routing that requires mocking with mock.patch.object(helpers, 'url', (lambda arg, qualified=False, **kwargs: ('https://localhost' if qualified else '') + '/fake/' + arg)): cmd = CreatePullRequestAction(org_repo, other_repo, org_ref, other_ref, title, u'No description', owner_user, reviewers) pull_request = cmd.execute() Session().commit() return pull_request.pull_request_id
def test_my_account_my_watched(self): self.log_user() response = self.app.get(url('my_account_watched')) cnt = UserFollowing.query().filter(UserFollowing.user == User.get_by_username(TEST_USER_ADMIN_LOGIN)).count() response.mustcontain('"totalRecords": %s' % cnt)
def _get_permission_for_user(user, repo): perm = UserRepoToPerm.query()\ .filter(UserRepoToPerm.repository == Repository.get_by_repo_name(repo))\ .filter(UserRepoToPerm.user == User.get_by_username(user))\ .all() return perm
def _extract_mentions(self, s): user_objects = [] for username in extract_mentioned_users(s): user_obj = User.get_by_username(username, case_insensitive=True) if user_obj: user_objects.append(user_obj) return user_objects
def test_push_unlocks_repository_git(self, webserver): # enable locking fork_name = '%s_fork%s' % (GIT_REPO, _RandomNameSequence().next()) fixture.create_fork(GIT_REPO, fork_name) r = Repository.get_by_repo_name(fork_name) r.enable_locking = True Session().commit() #clone some temp DEST = _get_tmp_dir() clone_url = webserver.repo_url(fork_name) stdout, stderr = Command(TESTS_TMP_PATH).execute('git clone', clone_url, DEST) #check for lock repo after clone r = Repository.get_by_repo_name(fork_name) assert r.locked[0] == User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id #push is ok and repo is now unlocked stdout, stderr = _add_files_and_push(webserver, 'git', DEST, clone_url=clone_url) _check_proper_git_push(stdout, stderr) assert ('remote: Released lock on repo `%s`' % fork_name) in stderr #we need to cleanup the Session Here ! Session.remove() r = Repository.get_by_repo_name(fork_name) assert r.locked == [None, None]
def _store_user_in_session(self, username, remember=False): user = User.get_by_username(username, case_insensitive=True) auth_user = AuthUser(user.user_id) auth_user.set_authenticated() cs = auth_user.get_cookie_store() session['authuser'] = cs user.update_lastlogin() Session().commit() # If they want to be remembered, update the cookie if remember: _year = (datetime.datetime.now() + datetime.timedelta(seconds=60 * 60 * 24 * 365)) session._set_cookie_expires(_year) session.save() log.info('user %s is now authenticated and stored in ' 'session, session attrs %s' % (username, cs)) # dumps session attrs back to cookie session._update_cookie_out() # we set new cookie headers = None if session.request['set_cookie']: # send set-cookie headers back to response to update cookie headers = [('Set-Cookie', session.request['cookie_out'])] return headers
def test_api_keys(self): self.log_user() user = User.get_by_username(TEST_USER_REGULAR_LOGIN) response = self.app.get(url('edit_user_api_keys', id=user.user_id)) response.mustcontain(user.api_key) response.mustcontain('expires: never')
def test_push_on_locked_repo_by_other_user_git(self, webserver): # Note: Git hooks must be executable on unix. This test will thus fail # for example on Linux if /tmp is mounted noexec. #clone some temp DEST = _get_tmp_dir() clone_url = webserver.repo_url(GIT_REPO) stdout, stderr = Command(TESTS_TMP_PATH).execute('git clone', clone_url, DEST) #lock repo r = Repository.get_by_repo_name(GIT_REPO) # let this user actually push ! RepoModel().grant_user_permission(repo=r, user=TEST_USER_REGULAR_LOGIN, perm='repository.write') Session().commit() Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id) #push fails repo is locked by other user ! stdout, stderr = _add_files_and_push(webserver, 'git', DEST, username=TEST_USER_REGULAR_LOGIN, password=TEST_USER_REGULAR_PASS, ignoreReturnCode=True) err = 'Repository `%s` locked by user `%s`' % (GIT_REPO, TEST_USER_ADMIN_LOGIN) assert err in stderr #TODO: fix this somehow later on Git, Git is stupid and even if we throw #back 423 to it, it makes ANOTHER request and we fail there with 405 :/ msg = ("""abort: HTTP Error 423: Repository `%s` locked by user `%s`""" % (GIT_REPO, TEST_USER_ADMIN_LOGIN))
def test_update(self, name, attrs): self.log_user() usr = fixture.create_user(self.test_user_1, password='******', email='*****@*****.**', extern_type='internal', extern_name=self.test_user_1, skip_if_exists=True) Session().commit() params = usr.get_api_data(True) params.update({'password_confirmation': ''}) params.update({'new_password': ''}) params.update(attrs) if name == 'email': params['emails'] = [attrs['email']] if name == 'extern_type': #cannot update this via form, expected value is original one params['extern_type'] = "internal" if name == 'extern_name': #cannot update this via form, expected value is original one params['extern_name'] = self.test_user_1 # special case since this user is not # logged in yet his data is not filled # so we use creation data response = self.app.put(url('user', id=usr.user_id), params) self.checkSessionFlash(response, 'User updated successfully') updated_user = User.get_by_username(self.test_user_1) updated_params = updated_user.get_api_data(True) updated_params.update({'password_confirmation': ''}) updated_params.update({'new_password': ''}) self.assertEqual(params, updated_params)
def log_pull_action(ui, repo, **kwargs): """ Logs user last pull action :param ui: :param repo: """ ex = _extract_extras() user = User.get_by_username(ex.username) action = 'pull' action_logger(user, action, ex.repository, ex.ip, commit=True) # extension hook call from kallithea import EXTENSIONS callback = getattr(EXTENSIONS, 'PULL_HOOK', None) if callable(callback): kw = {} kw.update(ex) callback(**kw) if ex.make_lock is not None and ex.make_lock: Repository.lock(Repository.get_by_repo_name(ex.repository), user.user_id) #msg = 'Made lock on repo `%s`' % repository #sys.stdout.write(msg) if ex.locked_by[0]: locked_by = User.get(ex.locked_by[0]).username _http_ret = HTTPLockedRC(ex.repository, locked_by) if str(_http_ret.code).startswith('2'): #2xx Codes don't raise exceptions sys.stdout.write(_http_ret.title) return 0
def test_push_on_locked_repo_by_other_user_git(self): #clone some temp DEST = _get_tmp_dir() clone_url = _construct_url(GIT_REPO, dest=DEST) stdout, stderr = Command('/tmp').execute('git clone', clone_url) #lock repo r = Repository.get_by_repo_name(GIT_REPO) # let this user actually push ! RepoModel().grant_user_permission(repo=r, user=TEST_USER_REGULAR_LOGIN, perm='repository.write') Session().commit() Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id) #push fails repo is locked by other user ! stdout, stderr = _add_files_and_push('git', DEST, user=TEST_USER_REGULAR_LOGIN, passwd=TEST_USER_REGULAR_PASS) err = 'Repository `%s` locked by user `%s`' % (GIT_REPO, TEST_USER_ADMIN_LOGIN) assert err in stderr #TODO: fix this somehow later on Git, Git is stupid and even if we throw #back 423 to it, it makes ANOTHER request and we fail there with 405 :/ msg = ("""abort: HTTP Error 423: Repository `%s` locked by user `%s`""" % (GIT_REPO, TEST_USER_ADMIN_LOGIN))
def is_following_user(self, username, user_id, cache=False): u = User.get_by_username(username) f = self.sa.query(UserFollowing)\ .filter(UserFollowing.follows_user == u)\ .filter(UserFollowing.user_id == user_id).scalar() return f is not None
def test_clone_after_repo_was_locked_git(self): #lock repo r = Repository.get_by_repo_name(GIT_REPO) Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id) #pull fails since repo is locked clone_url = _construct_url(GIT_REPO) stdout, stderr = Command('/tmp').execute('git clone', clone_url) msg = ("""The requested URL returned error: 423""") assert msg in stderr
def extract_mentioned_users(text): """ Returns set of actual database Users @mentioned in given text. """ from kallithea.model.db import User result = set() for name in extract_mentioned_usernames(text): user = User.get_by_username(name, case_insensitive=True) if user is not None and not user.is_default_user: result.add(user) return result
def test_clone_after_repo_was_locked_git(self, webserver): #lock repo r = Repository.get_by_repo_name(GIT_REPO) Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id) #pull fails since repo is locked clone_url = webserver.repo_url(GIT_REPO) stdout, stderr = Command(TESTS_TMP_PATH).execute('git clone', clone_url, _get_tmp_dir(), ignoreReturnCode=True) msg = ("""The requested URL returned error: 423""") assert msg in stderr
def update(self, repo, **kwargs): try: cur_repo = self._get_repo(repo) org_repo_name = cur_repo.repo_name if 'user' in kwargs: cur_repo.user = User.get_by_username(kwargs['user']) if 'repo_group' in kwargs: cur_repo.group = RepoGroup.get(kwargs['repo_group']) log.debug('Updating repo %s with params:%s' % (cur_repo, kwargs)) for strip, k in [(1, 'repo_enable_downloads'), (1, 'repo_description'), (1, 'repo_enable_locking'), (1, 'repo_landing_rev'), (1, 'repo_private'), (1, 'repo_enable_statistics'), (0, 'clone_uri'),]: if k in kwargs: val = kwargs[k] if strip: k = remove_prefix(k, 'repo_') if k == 'clone_uri': from kallithea.model.validators import Missing _change = kwargs.get('clone_uri_change') if _change == Missing: # we don't change the value, so use original one val = cur_repo.clone_uri setattr(cur_repo, k, val) new_name = cur_repo.get_new_name(kwargs['repo_name']) cur_repo.repo_name = new_name #if private flag is set, reset default permission to NONE if kwargs.get('repo_private'): EMPTY_PERM = 'repository.none' RepoModel().grant_user_permission( repo=cur_repo, user='******', perm=EMPTY_PERM ) #handle extra fields for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), kwargs): k = RepositoryField.un_prefix_key(field) ex_field = RepositoryField.get_by_key_name(key=k, repo=cur_repo) if ex_field: ex_field.field_value = kwargs[field] self.sa.add(ex_field) self.sa.add(cur_repo) if org_repo_name != new_name: # rename repository self._rename_filesystem_repo(old=org_repo_name, new=new_name) return cur_repo except Exception: log.error(traceback.format_exc()) raise
def test_clone_after_repo_was_locked_hg(self): #lock repo r = Repository.get_by_repo_name(HG_REPO) Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id) #pull fails since repo is locked clone_url = _construct_url(HG_REPO) stdout, stderr = Command('/tmp').execute('hg clone', clone_url) msg = ("""abort: HTTP Error 423: Repository `%s` locked by user `%s`""" % (HG_REPO, TEST_USER_ADMIN_LOGIN)) assert msg in stderr
def test_clone_after_repo_was_locked_hg(self, webserver): #lock repo r = Repository.get_by_repo_name(HG_REPO) Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id) #pull fails since repo is locked clone_url = webserver.repo_url(HG_REPO) stdout, stderr = Command(TESTS_TMP_PATH).execute('hg clone', clone_url, _get_tmp_dir(), ignoreReturnCode=True) msg = ("""abort: HTTP Error 423: Repository `%s` locked by user `%s`""" % (HG_REPO, TEST_USER_ADMIN_LOGIN)) assert msg in stderr
def action_logger(user, action, repo, ipaddr='', sa=None, commit=False): """ Action logger for various actions made by users :param user: user that made this action, can be a unique username string or object containing user_id attribute :param action: action to log, should be on of predefined unique actions for easy translations :param repo: string name of repository or object containing repo_id, that action was made on :param ipaddr: optional IP address from what the action was made :param sa: optional sqlalchemy session """ if not sa: sa = meta.Session() # if we don't get explicit IP address try to get one from registered user # in tmpl context var if not ipaddr: ipaddr = getattr(get_current_authuser(), 'ip_addr', '') if getattr(user, 'user_id', None): user_obj = User.get(user.user_id) elif isinstance(user, basestring): user_obj = User.get_by_username(user) else: raise Exception('You have to provide a user object or a username') if getattr(repo, 'repo_id', None): repo_obj = Repository.get(repo.repo_id) repo_name = repo_obj.repo_name elif isinstance(repo, basestring): repo_name = repo.lstrip('/') repo_obj = Repository.get_by_repo_name(repo_name) else: repo_obj = None repo_name = u'' user_log = UserLog() user_log.user_id = user_obj.user_id user_log.username = user_obj.username user_log.action = safe_unicode(action) user_log.repository = repo_obj user_log.repository_name = repo_name user_log.action_date = datetime.datetime.now() user_log.user_ip = ipaddr sa.add(user_log) log.info('Logging action:%s on %s by user:%s ip:%s', action, safe_unicode(repo), user_obj, ipaddr) if commit: sa.commit()
def get_user(self, username=None, **kwargs): """ Helper method for user fetching in plugins, by default it's using simple fetch by username, but this method can be customized in plugins eg. container auth plugin to fetch user by environ params :param username: username if given to fetch from database :param kwargs: extra arguments needed for user fetching. """ user = None log.debug('Trying to fetch user `%s` from Kallithea database' % (username)) if username: user = User.get_by_username(username) if not user: log.debug('Fallback to fetch user in case insensitive mode') user = User.get_by_username(username, case_insensitive=True) else: log.debug('provided username:`%s` is empty skipping...' % username) return user
def test_clone_and_create_lock_git(self, webserver): # enable locking r = Repository.get_by_repo_name(GIT_REPO) r.enable_locking = True Session().commit() # clone clone_url = webserver.repo_url(GIT_REPO) stdout, stderr = Command(TESTS_TMP_PATH).execute('git clone', clone_url, _get_tmp_dir()) #check if lock was made r = Repository.get_by_repo_name(GIT_REPO) assert r.locked[0] == User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id
def _create_gist(f_name, content='some gist', lifetime=-1, description=u'gist-desc', gist_type='public', owner=TEST_USER_ADMIN_LOGIN): gist_mapping = { f_name: {'content': content} } user = User.get_by_username(owner) gist = GistModel().create(description, owner=user, gist_mapping=gist_mapping, gist_type=gist_type, lifetime=lifetime) Session().commit() return gist
def post(self, repo_name, pull_request_id): pull_request = PullRequest.get_or_404(pull_request_id) if pull_request.is_closed(): raise HTTPForbidden() assert pull_request.other_repo.repo_name == repo_name #only owner or admin can update it owner = pull_request.owner_id == request.authuser.user_id repo_admin = h.HasRepoPermissionLevel('admin')(c.repo_name) if not (h.HasPermissionAny('hg.admin')() or repo_admin or owner): raise HTTPForbidden() _form = PullRequestPostForm()().to_python(request.POST) cur_reviewers = set(pull_request.get_reviewer_users()) new_reviewers = set(_get_reviewer(s) for s in _form['review_members']) old_reviewers = set(_get_reviewer(s) for s in _form['org_review_members']) other_added = cur_reviewers - old_reviewers other_removed = old_reviewers - cur_reviewers if other_added: h.flash(_('Meanwhile, the following reviewers have been added: %s') % (', '.join(u.username for u in other_added)), category='warning') if other_removed: h.flash(_('Meanwhile, the following reviewers have been removed: %s') % (', '.join(u.username for u in other_removed)), category='warning') if _form['updaterev']: return self.create_new_iteration(pull_request, _form['updaterev'], _form['pullrequest_title'], _form['pullrequest_desc'], new_reviewers) added_reviewers = new_reviewers - old_reviewers - cur_reviewers removed_reviewers = (old_reviewers - new_reviewers) & cur_reviewers old_description = pull_request.description pull_request.title = _form['pullrequest_title'] pull_request.description = _form['pullrequest_desc'].strip() or _('No description') pull_request.owner = User.get_by_username(_form['owner']) user = User.get(request.authuser.user_id) PullRequestModel().mention_from_description(user, pull_request, old_description) PullRequestModel().add_reviewers(user, pull_request, added_reviewers) PullRequestModel().remove_reviewers(user, pull_request, removed_reviewers) Session().commit() h.flash(_('Pull request updated'), category='success') raise HTTPFound(location=pull_request.url())
def test_forgot_password(self): response = self.app.get(url(controller='login', action='password_reset')) self.assertEqual(response.status, '200 OK') username = '******' password = '******' email = '*****@*****.**' name = 'passwd' lastname = 'reset' new = User() new.username = username new.password = password new.email = email new.name = name new.lastname = lastname new.api_key = generate_api_key(username) Session().add(new) Session().commit() response = self.app.post(url(controller='login', action='password_reset'), {'email': email, }) self.checkSessionFlash(response, 'Your password reset link was sent') response = response.follow() # BAD KEY key = "bad" response = self.app.get(url(controller='login', action='password_reset_confirmation', key=key)) self.assertEqual(response.status, '302 Found') self.assertTrue(response.location.endswith(url('reset_password'))) # GOOD KEY key = User.get_by_username(username).api_key response = self.app.get(url(controller='login', action='password_reset_confirmation', key=key)) self.assertEqual(response.status, '302 Found') self.assertTrue(response.location.endswith(url('login_home'))) self.checkSessionFlash(response, ('Your password reset was successful, ' 'new password has been sent to your email')) response = response.follow()
def validate_python(self, value, state): if value in ['default', 'new_user']: msg = M(self, 'system_invalid_username', state, username=value) raise formencode.Invalid(msg, value, state) #check if user is unique old_un = None if edit: old_un = User.get(old_data.get('user_id')).username if old_un != value or not edit: if User.get_by_username(value, case_insensitive=True): msg = M(self, 'username_exists', state, username=value) raise formencode.Invalid(msg, value, state) if re.match(r'^[a-zA-Z0-9\_]{1}[a-zA-Z0-9\-\_\.]*$', value) is None: msg = M(self, 'invalid_username', state) raise formencode.Invalid(msg, value, state)
def auto_clear_ip_permissions(): """Fixture that provides nothing but clearing IP permissions upon test exit. This clearing is needed to avoid other test failing to make fake http accesses.""" yield # cleanup user_model = UserModel() user_ids = [] user_ids.append(User.get_default_user().user_id) user_ids.append(User.get_by_username(TEST_USER_REGULAR_LOGIN).user_id) for user_id in user_ids: for ip in UserIpMap.query().filter(UserIpMap.user_id == user_id): user_model.delete_extra_ip(user_id, ip.ip_id) # IP permissions are cached, need to invalidate this cache explicitly invalidate_all_caches()
def test_my_account_update(self, name, attrs): usr = fixture.create_user(self.test_user_1, password='******', email='*****@*****.**', extern_type='internal', extern_name=self.test_user_1, skip_if_exists=True) params = usr.get_api_data(True) # current user data user_id = usr.user_id self.log_user(username=self.test_user_1, password='******') params.update({'password_confirmation': ''}) params.update({'new_password': ''}) params.update({'extern_type': 'internal'}) params.update({'extern_name': self.test_user_1}) params.update(attrs) response = self.app.post(url('my_account'), params) self.checkSessionFlash(response, 'Your account was updated successfully') updated_user = User.get_by_username(self.test_user_1) updated_params = updated_user.get_api_data(True) updated_params.update({'password_confirmation': ''}) updated_params.update({'new_password': ''}) params['last_login'] = updated_params['last_login'] if name == 'email': params['emails'] = [attrs['email']] if name == 'extern_type': #cannot update this via form, expected value is original one params['extern_type'] = "internal" if name == 'extern_name': #cannot update this via form, expected value is original one params['extern_name'] = str(user_id) if name == 'active': #my account cannot deactivate account params['active'] = True if name == 'admin': #my account cannot make you an admin ! params['admin'] = False self.assertEqual(params, updated_params)
def test_remove_api_key(self): self.log_user() user = User.get_by_username(TEST_USER_REGULAR_LOGIN) user_id = user.user_id response = self.app.post(url('edit_user_api_keys', id=user_id), {'_method': 'put', 'description': 'desc', 'lifetime': -1}) self.checkSessionFlash(response, 'Api key successfully created') response = response.follow() #now delete our key keys = UserApiKeys.query().filter(UserApiKeys.user_id == user_id).all() self.assertEqual(1, len(keys)) response = self.app.post(url('edit_user_api_keys', id=user_id), {'_method': 'delete', 'del_api_key': keys[0].api_key}) self.checkSessionFlash(response, 'Api key successfully deleted') keys = UserApiKeys.query().filter(UserApiKeys.user_id == user_id).all() self.assertEqual(0, len(keys))
def create(self, created_by, org_repo, org_ref, other_repo, other_ref, revisions, reviewers, title, description=None): from kallithea.model.changeset_status import ChangesetStatusModel created_by_user = self._get_user(created_by) org_repo = self._get_repo(org_repo) other_repo = self._get_repo(other_repo) new = PullRequest() new.org_repo = org_repo new.org_ref = org_ref new.other_repo = other_repo new.other_ref = other_ref new.revisions = revisions new.title = title new.description = description new.author = created_by_user Session().add(new) Session().flush() #reset state to under-review from kallithea.model.comment import ChangesetCommentsModel comment = ChangesetCommentsModel().create( text=u'Auto status change to %s' % (ChangesetStatus.get_status_lbl(ChangesetStatus.STATUS_UNDER_REVIEW)), repo=org_repo, user=new.author, pull_request=new, send_email=False ) ChangesetStatusModel().set_status( org_repo, ChangesetStatus.STATUS_UNDER_REVIEW, new.author, comment, pull_request=new ) mention_recipients = set(User.get_by_username(username, case_insensitive=True) for username in extract_mentioned_users(new.description)) self.__add_reviewers(new, reviewers, mention_recipients) return new
def get_user(self, username=None, **kwargs): """ Helper method for user fetching in plugins, by default it's using simple fetch by username, but this method can be customized in plugins eg. container auth plugin to fetch user by environ params :param username: username if given to fetch from database :param kwargs: extra arguments needed for user fetching. """ user = None log.debug('Trying to fetch user `%s` from Kallithea database', username) if username: user = User.get_by_username_or_email(username) if user is None: log.debug('Fallback to fetch user in case insensitive mode') user = User.get_by_username(username, case_insensitive=True) else: log.debug('provided username:`%s` is empty skipping...', username) return user
def reset_permissions(self, username): """ Resets permissions to default state, useful when old systems had bad permissions, we must clean them up :param username: """ default_user = User.get_by_username(username) if not default_user: return u2p = UserToPerm.query() \ .filter(UserToPerm.user == default_user).all() fixed = False if len(u2p) != len(Permission.DEFAULT_USER_PERMISSIONS): for p in u2p: Session().delete(p) fixed = True self.populate_default_permissions() return fixed
def _determine_auth_user(session_authuser, ip_addr): """ Create an `AuthUser` object given the API key/bearer token (if any) and the value of the authuser session cookie. Returns None if no valid user is found (like not active or no access for IP). """ # Authenticate by session cookie # In ancient login sessions, 'authuser' may not be a dict. # In that case, the user will have to log in again. # v0.3 and earlier included an 'is_authenticated' key; if present, # this must be True. if isinstance(session_authuser, dict) and session_authuser.get( 'is_authenticated', True): return AuthUser.from_cookie(session_authuser, ip_addr=ip_addr) # Authenticate by auth_container plugin (if enabled) if any(plugin.is_container_auth for plugin in auth_modules.get_auth_plugins()): try: user_info = auth_modules.authenticate('', '', request.environ) except UserCreationError as e: from kallithea.lib import helpers as h h.flash(e, 'error', logf=log.error) else: if user_info is not None: username = user_info['username'] user = User.get_by_username(username, case_insensitive=True) return log_in_user(user, remember=False, is_external_auth=True, ip_addr=ip_addr) # User is default user (if active) or anonymous default_user = User.get_default_user() authuser = AuthUser.make(dbuser=default_user, ip_addr=ip_addr) if authuser is None: # fall back to anonymous authuser = AuthUser( dbuser=default_user) # TODO: somehow use .make? return authuser
def test_delete_ip(self): self.log_user() user = User.get_by_username(TEST_USER_REGULAR_LOGIN) user_id = user.user_id ip = '127.0.0.1/32' ip_range = '127.0.0.1 - 127.0.0.1' new_ip = UserModel().add_extra_ip(user_id, ip) Session().commit() new_ip_id = new_ip.ip_id response = self.app.get(url('edit_user_ips', id=user_id)) response.mustcontain(ip) response.mustcontain(ip_range) self.app.post(url('edit_user_ips', id=user_id), params=dict(_method='delete', del_ip_id=new_ip_id)) response = self.app.get(url('edit_user_ips', id=user_id)) response.mustcontain('All IP addresses are allowed') response.mustcontain(no=[ip]) response.mustcontain(no=[ip_range])
def test_push_on_locked_repo_by_other_user_hg(self): #clone some temp DEST = _get_tmp_dir() clone_url = _construct_url(HG_REPO, dest=DEST) stdout, stderr = Command('/tmp').execute('hg clone', clone_url) #lock repo r = Repository.get_by_repo_name(HG_REPO) # let this user actually push ! RepoModel().grant_user_permission(repo=r, user=TEST_USER_REGULAR_LOGIN, perm='repository.write') Session().commit() Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id) #push fails repo is locked by other user ! stdout, stderr = _add_files_and_push('hg', DEST, user=TEST_USER_REGULAR_LOGIN, passwd=TEST_USER_REGULAR_PASS) msg = ("""abort: HTTP Error 423: Repository `%s` locked by user `%s`""" % (HG_REPO, TEST_USER_ADMIN_LOGIN)) assert msg in stderr
def test_create_and_remove(self): usr = UserModel().create_or_update(username=u'test_user', password=u'qweqwe', email=u'*****@*****.**', firstname=u'u1', lastname=u'u1') Session().commit() self.assertEqual(User.get_by_username(u'test_user'), usr) # make user group user_group = fixture.create_user_group('some_example_group') Session().commit() UserGroupModel().add_user_to_group(user_group, usr) Session().commit() self.assertEqual(UserGroup.get(user_group.users_group_id), user_group) self.assertEqual(UserGroupMember.query().count(), 1) UserModel().delete(usr.user_id) Session().commit() self.assertEqual(UserGroupMember.query().all(), [])
def log_pull_action(ui, repo, **kwargs): """Logs user last pull action Called as Mercurial hook outgoing.pull_logger or from Kallithea before invoking Git. Does *not* use the action from the hook environment but is always 'pull'. """ ex = get_hook_environment() user = User.get_by_username(ex.username) action = 'pull' action_logger(user, action, ex.repository, ex.ip, commit=True) # extension hook call from kallithea import EXTENSIONS callback = getattr(EXTENSIONS, 'PULL_HOOK', None) if callable(callback): kw = {} kw.update(ex) callback(**kw) return 0
def test_add_api_keys(self, desc, lifetime): self.log_user() user = User.get_by_username(base.TEST_USER_REGULAR_LOGIN) user_id = user.user_id response = self.app.post( base.url('edit_user_api_keys_update', id=user_id), { 'description': desc, 'lifetime': lifetime, '_session_csrf_secret_token': self.session_csrf_secret_token() }) self.checkSessionFlash(response, 'API key successfully created') try: response = response.follow() user = User.get(user_id) for api_key in user.api_keys: response.mustcontain(api_key) finally: for api_key in UserApiKeys.query().filter( UserApiKeys.user_id == user_id).all(): Session().delete(api_key) Session().commit()
def test_send_mail_with_author(self): mailserver = 'smtp.mailserver.org' recipients = ['rcpt1', 'rcpt2'] envelope_from = '*****@*****.**' subject = 'subject' body = 'body' html_body = 'html_body' author = User.get_by_username(base.TEST_USER_REGULAR_LOGIN) config_mock = { 'smtp_server': mailserver, 'app_email_from': envelope_from, } with mock.patch('kallithea.lib.celerylib.tasks.config', config_mock): kallithea.lib.celerylib.tasks.send_email(recipients, subject, body, html_body, from_name=author.full_name_or_username) assert smtplib_mock.lastdest == set(recipients) assert smtplib_mock.lastsender == envelope_from assert 'From: "Kallithea Admin (no-reply)" <%s>' % envelope_from in smtplib_mock.lastmsg assert 'Subject: %s' % subject in smtplib_mock.lastmsg assert body in smtplib_mock.lastmsg assert html_body in smtplib_mock.lastmsg
def test_add_ip(self, test_name, ip, ip_range, failure): self.log_user() user = User.get_by_username(TEST_USER_REGULAR_LOGIN) user_id = user.user_id response = self.app.put(url('edit_user_ips', id=user_id), params=dict(new_ip=ip)) if failure: self.checkSessionFlash(response, 'Please enter a valid IPv4 or IpV6 address') response = self.app.get(url('edit_user_ips', id=user_id)) response.mustcontain(no=[ip]) response.mustcontain(no=[ip_range]) else: response = self.app.get(url('edit_user_ips', id=user_id)) response.mustcontain(ip) response.mustcontain(ip_range) ## cleanup for del_ip in UserIpMap.query().filter(UserIpMap.user_id == user_id).all(): Session().delete(del_ip) Session().commit()
def test_push_unlocks_repository_hg(self): # enable locking r = Repository.get_by_repo_name(HG_REPO) r.enable_locking = True Session().add(r) Session().commit() #clone some temp DEST = _get_tmp_dir() clone_url = _construct_url(HG_REPO, dest=DEST) stdout, stderr = Command('/tmp').execute('hg clone', clone_url) #check for lock repo after clone r = Repository.get_by_repo_name(HG_REPO) uid = User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id assert r.locked[0] == uid #push is ok and repo is now unlocked stdout, stderr = _add_files_and_push('hg', DEST) assert ('remote: Released lock on repo `%s`' % HG_REPO) in stdout #we need to cleanup the Session Here ! Session.remove() r = Repository.get_by_repo_name(HG_REPO) assert r.locked == [None, None]
def test_add_ip(self, test_name, ip, ip_range, failure, auto_clear_ip_permissions): self.log_user() user = User.get_by_username(base.TEST_USER_REGULAR_LOGIN) user_id = user.user_id response = self.app.post( base.url('edit_user_ips_update', id=user_id), params=dict( new_ip=ip, _session_csrf_secret_token=self.session_csrf_secret_token())) if failure: self.checkSessionFlash( response, 'Please enter a valid IPv4 or IPv6 address') response = self.app.get(base.url('edit_user_ips', id=user_id)) response.mustcontain(no=[ip]) response.mustcontain(no=[ip_range]) else: response = self.app.get(base.url('edit_user_ips', id=user_id)) response.mustcontain(ip) response.mustcontain(ip_range)
def test_update(self, name, attrs): self.log_user() usr = fixture.create_user(self.test_user_1, password='******', email='*****@*****.**', extern_type='internal', extern_name=self.test_user_1, skip_if_exists=True) Session().commit() params = usr.get_api_data(True) params.update({'password_confirmation': ''}) params.update({'new_password': ''}) params.update(attrs) if name == 'email': params['emails'] = [attrs['email']] if name == 'extern_type': # cannot update this via form, expected value is original one params['extern_type'] = "internal" if name == 'extern_name': # cannot update this via form, expected value is original one params['extern_name'] = self.test_user_1 # special case since this user is not logged in yet his data is # not filled so we use creation data params.update( {'_session_csrf_secret_token': self.session_csrf_secret_token()}) response = self.app.post(base.url('update_user', id=usr.user_id), params) self.checkSessionFlash(response, 'User updated successfully') params.pop('_session_csrf_secret_token') updated_user = User.get_by_username(self.test_user_1) updated_params = updated_user.get_api_data(True) updated_params.update({'password_confirmation': ''}) updated_params.update({'new_password': ''}) assert params == updated_params
def test_update_user_attributes_from_ldap(monkeypatch, create_test_user, arrange_ldap_auth): """Authenticate user with mocked LDAP, verify attributes are updated. """ # Arrange test user. uniqifier = uuid.uuid4() username = '******'.format(uniqifier) assert User.get_by_username(username) is None user_input = dict(username='******'.format(uniqifier), password='******', email='spam-email-{0}'.format(uniqifier), firstname=u'spam first name', lastname=u'spam last name', active=True, admin=False) user = create_test_user(user_input) # Arrange LDAP auth. monkeypatch.setattr(auth_ldap, 'AuthLdap', _AuthLdapMock) # Authenticate with LDAP. user_data = authenticate(username, 'password') # Verify that authenication succeeded and retrieved correct attributes # from LDAP. assert user_data is not None assert user_data.get('firstname') == u'spam ldap first name' assert user_data.get('lastname') == u'spam ldap last name' assert user_data.get('email') == 'spam ldap email' # Verify that authentication overwrote user attributes with the ones # retrieved from LDAP. assert user.firstname == u'spam ldap first name' assert user.lastname == u'spam ldap last name' assert user.email == 'spam ldap email'
def validate_python(self, value, state): from kallithea.lib import auth_modules password = value['password'] username = value['username'] if not auth_modules.authenticate(username, password): user = User.get_by_username(username) if user and not user.active: log.warning('user %s is disabled' % username) msg = M(self, 'disabled_account', state) raise formencode.Invalid(msg, value, state, error_dict=dict(username=msg)) else: log.warning('user %s failed to authenticate' % username) msg = M(self, 'invalid_username', state) msg2 = M(self, 'invalid_password', state) raise formencode.Invalid(msg, value, state, error_dict=dict(username=msg, password=msg2))
def test_push_on_locked_repo_by_other_user_git(self, webserver): # Note: Git hooks must be executable on unix. This test will thus fail # for example on Linux if /tmp is mounted noexec. #clone some temp DEST = _get_tmp_dir() clone_url = webserver.repo_url(GIT_REPO) stdout, stderr = Command(TESTS_TMP_PATH).execute( 'git clone', clone_url, DEST) #lock repo r = Repository.get_by_repo_name(GIT_REPO) # let this user actually push ! RepoModel().grant_user_permission(repo=r, user=TEST_USER_REGULAR_LOGIN, perm='repository.write') Session().commit() Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id) #push fails repo is locked by other user ! stdout, stderr = _add_files_and_push(webserver, 'git', DEST, username=TEST_USER_REGULAR_LOGIN, password=TEST_USER_REGULAR_PASS, ignoreReturnCode=True) err = 'Repository `%s` locked by user `%s`' % (GIT_REPO, TEST_USER_ADMIN_LOGIN) assert err in stderr #TODO: fix this somehow later on Git, Git is stupid and even if we throw #back 423 to it, it makes ANOTHER request and we fail there with 405 :/ msg = ( """abort: HTTP Error 423: Repository `%s` locked by user `%s`""" % (GIT_REPO, TEST_USER_ADMIN_LOGIN))
def update(self, repo, **kwargs): try: cur_repo = Repository.guess_instance(repo) org_repo_name = cur_repo.repo_name if 'owner' in kwargs: cur_repo.owner = User.get_by_username(kwargs['owner']) if 'repo_group' in kwargs: assert kwargs[ 'repo_group'] != '-1', kwargs # RepoForm should have converted to None cur_repo.group = RepoGroup.get(kwargs['repo_group']) cur_repo.repo_name = cur_repo.get_new_name(cur_repo.just_name) log.debug('Updating repo %s with params:%s', cur_repo, kwargs) for k in [ 'repo_enable_downloads', 'repo_description', 'repo_landing_rev', 'repo_private', 'repo_enable_statistics', ]: if k in kwargs: setattr(cur_repo, remove_prefix(k, 'repo_'), kwargs[k]) clone_uri = kwargs.get('clone_uri') if clone_uri is not None and clone_uri != cur_repo.clone_uri_hidden: # clone_uri is modified - if given a value, check it is valid if clone_uri != '': # will raise exception on error is_valid_repo_uri(cur_repo.repo_type, clone_uri, make_ui()) cur_repo.clone_uri = clone_uri if 'repo_name' in kwargs: repo_name = kwargs['repo_name'] if kallithea.lib.utils2.repo_name_slug(repo_name) != repo_name: raise Exception('invalid repo name %s' % repo_name) cur_repo.repo_name = cur_repo.get_new_name(repo_name) # if private flag is set, reset default permission to NONE if kwargs.get('repo_private'): EMPTY_PERM = 'repository.none' RepoModel().grant_user_permission(repo=cur_repo, user='******', perm=EMPTY_PERM) # handle extra fields for field in [ k for k in kwargs if k.startswith(RepositoryField.PREFIX) ]: k = RepositoryField.un_prefix_key(field) ex_field = RepositoryField.get_by_key_name(key=k, repo=cur_repo) if ex_field: ex_field.field_value = kwargs[field] if org_repo_name != cur_repo.repo_name: # rename repository self._rename_filesystem_repo(old=org_repo_name, new=cur_repo.repo_name) return cur_repo except Exception: log.error(traceback.format_exc()) raise
def _cached_perms_data(user_id, user_is_admin, user_inherit_default_permissions, explicit, algo): RK = 'repositories' GK = 'repositories_groups' UK = 'user_groups' GLOBAL = 'global' PERM_WEIGHTS = Permission.PERM_WEIGHTS permissions = {RK: {}, GK: {}, UK: {}, GLOBAL: set()} def _choose_perm(new_perm, cur_perm): new_perm_val = PERM_WEIGHTS[new_perm] cur_perm_val = PERM_WEIGHTS[cur_perm] if algo == 'higherwin': if new_perm_val > cur_perm_val: return new_perm return cur_perm elif algo == 'lowerwin': if new_perm_val < cur_perm_val: return new_perm return cur_perm #====================================================================== # fetch default permissions #====================================================================== default_user = User.get_by_username('default', cache=True) default_user_id = default_user.user_id default_repo_perms = Permission.get_default_perms(default_user_id) default_repo_groups_perms = Permission.get_default_group_perms( default_user_id) default_user_group_perms = Permission.get_default_user_group_perms( default_user_id) if user_is_admin: #================================================================== # admin user have all default rights for repositories # and groups set to admin #================================================================== permissions[GLOBAL].add('hg.admin') permissions[GLOBAL].add('hg.create.write_on_repogroup.true') # repositories for perm in default_repo_perms: r_k = perm.UserRepoToPerm.repository.repo_name p = 'repository.admin' permissions[RK][r_k] = p # repository groups for perm in default_repo_groups_perms: rg_k = perm.UserRepoGroupToPerm.group.group_name p = 'group.admin' permissions[GK][rg_k] = p # user groups for perm in default_user_group_perms: u_k = perm.UserUserGroupToPerm.user_group.users_group_name p = 'usergroup.admin' permissions[UK][u_k] = p return permissions #================================================================== # SET DEFAULTS GLOBAL, REPOS, REPOSITORY GROUPS #================================================================== uid = user_id # default global permissions taken from the default user default_global_perms = UserToPerm.query()\ .filter(UserToPerm.user_id == default_user_id)\ .options(joinedload(UserToPerm.permission)) for perm in default_global_perms: permissions[GLOBAL].add(perm.permission.permission_name) # defaults for repositories, taken from default user for perm in default_repo_perms: r_k = perm.UserRepoToPerm.repository.repo_name if perm.Repository.private and not (perm.Repository.user_id == uid): # disable defaults for private repos, p = 'repository.none' elif perm.Repository.user_id == uid: # set admin if owner p = 'repository.admin' else: p = perm.Permission.permission_name permissions[RK][r_k] = p # defaults for repository groups taken from default user permission # on given group for perm in default_repo_groups_perms: rg_k = perm.UserRepoGroupToPerm.group.group_name p = perm.Permission.permission_name permissions[GK][rg_k] = p # defaults for user groups taken from default user permission # on given user group for perm in default_user_group_perms: u_k = perm.UserUserGroupToPerm.user_group.users_group_name p = perm.Permission.permission_name permissions[UK][u_k] = p #====================================================================== # !! OVERRIDE GLOBALS !! with user permissions if any found #====================================================================== # those can be configured from groups or users explicitly _configurable = set([ 'hg.fork.none', 'hg.fork.repository', 'hg.create.none', 'hg.create.repository', 'hg.usergroup.create.false', 'hg.usergroup.create.true' ]) # USER GROUPS comes first # user group global permissions user_perms_from_users_groups = Session().query(UserGroupToPerm)\ .options(joinedload(UserGroupToPerm.permission))\ .join((UserGroupMember, UserGroupToPerm.users_group_id == UserGroupMember.users_group_id))\ .filter(UserGroupMember.user_id == uid)\ .order_by(UserGroupToPerm.users_group_id)\ .all() # need to group here by groups since user can be in more than # one group _grouped = [[x, list(y)] for x, y in itertools.groupby( user_perms_from_users_groups, lambda x: x.users_group)] for gr, perms in _grouped: # since user can be in multiple groups iterate over them and # select the lowest permissions first (more explicit) ##TODO: do this^^ if not gr.inherit_default_permissions: # NEED TO IGNORE all configurable permissions and # replace them with explicitly set permissions[GLOBAL] = permissions[GLOBAL]\ .difference(_configurable) for perm in perms: permissions[GLOBAL].add(perm.permission.permission_name) # user specific global permissions user_perms = Session().query(UserToPerm)\ .options(joinedload(UserToPerm.permission))\ .filter(UserToPerm.user_id == uid).all() if not user_inherit_default_permissions: # NEED TO IGNORE all configurable permissions and # replace them with explicitly set permissions[GLOBAL] = permissions[GLOBAL]\ .difference(_configurable) for perm in user_perms: permissions[GLOBAL].add(perm.permission.permission_name) ## END GLOBAL PERMISSIONS #====================================================================== # !! PERMISSIONS FOR REPOSITORIES !! #====================================================================== #====================================================================== # check if user is part of user groups for this repository and # fill in his permission from it. _choose_perm decides of which # permission should be selected based on selected method #====================================================================== # user group for repositories permissions user_repo_perms_from_users_groups = \ Session().query(UserGroupRepoToPerm, Permission, Repository,)\ .join((Repository, UserGroupRepoToPerm.repository_id == Repository.repo_id))\ .join((Permission, UserGroupRepoToPerm.permission_id == Permission.permission_id))\ .join((UserGroupMember, UserGroupRepoToPerm.users_group_id == UserGroupMember.users_group_id))\ .filter(UserGroupMember.user_id == uid)\ .all() multiple_counter = collections.defaultdict(int) for perm in user_repo_perms_from_users_groups: r_k = perm.UserGroupRepoToPerm.repository.repo_name multiple_counter[r_k] += 1 p = perm.Permission.permission_name cur_perm = permissions[RK][r_k] if perm.Repository.user_id == uid: # set admin if owner p = 'repository.admin' else: if multiple_counter[r_k] > 1: p = _choose_perm(p, cur_perm) permissions[RK][r_k] = p # user explicit permissions for repositories, overrides any specified # by the group permission user_repo_perms = Permission.get_default_perms(uid) for perm in user_repo_perms: r_k = perm.UserRepoToPerm.repository.repo_name cur_perm = permissions[RK][r_k] # set admin if owner if perm.Repository.user_id == uid: p = 'repository.admin' else: p = perm.Permission.permission_name if not explicit: p = _choose_perm(p, cur_perm) permissions[RK][r_k] = p #====================================================================== # !! PERMISSIONS FOR REPOSITORY GROUPS !! #====================================================================== #====================================================================== # check if user is part of user groups for this repository groups and # fill in his permission from it. _choose_perm decides of which # permission should be selected based on selected method #====================================================================== # user group for repo groups permissions user_repo_group_perms_from_users_groups = \ Session().query(UserGroupRepoGroupToPerm, Permission, RepoGroup)\ .join((RepoGroup, UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\ .join((Permission, UserGroupRepoGroupToPerm.permission_id == Permission.permission_id))\ .join((UserGroupMember, UserGroupRepoGroupToPerm.users_group_id == UserGroupMember.users_group_id))\ .filter(UserGroupMember.user_id == uid)\ .all() multiple_counter = collections.defaultdict(int) for perm in user_repo_group_perms_from_users_groups: g_k = perm.UserGroupRepoGroupToPerm.group.group_name multiple_counter[g_k] += 1 p = perm.Permission.permission_name cur_perm = permissions[GK][g_k] if multiple_counter[g_k] > 1: p = _choose_perm(p, cur_perm) permissions[GK][g_k] = p # user explicit permissions for repository groups user_repo_groups_perms = Permission.get_default_group_perms(uid) for perm in user_repo_groups_perms: rg_k = perm.UserRepoGroupToPerm.group.group_name p = perm.Permission.permission_name cur_perm = permissions[GK][rg_k] if not explicit: p = _choose_perm(p, cur_perm) permissions[GK][rg_k] = p #====================================================================== # !! PERMISSIONS FOR USER GROUPS !! #====================================================================== # user group for user group permissions user_group_user_groups_perms = \ Session().query(UserGroupUserGroupToPerm, Permission, UserGroup)\ .join((UserGroup, UserGroupUserGroupToPerm.target_user_group_id == UserGroup.users_group_id))\ .join((Permission, UserGroupUserGroupToPerm.permission_id == Permission.permission_id))\ .join((UserGroupMember, UserGroupUserGroupToPerm.user_group_id == UserGroupMember.users_group_id))\ .filter(UserGroupMember.user_id == uid)\ .all() multiple_counter = collections.defaultdict(int) for perm in user_group_user_groups_perms: g_k = perm.UserGroupUserGroupToPerm.target_user_group.users_group_name multiple_counter[g_k] += 1 p = perm.Permission.permission_name cur_perm = permissions[UK][g_k] if multiple_counter[g_k] > 1: p = _choose_perm(p, cur_perm) permissions[UK][g_k] = p #user explicit permission for user groups user_user_groups_perms = Permission.get_default_user_group_perms(uid) for perm in user_user_groups_perms: u_k = perm.UserUserGroupToPerm.user_group.users_group_name p = perm.Permission.permission_name cur_perm = permissions[UK][u_k] if not explicit: p = _choose_perm(p, cur_perm) permissions[UK][u_k] = p return permissions
def test_forgot_password(self): response = self.app.get(base.url(controller='login', action='password_reset')) assert response.status == '200 OK' username = '******' password = '******' email = '*****@*****.**' name = 'passwd' lastname = 'reset' timestamp = int(time.time()) new = User() new.username = username new.password = password new.email = email new.name = name new.lastname = lastname new.api_key = generate_api_key() Session().add(new) Session().commit() token = UserModel().get_reset_password_token( User.get_by_username(username), timestamp, self.session_csrf_secret_token()) collected = [] def mock_send_email(recipients, subject, body='', html_body='', headers=None, from_name=None): collected.append((recipients, subject, body, html_body)) with mock.patch.object(kallithea.lib.celerylib.tasks, 'send_email', mock_send_email), \ mock.patch.object(time, 'time', lambda: timestamp): response = self.app.post(base.url(controller='login', action='password_reset'), {'email': email, '_session_csrf_secret_token': self.session_csrf_secret_token()}) self.checkSessionFlash(response, 'A password reset confirmation code has been sent') ((recipients, subject, body, html_body),) = collected assert recipients == ['*****@*****.**'] assert subject == 'Password reset link' assert '\n%s\n' % token in body (confirmation_url,) = (line for line in body.splitlines() if line.startswith('http://')) assert ' href="%s"' % confirmation_url.replace('&', '&').replace('@', '%40') in html_body d = urllib.parse.parse_qs(urllib.parse.urlparse(confirmation_url).query) assert d['token'] == [token] assert d['timestamp'] == [str(timestamp)] assert d['email'] == [email] response = response.follow() # BAD TOKEN bad_token = "bad" response = self.app.post(base.url(controller='login', action='password_reset_confirmation'), {'email': email, 'timestamp': timestamp, 'password': "******", 'password_confirm': "p@ssw0rd", 'token': bad_token, '_session_csrf_secret_token': self.session_csrf_secret_token(), }) assert response.status == '200 OK' response.mustcontain('Invalid password reset token') # GOOD TOKEN response = self.app.get(confirmation_url) assert response.status == '200 OK' response.mustcontain("You are about to set a new password for the email address %s" % email) response.mustcontain('<form action="%s" method="post">' % base.url(controller='login', action='password_reset_confirmation')) response.mustcontain('value="%s"' % self.session_csrf_secret_token()) response.mustcontain('value="%s"' % token) response.mustcontain('value="%s"' % timestamp) response.mustcontain('value="*****@*****.**"') # fake a submit of that form response = self.app.post(base.url(controller='login', action='password_reset_confirmation'), {'email': email, 'timestamp': timestamp, 'password': "******", 'password_confirm': "p@ssw0rd", 'token': token, '_session_csrf_secret_token': self.session_csrf_secret_token(), }) assert response.status == '302 Found' self.checkSessionFlash(response, 'Successfully updated password') response = response.follow()
def test_ips(self): self.log_user() user = User.get_by_username(TEST_USER_REGULAR_LOGIN) response = self.app.get(url('edit_user_ips', id=user.user_id)) response.mustcontain('All IP addresses are allowed')
def _get_logged_user(self): return User.get_by_username(self._logged_username)
def update(self, form_result): perm_user = User.get_by_username( username=form_result['perm_user_name']) try: # stage 1 set anonymous access if perm_user.is_default_user: perm_user.active = str2bool(form_result['anonymous']) # stage 2 reset defaults and set them from form data def _make_new(usr, perm_name): log.debug('Creating new permission:%s', perm_name) new = UserToPerm() new.user = usr new.permission = Permission.get_by_key(perm_name) return new # clear current entries, to make this function idempotent # it will fix even if we define more permissions or permissions # are somehow missing u2p = UserToPerm.query() \ .filter(UserToPerm.user == perm_user) \ .all() for p in u2p: Session().delete(p) # create fresh set of permissions for def_perm_key in [ 'default_repo_perm', 'default_group_perm', 'default_user_group_perm', 'default_repo_create', 'create_on_write', # special case for create repos on write access to group #'default_repo_group_create', # not implemented yet 'default_user_group_create', 'default_fork', 'default_register', 'default_extern_activate' ]: p = _make_new(perm_user, form_result[def_perm_key]) Session().add(p) # stage 3 update all default permissions for repos if checked if form_result['overwrite_default_repo']: _def_name = form_result['default_repo_perm'].split( 'repository.')[-1] _def = Permission.get_by_key('repository.' + _def_name) # repos for r2p in UserRepoToPerm.query() \ .filter(UserRepoToPerm.user == perm_user) \ .all(): # don't reset PRIVATE repositories if not r2p.repository.private: r2p.permission = _def if form_result['overwrite_default_group']: _def_name = form_result['default_group_perm'].split( 'group.')[-1] # groups _def = Permission.get_by_key('group.' + _def_name) for g2p in UserRepoGroupToPerm.query() \ .filter(UserRepoGroupToPerm.user == perm_user) \ .all(): g2p.permission = _def if form_result['overwrite_default_user_group']: _def_name = form_result['default_user_group_perm'].split( 'usergroup.')[-1] # groups _def = Permission.get_by_key('usergroup.' + _def_name) for g2p in UserUserGroupToPerm.query() \ .filter(UserUserGroupToPerm.user == perm_user) \ .all(): g2p.permission = _def Session().commit() except (DatabaseError, ): log.error(traceback.format_exc()) Session().rollback() raise
def teardown_class(cls): if User.get_by_username(cls.test_user_1): UserModel().delete(cls.test_user_1) Session().commit()
def create_or_update(self, username, password, email, firstname='', lastname='', active=True, admin=False, extern_type=None, extern_name=None, cur_user=None): """ Creates a new instance if not found, or updates current one :param username: :param password: :param email: :param active: :param firstname: :param lastname: :param active: :param admin: :param extern_name: :param extern_type: :param cur_user: """ if not cur_user: cur_user = getattr(get_current_authuser(), 'username', None) from kallithea.lib.auth import get_crypt_password, check_password from kallithea.lib.hooks import log_create_user, \ check_allowed_create_user user_data = { 'username': username, 'password': password, 'email': email, 'firstname': firstname, 'lastname': lastname, 'active': active, 'admin': admin } # raises UserCreationError if it's not allowed check_allowed_create_user(user_data, cur_user) log.debug('Checking for %s account in Kallithea database', username) user = User.get_by_username(username, case_insensitive=True) if user is None: log.debug('creating new user %s', username) new_user = User() edit = False else: log.debug('updating user %s', username) new_user = user edit = True try: new_user.username = username new_user.admin = admin new_user.email = email new_user.active = active new_user.extern_name = extern_name new_user.extern_type = extern_type new_user.name = firstname new_user.lastname = lastname if not edit: new_user.api_key = generate_api_key() # set password only if creating an user or password is changed password_change = new_user.password and \ not check_password(password, new_user.password) if not edit or password_change: reason = 'new password' if edit else 'new user' log.debug('Updating password reason=>%s', reason) new_user.password = get_crypt_password(password) \ if password else '' if user is None: Session().add(new_user) Session().flush() # make database assign new_user.user_id if not edit: log_create_user(new_user.get_dict(), cur_user) return new_user except (DatabaseError,): log.error(traceback.format_exc()) raise
def test_edit(self): self.log_user() user = User.get_by_username(TEST_USER_ADMIN_LOGIN) response = self.app.get(url('edit_user', id=user.user_id))