Changeset - 9ab21c5ddb84
[Not reviewed]
CONTRIBUTORS
Show inline comments
 
List of contributors to RhodeCode project:
 
    Marcin Kuźmiński <marcin@python-works.com>
 
    Lukasz Balcerzak <lukaszbalcerzak@gmail.com>
 
    Jason Harris <jason@jasonfharris.com>
 
    Thayne Harbaugh  <thayne@fusionio.com>
 
    cejones
 
    Thomas Waldmann <tw-public@gmx.de>
 
    Lorenzo M. Catucci <lorenzo@sancho.ccd.uniroma2.it>
 
    Dmitri Kuznetsov
 
    Jared Bunting <jared.bunting@peachjean.com>
 
    Steve Romanow <slestak989@gmail.com>
 
    Augosto Hermann <augusto.herrmann@planejamento.gov.br>    
 
    Ankit Solanki <ankit.solanki@gmail.com>    
 
    Liad Shani <liadff@gmail.com>
 
    Les Peabody <lpeabody@gmail.com>
 
    Jonas Oberschweiber <jonas.oberschweiber@d-velop.de>
 
    Matt Zuba <matt.zuba@goodwillaz.org>
 
    Aras Pranckevicius <aras@unity3d.com>
 
\ No newline at end of file
 
    Aras Pranckevicius <aras@unity3d.com>
 
    Tony Bussieres <t.bussieres@gmail.com>
docs/changelog.rst
Show inline comments
 
.. _changelog:
 

	
 
Changelog
 
=========
 

	
 

	
 
1.3.2 (**2012-02-28**)
 
----------------------
 

	
 
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
 
- fixes #371 fixed issues with beaker/sqlalchemy and non-ascii cache keys 
 
- fixed #373 missing cascade drop on user_group_to_perm table
 

	
 
1.3.1 (**2012-02-27**)
 
----------------------
 

	
 
news
 
++++
 

	
 

	
 
fixes
 
+++++
 

	
 
- 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**)
 
----------------------
 

	
 
news
 
++++
 

	
 
- 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 
rhodecode/controllers/admin/repos_groups.py
Show inline comments
 
@@ -218,96 +218,101 @@ class ReposGroupsController(BaseControll
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('error occurred during deletion of repos '
 
                      'group %s' % gr.group_name), category='error')
 

	
 
        return redirect(url('repos_groups'))
 

	
 
    @HasReposGroupPermissionAnyDecorator('group.admin')
 
    def delete_repos_group_user_perm(self, group_name):
 
        """
 
        DELETE an existing repositories group permission user
 

	
 
        :param group_name:
 
        """
 

	
 
        try:
 
            ReposGroupModel().revoke_user_permission(
 
                repos_group=group_name, user=request.POST['user_id']
 
            )
 
            Session.commit()
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during deletion of group user'),
 
                    category='error')
 
            raise HTTPInternalServerError()
 

	
 
    @HasReposGroupPermissionAnyDecorator('group.admin')
 
    def delete_repos_group_users_group_perm(self, group_name):
 
        """
 
        DELETE an existing repositories group permission users group
 

	
 
        :param group_name:
 
        """
 

	
 
        try:
 
            ReposGroupModel().revoke_users_group_permission(
 
                repos_group=group_name,
 
                group_name=request.POST['users_group_id']
 
            )
 
            Session.commit()
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during deletion of group'
 
                      ' users groups'),
 
                    category='error')
 
            raise HTTPInternalServerError()
 

	
 
    def show_by_name(self, group_name):
 
        """
 
        This is a proxy that does a lookup group_name -> id, and shows
 
        the group by id view instead
 
        """
 
        group_name = group_name.rstrip('/')
 
        id_ = RepoGroup.get_by_group_name(group_name).group_id
 
        return self.show(id_)
 

	
 
    @HasReposGroupPermissionAnyDecorator('group.read', 'group.write',
 
                                         'group.admin')
 
    def show(self, id, format='html'):
 
        """GET /repos_groups/id: Show a specific item"""
 
        # url('repos_group', id=ID)
 

	
 
        c.group = RepoGroup.get(id)
 

	
 
        if c.group:
 
            c.group_repos = c.group.repositories.all()
 
        else:
 
            return redirect(url('home'))
 

	
 
        #overwrite our cached list with current filter
 
        gr_filter = c.group_repos
 
        c.cached_repo_list = self.scm_model.get_repos(all_repos=gr_filter)
 

	
 
        c.repos_list = c.cached_repo_list
 

	
 
        c.repo_cnt = 0
 

	
 
        c.groups = self.sa.query(RepoGroup).order_by(RepoGroup.group_name)\
 
            .filter(RepoGroup.group_parent_id == id).all()
 

	
 
        return render('admin/repos_groups/repos_groups.html')
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def edit(self, id, format='html'):
 
        """GET /repos_groups/id/edit: Form to edit an existing item"""
 
        # url('edit_repos_group', id=ID)
 

	
 
        id_ = int(id)
 

	
 
        c.repos_group = RepoGroup.get(id_)
 
        defaults = self.__load_data(id_)
 

	
 
        # we need to exclude this group from the group list for editing
 
        c.repo_groups = filter(lambda x: x[0] != id_, c.repo_groups)
 

	
 
        return htmlfill.render(
 
            render('admin/repos_groups/repos_groups_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False
 
        )
rhodecode/controllers/admin/users_groups.py
Show inline comments
 
@@ -115,101 +115,102 @@ class UsersGroupsController(BaseControll
 
        c.available_members = [(x.user_id, x.username) for x in
 
                               self.sa.query(User).all()]
 

	
 
        available_members = [safe_unicode(x[0]) for x in c.available_members]
 

	
 
        users_group_form = UsersGroupForm(edit=True,
 
                                          old_data=c.users_group.get_dict(),
 
                                          available_members=available_members)()
 

	
 
        try:
 
            form_result = users_group_form.to_python(request.POST)
 
            UsersGroupModel().update(c.users_group, form_result)
 
            h.flash(_('updated users group %s') \
 
                        % form_result['users_group_name'],
 
                    category='success')
 
            #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
 
            Session.commit()
 
        except formencode.Invalid, errors:
 
            e = errors.error_dict or {}
 

	
 
            perm = Permission.get_by_key('hg.create.repository')
 
            e.update({'create_repo_perm':
 
                         UsersGroupModel().has_perm(id, perm)})
 

	
 
            return htmlfill.render(
 
                render('admin/users_groups/users_group_edit.html'),
 
                defaults=errors.value,
 
                errors=e,
 
                prefix_error=False,
 
                encoding="UTF-8")
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('error occurred during update of users group %s') \
 
                    % request.POST.get('users_group_name'), category='error')
 

	
 
        return redirect(url('users_groups'))
 

	
 
    def delete(self, id):
 
        """DELETE /users_groups/id: Delete an existing item"""
 
        # Forms posted to this method should contain a hidden field:
 
        #    <input type="hidden" name="_method" value="DELETE" />
 
        # Or using helpers:
 
        #    h.form(url('users_group', id=ID),
 
        #           method='delete')
 
        # url('users_group', id=ID)
 

	
 
        try:
 
            UsersGroupModel().delete(id)
 
            Session.commit()
 
            h.flash(_('successfully deleted users group'), category='success')
 
            Session.commit()
 
        except UsersGroupsAssignedException, e:
 
            h.flash(e, category='error')
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('An error occurred during deletion of users group'),
 
                    category='error')
 
        return redirect(url('users_groups'))
 

	
 
    def show(self, id, format='html'):
 
        """GET /users_groups/id: Show a specific item"""
 
        # url('users_group', id=ID)
 

	
 
    def edit(self, id, format='html'):
 
        """GET /users_groups/id/edit: Form to edit an existing item"""
 
        # url('edit_users_group', id=ID)
 

	
 
        c.users_group = self.sa.query(UsersGroup).get(id)
 
        if not c.users_group:
 
            return redirect(url('users_groups'))
 

	
 
        c.users_group.permissions = {}
 
        c.group_members_obj = [x.user for x in c.users_group.members]
 
        c.group_members = [(x.user_id, x.username) for x in
 
                           c.group_members_obj]
 
        c.available_members = [(x.user_id, x.username) for x in
 
                               self.sa.query(User).all()]
 
        defaults = c.users_group.get_dict()
 
        perm = Permission.get_by_key('hg.create.repository')
 
        defaults.update({'create_repo_perm':
 
                         UsersGroupModel().has_perm(c.users_group, perm)})
 
        return htmlfill.render(
 
            render('admin/users_groups/users_group_edit.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False
 
        )
 

	
 
    def update_perm(self, id):
 
        """PUT /users_perm/id: Update an existing item"""
 
        # url('users_group_perm', id=ID, method='put')
 

	
 
        grant_perm = request.POST.get('create_repo_perm', False)
 

	
 
        if grant_perm:
 
            perm = Permission.get_by_key('hg.create.none')
 
            UsersGroupModel().revoke_perm(id, perm)
 

	
 
            perm = Permission.get_by_key('hg.create.repository')
 
            UsersGroupModel().grant_perm(id, perm)
 
            h.flash(_("Granted 'repository create' permission to user"),
 
                    category='success')
 

	
rhodecode/lib/caching_query.py
Show inline comments
 
"""caching_query.py
 

	
 
Represent persistence structures which allow the usage of
 
Beaker caching with SQLAlchemy.
 

	
 
The three new concepts introduced here are:
 

	
 
 * CachingQuery - a Query subclass that caches and
 
   retrieves results in/from Beaker.
 
 * FromCache - a query option that establishes caching
 
   parameters on a Query
 
 * RelationshipCache - a variant of FromCache which is specific
 
   to a query invoked during a lazy load.
 
 * _params_from_query - extracts value parameters from
 
   a Query.
 

	
 
The rest of what's here are standard SQLAlchemy and
 
Beaker constructs.
 

	
 
"""
 
import beaker
 
from beaker.exceptions import BeakerException
 

	
 
from sqlalchemy.orm.interfaces import MapperOption
 
from sqlalchemy.orm.query import Query
 
from sqlalchemy.sql import visitors
 
from rhodecode.lib import safe_str
 

	
 

	
 
class CachingQuery(Query):
 
    """A Query subclass which optionally loads full results from a Beaker
 
    cache region.
 

	
 
    The CachingQuery stores additional state that allows it to consult
 
    a Beaker cache before accessing the database:
 

	
 
    * A "region", which is a cache region argument passed to a
 
      Beaker CacheManager, specifies a particular cache configuration
 
      (including backend implementation, expiration times, etc.)
 
    * A "namespace", which is a qualifying name that identifies a
 
      group of keys within the cache.  A query that filters on a name
 
      might use the name "by_name", a query that filters on a date range
 
      to a joined table might use the name "related_date_range".
 

	
 
    When the above state is present, a Beaker cache is retrieved.
 

	
 
    The "namespace" name is first concatenated with
 
    a string composed of the individual entities and columns the Query
 
    requests, i.e. such as ``Query(User.id, User.name)``.
 

	
 
    The Beaker cache is then loaded from the cache manager based
 
    on the region and composed namespace.  The key within the cache
 
    itself is then constructed against the bind parameters specified
 
    by this query, which are usually literals defined in the
 
    WHERE clause.
 

	
 
    The FromCache and RelationshipCache mapper options below represent
 
    the "public" method of configuring this state upon the CachingQuery.
 

	
 
    """
 

	
 
    def __init__(self, manager, *args, **kw):
 
        self.cache_manager = manager
 
        Query.__init__(self, *args, **kw)
 

	
 
    def __iter__(self):
 
        """override __iter__ to pull results from Beaker
 
           if particular attributes have been configured.
 

	
 
           Note that this approach does *not* detach the loaded objects from
 
           the current session. If the cache backend is an in-process cache
 
           (like "memory") and lives beyond the scope of the current session's
 
           transaction, those objects may be expired. The method here can be
 
           modified to first expunge() each loaded item from the current
 
           session before returning the list of items, so that the items
 
@@ -92,99 +93,100 @@ class CachingQuery(Query):
 

	
 
        Raise KeyError if no value present and no
 
        createfunc specified.
 

	
 
        """
 
        cache, cache_key = _get_cache_parameters(self)
 
        ret = cache.get_value(cache_key, createfunc=createfunc)
 
        if merge:
 
            ret = self.merge_result(ret, load=False)
 
        return ret
 

	
 
    def set_value(self, value):
 
        """Set the value in the cache for this query."""
 

	
 
        cache, cache_key = _get_cache_parameters(self)
 
        cache.put(cache_key, value)
 

	
 

	
 
def query_callable(manager, query_cls=CachingQuery):
 
    def query(*arg, **kw):
 
        return query_cls(manager, *arg, **kw)
 
    return query
 

	
 

	
 
def get_cache_region(name, region):
 
    if region not in beaker.cache.cache_regions:
 
        raise BeakerException('Cache region `%s` not configured '
 
            'Check if proper cache settings are in the .ini files' % region)
 
    kw = beaker.cache.cache_regions[region]
 
    return beaker.cache.Cache._get_cache(name, kw)
 

	
 

	
 
def _get_cache_parameters(query):
 
    """For a query with cache_region and cache_namespace configured,
 
    return the correspoinding Cache instance and cache key, based
 
    on this query's current criterion and parameter values.
 

	
 
    """
 
    if not hasattr(query, '_cache_parameters'):
 
        raise ValueError("This Query does not have caching "
 
                         "parameters configured.")
 

	
 
    region, namespace, cache_key = query._cache_parameters
 

	
 
    namespace = _namespace_from_query(namespace, query)
 

	
 
    if cache_key is None:
 
        # cache key - the value arguments from this query's parameters.
 
        args = [str(x) for x in _params_from_query(query)]
 
        args = [safe_str(x) for x in _params_from_query(query)]
 
        args.extend(filter(lambda k:k not in ['None', None, u'None'],
 
                           [str(query._limit), str(query._offset)]))
 

	
 
        cache_key = " ".join(args)
 

	
 
    if cache_key is None:
 
        raise Exception('Cache key cannot be None')
 

	
 
    # get cache
 
    #cache = query.cache_manager.get_cache_region(namespace, region)
 
    cache = get_cache_region(namespace, region)
 
    # optional - hash the cache_key too for consistent length
 
    # import uuid
 
    # cache_key= str(uuid.uuid5(uuid.NAMESPACE_DNS, cache_key))
 

	
 
    return cache, cache_key
 

	
 

	
 
def _namespace_from_query(namespace, query):
 
    # cache namespace - the token handed in by the
 
    # option + class we're querying against
 
    namespace = " ".join([namespace] + [str(x) for x in query._entities])
 

	
 
    # memcached wants this
 
    namespace = namespace.replace(' ', '_')
 

	
 
    return namespace
 

	
 

	
 
def _set_cache_parameters(query, region, namespace, cache_key):
 

	
 
    if hasattr(query, '_cache_parameters'):
 
        region, namespace, cache_key = query._cache_parameters
 
        raise ValueError("This query is already configured "
 
                        "for region %r namespace %r" %
 
                        (region, namespace)
 
                    )
 
    query._cache_parameters = region, namespace, cache_key
 

	
 

	
 
class FromCache(MapperOption):
 
    """Specifies that a Query should load results from a cache."""
 

	
 
    propagate_to_loaders = False
 

	
 
    def __init__(self, region, namespace, cache_key=None):
 
        """Construct a new FromCache.
 

	
 
        :param region: the cache region.  Should be a
 
        region configured in the Beaker CacheManager.
 

	
rhodecode/lib/middleware/https_fixup.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.lib.middleware.https_fixup
 
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
    middleware to handle https correctly
 

	
 
    :created_on: May 23, 2010
 
    :author: marcink
 
    :copyright: (C) 2010-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/>.
 

	
 
from rhodecode.lib import str2bool
 

	
 

	
 
class HttpsFixup(object):
 

	
 
    def __init__(self, app, config):
 
        self.application = app
 
        self.config = config
 

	
 
    def __call__(self, environ, start_response):
 
        self.__fixup(environ)
 
        return self.application(environ, start_response)
 

	
 
    def __fixup(self, environ):
 
        """
 
        Function to fixup the environ as needed. In order to use this
 
        middleware you should set this header inside your
 
        proxy ie. nginx, apache etc.
 
        """
 
        proto = environ.get('HTTP_X_URL_SCHEME')
 

	
 
        if str2bool(self.config.get('force_https')):
 
            proto = 'https'
 

	
 
        else:
 
            if 'HTTP_X_URL_SCHEME' in environ:
 
                proto = environ.get('HTTP_X_URL_SCHEME')
 
            elif 'HTTP_X_FORWARDED_SCHEME' in environ:
 
                proto = environ.get('HTTP_X_FORWARDED_SCHEME')
 
            elif 'HTTP_X_FORWARDED_PROTO' in environ:
 
                proto = environ.get('HTTP_X_FORWARDED_PROTO')
 
            else:
 
                proto = 'http'
 
        if proto == 'https':
 
            environ['wsgi.url_scheme'] = proto
 
        else:
 
            environ['wsgi.url_scheme'] = 'http'
 

	
 
        return None
rhodecode/lib/middleware/simplegit.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.lib.middleware.simplegit
 
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
    SimpleGit middleware for handling git protocol request (push/clone etc.)
 
    It's implemented with basic auth function
 

	
 
    :created_on: Apr 28, 2010
 
    :author: marcink
 
    :copyright: (C) 2010-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 re
 
import logging
 
import traceback
 

	
 
from dulwich import server as dulserver
 

	
 

	
 
class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
 

	
 
    def handle(self):
 
        write = lambda x: self.proto.write_sideband(1, x)
 

	
 
        graph_walker = dulserver.ProtocolGraphWalker(self,
 
                                                     self.repo.object_store,
 
                                                     self.repo.get_peeled)
 
        objects_iter = self.repo.fetch_objects(
 
          graph_walker.determine_wants, graph_walker, self.progress,
 
          get_tagged=self.get_tagged)
 

	
 
        # Do they want any objects?
 
        if objects_iter is None or len(objects_iter) == 0:
 
            return
 

	
 
        self.progress("counting objects: %d, done.\n" % len(objects_iter))
 
        dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
 
                                  objects_iter, len(objects_iter))
 
        messages = []
 
        messages.append('thank you for using rhodecode')
 

	
 
        for msg in messages:
 
            self.progress(msg + "\n")
 
        # we are done
 
        self.proto.write("0000")
 

	
 
dulserver.DEFAULT_HANDLERS = {
 
  'git-upload-pack': SimpleGitUploadPackHandler,
 
  'git-receive-pack': dulserver.ReceivePackHandler,
 
}
 

	
 
from dulwich.repo import Repo
 
from dulwich.web import HTTPGitApplication
 

	
 
from paste.httpheaders import REMOTE_USER, AUTH_TYPE
 

	
 
from rhodecode.lib import safe_str
 
from rhodecode.lib.base import BaseVCSController
 
from rhodecode.lib.auth import get_container_username
 
from rhodecode.lib.utils import is_valid_repo
 
from rhodecode.model.db import User
 

	
 
from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
def is_git(environ):
 
    """Returns True if request's target is git server.
 
    ``HTTP_USER_AGENT`` would then have git client version given.
 
GIT_PROTO_PAT = re.compile(r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)')
 

	
 

	
 
    :param environ:
 
    """
 
    http_user_agent = environ.get('HTTP_USER_AGENT')
 
    if http_user_agent and http_user_agent.startswith('git'):
 
        return True
 
    return False
 
def is_git(environ):
 
    path_info = environ['PATH_INFO']
 
    isgit_path = GIT_PROTO_PAT.match(path_info)
 
    log.debug('is a git path %s pathinfo : %s' % (isgit_path, path_info))
 
    return isgit_path
 

	
 

	
 
class SimpleGit(BaseVCSController):
 

	
 
    def _handle_request(self, environ, start_response):
 

	
 
        if not is_git(environ):
 
            return self.application(environ, start_response)
 

	
 
        proxy_key = 'HTTP_X_REAL_IP'
 
        def_key = 'REMOTE_ADDR'
 
        ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
 
        username = None
 
        # skip passing error to error controller
 
        environ['pylons.status_code_redirect'] = True
 

	
 
        #======================================================================
 
        # EXTRACT REPOSITORY NAME FROM ENV
 
        #======================================================================
 
        try:
 
            repo_name = self.__get_repository(environ)
 
            log.debug('Extracted repo name is %s' % repo_name)
 
        except:
 
            return HTTPInternalServerError()(environ, start_response)
 

	
 
        #======================================================================
 
        # GET ACTION PULL or PUSH
 
        #======================================================================
 
        action = self.__get_action(environ)
 

	
 
        #======================================================================
 
        # CHECK ANONYMOUS PERMISSION
 
        #======================================================================
 
        if action in ['pull', 'push']:
 
            anonymous_user = self.__get_user('default')
 
            username = anonymous_user.username
 
            anonymous_perm = self._check_permission(action, anonymous_user,
 
                                                    repo_name)
 

	
 
            if anonymous_perm is not True or anonymous_user.active is False:
 
                if anonymous_perm is not True:
 
                    log.debug('Not enough credentials to access this '
 
                              'repository as anonymous user')
 
                if anonymous_user.active is False:
 
                    log.debug('Anonymous access is disabled, running '
 
                              'authentication')
 
                #==============================================================
 
                # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
 
                # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
 
                #==============================================================
 

	
 
                # Attempting to retrieve username from the container
 
                username = get_container_username(environ, self.config)
 

	
 
@@ -173,76 +173,75 @@ class SimpleGit(BaseVCSController):
 
                                                   repo_name)
 
                    if perm is not True:
 
                        return HTTPForbidden()(environ, start_response)
 

	
 
        #===================================================================
 
        # GIT REQUEST HANDLING
 
        #===================================================================
 

	
 
        repo_path = safe_str(os.path.join(self.basepath, repo_name))
 
        log.debug('Repository path is %s' % repo_path)
 

	
 
        # quick check if that dir exists...
 
        if is_valid_repo(repo_name, self.basepath) is False:
 
            return HTTPNotFound()(environ, start_response)
 

	
 
        try:
 
            #invalidate cache on push
 
            if action == 'push':
 
                self._invalidate_cache(repo_name)
 
            log.info('%s action on GIT repo "%s"' % (action, repo_name))
 
            app = self.__make_app(repo_name, repo_path)
 
            return app(environ, start_response)
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            return HTTPInternalServerError()(environ, start_response)
 

	
 
    def __make_app(self, repo_name, repo_path):
 
        """
 
        Make an wsgi application using dulserver
 

	
 
        :param repo_name: name of the repository
 
        :param repo_path: full path to the repository
 
        """
 

	
 
        _d = {'/' + repo_name: Repo(repo_path)}
 
        backend = dulserver.DictBackend(_d)
 
        gitserve = HTTPGitApplication(backend)
 

	
 
        return gitserve
 

	
 
    def __get_repository(self, environ):
 
        """
 
        Get's repository name out of PATH_INFO header
 

	
 
        :param environ: environ where PATH_INFO is stored
 
        """
 
        try:
 
            environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
 
            repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
 
            if repo_name.endswith('/'):
 
                repo_name = repo_name.rstrip('/')
 
            repo_name = GIT_PROTO_PAT.match(environ['PATH_INFO']).group(1)
 
        except:
 
            log.error(traceback.format_exc())
 
            raise
 
        repo_name = repo_name.split('/')[0]
 

	
 
        return repo_name
 

	
 
    def __get_user(self, username):
 
        return User.get_by_username(username)
 

	
 
    def __get_action(self, environ):
 
        """Maps git request commands into a pull or push command.
 

	
 
        :param environ:
 
        """
 
        service = environ['QUERY_STRING'].split('=')
 
        if len(service) > 1:
 
            service_cmd = service[1]
 
            mapping = {'git-receive-pack': 'push',
 
            mapping = {
 
                'git-receive-pack': 'push',
 
                       'git-upload-pack': 'pull',
 
                       }
 

	
 
            return mapping.get(service_cmd,
 
                               service_cmd if service_cmd else 'other')
 
        else:
 
            return 'other'
rhodecode/lib/utils.py
Show inline comments
 
@@ -47,101 +47,107 @@ from rhodecode.lib.vcs.utils.helpers imp
 
from rhodecode.lib.vcs.exceptions import VCSError
 

	
 
from rhodecode.lib.caching_query import FromCache
 

	
 
from rhodecode.model import meta
 
from rhodecode.model.db import Repository, User, RhodeCodeUi, \
 
    UserLog, RepoGroup, RhodeCodeSetting, UserRepoGroupToPerm
 
from rhodecode.model.meta import Session
 
from rhodecode.model.repos_group import ReposGroupModel
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
def recursive_replace(str_, replace=' '):
 
    """Recursive replace of given sign to just one instance
 

	
 
    :param str_: given string
 
    :param replace: char to find and replace multiple instances
 

	
 
    Examples::
 
    >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
 
    'Mighty-Mighty-Bo-sstones'
 
    """
 

	
 
    if str_.find(replace * 2) == -1:
 
        return str_
 
    else:
 
        str_ = str_.replace(replace * 2, replace)
 
        return recursive_replace(str_, replace)
 

	
 

	
 
def repo_name_slug(value):
 
    """Return slug of name of repository
 
    This function is called on each creation/modification
 
    of repository to prevent bad names in repo
 
    """
 

	
 
    slug = remove_formatting(value)
 
    slug = strip_tags(slug)
 

	
 
    for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
 
        slug = slug.replace(c, '-')
 
    slug = recursive_replace(slug, '-')
 
    slug = collapse(slug, '-')
 
    return slug
 

	
 

	
 
def get_repo_slug(request):
 
    return request.environ['pylons.routes_dict'].get('repo_name')
 
    _repo = request.environ['pylons.routes_dict'].get('repo_name')
 
    if _repo:
 
        _repo = _repo.rstrip('/')
 
    return _repo
 

	
 

	
 
def get_repos_group_slug(request):
 
    return request.environ['pylons.routes_dict'].get('group_name')
 
    _group = request.environ['pylons.routes_dict'].get('group_name')
 
    if _group:
 
        _group = _group.rstrip('/')
 
    return _group
 

	
 

	
 
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
 

	
 
    try:
 
        if hasattr(user, 'user_id'):
 
            user_obj = user
 
        elif isinstance(user, basestring):
 
            user_obj = User.get_by_username(user)
 
        else:
 
            raise Exception('You have to provide user object or username')
 

	
 
        if hasattr(repo, 'repo_id'):
 
            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:
 
            raise Exception('You have to provide repository to action logger')
 

	
 
        user_log = UserLog()
 
        user_log.user_id = user_obj.user_id
 
        user_log.action = action
 

	
 
        user_log.repository_id = repo_obj.repo_id
 
        user_log.repository_name = repo_name
 

	
 
        user_log.action_date = datetime.datetime.now()
 
        user_log.user_ip = ipaddr
 
        sa.add(user_log)
 

	
rhodecode/model/db.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.model.db
 
    ~~~~~~~~~~~~~~~~~~
 

	
 
    Database Models for RhodeCode
 

	
 
    :created_on: Apr 08, 2010
 
    :author: marcink
 
    :copyright: (C) 2010-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 datetime
 
import traceback
 
from collections import defaultdict
 

	
 
from sqlalchemy import *
 
from sqlalchemy.ext.hybrid import hybrid_property
 
from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
 
from beaker.cache import cache_region, region_invalidate
 

	
 
from rhodecode.lib.vcs import get_backend
 
from rhodecode.lib.vcs.utils.helpers import get_scm
 
from rhodecode.lib.vcs.exceptions import VCSError
 
from rhodecode.lib.vcs.utils.lazy import LazyProperty
 

	
 
from rhodecode.lib import str2bool, safe_str, get_changeset_safe, safe_unicode
 
from rhodecode.lib.compat import json
 
from rhodecode.lib.caching_query import FromCache
 

	
 
from rhodecode.model.meta import Base, Session
 
import hashlib
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 
#==============================================================================
 
# BASE CLASSES
 
#==============================================================================
 

	
 
_hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
 

	
 

	
 
class ModelSerializer(json.JSONEncoder):
 
    """
 
    Simple Serializer for JSON,
 

	
 
    usage::
 

	
 
        to make object customized for serialization implement a __json__
 
        method that will return a dict for serialization into json
 

	
 
    example::
 

	
 
        class Task(object):
 

	
 
            def __init__(self, name, value):
 
                self.name = name
 
                self.value = value
 

	
 
            def __json__(self):
 
                return dict(name=self.name,
 
                            value=self.value)
 

	
 
    """
 

	
 
    def default(self, obj):
 

	
 
        if hasattr(obj, '__json__'):
 
            return obj.__json__()
 
        else:
 
            return json.JSONEncoder.default(self, obj)
 

	
 

	
 
class BaseModel(object):
 
    """
 
    Base Model for all classess
 
    """
 

	
 
    @classmethod
 
    def _get_keys(cls):
 
        """return column names for this model """
 
        return class_mapper(cls).c.keys()
 

	
 
    def get_dict(self):
 
        """
 
        return dict with keys and values corresponding
 
        to this model data """
 

	
 
        d = {}
 
@@ -292,179 +295,186 @@ class User(Base, BaseModel):
 
    user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
 
    repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
 

	
 
    group_member = relationship('UsersGroupMember', cascade='all')
 

	
 
    notifications = relationship('UserNotification',)
 

	
 
    @hybrid_property
 
    def email(self):
 
        return self._email
 

	
 
    @email.setter
 
    def email(self, val):
 
        self._email = val.lower() if val else None
 

	
 
    @property
 
    def full_name(self):
 
        return '%s %s' % (self.name, self.lastname)
 

	
 
    @property
 
    def full_name_or_username(self):
 
        return ('%s %s' % (self.name, self.lastname)
 
                if (self.name and self.lastname) else self.username)
 

	
 
    @property
 
    def full_contact(self):
 
        return '%s %s <%s>' % (self.name, self.lastname, self.email)
 

	
 
    @property
 
    def short_contact(self):
 
        return '%s %s' % (self.name, self.lastname)
 

	
 
    @property
 
    def is_admin(self):
 
        return self.admin
 

	
 
    def __repr__(self):
 
        return "<%s('id:%s:%s')>" % (self.__class__.__name__,
 
                                     self.user_id, self.username)
 

	
 
    @classmethod
 
    def get_by_username(cls, username, case_insensitive=False, cache=False):
 
        if case_insensitive:
 
            q = cls.query().filter(cls.username.ilike(username))
 
        else:
 
            q = cls.query().filter(cls.username == username)
 

	
 
        if cache:
 
            q = q.options(FromCache("sql_cache_short",
 
                                    "get_user_%s" % username))
 
            q = q.options(FromCache(
 
                            "sql_cache_short",
 
                            "get_user_%s" % _hash_key(username)
 
                          )
 
            )
 
        return q.scalar()
 

	
 
    @classmethod
 
    def get_by_api_key(cls, api_key, cache=False):
 
        q = cls.query().filter(cls.api_key == api_key)
 

	
 
        if cache:
 
            q = q.options(FromCache("sql_cache_short",
 
                                    "get_api_key_%s" % api_key))
 
        return q.scalar()
 

	
 
    @classmethod
 
    def get_by_email(cls, email, case_insensitive=False, cache=False):
 
        if case_insensitive:
 
            q = cls.query().filter(cls.email.ilike(email))
 
        else:
 
            q = cls.query().filter(cls.email == email)
 

	
 
        if cache:
 
            q = q.options(FromCache("sql_cache_short",
 
                                    "get_api_key_%s" % email))
 
        return q.scalar()
 

	
 
    def update_lastlogin(self):
 
        """Update user lastlogin"""
 
        self.last_login = datetime.datetime.now()
 
        Session.add(self)
 
        log.debug('updated user %s lastlogin' % self.username)
 

	
 
    def __json__(self):
 
        return dict(
 
            email=self.email,
 
            full_name=self.full_name,
 
            full_name_or_username=self.full_name_or_username,
 
            short_contact=self.short_contact,
 
            full_contact=self.full_contact
 
        )
 

	
 

	
 
class UserLog(Base, BaseModel):
 
    __tablename__ = 'user_logs'
 
    __table_args__ = {'extend_existing': True}
 
    user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
 
    repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
 
    repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
 

	
 
    @property
 
    def action_as_day(self):
 
        return datetime.date(*self.action_date.timetuple()[:3])
 

	
 
    user = relationship('User')
 
    repository = relationship('Repository',cascade='')
 

	
 

	
 
class UsersGroup(Base, BaseModel):
 
    __tablename__ = 'users_groups'
 
    __table_args__ = {'extend_existing': True}
 

	
 
    users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
 
    users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
 

	
 
    members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
 
    users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
 

	
 
    def __repr__(self):
 
        return '<userGroup(%s)>' % (self.users_group_name)
 

	
 
    @classmethod
 
    def get_by_group_name(cls, group_name, cache=False,
 
                          case_insensitive=False):
 
        if case_insensitive:
 
            q = cls.query().filter(cls.users_group_name.ilike(group_name))
 
        else:
 
            q = cls.query().filter(cls.users_group_name == group_name)
 
        if cache:
 
            q = q.options(FromCache("sql_cache_short",
 
                                    "get_user_%s" % group_name))
 
            q = q.options(FromCache(
 
                            "sql_cache_short",
 
                            "get_user_%s" % _hash_key(group_name)
 
                          )
 
            )
 
        return q.scalar()
 

	
 
    @classmethod
 
    def get(cls, users_group_id, cache=False):
 
        users_group = cls.query()
 
        if cache:
 
            users_group = users_group.options(FromCache("sql_cache_short",
 
                                    "get_users_group_%s" % users_group_id))
 
        return users_group.get(users_group_id)
 

	
 

	
 
class UsersGroupMember(Base, BaseModel):
 
    __tablename__ = 'users_groups_members'
 
    __table_args__ = {'extend_existing': True}
 

	
 
    users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
 

	
 
    user = relationship('User', lazy='joined')
 
    users_group = relationship('UsersGroup')
 

	
 
    def __init__(self, gr_id='', u_id=''):
 
        self.users_group_id = gr_id
 
        self.user_id = u_id
 

	
 

	
 
class Repository(Base, BaseModel):
 
    __tablename__ = 'repositories'
 
    __table_args__ = (
 
        UniqueConstraint('repo_name'),
 
        {'extend_existing': True},
 
    )
 

	
 
    repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
 
    clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
 
    repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
 
    user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
 
    private = Column("private", Boolean(), nullable=True, unique=None, default=None)
 
    enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
 
    enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
 
    description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
 

	
 
    fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
 
    group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
 

	
 
@@ -703,98 +713,101 @@ class RepoGroup(Base, BaseModel):
 
        {'extend_existing': True},
 
    )
 
    __mapper_args__ = {'order_by': 'group_name'}
 

	
 
    group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
 
    group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
 
    group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
 
    group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 

	
 
    repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
 
    users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
 

	
 
    parent_group = relationship('RepoGroup', remote_side=group_id)
 

	
 
    def __init__(self, group_name='', parent_group=None):
 
        self.group_name = group_name
 
        self.parent_group = parent_group
 

	
 
    def __repr__(self):
 
        return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
 
                                  self.group_name)
 

	
 
    @classmethod
 
    def groups_choices(cls):
 
        from webhelpers.html import literal as _literal
 
        repo_groups = [('', '')]
 
        sep = ' &raquo; '
 
        _name = lambda k: _literal(sep.join(k))
 

	
 
        repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
 
                              for x in cls.query().all()])
 

	
 
        repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
 
        return repo_groups
 

	
 
    @classmethod
 
    def url_sep(cls):
 
        return '/'
 

	
 
    @classmethod
 
    def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
 
        if case_insensitive:
 
            gr = cls.query()\
 
                .filter(cls.group_name.ilike(group_name))
 
        else:
 
            gr = cls.query()\
 
                .filter(cls.group_name == group_name)
 
        if cache:
 
            gr = gr.options(FromCache("sql_cache_short",
 
                                          "get_group_%s" % group_name))
 
            gr = gr.options(FromCache(
 
                            "sql_cache_short",
 
                            "get_group_%s" % _hash_key(group_name)
 
                            )
 
            )
 
        return gr.scalar()
 

	
 
    @property
 
    def parents(self):
 
        parents_recursion_limit = 5
 
        groups = []
 
        if self.parent_group is None:
 
            return groups
 
        cur_gr = self.parent_group
 
        groups.insert(0, cur_gr)
 
        cnt = 0
 
        while 1:
 
            cnt += 1
 
            gr = getattr(cur_gr, 'parent_group', None)
 
            cur_gr = cur_gr.parent_group
 
            if gr is None:
 
                break
 
            if cnt == parents_recursion_limit:
 
                # this will prevent accidental infinit loops
 
                log.error('group nested more than %s' %
 
                          parents_recursion_limit)
 
                break
 

	
 
            groups.insert(0, gr)
 
        return groups
 

	
 
    @property
 
    def children(self):
 
        return RepoGroup.query().filter(RepoGroup.parent_group == self)
 

	
 
    @property
 
    def name(self):
 
        return self.group_name.split(RepoGroup.url_sep())[-1]
 

	
 
    @property
 
    def full_path(self):
 
        return self.group_name
 

	
 
    @property
 
    def full_path_splitted(self):
 
        return self.group_name.split(RepoGroup.url_sep())
 

	
 
    @property
 
    def repositories(self):
 
        return Repository.query().filter(Repository.group == self)
 

	
 
    @property
 
    def repositories_recursive_count(self):
rhodecode/model/forms.py
Show inline comments
 
@@ -300,122 +300,136 @@ def ValidRepoName(edit, old_data):
 
                repo_name_full = repo_name
 

	
 
            value['repo_name_full'] = repo_name_full
 
            rename = old_data.get('repo_name') != repo_name_full
 
            create = not edit
 
            if  rename or create:
 

	
 
                if group_path != '':
 
                    if Repository.get_by_repo_name(repo_name_full):
 
                        e_dict = {
 
                            'repo_name': _('This repository already exists in '
 
                                           'a group "%s"') % gr.group_name
 
                        }
 
                        raise formencode.Invalid('', value, state,
 
                                                 error_dict=e_dict)
 
                elif RepoGroup.get_by_group_name(repo_name_full):
 
                        e_dict = {
 
                            'repo_name': _('There is a group with this name '
 
                                           'already "%s"') % repo_name_full
 
                        }
 
                        raise formencode.Invalid('', value, state,
 
                                                 error_dict=e_dict)
 

	
 
                elif Repository.get_by_repo_name(repo_name_full):
 
                        e_dict = {'repo_name': _('This repository '
 
                                                'already exists')}
 
                        raise formencode.Invalid('', value, state,
 
                                                 error_dict=e_dict)
 

	
 
            return value
 

	
 
    return _ValidRepoName
 

	
 

	
 
def ValidForkName(*args, **kwargs):
 
    return ValidRepoName(*args, **kwargs)
 

	
 

	
 
def SlugifyName():
 
    class _SlugifyName(formencode.validators.FancyValidator):
 

	
 
        def to_python(self, value, state):
 
            return repo_name_slug(value)
 

	
 
    return _SlugifyName
 

	
 

	
 
def ValidCloneUri():
 
    from rhodecode.lib.utils import make_ui
 

	
 
    def url_handler(repo_type, url, proto, ui=None):
 
        if repo_type == 'hg':
 
    from mercurial.httprepo import httprepository, httpsrepository
 
    from rhodecode.lib.utils import make_ui
 
            if proto == 'https':
 
                httpsrepository(make_ui('db'), url).capabilities
 
            elif proto == 'http':
 
                httprepository(make_ui('db'), url).capabilities
 
        elif repo_type == 'git':
 
            #TODO: write a git url validator
 
            pass
 

	
 
    class _ValidCloneUri(formencode.validators.FancyValidator):
 

	
 
        def to_python(self, value, state):
 
            if not value:
 

	
 
            repo_type = value.get('repo_type')
 
            url = value.get('clone_uri')
 
            e_dict = {'clone_uri': _('invalid clone url')}
 

	
 
            if not url:
 
                pass
 
            elif value.startswith('https'):
 
            elif url.startswith('https'):
 
                try:
 
                    httpsrepository(make_ui('db'), value).capabilities
 
                    url_handler(repo_type, url, 'https', make_ui('db'))
 
                except Exception:
 
                    log.error(traceback.format_exc())
 
                    raise formencode.Invalid(_('invalid clone url'), value,
 
                                             state)
 
            elif value.startswith('http'):
 
                    raise formencode.Invalid('', value, state, error_dict=e_dict)
 
            elif url.startswith('http'):
 
                try:
 
                    httprepository(make_ui('db'), value).capabilities
 
                    url_handler(repo_type, url, 'http', make_ui('db'))
 
                except Exception:
 
                    log.error(traceback.format_exc())
 
                    raise formencode.Invalid(_('invalid clone url'), value,
 
                                             state)
 
                    raise formencode.Invalid('', value, state, error_dict=e_dict)
 
            else:
 
                raise formencode.Invalid(_('Invalid clone url, provide a '
 
                                           'valid clone http\s url'), value,
 
                                         state)
 
                e_dict = {'clone_uri': _('Invalid clone url, provide a '
 
                                         'valid clone http\s url')}
 
                raise formencode.Invalid('', value, state, error_dict=e_dict)
 

	
 
            return value
 

	
 
    return _ValidCloneUri
 

	
 

	
 
def ValidForkType(old_data):
 
    class _ValidForkType(formencode.validators.FancyValidator):
 

	
 
        def to_python(self, value, state):
 
            if old_data['repo_type'] != value:
 
                raise formencode.Invalid(_('Fork have to be the same '
 
                                           'type as original'), value, state)
 

	
 
            return value
 
    return _ValidForkType
 

	
 

	
 
def ValidPerms(type_='repo'):
 
    if type_ == 'group':
 
        EMPTY_PERM = 'group.none'
 
    elif type_ == 'repo':
 
        EMPTY_PERM = 'repository.none'
 

	
 
    class _ValidPerms(formencode.validators.FancyValidator):
 
        messages = {
 
            'perm_new_member_name':
 
                _('This username or users group name is not valid')
 
        }
 

	
 
        def to_python(self, value, state):
 
            perms_update = []
 
            perms_new = []
 
            # build a list of permission to update and new permission to create
 
            for k, v in value.items():
 
                # means new added member to permissions
 
                if k.startswith('perm_new_member'):
 
                    new_perm = value.get('perm_new_member', False)
 
                    new_member = value.get('perm_new_member_name', False)
 
                    new_type = value.get('perm_new_member_type')
 

	
 
                    if new_member and new_perm:
 
                        if (new_member, new_perm, new_type) not in perms_new:
 
                            perms_new.append((new_member, new_perm, new_type))
 
                elif k.startswith('u_perm_') or k.startswith('g_perm_'):
 
                    member = k[7:]
 
                    t = {'u': 'user',
 
                         'g': 'users_group'
 
                    }[k[0]]
 
@@ -600,110 +614,111 @@ def ReposGroupForm(edit=False, old_data=
 
        filter_extra_fields = False
 

	
 
        group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
 
                               SlugifyName())
 
        group_description = UnicodeString(strip=True, min=1,
 
                                                not_empty=True)
 
        group_parent_id = OneOf(available_groups, hideList=False,
 
                                        testValueList=True,
 
                                        if_missing=None, not_empty=False)
 

	
 
        chained_validators = [ValidReposGroup(edit, old_data), ValidPerms('group')]
 

	
 
    return _ReposGroupForm
 

	
 

	
 
def RegisterForm(edit=False, old_data={}):
 
    class _RegisterForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = True
 
        username = All(ValidUsername(edit, old_data),
 
                       UnicodeString(strip=True, min=1, not_empty=True))
 
        password = All(UnicodeString(strip=True, min=6, not_empty=True))
 
        password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
 
        active = StringBoolean(if_missing=False)
 
        name = UnicodeString(strip=True, min=1, not_empty=False)
 
        lastname = UnicodeString(strip=True, min=1, not_empty=False)
 
        email = All(Email(not_empty=True), UniqSystemEmail(old_data))
 

	
 
        chained_validators = [ValidPasswordsMatch, ValidPassword]
 

	
 
    return _RegisterForm
 

	
 

	
 
def PasswordResetForm():
 
    class _PasswordResetForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = True
 
        email = All(ValidSystemEmail(), Email(not_empty=True))
 
    return _PasswordResetForm
 

	
 

	
 
def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
 
             repo_groups=[]):
 
    class _RepoForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = False
 
        repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
 
                        SlugifyName())
 
        clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False),
 
                        ValidCloneUri()())
 
        clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False))
 
        repo_group = OneOf(repo_groups, hideList=True)
 
        repo_type = OneOf(supported_backends)
 
        description = UnicodeString(strip=True, min=1, not_empty=True)
 
        private = StringBoolean(if_missing=False)
 
        enable_statistics = StringBoolean(if_missing=False)
 
        enable_downloads = StringBoolean(if_missing=False)
 

	
 
        if edit:
 
            #this is repo owner
 
            user = All(UnicodeString(not_empty=True), ValidRepoUser)
 

	
 
        chained_validators = [ValidRepoName(edit, old_data), ValidPerms()]
 
        chained_validators = [ValidCloneUri()(),
 
                              ValidRepoName(edit, old_data),
 
                              ValidPerms()]
 
    return _RepoForm
 

	
 

	
 
def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
 
                 repo_groups=[]):
 
    class _RepoForkForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = False
 
        repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
 
                        SlugifyName())
 
        repo_group = OneOf(repo_groups, hideList=True)
 
        repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
 
        description = UnicodeString(strip=True, min=1, not_empty=True)
 
        private = StringBoolean(if_missing=False)
 
        copy_permissions = StringBoolean(if_missing=False)
 
        update_after_clone = StringBoolean(if_missing=False)
 
        fork_parent_id = UnicodeString()
 
        chained_validators = [ValidForkName(edit, old_data)]
 

	
 
    return _RepoForkForm
 

	
 

	
 
def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
 
                     repo_groups=[]):
 
    class _RepoForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = False
 
        repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
 
                        SlugifyName())
 
        description = UnicodeString(strip=True, min=1, not_empty=True)
 
        repo_group = OneOf(repo_groups, hideList=True)
 
        private = StringBoolean(if_missing=False)
 

	
 
        chained_validators = [ValidRepoName(edit, old_data), ValidPerms(),
 
                              ValidSettings]
 
    return _RepoForm
 

	
 

	
 
def ApplicationSettingsForm():
 
    class _ApplicationSettingsForm(formencode.Schema):
 
        allow_extra_fields = True
 
        filter_extra_fields = False
 
        rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
 
        rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
 
        rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
 

	
 
    return _ApplicationSettingsForm
 

	
rhodecode/model/repos_group.py
Show inline comments
 
@@ -142,110 +142,110 @@ class ReposGroupModel(BaseModel):
 
            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):
 

	
 
        try:
 
            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']:
 
                if member_type == 'user':
 
                    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
 
                    )
 

	
 
            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)
 

	
 
            self.__rename_group(old_path, new_path)
 

	
 
            return repos_group
 
        except:
 
            log.error(traceback.format_exc())
 
            raise
 

	
 
    def delete(self, users_group_id):
 
        try:
 
            users_group = RepoGroup.get(users_group_id)
 
            self.sa.delete(users_group)
 
            self.__delete_group(users_group)
 
        except:
 
            log.error(traceback.format_exc())
 
            raise
 

	
 
    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
 

	
 
        :param repos_group: Instance of ReposGroup, repositories_group_id,
 
            or repositories_group name
rhodecode/tests/functional/test_admin_users_groups.py
Show inline comments
 
from rhodecode.tests import *
 
from rhodecode.model.db import UsersGroup
 
from rhodecode.model.db import UsersGroup, UsersGroupToPerm, Permission
 

	
 
TEST_USERS_GROUP = 'admins_test'
 

	
 

	
 
class TestAdminUsersGroupsController(TestController):
 

	
 
    def test_index(self):
 
        response = self.app.get(url('users_groups'))
 
        # Test response...
 

	
 
    def test_index_as_xml(self):
 
        response = self.app.get(url('formatted_users_groups', format='xml'))
 

	
 
    def test_create(self):
 
        self.log_user()
 
        users_group_name = TEST_USERS_GROUP
 
        response = self.app.post(url('users_groups'),
 
                                 {'users_group_name':users_group_name,
 
                                  'active':True})
 
        response.follow()
 

	
 
        self.checkSessionFlash(response,
 
                               'created users group %s' % TEST_USERS_GROUP)
 

	
 
    def test_new(self):
 
        response = self.app.get(url('new_users_group'))
 

	
 
    def test_new_as_xml(self):
 
        response = self.app.get(url('formatted_new_users_group', format='xml'))
 

	
 
    def test_update(self):
 
        response = self.app.put(url('users_group', id=1))
 

	
 
    def test_update_browser_fakeout(self):
 
        response = self.app.post(url('users_group', id=1),
 
                                 params=dict(_method='put'))
 

	
 
    def test_delete(self):
 
        self.log_user()
 
        users_group_name = TEST_USERS_GROUP + 'another'
 
        response = self.app.post(url('users_groups'),
 
                                 {'users_group_name':users_group_name,
 
                                  'active':True})
 
        response.follow()
 

	
 
        self.checkSessionFlash(response,
 
                               'created users group %s' % users_group_name)
 

	
 

	
 
        gr = self.Session.query(UsersGroup)\
 
                           .filter(UsersGroup.users_group_name ==
 
                                   users_group_name).one()
 

	
 
        response = self.app.delete(url('users_group', id=gr.users_group_id))
 

	
 
        gr = self.Session.query(UsersGroup)\
 
                           .filter(UsersGroup.users_group_name ==
 
                                   users_group_name).scalar()
 

	
 
        self.assertEqual(gr, None)
 

	
 
    def test_enable_repository_read_on_group(self):
 
        self.log_user()
 
        users_group_name = TEST_USERS_GROUP + 'another2'
 
        response = self.app.post(url('users_groups'),
 
                                 {'users_group_name': users_group_name,
 
                                  'active':True})
 
        response.follow()
 

	
 
        ug = UsersGroup.get_by_group_name(users_group_name)
 
        self.checkSessionFlash(response,
 
                               'created users group %s' % users_group_name)
 

	
 
        response = self.app.put(url('users_group_perm', id=ug.users_group_id),
 
                                 {'create_repo_perm': True})
 

	
 
        response.follow()
 
        ug = UsersGroup.get_by_group_name(users_group_name)
 
        p = Permission.get_by_key('hg.create.repository')
 
        # check if user has this perm
 
        perms = UsersGroupToPerm.query()\
 
            .filter(UsersGroupToPerm.users_group == ug).all()
 
        perms = [[x.__dict__['users_group_id'],
 
                  x.__dict__['permission_id'],] for x in perms]
 
        self.assertEqual(
 
            perms,
 
            [[ug.users_group_id, p.permission_id]]
 
        )
 

	
 
        # DELETE !
 
        ug = UsersGroup.get_by_group_name(users_group_name)
 
        ugid = ug.users_group_id
 
        response = self.app.delete(url('users_group', id=ug.users_group_id))
 
        response = response.follow()
 
        gr = self.Session.query(UsersGroup)\
 
                           .filter(UsersGroup.users_group_name ==
 
                                   users_group_name).scalar()
 

	
 
        self.assertEqual(gr, None)
 
        p = Permission.get_by_key('hg.create.repository')
 
        perms = UsersGroupToPerm.query()\
 
            .filter(UsersGroupToPerm.users_group_id == ugid).all()
 
        perms = [[x.__dict__['users_group_id'],
 
                  x.__dict__['permission_id'],] for x in perms]
 
        self.assertEqual(
 
            perms,
 
            []
 
        )
 

	
 
    def test_delete_browser_fakeout(self):
 
        response = self.app.post(url('users_group', id=1),
 
                                 params=dict(_method='delete'))
 

	
 
    def test_show(self):
 
        response = self.app.get(url('users_group', id=1))
 

	
 
    def test_show_as_xml(self):
 
        response = self.app.get(url('formatted_users_group', id=1, format='xml'))
 

	
 
    def test_edit(self):
 
        response = self.app.get(url('edit_users_group', id=1))
 

	
 
    def test_edit_as_xml(self):
 
        response = self.app.get(url('formatted_edit_users_group', id=1, format='xml'))
 

	
 
    def test_assign_members(self):
 
        pass
 

	
 
    def test_add_create_permission(self):
 
        pass
 

	
 
    def test_revoke_members(self):
 
        pass
rhodecode/tests/test_models.py
Show inline comments
 
import os
 
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()
 
    return gr
 

	
 

	
 
class TestReposGroups(unittest.TestCase):
 

	
 
    def setUp(self):
 
        self.g1 = _make_group('test1', skip_if_exists=True)
 
        Session.commit()
 
        self.g2 = _make_group('test2', skip_if_exists=True)
 
        Session.commit()
 
        self.g3 = _make_group('test3', skip_if_exists=True)
 
        Session.commit()
 

	
 
    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,
 
        form_data = dict(
 
            group_name=path,
 
                         group_description=desc,
 
                         group_parent_id=parent_id,
 
                         perms_updates=[],
 
                         perms_new=[])
 

	
 
            perms_new=[]
 
        )
 
        gr = ReposGroupModel().update(id_, form_data)
 
        return gr
 

	
 
    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.__delete_group(sg1.group_id)
 

	
 
        self.assertEqual(RepoGroup.get(sg1.group_id), None)
 
        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')
 
        Session.commit()
 
        g2 = _make_group('t22',parent_id=g1.group_id)
 
        Session.commit()
 

	
 
        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)
 
        Session.commit()
 

	
 
        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')
 
        Session.commit()
 
        self.assertEqual(User.get_by_username(u'test_user'), usr)
 

	
 
        # make users group
 
        users_group = UsersGroupModel().create('some_example_group')
 
        Session.commit()
 

	
 
        UsersGroupModel().add_user_to_group(users_group, usr)
 
        Session.commit()
 

	
 
        self.assertEqual(UsersGroup.get(users_group.users_group_id), users_group)
 
        self.assertEqual(UsersGroupMember.query().count(), 1)
 
        UserModel().delete(usr.user_id)
 
        Session.commit()
 

	
 
        self.assertEqual(UsersGroupMember.query().all(), [])
 

	
 

	
 
class TestNotifications(unittest.TestCase):
 

	
 
    def __init__(self, methodName='runTest'):
 
        Session.remove()
 
        self.u1 = UserModel().create_or_update(username=u'u1',
 
                                        password=u'qweqwe',
 
                                        email=u'u1@rhodecode.org',
 
                                        name=u'u1', lastname=u'u1')
 
        Session.commit()
 
        self.u1 = self.u1.user_id
 

	
 
        self.u2 = UserModel().create_or_update(username=u'u2',
 
                                        password=u'qweqwe',
 
                                        email=u'u2@rhodecode.org',
 
                                        name=u'u2', lastname=u'u3')
 
        Session.commit()
 
        self.u2 = self.u2.user_id
 

	
 
        self.u3 = UserModel().create_or_update(username=u'u3',
 
                                        password=u'qweqwe',
 
                                        email=u'u3@rhodecode.org',
0 comments (0 inline, 0 general)