# HG changeset patch # User Marcin Kuzminski # Date 2010-08-21 14:34:37 # Node ID 3ed2d46a2ca7983fbccbd3b8eb45afb6ff713bcf # Parent 25ab66a269756161eb60d83871db2647f7bfa138 permission refactoring, Implemented views for default permissions, fixes #23 user registration is controlled by permission system. Implemented manual registration option websetup fills default permissions diff --git a/pylons_app/controllers/admin/permissions.py b/pylons_app/controllers/admin/permissions.py --- a/pylons_app/controllers/admin/permissions.py +++ b/pylons_app/controllers/admin/permissions.py @@ -2,7 +2,7 @@ # encoding: utf-8 # permissions controller for pylons # Copyright (C) 2009-2010 Marcin Kuzminski - +# # 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 @@ -22,6 +22,7 @@ Created on April 27, 2010 permissions controller for pylons @author: marcink """ + from formencode import htmlfill from pylons import request, session, tmpl_context as c, url from pylons.controllers.util import abort, redirect @@ -30,10 +31,12 @@ from pylons_app.lib import helpers as h from pylons_app.lib.auth import LoginRequired, HasPermissionAllDecorator from pylons_app.lib.base import BaseController, render from pylons_app.model.db import User, UserLog -from pylons_app.model.forms import UserForm +from pylons_app.model.forms import UserForm, DefaultPermissionsForm +from pylons_app.model.permission_model import PermissionModel from pylons_app.model.user_model import UserModel import formencode import logging +import traceback log = logging.getLogger(__name__) @@ -44,16 +47,30 @@ class PermissionsController(BaseControll # map.resource('permission', 'permissions') @LoginRequired() - #@HasPermissionAllDecorator('hg.admin') + @HasPermissionAllDecorator('hg.admin') def __before__(self): c.admin_user = session.get('admin_user') c.admin_username = session.get('admin_username') super(PermissionsController, self).__before__() + self.perms_choices = [('repository.none', _('None'),), + ('repository.read', _('Read'),), + ('repository.write', _('Write'),), + ('repository.admin', _('Admin'),)] + self.register_choices = [ + ('hg.register.none', 'disabled'), + ('hg.register.manual_activate', + _('allowed with manual account activation')), + ('hg.register.auto_activate', + _('allowed with automatic account activation')), ] + + self.create_choices = [('hg.create.none', _('Disabled')), + ('hg.create.repository', _('Enabled'))] + + def index(self, format='html'): """GET /permissions: All items in the collection""" # url('permissions') - return render('admin/permissions/permissions.html') def create(self): """POST /permissions: Create a new item""" @@ -71,6 +88,38 @@ class PermissionsController(BaseControll # h.form(url('permission', id=ID), # method='put') # url('permission', id=ID) + + permission_model = PermissionModel() + + _form = DefaultPermissionsForm([x[0] for x in self.perms_choices], + [x[0] for x in self.register_choices], + [x[0] for x in self.create_choices])() + + try: + form_result = _form.to_python(dict(request.POST)) + permission_model.update(form_result) + h.flash(_('Default permissions updated succesfully'), + category='success') + + except formencode.Invalid as errors: + c.perms_choices = self.perms_choices + c.register_choices = self.register_choices + c.create_choices = self.create_choices + + return htmlfill.render( + render('admin/permissions/permissions.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 occured during update of permissions'), + category='error') + + return redirect(url('edit_permission', id=id)) + + def delete(self, id): """DELETE /permissions/id: Delete an existing item""" @@ -87,4 +136,27 @@ class PermissionsController(BaseControll def edit(self, id, format='html'): """GET /permissions/id/edit: Form to edit an existing item""" - # url('edit_permission', id=ID) + #url('edit_permission', id=ID) + c.perms_choices = self.perms_choices + c.register_choices = self.register_choices + c.create_choices = self.create_choices + + if id == 'default': + defaults = {'_method':'put'} + for p in UserModel().get_default().user_perms: + if p.permission.permission_name.startswith('repository.'): + defaults['default_perm'] = p.permission.permission_name + + if p.permission.permission_name.startswith('hg.register.'): + defaults['default_register'] = p.permission.permission_name + + if p.permission.permission_name.startswith('hg.create.'): + defaults['default_create'] = p.permission.permission_name + + return htmlfill.render( + render('admin/permissions/permissions.html'), + defaults=defaults, + encoding="UTF-8", + force_defaults=True,) + else: + return redirect(url('admin_home')) diff --git a/pylons_app/controllers/admin/repos.py b/pylons_app/controllers/admin/repos.py --- a/pylons_app/controllers/admin/repos.py +++ b/pylons_app/controllers/admin/repos.py @@ -50,7 +50,7 @@ class ReposController(BaseController): # map.resource('repo', 'repos') @LoginRequired() - @HasPermissionAnyDecorator('hg.admin', 'repository.create') + @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository') def __before__(self): c.admin_user = session.get('admin_user') c.admin_username = session.get('admin_username') @@ -64,7 +64,7 @@ class ReposController(BaseController): c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort')) return render('admin/repos/repos.html') - @HasPermissionAnyDecorator('hg.admin', 'repository.create') + @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository') def create(self): """POST /repos: Create a new item""" # url('repos') diff --git a/pylons_app/controllers/admin/settings.py b/pylons_app/controllers/admin/settings.py --- a/pylons_app/controllers/admin/settings.py +++ b/pylons_app/controllers/admin/settings.py @@ -271,7 +271,7 @@ class SettingsController(BaseController) return redirect(url('my_account')) - @HasPermissionAnyDecorator('repository.create', 'hg.admin') + @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository') def create_repository(self): """GET /_admin/create_repository: Form to create a new item""" new_repo = request.GET.get('repo', '') diff --git a/pylons_app/controllers/admin/users.py b/pylons_app/controllers/admin/users.py --- a/pylons_app/controllers/admin/users.py +++ b/pylons_app/controllers/admin/users.py @@ -37,7 +37,6 @@ import formencode import logging import traceback - log = logging.getLogger(__name__) class UsersController(BaseController): diff --git a/pylons_app/controllers/login.py b/pylons_app/controllers/login.py --- a/pylons_app/controllers/login.py +++ b/pylons_app/controllers/login.py @@ -17,20 +17,21 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. + +""" +Created on April 22, 2010 +login controller for pylons +@author: marcink +""" from formencode import htmlfill from pylons import request, response, session, tmpl_context as c, url from pylons.controllers.util import abort, redirect -from pylons_app.lib.auth import AuthUser +from pylons_app.lib.auth import AuthUser, HasPermissionAnyDecorator from pylons_app.lib.base import BaseController, render from pylons_app.model.forms import LoginForm, RegisterForm from pylons_app.model.user_model import UserModel import formencode import logging -""" -Created on April 22, 2010 -login controller for pylons -@author: marcink -""" log = logging.getLogger(__name__) @@ -61,13 +62,21 @@ class LoginController(BaseController): return render('/login.html') - + @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate') def register(self): + user_model = UserModel() + c.auto_active = False + for perm in user_model.get_default().user_perms: + if perm.permission.permission_name == 'hg.register.auto_activate': + c.auto_active = False + break + if request.POST: - user_model = UserModel() + register_form = RegisterForm()() try: form_result = register_form.to_python(dict(request.POST)) + form_result['active'] = c.auto_active user_model.create_registration(form_result) return redirect(url('login_home')) diff --git a/pylons_app/lib/auth.py b/pylons_app/lib/auth.py --- a/pylons_app/lib/auth.py +++ b/pylons_app/lib/auth.py @@ -27,7 +27,8 @@ from pylons import config, session, url, from pylons.controllers.util import abort, redirect from pylons_app.lib.utils import get_repo_slug from pylons_app.model import meta -from pylons_app.model.db import User, RepoToPerm, Repository, Permission +from pylons_app.model.db import User, RepoToPerm, Repository, Permission, \ + UserToPerm from sqlalchemy.exc import OperationalError from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound import bcrypt @@ -135,24 +136,39 @@ def fill_perms(user): user.permissions['repositories'] = {} user.permissions['global'] = set() - #first fetch default permissions - default_perms = sa.query(RepoToPerm, Repository, Permission)\ + #=========================================================================== + # fetch default permissions + #=========================================================================== + default_perms = sa.query(RepoToPerm, UserToPerm, Repository, Permission)\ + .outerjoin((UserToPerm, RepoToPerm.user_id == UserToPerm.user_id))\ .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\ .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\ .filter(RepoToPerm.user_id == sa.query(User).filter(User.username == 'default').one().user_id).all() - + if user.is_admin: + #======================================================================= + # #admin have all rights set to admin + #======================================================================= user.permissions['global'].add('hg.admin') - #admin have all rights set to admin + for perm in default_perms: p = 'repository.admin' user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p else: - user.permissions['global'].add('repository.create') - user.permissions['global'].add('hg.register') + #======================================================================= + # set default permissions + #======================================================================= + #default global + for perm in default_perms: + user.permissions['global'].add(perm.UserToPerm.permission.permission_name) + +# user.permissions['global'].add('hg.create.repository') +# user.permissions['global'].add('hg.register') + + #default repositories for perm in default_perms: if perm.Repository.private and not perm.Repository.user_id == user.user_id: #disable defaults for private repos, @@ -165,16 +181,18 @@ def fill_perms(user): user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p - - user_perms = sa.query(RepoToPerm, Permission, Repository)\ + #======================================================================= + # #overwrite default with user permissions if any + #======================================================================= + user_perms = sa.query(RepoToPerm, UserToPerm, Permission, Repository)\ + .outerjoin((UserToPerm, RepoToPerm.user_id == UserToPerm.user_id))\ .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\ .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\ .filter(RepoToPerm.user_id == user.user_id).all() - #overwrite userpermissions with defaults + for perm in user_perms: - #set write if owner - if perm.Repository.user_id == user.user_id: - p = 'repository.write' + if perm.Repository.user_id == user.user_id:#set admin if owner + p = 'repository.admin' else: p = perm.Permission.permission_name user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p diff --git a/pylons_app/lib/db_manage.py b/pylons_app/lib/db_manage.py --- a/pylons_app/lib/db_manage.py +++ b/pylons_app/lib/db_manage.py @@ -34,7 +34,8 @@ sys.path.append(ROOT) from pylons_app.lib.auth import get_crypt_password from pylons_app.lib.utils import ask_ok from pylons_app.model import init_model -from pylons_app.model.db import User, Permission, HgAppUi, HgAppSettings +from pylons_app.model.db import User, Permission, HgAppUi, HgAppSettings, \ + UserToPerm from pylons_app.model import meta from sqlalchemy.engine import create_engine import logging @@ -189,8 +190,12 @@ class DbManage(object): ('repository.read', 'Repository read access'), ('repository.write', 'Repository write access'), ('repository.admin', 'Repository admin access'), - ('repository.create', 'Repository create'), ('hg.admin', 'Hg Administrator'), + ('hg.create.repository', 'Repository create'), + ('hg.create.none', 'Repository creation disabled'), + ('hg.register.none', 'Register disabled'), + ('hg.register.manual_activate', 'Register new user with hg-app without manual activation'), + ('hg.register.auto_activate', 'Register new user with hg-app without auto activation'), ] for p in perms: @@ -203,3 +208,37 @@ class DbManage(object): except: self.sa.rollback() raise + + def populate_default_permissions(self): + log.info('creating default user permissions') + + default_user = self.sa.query(User)\ + .filter(User.username == 'default').scalar() + + reg_perm = UserToPerm() + reg_perm.user = default_user + reg_perm.permission = self.sa.query(Permission)\ + .filter(Permission.permission_name == 'hg.register.manual_activate')\ + .scalar() + + create_repo_perm = UserToPerm() + create_repo_perm.user = default_user + create_repo_perm.permission = self.sa.query(Permission)\ + .filter(Permission.permission_name == 'hg.create.repository')\ + .scalar() + + default_repo_perm = UserToPerm() + default_repo_perm.user = default_user + default_repo_perm.permission = self.sa.query(Permission)\ + .filter(Permission.permission_name == 'repository.read')\ + .scalar() + + try: + self.sa.add(reg_perm) + self.sa.add(create_repo_perm) + self.sa.add(default_repo_perm) + self.sa.commit() + except: + self.sa.rollback() + raise + diff --git a/pylons_app/model/db.py b/pylons_app/model/db.py --- a/pylons_app/model/db.py +++ b/pylons_app/model/db.py @@ -34,13 +34,14 @@ class User(Base): last_login = Column("last_login", DATETIME(timezone=False), nullable=True, unique=None, default=None) user_log = relation('UserLog') + user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id") @LazyProperty def full_contact(self): return '%s %s <%s>' % (self.name, self.lastname, self.email) def __repr__(self): - return "" % (self.user_id, self.username) + return "" % (self.user_id, self.username) class UserLog(Base): __tablename__ = 'user_logs' @@ -66,6 +67,9 @@ class Repository(Base): user = relation('User') repo_to_perm = relation('RepoToPerm', cascade='all') + def __repr__(self): + return "" % (self.repo_id, self.repo_name) + class Permission(Base): __tablename__ = 'permissions' __table_args__ = {'useexisting':True} diff --git a/pylons_app/model/forms.py b/pylons_app/model/forms.py --- a/pylons_app/model/forms.py +++ b/pylons_app/model/forms.py @@ -328,3 +328,12 @@ def ApplicationUiSettingsForm(): return _ApplicationUiSettingsForm +def DefaultPermissionsForm(perms_choices, register_choices, create_choices): + class _DefaultPermissionsForm(formencode.Schema): + allow_extra_fields = True + filter_extra_fields = True + default_perm = OneOf(perms_choices) + default_register = OneOf(register_choices) + default_create = OneOf(create_choices) + + return _DefaultPermissionsForm diff --git a/pylons_app/model/permission_model.py b/pylons_app/model/permission_model.py new file mode 100644 --- /dev/null +++ b/pylons_app/model/permission_model.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Model for permissions +# Copyright (C) 2009-2010 Marcin Kuzminski + +# 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 +# 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. +""" +Created on Aug 20, 2010 +Model for permissions +@author: marcink +""" + +from pylons.i18n.translation import _ +from pylons_app.model.db import User, Permission +from pylons_app.model.meta import Session +import logging +log = logging.getLogger(__name__) + + +class PermissionModel(object): + + def __init__(self): + self.sa = Session() + + def get_default(self): + return self.sa.query(User).filter(User.username == 'default').scalar() + + def get_permission(self, id): + return self.sa.query(Permission).get(id) + + def get_permission_by_name(self, name): + return self.sa.query(Permission)\ + .filter(Permission.permission_name == name).scalar() + + + def update(self, form_result): + print form_result + pass diff --git a/pylons_app/model/repo_model.py b/pylons_app/model/repo_model.py --- a/pylons_app/model/repo_model.py +++ b/pylons_app/model/repo_model.py @@ -26,6 +26,7 @@ from pylons import app_globals as g from pylons_app.lib.utils import check_repo from pylons_app.model.db import Repository, RepoToPerm, User, Permission from pylons_app.model.meta import Session +from pylons_app.model.user_model import UserModel import logging import os import shutil @@ -111,8 +112,14 @@ class RepoModel(object): #create default permission repo_to_perm = RepoToPerm() - default_perm = 'repository.none' if form_data['private'] \ - else 'repository.read' + default = 'repository.read' + for p in UserModel().get_default().user_perms: + if p.permission.permission_name.startswith('repository.'): + default = p.permission.permission_name + break + + default_perm = 'repository.none' if form_data['private'] else default + repo_to_perm.permission_id = self.sa.query(Permission)\ .filter(Permission.permission_name == default_perm)\ .one().permission_id diff --git a/pylons_app/model/user_model.py b/pylons_app/model/user_model.py --- a/pylons_app/model/user_model.py +++ b/pylons_app/model/user_model.py @@ -37,6 +37,9 @@ class UserModel(object): def __init__(self): self.sa = Session() + def get_default(self): + return self.sa.query(User).filter(User.username == 'default').scalar() + def get_user(self, id): return self.sa.query(User).get(id) @@ -57,9 +60,8 @@ class UserModel(object): try: new_user = User() for k, v in form_data.items(): - if k != 'admin' or k != 'active': + if k != 'admin': setattr(new_user, k, v) - setattr(new_user, 'active', True) self.sa.add(new_user) self.sa.commit() diff --git a/pylons_app/templates/admin/permissions/permissions.html b/pylons_app/templates/admin/permissions/permissions.html --- a/pylons_app/templates/admin/permissions/permissions.html +++ b/pylons_app/templates/admin/permissions/permissions.html @@ -21,23 +21,40 @@
${self.breadcrumbs()}
-

${_('Repositories permissions')}

- ${h.form(url('permission', id='default_perm'),method='put')} +

${_('Default permissions')}

+ ${h.form(url('permission', id='default'),method='put')}
- +
- ${h.select('default_perm','repository.read',['repository.none','repository.read','repository.write','repository.admin'])} + ${h.select('default_perm','',c.perms_choices)} +
+
+
+
+ +
+
+ ${h.select('default_register','',c.register_choices)}
-
+
+
+
+ +
+
+ ${h.select('default_create','',c.create_choices)} +
+
+
${h.submit('set','set',class_="ui-button ui-widget ui-state-default ui-corner-all")} -
+
${h.end_form()} diff --git a/pylons_app/templates/base/base.html b/pylons_app/templates/base/base.html --- a/pylons_app/templates/base/base.html +++ b/pylons_app/templates/base/base.html @@ -219,7 +219,7 @@
  • ${h.link_to(_('repositories'),h.url('repos'),class_='repos')}
  • ${h.link_to(_('users'),h.url('users'),class_='users')}
  • -
  • ${h.link_to(_('permissions'),h.url('permissions'),class_='permissions')}
  • +
  • ${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}
  • ${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}
diff --git a/pylons_app/templates/index.html b/pylons_app/templates/index.html --- a/pylons_app/templates/index.html +++ b/pylons_app/templates/index.html @@ -27,7 +27,7 @@
${_('Dashboard')}
- %if h.HasPermissionAny('repository.create','hg.admin')(): + %if h.HasPermissionAny('hg.admin','hg.create.repository')():
diff --git a/pylons_app/websetup.py b/pylons_app/websetup.py --- a/pylons_app/websetup.py +++ b/pylons_app/websetup.py @@ -19,5 +19,6 @@ def setup_app(command, conf, vars): dbmanage.config_prompt() dbmanage.admin_prompt() dbmanage.create_permissions() + dbmanage.populate_default_permissions() load_environment(conf.global_conf, conf.local_conf, initial=True)