.. _changelog:
Changelog
=========
1.3.2 (**2012-XX-XX**)
----------------------
:status: in-progress
:branch: beta
news
++++
fixes
+++++
- fixed git protocol issues with repos-groups
- fixed git remote repos validator that prevented from cloning remote git repos
- fixes #370 ending slashes fixes for repo and groups
- fixes #368 improved git-protocol detection to handle other clients
- fixes #366 When Setting Repository Group To Blank Repo Group Wont Be
Moved To Root
1.3.1 (**2012-02-27**)
- redirection loop occurs when remember-me wasn't checked during login
- fixes issues with git blob history generation
- don't fetch branch for git in file history dropdown. Causes unneeded slowness
1.3.0 (**2012-02-26**)
- code review, inspired by github code-comments
- #215 rst and markdown README files support
- #252 Container-based and proxy pass-through authentication support
- #44 branch browser. Filtering of changelog by branches
- mercurial bookmarks support
- new hover top menu, optimized to add maximum size for important views
- configurable clone url template with possibility to specify protocol like
ssh:// or http:// and also manually alter other parts of clone_url.
- enabled largefiles extension by default
- optimized summary file pages and saved a lot of unused space in them
- #239 option to manually mark repository as fork
- #320 mapping of commit authors to RhodeCode users
- #304 hashes are displayed using monospace font
- diff configuration, toggle white lines and context lines
- #307 configurable diffs, whitespace toggle, increasing context lines
- sorting on branches, tags and bookmarks using YUI datatable
- improved file filter on files page
- implements #330 api method for listing nodes ar particular revision
- #73 added linking issues in commit messages to chosen issue tracker url
based on user defined regular expression
- added linking of changesets in commit messages
- new compact changelog with expandable commit messages
- firstname and lastname are optional in user creation
- #348 added post-create repository hook
- #212 global encoding settings is now configurable from .ini files
- #227 added repository groups permissions
- markdown gets codehilite extensions
- new API methods, delete_repositories, grante/revoke permissions for groups
and repos
- rewrote dbsession management for atomic operations, and better error handling
- fixed sorting of repo tables
- #326 escape of special html entities in diffs
- normalized user_name => username in api attributes
- fixes #298 ldap created users with mixed case emails created conflicts
on saving a form
- fixes issue when owner of a repo couldn't revoke permissions for users
and groups
- fixes #271 rare JSON serialization problem with statistics
- fixes #337 missing validation check for conflicting names of a group with a
repositories group
- #340 fixed session problem for mysql and celery tasks
- fixed #331 RhodeCode mangles repository names if the a repository group
contains the "full path" to the repositories
- #355 RhodeCode doesn't store encrypted LDAP passwords
1.2.5 (**2012-01-28**)
- #340 Celery complains about MySQL server gone away, added session cleanup
for celery tasks
- #341 "scanning for repositories in None" log message during Rescan was missing
a parameter
- fixed creating archives with subrepos. Some hooks were triggered during that
operation leading to crash.
- fixed missing email in account page.
- Reverted Mercurial to 2.0.1 for windows due to bug in Mercurial that makes
forking on windows impossible
1.2.4 (**2012-01-19**)
- RhodeCode is bundled with mercurial series 2.0.X by default, with
full support to largefiles extension. Enabled by default in new installations
- #329 Ability to Add/Remove Groups to/from a Repository via AP
- added requires.txt file with requirements
- fixes db session issues with celery when emailing admins
- #331 RhodeCode mangles repository names if the a repository group
- #298 Conflicting e-mail addresses for LDAP and RhodeCode users
- DB session cleanup after hg protocol operations, fixes issues with
`mysql has gone away` errors
- #333 doc fixes for get_repo api function
- #271 rare JSON serialization problem with statistics enabled
- #337 Fixes issues with validation of repository name conflicting with
a group name. A proper message is now displayed.
- #292 made ldap_dn in user edit readonly, to get rid of confusion that field
doesn't work
- #316 fixes issues with web description in hgrc files
1.2.3 (**2011-11-02**)
- added option to manage repos group for non admin users
- added following API methods for get_users, create_user, get_users_groups,
get_users_group, create_users_group, add_user_to_users_groups, get_repos,
get_repo, create_repo, add_user_to_repo
- implements #237 added password confirmation for my account
and admin edit user.
- implements #291 email notification for global events are now sent to all
administrator users, and global config email.
- added option for passing auth method for smtp mailer
- #276 issue with adding a single user with id>10 to usergroups
- #277 fixes windows LDAP settings in which missing values breaks the ldap auth
- #288 fixes managing of repos in a group for non admin user
1.2.2 (**2011-10-17**)
- #226 repo groups are available by path instead of numerical id
- #259 Groups with the same name but with different parent group
- #260 Put repo in group, then move group to another group -> repo becomes unavailable
- #258 RhodeCode 1.2 assumes egg folder is writable (lockfiles problems)
- #265 ldap save fails sometimes on converting attributes to booleans,
added getter and setter into model that will prevent from this on db model level
- fixed problems with timestamps issues #251 and #213
- fixes #266 RhodeCode allows to create repo with the same name and in
the same parent as group
- fixes #245 Rescan of the repositories on Windows
- fixes #248 cannot edit repos inside a group on windows
- fixes #219 forking problems on windows
1.2.1 (**2011-10-08**)
- fixed problems with basic auth and push problems
- gui fixes
- fixed logger
1.2.0 (**2011-10-07**)
- implemented #47 repository groups
- implemented #89 Can setup google analytics code from settings menu
- implemented #91 added nicer looking archive urls with more download options
like tags, branches
- implemented #44 into file browsing, and added follow branch option
- implemented #84 downloads can be enabled/disabled for each repository
- anonymous repository can be cloned without having to pass default:default
into clone url
- fixed #90 whoosh indexer can index chooses repositories passed in command
line
- extended journal with day aggregates and paging
- implemented #107 source code lines highlight ranges
- implemented #93 customizable changelog on combined revision ranges -
equivalent of githubs compare view
- implemented #108 extended and more powerful LDAP configuration
- implemented #56 users groups
- major code rewrites optimized codes for speed and memory usage
- raw and diff downloads are now in git format
- setup command checks for write access to given path
- fixed many issues with international characters and unicode. It uses utf8
decode with replace to provide less errors even with non utf8 encoded strings
- #125 added API KEY access to feeds
- #109 Repository can be created from external Mercurial link (aka. remote
repository, and manually updated (via pull) from admin panel
- beta git support - push/pull server + basic view for git repos
- added followers page and forks page
- server side file creation (with binary file upload interface)
and edition with commits powered by codemirror
- #111 file browser file finder, quick lookup files on whole file tree
- added quick login sliding menu into main page
- changelog uses lazy loading of affected files details, in some scenarios
this can improve speed of changelog page dramatically especially for
larger repositories.
- implements #214 added support for downloading subrepos in download menu.
- Added basic API for direct operations on rhodecode via JSON
- Implemented advanced hook management
- fixed file browser bug, when switching into given form revision the url was
not changing
- fixed propagation to error controller on simplehg and simplegit middlewares
- fixed error when trying to make a download on empty repository
- fixed problem with '[' chars in commit messages in journal
- fixed #99 Unicode errors, on file node paths with non utf-8 characters
- journal fork fixes
- removed issue with space inside renamed repository after deletion
- fixed strange issue on formencode imports
- fixed #126 Deleting repository on Windows, rename used incompatible chars.
- #150 fixes for errors on repositories mapped in db but corrupted in
filesystem
- fixed problem with ascendant characters in realm #181
- fixed problem with sqlite file based database connection pool
- whoosh indexer and code stats share the same dynamic extensions map
- fixes #188 - relationship delete of repo_to_perm entry on user removal
- fixes issue #189 Trending source files shows "show more" when no more exist
- fixes issue #197 Relative paths for pidlocks
- fixes issue #198 password will require only 3 chars now for login form
- fixes issue #199 wrong redirection for non admin users after creating a repository
- fixes issues #202, bad db constraint made impossible to attach same group
more than one time. Affects only mysql/postgres
- fixes #218 os.kill patch for windows was missing sig param
- improved rendering of dag (they are not trimmed anymore when number of
heads exceeds 5)
1.1.8 (**2011-04-12**)
- improved windows support
- fixed #140 freeze of python dateutil library, since new version is python2.x
incompatible
- setup-app will check for write permission in given path
- cleaned up license info issue #149
- fixes for issues #137,#116 and problems with unicode and accented characters.
- fixes crashes on gravatar, when passed in email as unicode
- fixed tooltip flickering problems
- fixed came_from redirection on windows
- fixed logging modules, and sql formatters
- windows fixes for os.kill issue #133
- fixes path splitting for windows issues #148
- fixed issue #143 wrong import on migration to 1.1.X
- fixed problems with displaying binary files, thanks to Thomas Waldmann
- removed name from archive files since it's breaking ui for long repo names
- fixed issue with archive headers sent to browser, thanks to Thomas Waldmann
- fixed compatibility for 1024px displays, and larger dpi settings, thanks to
Thomas Waldmann
- fixed issue #166 summary pager was skipping 10 revisions on second page
1.1.7 (**2011-03-23**)
- fixed (again) #136 installation support for FreeBSD
1.1.6 (**2011-03-21**)
- fixed #136 installation support for FreeBSD
- RhodeCode will check for python version during installation
1.1.5 (**2011-03-17**)
- basic windows support, by exchanging pybcrypt into sha256 for windows only
highly inspired by idea of mantis406
- fixed sorting by author in main page
- fixed crashes with diffs on binary files
- fixed #131 problem with boolean values for LDAP
- fixed #122 mysql problems thanks to striker69
- fixed problem with errors on calling raw/raw_files/annotate functions
with unknown revisions
- fixed returned rawfiles attachment names with international character
- cleaned out docs, big thanks to Jason Harris
1.1.4 (**2011-02-19**)
- fixed formencode import problem on settings page, that caused server crash
when that page was accessed as first after server start
- journal fixes
- fixed option to access repository just by entering http://server/<repo_name>
1.1.3 (**2011-02-16**)
- implemented #102 allowing the '.' character in username
- added option to access repository just by entering http://server/<repo_name>
- celery task ignores result for better performance
- fixed ehlo command and non auth mail servers on smtp_lib. Thanks to
apollo13 and Johan Walles
- small fixes in journal
- fixed problems with getting setting for celery from .ini files
- registration, password reset and login boxes share the same title as main
application now
- fixed #113: to high permissions to fork repository
- db transaction fixes when filesystem repository creation failed
- fixed #106 relation issues on databases different than sqlite
- fixed static files paths links to use of url() method
1.1.2 (**2011-01-12**)
- fixes #98 protection against float division of percentage stats
- fixed graph bug
- forced webhelpers version since it was making troubles during installation
1.1.1 (**2011-01-06**)
- added force https option into ini files for easier https usage (no need to
set server headers with this options)
- small css updates
- fixed #96 redirect loop on files view on repositories without changesets
- fixed #97 unicode string passed into server header in special cases (mod_wsgi)
and server crashed with errors
- fixed large tooltips problems on main page
- fixed #92 whoosh indexer is more error proof
1.1.0 (**2010-12-18**)
- rewrite of internals for vcs >=0.1.10
- uses mercurial 1.7 with dotencode disabled for maintaining compatibility
with older clients
- anonymous access, authentication via ldap
- performance upgrade for cached repos list - each repository has its own
cache that's invalidated when needed.
- performance upgrades on repositories with large amount of commits (20K+)
- main page quick filter for filtering repositories
- user dashboards with ability to follow chosen repositories actions
- sends email to admin on new user registration
- added cache/statistics reset options into repository settings
- more detailed action logger (based on hooks) with pushed changesets lists
and options to disable those hooks from admin panel
- introduced new enhanced changelog for merges that shows more accurate results
- new improved and faster code stats (based on pygments lexers mapping tables,
showing up to 10 trending sources for each repository. Additionally stats
can be disabled in repository settings.
- gui optimizations, fixed application width to 1024px
- added cut off (for large files/changesets) limit into config files
- whoosh, celeryd, upgrade moved to paster command
- other than sqlite database backends can be used
- fixes #61 forked repo was showing only after cache expired
- fixes #76 no confirmation on user deletes
- fixes #66 Name field misspelled
- fixes #72 block user removal when he owns repositories
- fixes #69 added password confirmation fields
- fixes #87 RhodeCode crashes occasionally on updating repository owner
- fixes #82 broken annotations on files with more than 1 blank line at the end
- a lot of fixes and tweaks for file browser
- fixed detached session issues
- fixed when user had no repos he would see all repos listed in my account
- fixed ui() instance bug when global hgrc settings was loaded for server
instance and all hgrc options were merged with our db ui() object
- numerous small bugfixes
(special thanks for TkSoh for detailed feedback)
1.0.2 (**2010-11-12**)
- tested under python2.7
- bumped sqlalchemy and celery versions
- fixed #59 missing graph.js
- fixed repo_size crash when repository had broken symlinks
- fixed python2.5 crashes.
1.0.1 (**2010-11-10**)
- small css updated
- fixed #53 python2.5 incompatible enumerate calls
- fixed #52 disable mercurial extension for web
- fixed #51 deleting repositories don't delete it's dependent objects
1.0.0 (**2010-11-02**)
- security bugfix simplehg wasn't checking for permissions on commands
other than pull or push.
- fixed doubled messages after push or pull in admin journal
- templating and css corrections, fixed repo switcher on chrome, updated titles
- admin menu accessible from options menu on repository view
- permissions cached queries
1.0.0rc4 (**2010-10-12**)
--------------------------
- fixed python2.5 missing simplejson imports (thanks to Jens Bäckman)
- removed cache_manager settings from sqlalchemy meta
- added sqlalchemy cache settings to ini files
- validated password length and added second try of failure on paster setup-app
- fixed setup database destroy prompt even when there was no db
1.0.0rc3 (**2010-10-11**)
-------------------------
- fixed i18n during installation.
1.0.0rc2 (**2010-10-11**)
- Disabled dirsize in file browser, it's causing nasty bug when dir renames
occure. After vcs is fixed it'll be put back again.
- templating/css rewrites, optimized css.
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
rhodecode.model.user_group
~~~~~~~~~~~~~~~~~~~~~~~~~~
users groups model for RhodeCode
:created_on: Jan 25, 2011
:author: marcink
:copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
:license: GPLv3, see COPYING for more details.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# 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 General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import logging
import traceback
import shutil
from rhodecode.lib import LazyProperty
from rhodecode.model import BaseModel
from rhodecode.model.db import RepoGroup, RhodeCodeUi, UserRepoGroupToPerm, \
User, Permission, UsersGroupRepoGroupToPerm, UsersGroup
log = logging.getLogger(__name__)
class ReposGroupModel(BaseModel):
def __get_user(self, user):
return self._get_instance(User, user, callback=User.get_by_username)
def __get_users_group(self, users_group):
return self._get_instance(UsersGroup, users_group,
callback=UsersGroup.get_by_group_name)
def __get_repos_group(self, repos_group):
return self._get_instance(RepoGroup, repos_group,
callback=RepoGroup.get_by_group_name)
def __get_perm(self, permission):
return self._get_instance(Permission, permission,
callback=Permission.get_by_key)
@LazyProperty
def repos_path(self):
Get's the repositories root path from database
q = RhodeCodeUi.get_by_key('/').one()
return q.ui_value
def _create_default_perms(self, new_group):
# create default permission
repo_group_to_perm = UserRepoGroupToPerm()
default_perm = 'group.read'
for p in User.get_by_username('default').user_perms:
if p.permission.permission_name.startswith('group.'):
default_perm = p.permission.permission_name
break
repo_group_to_perm.permission_id = self.sa.query(Permission)\
.filter(Permission.permission_name == default_perm)\
.one().permission_id
repo_group_to_perm.group = new_group
repo_group_to_perm.user_id = User.get_by_username('default').user_id
self.sa.add(repo_group_to_perm)
def __create_group(self, group_name):
makes repositories group on filesystem
:param repo_name:
:param parent_id:
create_path = os.path.join(self.repos_path, group_name)
log.debug('creating new group in %s' % create_path)
if os.path.isdir(create_path):
raise Exception('That directory already exists !')
os.makedirs(create_path)
def __rename_group(self, old, new):
Renames a group on filesystem
:param group_name:
if old == new:
log.debug('skipping group rename')
return
log.debug('renaming repos group from %s to %s' % (old, new))
old_path = os.path.join(self.repos_path, old)
new_path = os.path.join(self.repos_path, new)
log.debug('renaming repos paths from %s to %s' % (old_path, new_path))
if os.path.isdir(new_path):
raise Exception('Was trying to rename to already '
'existing dir %s' % new_path)
shutil.move(old_path, new_path)
def __delete_group(self, group):
Deletes a group from a filesystem
:param group: instance of group from database
paths = group.full_path.split(RepoGroup.url_sep())
paths = os.sep.join(paths)
rm_path = os.path.join(self.repos_path, paths)
if os.path.isdir(rm_path):
# delete only if that path really exists
os.rmdir(rm_path)
def create(self, group_name, group_description, parent, just_db=False):
try:
new_repos_group = RepoGroup()
new_repos_group.group_description = group_description
new_repos_group.parent_group = self.__get_repos_group(parent)
new_repos_group.group_name = new_repos_group.get_new_name(group_name)
self.sa.add(new_repos_group)
self._create_default_perms(new_repos_group)
if not just_db:
# we need to flush here, in order to check if database won't
# throw any exceptions, create filesystem dirs at the very end
self.sa.flush()
self.__create_group(new_repos_group.group_name)
return new_repos_group
except:
log.error(traceback.format_exc())
raise
def update(self, repos_group_id, form_data):
repos_group = RepoGroup.get(repos_group_id)
# update permissions
for member, perm, member_type in form_data['perms_updates']:
if member_type == 'user':
# this updates also current one if found
ReposGroupModel().grant_user_permission(
repos_group=repos_group, user=member, perm=perm
)
else:
ReposGroupModel().grant_users_group_permission(
repos_group=repos_group, group_name=member, perm=perm
# set new permissions
for member, perm, member_type in form_data['perms_new']:
old_path = repos_group.full_path
# change properties
repos_group.group_description = form_data['group_description']
repos_group.parent_group = RepoGroup.get(form_data['group_parent_id'])
repos_group.group_parent_id = form_data['group_parent_id']
repos_group.group_name = repos_group.get_new_name(form_data['group_name'])
new_path = repos_group.full_path
self.sa.add(repos_group)
self.__rename_group(old_path, new_path)
# we need to get all repositories from this new group and
# rename them accordingly to new group path
for r in repos_group.repositories:
r.repo_name = r.get_new_name(r.just_name)
self.sa.add(r)
return repos_group
def delete(self, users_group_id):
users_group = RepoGroup.get(users_group_id)
self.sa.delete(users_group)
self.__delete_group(users_group)
def grant_user_permission(self, repos_group, user, perm):
Grant permission for user on given repositories group, or update
existing one if found
:param repos_group: Instance of ReposGroup, repositories_group_id,
or repositories_group name
:param user: Instance of User, user_id or username
:param perm: Instance of Permission, or permission_name
repos_group = self.__get_repos_group(repos_group)
user = self.__get_user(user)
permission = self.__get_perm(perm)
# check if we have that permission already
obj = self.sa.query(UserRepoGroupToPerm)\
.filter(UserRepoGroupToPerm.user == user)\
.filter(UserRepoGroupToPerm.group == repos_group)\
.scalar()
if obj is None:
# create new !
obj = UserRepoGroupToPerm()
obj.group = repos_group
obj.user = user
obj.permission = permission
self.sa.add(obj)
def revoke_user_permission(self, repos_group, user):
Revoke permission for user on given repositories group
.one()
self.sa.delete(obj)
def grant_users_group_permission(self, repos_group, group_name, perm):
Grant permission for users group on given repositories group, or update
:param group_name: Instance of UserGroup, users_group_id,
or users group name
group_name = self.__get_users_group(group_name)
obj = self.sa.query(UsersGroupRepoGroupToPerm)\
.filter(UsersGroupRepoGroupToPerm.group == repos_group)\
.filter(UsersGroupRepoGroupToPerm.users_group == group_name)\
# create new
obj = UsersGroupRepoGroupToPerm()
obj.users_group = group_name
def revoke_users_group_permission(self, repos_group, group_name):
Revoke permission for users group on given repositories group
import unittest
from rhodecode.tests import *
from rhodecode.model.repos_group import ReposGroupModel
from rhodecode.model.repo import RepoModel
from rhodecode.model.db import RepoGroup, User, Notification, UserNotification, \
UsersGroup, UsersGroupMember, Permission
from sqlalchemy.exc import IntegrityError
from rhodecode.model.user import UserModel
from rhodecode.model.meta import Session
from rhodecode.model.notification import NotificationModel
from rhodecode.model.users_group import UsersGroupModel
from rhodecode.lib.auth import AuthUser
def _make_group(path, desc='desc', parent_id=None,
skip_if_exists=False):
gr = RepoGroup.get_by_group_name(path)
if gr and skip_if_exists:
return gr
gr = ReposGroupModel().create(path, desc, parent_id)
Session.commit()
class TestReposGroups(unittest.TestCase):
def setUp(self):
self.g1 = _make_group('test1', skip_if_exists=True)
self.g2 = _make_group('test2', skip_if_exists=True)
self.g3 = _make_group('test3', skip_if_exists=True)
def tearDown(self):
print 'out'
def __check_path(self, *path):
Checks the path for existance !
path = [TESTS_TMP_PATH] + list(path)
path = os.path.join(*path)
return os.path.isdir(path)
def _check_folders(self):
print os.listdir(TESTS_TMP_PATH)
def __delete_group(self, id_):
ReposGroupModel().delete(id_)
def __update_group(self, id_, path, desc='desc', parent_id=None):
form_data = dict(group_name=path,
group_description=desc,
group_parent_id=parent_id,
perms_updates=[],
perms_new=[])
form_data = dict(
group_name=path,
perms_new=[]
gr = ReposGroupModel().update(id_, form_data)
def test_create_group(self):
g = _make_group('newGroup')
self.assertEqual(g.full_path, 'newGroup')
self.assertTrue(self.__check_path('newGroup'))
def test_create_same_name_group(self):
self.assertRaises(IntegrityError, lambda:_make_group('newGroup'))
Session.rollback()
def test_same_subgroup(self):
sg1 = _make_group('sub1', parent_id=self.g1.group_id)
self.assertEqual(sg1.parent_group, self.g1)
self.assertEqual(sg1.full_path, 'test1/sub1')
self.assertTrue(self.__check_path('test1', 'sub1'))
ssg1 = _make_group('subsub1', parent_id=sg1.group_id)
self.assertEqual(ssg1.parent_group, sg1)
self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
def test_remove_group(self):
sg1 = _make_group('deleteme')
self.__delete_group(sg1.group_id)
self.assertEqual(RepoGroup.get(sg1.group_id), None)
self.assertFalse(self.__check_path('deteteme'))
sg1 = _make_group('deleteme', parent_id=self.g1.group_id)
self.assertFalse(self.__check_path('test1', 'deteteme'))
def test_rename_single_group(self):
sg1 = _make_group('initial')
new_sg1 = self.__update_group(sg1.group_id, 'after')
self.assertTrue(self.__check_path('after'))
self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
def test_update_group_parent(self):
sg1 = _make_group('initial', parent_id=self.g1.group_id)
new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
self.assertTrue(self.__check_path('test1', 'after'))
self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
self.assertTrue(self.__check_path('test3', 'after'))
self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
new_sg1 = self.__update_group(sg1.group_id, 'hello')
self.assertTrue(self.__check_path('hello'))
self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
def test_subgrouping_with_repo(self):
g1 = _make_group('g1')
g2 = _make_group('g2')
# create new repo
form_data = dict(repo_name='john',
repo_name_full='john',
fork_name=None,
description=None,
repo_group=None,
private=False,
repo_type='hg',
clone_uri=None)
cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
r = RepoModel().create(form_data, cur_user)
self.assertEqual(r.repo_name, 'john')
# put repo into group
form_data = form_data
form_data['repo_group'] = g1.group_id
form_data['perms_new'] = []
form_data['perms_updates'] = []
RepoModel().update(r.repo_name, form_data)
self.assertEqual(r.repo_name, 'g1/john')
self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
self.assertTrue(self.__check_path('g2', 'g1'))
# test repo
self.assertEqual(r.repo_name, os.path.join('g2', 'g1', r.just_name))
def test_move_to_root(self):
g1 = _make_group('t11')
g2 = _make_group('t22',parent_id=g1.group_id)
self.assertEqual(g2.full_path,'t11/t22')
self.assertTrue(self.__check_path('t11', 't22'))
g2 = self.__update_group(g2.group_id, 'g22', parent_id=None)
self.assertEqual(g2.group_name,'g22')
# we moved out group from t1 to '' so it's full path should be 'g2'
self.assertEqual(g2.full_path,'g22')
self.assertFalse(self.__check_path('t11', 't22'))
self.assertTrue(self.__check_path('g22'))
class TestUser(unittest.TestCase):
def __init__(self, methodName='runTest'):
Session.remove()
super(TestUser, self).__init__(methodName=methodName)
def test_create_and_remove(self):
usr = UserModel().create_or_update(username=u'test_user', password=u'qweqwe',
email=u'u232@rhodecode.org',
name=u'u1', lastname=u'u1')
self.assertEqual(User.get_by_username(u'test_user'), usr)
# make users group
users_group = UsersGroupModel().create('some_example_group')
UsersGroupModel().add_user_to_group(users_group, usr)
self.assertEqual(UsersGroup.get(users_group.users_group_id), users_group)
self.assertEqual(UsersGroupMember.query().count(), 1)
UserModel().delete(usr.user_id)
self.assertEqual(UsersGroupMember.query().all(), [])
class TestNotifications(unittest.TestCase):
self.u1 = UserModel().create_or_update(username=u'u1',
password=u'qweqwe',
email=u'u1@rhodecode.org',
self.u1 = self.u1.user_id
self.u2 = UserModel().create_or_update(username=u'u2',
email=u'u2@rhodecode.org',
name=u'u2', lastname=u'u3')
self.u2 = self.u2.user_id
self.u3 = UserModel().create_or_update(username=u'u3',
email=u'u3@rhodecode.org',
name=u'u3', lastname=u'u3')
self.u3 = self.u3.user_id
super(TestNotifications, self).__init__(methodName=methodName)
def _clean_notifications(self):
for n in Notification.query().all():
Session.delete(n)
self.assertEqual(Notification.query().all(), [])
self._clean_notifications()
def test_create_notification(self):
self.assertEqual([], Notification.query().all())
self.assertEqual([], UserNotification.query().all())
usrs = [self.u1, self.u2]
notification = NotificationModel().create(created_by=self.u1,
subject=u'subj', body=u'hi there',
recipients=usrs)
u1 = User.get(self.u1)
u2 = User.get(self.u2)
u3 = User.get(self.u3)
notifications = Notification.query().all()
self.assertEqual(len(notifications), 1)
unotification = UserNotification.query()\
.filter(UserNotification.notification == notification).all()
self.assertEqual(notifications[0].recipients, [u1, u2])
self.assertEqual(notification.notification_id,
notifications[0].notification_id)
self.assertEqual(len(unotification), len(usrs))
self.assertEqual([x.user.user_id for x in unotification], usrs)
def test_user_notifications(self):
notification1 = NotificationModel().create(created_by=self.u1,
subject=u'subj', body=u'hi there1',
recipients=[self.u3])
notification2 = NotificationModel().create(created_by=self.u1,
subject=u'subj', body=u'hi there2',
u3 = Session.query(User).get(self.u3)
self.assertEqual(sorted([x.notification for x in u3.notifications]),
sorted([notification2, notification1]))
def test_delete_notifications(self):
subject=u'title', body=u'hi there3',
recipients=[self.u3, self.u1, self.u2])
self.assertTrue(notification in notifications)
Notification.delete(notification.notification_id)
self.assertFalse(notification in notifications)
un = UserNotification.query().filter(UserNotification.notification
== notification).all()
self.assertEqual(un, [])
def test_delete_association(self):
.filter(UserNotification.notification ==
notification)\
.filter(UserNotification.user_id == self.u3)\
self.assertEqual(unotification.user_id, self.u3)
NotificationModel().delete(self.u3,
notification.notification_id)
u3notification = UserNotification.query()\
self.assertEqual(u3notification, None)
# notification object is still there
self.assertEqual(Notification.query().all(), [notification])
#u1 and u2 still have assignments
u1notification = UserNotification.query()\
.filter(UserNotification.user_id == self.u1)\
self.assertNotEqual(u1notification, None)
u2notification = UserNotification.query()\
.filter(UserNotification.user_id == self.u2)\
self.assertNotEqual(u2notification, None)
def test_notification_counter(self):
NotificationModel().create(created_by=self.u1,
subject=u'title', body=u'hi there_delete',
recipients=[self.u3, self.u1])
self.assertEqual(NotificationModel()
.get_unread_cnt_for_user(self.u1), 1)
.get_unread_cnt_for_user(self.u2), 0)
.get_unread_cnt_for_user(self.u3), 1)
.get_unread_cnt_for_user(self.u1), 2)
.get_unread_cnt_for_user(self.u2), 1)
.get_unread_cnt_for_user(self.u3), 2)
class TestUsers(unittest.TestCase):
super(TestUsers, self).__init__(methodName=methodName)
perm = Permission.query().all()
for p in perm:
UserModel().revoke_perm(self.u1, p)
UserModel().delete(self.u1)
def test_add_perm(self):
perm = Permission.query().all()[0]
UserModel().grant_perm(self.u1, perm)
self.assertEqual(UserModel().has_perm(self.u1, perm), True)
def test_has_perm(self):
has_p = UserModel().has_perm(self.u1, p)
self.assertEqual(False, has_p)
def test_revoke_perm(self):
#revoke
UserModel().revoke_perm(self.u1, perm)
self.assertEqual(UserModel().has_perm(self.u1, perm), False)
class TestPermissions(unittest.TestCase):
super(TestPermissions, self).__init__(methodName=methodName)
self.u1 = UserModel().create_or_update(
username=u'u1', password=u'qweqwe',
email=u'u1@rhodecode.org', name=u'u1', lastname=u'u1'
self.a1 = UserModel().create_or_update(
username=u'a1', password=u'qweqwe',
email=u'a1@rhodecode.org', name=u'a1', lastname=u'a1', admin=True
UserModel().delete(self.a1)
if hasattr(self, 'g1'):
ReposGroupModel().delete(self.g1.group_id)
if hasattr(self, 'g2'):
ReposGroupModel().delete(self.g2.group_id)
if hasattr(self, 'ug1'):
UsersGroupModel().delete(self.ug1, force=True)
def test_default_perms_set(self):
u1_auth = AuthUser(user_id=self.u1.user_id)
perms = {
'repositories_groups': {},
'global': set([u'hg.create.repository', u'repository.read',
u'hg.register.manual_activate']),
'repositories': {u'vcs_test_hg': u'repository.read'}
}
self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
perms['repositories'][HG_REPO])
new_perm = 'repository.write'
RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], new_perm)
def test_default_admin_perms_set(self):
a1_auth = AuthUser(user_id=self.a1.user_id)
'global': set([u'hg.admin']),
'repositories': {u'vcs_test_hg': u'repository.admin'}
self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
RepoModel().grant_user_permission(repo=HG_REPO, user=self.a1, perm=new_perm)
# cannot really downgrade admins permissions !? they still get's set as
# admin !
u1_auth = AuthUser(user_id=self.a1.user_id)
def test_default_group_perms(self):
'repositories_groups': {u'test1': 'group.read', u'test2': 'group.read'},
'global': set([u'hg.create.repository', u'repository.read', u'hg.register.manual_activate']),
self.assertEqual(u1_auth.permissions['repositories_groups'],
perms['repositories_groups'])
def test_default_admin_group_perms(self):
'repositories_groups': {u'test1': 'group.admin', u'test2': 'group.admin'},
'global': set(['hg.admin']),
'repositories': {u'vcs_test_hg': 'repository.admin'}
self.assertEqual(a1_auth.permissions['repositories_groups'],
def test_propagated_permission_from_users_group(self):
# make group
self.ug1 = UsersGroupModel().create('G1')
# add user to group
UsersGroupModel().add_user_to_group(self.ug1, self.u1)
# set permission to lower
new_perm = 'repository.none'
new_perm)
# grant perm for group this should override permission from user
RepoModel().grant_users_group_permission(repo=HG_REPO,
group_name=self.ug1,
perm=new_perm)
# check perms
def test_propagated_permission_from_users_group_lower_weight(self):
new_perm_h = 'repository.write'
RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1,
perm=new_perm_h)
new_perm_h)
# grant perm for group this should NOT override permission from user
# since it's lower than granted
new_perm_l = 'repository.read'
perm=new_perm_l)
'repositories': {u'vcs_test_hg': u'repository.write'}
Status change: