@@ -59,48 +59,52 @@ def make_map(config):
function=check_repo))
m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
action="edit", conditions=dict(method=["GET"],
m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
m.connect("repo", "/repos/{repo_name:.*}",
action="show", conditions=dict(method=["GET"],
m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
#ajax delete repo perm user
m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
action="delete_perm_user", conditions=dict(method=["DELETE"],
#settings actions
m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
action="repo_stats", conditions=dict(method=["DELETE"],
m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
action="repo_cache", conditions=dict(method=["DELETE"],
#ADMIN USER REST ROUTES
map.resource('user', 'users', controller='admin/users', path_prefix='/_admin')
map.resource('users_group', 'users_groups', controller='admin/users_groups', path_prefix='/_admin')
#ADMIN GROUP REST ROUTES
map.resource('group', 'groups', controller='admin/groups', path_prefix='/_admin')
#ADMIN PERMISSIONS REST ROUTES
map.resource('permission', 'permissions', controller='admin/permissions', path_prefix='/_admin')
##ADMIN LDAP SETTINGS
map.connect('ldap_settings', '/_admin/ldap', controller='admin/ldap_settings',
action='ldap_settings', conditions=dict(method=["POST"]))
map.connect('ldap_home', '/_admin/ldap', controller='admin/ldap_settings',)
#ADMIN SETTINGS REST ROUTES
with map.submapper(path_prefix='/_admin', controller='admin/settings') as m:
m.connect("admin_settings", "/settings",
action="create", conditions=dict(method=["POST"]))
action="index", conditions=dict(method=["GET"]))
m.connect("formatted_admin_settings", "/settings.{format}",
m.connect("admin_new_setting", "/settings/new",
@@ -20,80 +20,103 @@
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
import logging
import traceback
import formencode
from formencode import htmlfill
from pylons import request, session, tmpl_context as c, url, config
from pylons.controllers.util import abort, redirect
from pylons.i18n.translation import _
from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
from rhodecode.lib import helpers as h
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
fill_perms
from rhodecode.lib.base import BaseController, render
from rhodecode.model.db import User, UsersGroup
from rhodecode.model.forms import UserForm
from rhodecode.model.forms import UserForm, UsersGroupForm
from rhodecode.model.user import UserModel
from rhodecode.model.users_group import UsersGroupModel
log = logging.getLogger(__name__)
class UsersGroupsController(BaseController):
"""REST Controller styled on the Atom Publishing Protocol"""
# To properly map this controller, ensure your config/routing.py
# file has a resource setup:
# map.resource('users_group', 'users_groups')
@LoginRequired()
@HasPermissionAllDecorator('hg.admin')
def __before__(self):
c.admin_user = session.get('admin_user')
c.admin_username = session.get('admin_username')
super(UsersGroupsController, self).__before__()
c.available_permissions = config['available_permissions']
def index(self, format='html'):
"""GET /users_groups: All items in the collection"""
# url('users_groups')
c.users_groups_list = []
c.users_groups_list = self.sa.query(UsersGroup).all()
return render('admin/users_groups/users_groups.html')
def create(self):
"""POST /users_groups: Create a new item"""
users_group_model = UsersGroupModel()
users_group_form = UsersGroupForm()()
try:
form_result = users_group_form.to_python(dict(request.POST))
users_group_model.create(form_result)
h.flash(_('created users group %s') % form_result['users_group_name'],
category='success')
#action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
except formencode.Invalid, errors:
return htmlfill.render(
render('admin/users_groups/users_group_add.html'),
defaults=errors.value,
errors=errors.error_dict or {},
prefix_error=False,
encoding="UTF-8")
except Exception:
log.error(traceback.format_exc())
h.flash(_('error occurred during creation of users group %s') \
% request.POST.get('users_group_name'), category='error')
return redirect(url('users_groups'))
def new(self, format='html'):
"""GET /users_groups/new: Form to create a new item"""
# url('new_users_group')
return render('admin/users_groups/users_group_add.html')
def update(self, id):
"""PUT /users_groups/id: Update an existing item"""
# Forms posted to this method should contain a hidden field:
# <input type="hidden" name="_method" value="PUT" />
# Or using helpers:
# h.form(url('users_group', id=ID),
# method='put')
# url('users_group', id=ID)
def delete(self, id):
"""DELETE /users_groups/id: Delete an existing item"""
# <input type="hidden" name="_method" value="DELETE" />
# method='delete')
def show(self, id, format='html'):
"""GET /users_groups/id: Show a specific item"""
def edit(self, id, format='html'):
@@ -137,61 +137,60 @@ class User(Base, BaseModel):
log.debug('updated user %s lastlogin', self.username)
except (DatabaseError,):
session.rollback()
class UserLog(Base, BaseModel):
__tablename__ = 'user_logs'
__table_args__ = {'useexisting':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(length=None, convert_unicode=False, assert_unicode=None), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
repository_name = Column("repository_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
user_ip = Column("user_ip", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
action = Column("action", String(length=None, 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)
user = relation('User')
repository = relation('Repository')
class UsersGroup(Base, BaseModel):
__tablename__ = 'users_groups'
user_group_id = Column("users_groups_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
user_group_name = Column("user_group_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
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=None, 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 = relation('UsersGroupMember')
class UsersGroupMember(Base, BaseModel):
__tablename__ = 'users_groups_members'
user_groups_members_id = Column("user_groups_members_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_groups_id'), nullable=False, unique=None, default=None)
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)
users_group = relation('UsersGroup')
class Repository(Base, BaseModel):
__tablename__ = 'repositories'
__table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
repo_name = Column("repo_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
repo_type = Column("repo_type", String(length=None, 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)
description = Column("description", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
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)
fork = relation('Repository', remote_side=repo_id)
group = relation('Group')
repo_to_perm = relation('RepoToPerm', cascade='all')
stats = relation('Statistics', cascade='all', uselist=False)
@@ -236,49 +235,49 @@ class RepoToPerm(Base, BaseModel):
__table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
permission = relation('Permission')
class UserToPerm(Base, BaseModel):
__tablename__ = 'user_to_perm'
__table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
class UsersGroupToPerm(Base, BaseModel):
__tablename__ = 'users_group_to_perm'
__table_args__ = (UniqueConstraint('users_group_id', 'permission_id'), {'useexisting':True})
users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_groups_id'), nullable=False, unique=None, default=None)
class GroupToPerm(Base, BaseModel):
__tablename__ = 'group_to_perm'
__table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'useexisting':True})
group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
class Statistics(Base, BaseModel):
__tablename__ = 'statistics'
__table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
@@ -15,48 +15,49 @@ ignore_key_missing False If Tru
<name> = formencode.validators.<name of validator>
<name> must equal form name
list=[1,2,3,4,5]
for SELECT use formencode.All(OneOf(list), Int())
"""
import os
import re
from formencode import All
from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
Email, Bool, StringBoolean
import rhodecode.lib.helpers as h
from rhodecode.lib.auth import authenticate, get_crypt_password
from rhodecode.lib.exceptions import LdapImportError
from rhodecode.model import meta
from rhodecode.model.repo import RepoModel
from rhodecode.model.db import User
from rhodecode import BACKENDS
from webhelpers.pylonslib.secure_form import authentication_token
#this is needed to translate the messages using _() in validators
class State_obj(object):
_ = staticmethod(_)
#===============================================================================
# VALIDATORS
class ValidAuthToken(formencode.validators.FancyValidator):
messages = {'invalid_token':_('Token mismatch')}
def validate_python(self, value, state):
if value != authentication_token():
raise formencode.Invalid(self.message('invalid_token', state,
search_number=value), value, state)
def ValidUsername(edit, old_data):
@@ -67,48 +68,80 @@ def ValidUsername(edit, old_data):
raise formencode.Invalid(_('Invalid username'), value, state)
#check if user is unique
old_un = None
if edit:
old_un = UserModel().get(old_data.get('user_id')).username
if old_un != value or not edit:
if UserModel().get_by_username(value, cache=False,
case_insensitive=True):
raise formencode.Invalid(_('This username already exists') ,
value, state)
if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_]+$', value) is None:
raise formencode.Invalid(_('Username may only contain '
'alphanumeric characters underscores '
'or dashes and must begin with '
'alphanumeric character'),
return _ValidUsername
def ValidUsersGroup(edit, old_data):
class _ValidUsersGroup(formencode.validators.FancyValidator):
if value in ['default']:
raise formencode.Invalid(_('Invalid group name'), value, state)
#check if group is unique
old_un = UserModel().get(old_data.get('users_group_id')).username
if UsersGroupModel().get_by_groupname(value, cache=False,
raise formencode.Invalid(_('This users group already exists') ,
if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
raise formencode.Invalid(_('Group name may only contain '
'alphanumeric characters underscores, '
'periods or dashes and must begin with '
return _ValidUsersGroup
class ValidPassword(formencode.validators.FancyValidator):
def to_python(self, value, state):
if value:
if value.get('password'):
value['password'] = get_crypt_password(value['password'])
except UnicodeEncodeError:
e_dict = {'password':_('Invalid characters in password')}
raise formencode.Invalid('', value, state, error_dict=e_dict)
if value.get('password_confirmation'):
value['password_confirmation'] = \
get_crypt_password(value['password_confirmation'])
e_dict = {'password_confirmation':_('Invalid characters in password')}
if value.get('new_password'):
value['new_password'] = \
@@ -347,48 +380,61 @@ class LoginForm(formencode.Schema):
#chained validators have access to all data
chained_validators = [ValidAuth]
def UserForm(edit=False, old_data={}):
class _UserForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = True
username = All(UnicodeString(strip=True, min=1, not_empty=True),
ValidUsername(edit, old_data))
new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
admin = StringBoolean(if_missing=False)
else:
password = All(UnicodeString(strip=True, min=6, not_empty=True))
active = StringBoolean(if_missing=False)
name = UnicodeString(strip=True, min=1, not_empty=True)
lastname = UnicodeString(strip=True, min=1, not_empty=True)
email = All(Email(not_empty=True), UniqSystemEmail(old_data))
chained_validators = [ValidPassword]
return _UserForm
def UsersGroupForm(edit=False, old_data={}):
class _UsersGroupForm(formencode.Schema):
users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
ValidUsersGroup(edit, old_data))
users_group_active = StringBoolean(if_missing=False)
return _UsersGroupForm
def RegisterForm(edit=False, old_data={}):
class _RegisterForm(formencode.Schema):
username = All(ValidUsername(edit, old_data),
UnicodeString(strip=True, min=1, not_empty=True))
password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
chained_validators = [ValidPasswordsMatch, ValidPassword]
return _RegisterForm
def PasswordResetForm():
class _PasswordResetForm(formencode.Schema):
email = All(ValidSystemEmail(), Email(not_empty=True))
return _PasswordResetForm
file renamed from rhodecode/model/user_group.py to rhodecode/model/users_group.py
@@ -3,24 +3,77 @@
rhodecode.model.user_group
~~~~~~~~~~~~~~~~~~~~~~~~~~
users groups model for RhodeCode
:created_on: Jan 25, 2011
:author: marcink
:copyright: (C) 2009-2011 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; version 2
# of the License or (at your opinion) any later version of the license.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
from rhodecode.model import BaseModel
from rhodecode.model.caching_query import FromCache
from rhodecode.model.db import UsersGroup
from sqlalchemy.exc import DatabaseError
class UsersGroupModel(BaseModel):
def get(self, users_group_id, cache=False):
users_group = self.sa.query(UsersGroup)
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)
def get_by_groupname(self, users_group_name, cache=False, case_insensitive=False):
if case_insensitive:
user = self.sa.query(UsersGroup)\
.filter(UsersGroup.users_group_name.ilike(users_group_name))
.filter(UsersGroup.users_group_name == users_group_name)
user = user.options(FromCache("sql_cache_short",
"get_user_%s" % users_group_name))
return user.scalar()
def create(self, form_data):
new_users_group = UsersGroup()
for k, v in form_data.items():
setattr(new_users_group, k, v)
self.sa.add(new_users_group)
self.sa.commit()
except:
self.sa.rollback()
raise
## -*- coding: utf-8 -*-
<%inherit file="/base/base.html"/>
<%def name="title()">
${_('Add users group')} - ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs_links()">
${h.link_to(_('Admin'),h.url('admin_home'))}
»
${h.link_to(_('Users groups'),h.url('users_groups'))}
${_('add new users group')}
<%def name="page_nav()">
${self.menu('admin')}
<%def name="main()">
<div class="box">
<!-- box / title -->
<div class="title">
${self.breadcrumbs()}
</div>
<!-- end box / title -->
${h.form(url('users_groups'))}
<div class="form">
<!-- fields -->
<div class="fields">
<div class="field">
<div class="label">
<label for="users_group_name">${_('Group name')}:</label>
<div class="input">
${h.text('users_group_name',class_='small')}
<div class="label label-checkbox">
<label for="users_group_active">${_('Active')}:</label>
<div class="checkboxes">
${h.checkbox('users_group_active',value=True)}
<div class="buttons">
${h.submit('save','save',class_="ui-button")}
${h.end_form()}
\ No newline at end of file
@@ -8,47 +8,46 @@
${h.link_to(_('Admin'),h.url('admin_home'))} » ${_('Users groups')}
<ul class="links">
<li>
<span>${h.link_to(u'ADD NEW USER GROUP',h.url('new_users_group'))}</span>
</li>
</ul>
<div class="table">
<table class="table_disp">
<tr class="header">
<th></th>
<th class="left">${_('group name')}</th>
<th class="left">${_('members')}</th>
<th class="left">${_('active')}</th>
<th class="left">${_('action')}</th>
</tr>
%for cnt,u_group in enumerate(c.users_groups_list):
<tr class="parity${cnt%2}">
<td>${h.link_to(u_group.groupname,h.url('edit_user_group', id=u_group.group_id))}</td>
<td>${u_group.members}</td>
<td>${h.bool2icon(u_group.active)}</td>
<td>${h.link_to(u_group.users_group_name,h.url('edit_users_group', id=u_group.users_group_id))}</td>
<td>${len(u_group.members)}</td>
<td>${h.bool2icon(u_group.users_group_active)}</td>
<td>
${h.form(url('users_group', id=group.group_id),method='delete')}
${h.submit('remove_','delete',id="remove_group_%s" % group.group_id,
class_="delete_icon action_button",onclick="return confirm('Confirm to delete this user group');")}
${h.form(url('users_group', id=u_group.users_group_id),method='delete')}
${h.submit('remove_','delete',id="remove_group_%s" % u_group.users_group_id,
class_="delete_icon action_button",onclick="return confirm('Confirm to delete this users group');")}
</td>
%endfor
</table>
Status change: