##// END OF EJS Templates
first permissions commit: added permission managment on repository edit. Changed db rmissions, validators.
marcink -
r296:29370bb7 default
parent child Browse files
Show More
@@ -1,175 +1,181 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # repos controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 # This program is free software; you can redistribute it and/or
6 6 # modify it under the terms of the GNU General Public License
7 7 # as published by the Free Software Foundation; version 2
8 8 # of the License or (at your opinion) any later version of the license.
9 9 #
10 10 # This program is distributed in the hope that it will be useful,
11 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 # GNU General Public License for more details.
14 14 #
15 15 # You should have received a copy of the GNU General Public License
16 16 # along with this program; if not, write to the Free Software
17 17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 18 # MA 02110-1301, USA.
19 19 """
20 20 Created on April 7, 2010
21 21 admin controller for pylons
22 22 @author: marcink
23 23 """
24 24 from operator import itemgetter
25 25 from pylons import request, response, session, tmpl_context as c, url, \
26 26 app_globals as g
27 27 from pylons.controllers.util import abort, redirect
28 28 from pylons.i18n.translation import _
29 29 from pylons_app.lib import helpers as h
30 30 from pylons_app.lib.auth import LoginRequired
31 31 from pylons_app.lib.base import BaseController, render
32 32 from pylons_app.lib.utils import invalidate_cache
33 33 from pylons_app.model.repo_model import RepoModel
34 34 from pylons_app.model.hg_model import HgModel
35 35 from pylons_app.model.forms import RepoForm
36 36 from pylons_app.model.meta import Session
37 37 from datetime import datetime
38 38 import formencode
39 39 from formencode import htmlfill
40 40 import logging
41 41 import os
42 42 import shutil
43 43 log = logging.getLogger(__name__)
44 44
45 45 class ReposController(BaseController):
46 46 """REST Controller styled on the Atom Publishing Protocol"""
47 47 # To properly map this controller, ensure your config/routing.py
48 48 # file has a resource setup:
49 49 # map.resource('repo', 'repos')
50 50 @LoginRequired()
51 51 def __before__(self):
52 52 c.admin_user = session.get('admin_user')
53 53 c.admin_username = session.get('admin_username')
54 54 super(ReposController, self).__before__()
55 55
56 56 def index(self, format='html'):
57 57 """GET /repos: All items in the collection"""
58 58 # url('repos')
59 59 cached_repo_list = HgModel().get_repos()
60 60 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
61 61 return render('admin/repos/repos.html')
62 62
63 63 def create(self):
64 64 """POST /repos: Create a new item"""
65 65 # url('repos')
66 66 repo_model = RepoModel()
67 67 _form = RepoForm()()
68 68 try:
69 69 form_result = _form.to_python(dict(request.POST))
70 70 repo_model.create(form_result, c.hg_app_user)
71 71 invalidate_cache('cached_repo_list')
72 72 h.flash(_('created repository %s') % form_result['repo_name'],
73 73 category='success')
74 74
75 75 except formencode.Invalid as errors:
76 76 c.form_errors = errors.error_dict
77 77 c.new_repo = errors.value['repo_name']
78 78 return htmlfill.render(
79 79 render('admin/repos/repo_add.html'),
80 80 defaults=errors.value,
81 81 encoding="UTF-8")
82 82
83 83 except Exception:
84 84 h.flash(_('error occured during creation of repository %s') \
85 85 % form_result['repo_name'], category='error')
86 86
87 87 return redirect('repos')
88 88
89 89 def new(self, format='html'):
90 90 """GET /repos/new: Form to create a new item"""
91 91 new_repo = request.GET.get('repo', '')
92 92 c.new_repo = h.repo_name_slug(new_repo)
93 93
94 94 return render('admin/repos/repo_add.html')
95 95
96 96 def update(self, id):
97 97 """PUT /repos/id: Update an existing item"""
98 98 # Forms posted to this method should contain a hidden field:
99 99 # <input type="hidden" name="_method" value="PUT" />
100 100 # Or using helpers:
101 101 # h.form(url('repo', id=ID),
102 102 # method='put')
103 103 # url('repo', id=ID)
104 104 repo_model = RepoModel()
105 105 _form = RepoForm(edit=True)()
106 106 try:
107 107 form_result = _form.to_python(dict(request.POST))
108 108 repo_model.update(id, form_result)
109 109 invalidate_cache('cached_repo_list')
110 h.flash(_('Repository updated succesfully'), category='success')
110 h.flash(_('Repository %s updated succesfully' % id), category='success')
111 111
112 112 except formencode.Invalid as errors:
113 113 c.repo_info = repo_model.get(id)
114 errors.value.update({'user':c.repo_info.user.username})
114 115 c.form_errors = errors.error_dict
115 116 return htmlfill.render(
116 117 render('admin/repos/repo_edit.html'),
117 118 defaults=errors.value,
118 119 encoding="UTF-8")
119 120 except Exception:
120 121 h.flash(_('error occured during update of repository %s') \
121 122 % form_result['repo_name'], category='error')
122 123 return redirect(url('repos'))
123 124
124 125 def delete(self, id):
125 126 """DELETE /repos/id: Delete an existing item"""
126 127 # Forms posted to this method should contain a hidden field:
127 128 # <input type="hidden" name="_method" value="DELETE" />
128 129 # Or using helpers:
129 130 # h.form(url('repo', id=ID),
130 131 # method='delete')
131 132 # url('repo', id=ID)
132 133
133 134 repo_model = RepoModel()
134 135 repo = repo_model.get(id)
135 136 if not repo:
136 137 h.flash(_('%s repository is not mapped to db perhaps'
137 138 ' it was moved or renamed from the filesystem'
138 139 ' please run the application again'
139 140 ' in order to rescan repositories') % id, category='error')
140 141
141 142 return redirect(url('repos'))
142 143 try:
143 144 repo_model.delete(repo)
144 145 invalidate_cache('cached_repo_list')
145 146 h.flash(_('deleted repository %s') % id, category='success')
146 147 except Exception:
147 148 h.flash(_('An error occured during deletion of %s') % id,
148 149 category='error')
149 150
150 151 return redirect(url('repos'))
151 152
152 153 def show(self, id, format='html'):
153 154 """GET /repos/id: Show a specific item"""
154 155 # url('repo', id=ID)
155 156
156 157 def edit(self, id, format='html'):
157 158 """GET /repos/id/edit: Form to edit an existing item"""
158 159 # url('edit_repo', id=ID)
159 160 repo_model = RepoModel()
160 161 c.repo_info = repo = repo_model.get(id)
161 162 if not repo:
162 163 h.flash(_('%s repository is not mapped to db perhaps'
163 164 ' it was created or renamed from the filesystem'
164 165 ' please run the application again'
165 166 ' in order to rescan repositories') % id, category='error')
166 167
167 168 return redirect(url('repos'))
168 169 defaults = c.repo_info.__dict__
169 defaults.update({'user':c.repo_info.user.username})
170 defaults.update({'user':c.repo_info.user.username})
171
172 for p in c.repo_info.repo2perm:
173 defaults.update({'perm_%s' % p.user.username:
174 p.permission.permission_name})
175
170 176 return htmlfill.render(
171 177 render('admin/repos/repo_edit.html'),
172 178 defaults=defaults,
173 179 encoding="UTF-8",
174 180 force_defaults=False
175 181 )
@@ -1,126 +1,142 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # database managment for hg app
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20
21 21 """
22 22 Created on April 10, 2010
23 23 database managment and creation for hg app
24 24 @author: marcink
25 25 """
26 26
27 27 from os.path import dirname as dn, join as jn
28 28 import os
29 29 import sys
30 30 ROOT = dn(dn(dn(os.path.realpath(__file__))))
31 31 sys.path.append(ROOT)
32 32
33 33 from pylons_app.lib.auth import get_crypt_password
34 34 from pylons_app.model import init_model
35 35 from pylons_app.model.db import User, Permission
36 36 from pylons_app.model.meta import Session, Base
37 37 from sqlalchemy.engine import create_engine
38 38 import logging
39 39
40 40 log = logging.getLogger('db manage')
41 41 log.setLevel(logging.DEBUG)
42 42 console_handler = logging.StreamHandler()
43 43 console_handler.setFormatter(logging.Formatter("%(asctime)s.%(msecs)03d"
44 44 " %(levelname)-5.5s [%(name)s] %(message)s"))
45 45 log.addHandler(console_handler)
46 46
47 47 class DbManage(object):
48 48 def __init__(self, log_sql):
49 49 self.dbname = 'hg_app.db'
50 50 dburi = 'sqlite:////%s' % jn(ROOT, self.dbname)
51 51 engine = create_engine(dburi, echo=log_sql)
52 52 init_model(engine)
53 53 self.sa = Session()
54 54 self.db_exists = False
55 55
56 56 def check_for_db(self, override):
57 57 log.info('checking for exisiting db')
58 58 if os.path.isfile(jn(ROOT, self.dbname)):
59 59 self.db_exists = True
60 60 log.info('database exisist')
61 61 if not override:
62 62 raise Exception('database already exists')
63 63
64 64 def create_tables(self, override=False):
65 65 """
66 66 Create a auth database
67 67 """
68 68 self.check_for_db(override)
69 69 if override:
70 70 log.info("database exisist and it's going to be destroyed")
71 71 if self.db_exists:
72 72 os.remove(jn(ROOT, self.dbname))
73 73 Base.metadata.create_all(checkfirst=override)
74 74 log.info('Created tables for %s', self.dbname)
75 75
76 76 def admin_prompt(self):
77 77 import getpass
78 78 username = raw_input('Specify admin username:')
79 79 password = getpass.getpass('Specify admin password:')
80 80 self.create_user(username, password, True)
81 81
82 82 def create_user(self, username, password, admin=False):
83
84 log.info('creating default user')
85 #create default user for handling default permissions.
86 def_user = User()
87 def_user.username = 'default'
88 def_user.password = 'default'
89 def_user.name = 'default'
90 def_user.lastname = 'default'
91 def_user.email = 'default@default'
92 def_user.admin = False
93 def_user.active = False
94
95 self.sa.add(def_user)
96
83 97 log.info('creating administrator user %s', username)
84
85 98 new_user = User()
86 99 new_user.username = username
87 100 new_user.password = get_crypt_password(password)
88 new_user.name = 'Admin'
101 new_user.name = 'Hg'
89 102 new_user.lastname = 'Admin'
90 103 new_user.email = 'admin@localhost'
91 104 new_user.admin = admin
92 105 new_user.active = True
93 106
94 107 try:
95 108 self.sa.add(new_user)
96 109 self.sa.commit()
97 110 except:
98 111 self.sa.rollback()
99 112 raise
100 113
101 114 def create_permissions(self):
102 115 #module.(access|create|change|delete)_[name]
103 perms = [('admin.access_home', 'Access to admin user view'),
104
116 #module.(read|write|owner)
117 perms = [('repository.none', 'Repository no access'),
118 ('repository.read', 'Repository read access'),
119 ('repository.write', 'Repository write access'),
120 ('repository.admin', 'Repository admin access'),
105 121 ]
106 122
107 123 for p in perms:
108 124 new_perm = Permission()
109 125 new_perm.permission_name = p[0]
110 126 new_perm.permission_longname = p[1]
111 127 try:
112 128 self.sa.add(new_perm)
113 129 self.sa.commit()
114 130 except:
115 131 self.sa.rollback()
116 132 raise
117 133
118 134
119 135
120 136 if __name__ == '__main__':
121 137 dbmanage = DbManage(log_sql=True)
122 138 dbmanage.create_tables(override=True)
123 139 dbmanage.admin_prompt()
124 140 dbmanage.create_permissions()
125 141
126 142
@@ -1,55 +1,71 b''
1 1 from pylons_app.model.meta import Base
2 2 from sqlalchemy.orm import relation, backref
3 3 from sqlalchemy import *
4 4 from vcs.utils.lazy import LazyProperty
5 5
6 6 class User(Base):
7 7 __tablename__ = 'users'
8 8 __table_args__ = {'useexisting':True}
9 9 user_id = Column("user_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
10 10 username = Column("username", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
11 11 password = Column("password", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
12 12 active = Column("active", BOOLEAN(), nullable=True, unique=None, default=None)
13 13 admin = Column("admin", BOOLEAN(), nullable=True, unique=None, default=None)
14 14 name = Column("name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
15 15 lastname = Column("lastname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
16 16 email = Column("email", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
17 17 last_login = Column("last_login", DATETIME(timezone=False), nullable=True, unique=None, default=None)
18 18
19 19 user_log = relation('UserLog')
20 20
21 21 @LazyProperty
22 22 def full_contact(self):
23 23 return '%s %s <%s>' % (self.name, self.lastname, self.email)
24 24
25 25 def __repr__(self):
26 26 return "<User('%s:%s')>" % (self.user_id, self.username)
27 27
28 28 class UserLog(Base):
29 29 __tablename__ = 'user_logs'
30 30 __table_args__ = {'useexisting':True}
31 user_log_id = Column("user_log_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=1)
31 user_log_id = Column("user_log_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
32 32 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
33 repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
33 user_ip = Column("user_ip", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
34 repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_name'), nullable=False, unique=None, default=None)
34 35 action = Column("action", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
35 36 action_date = Column("action_date", DATETIME(timezone=False), nullable=True, unique=None, default=None)
36 37
37 38 user = relation('User')
38
39
39 40 class Repository(Base):
40 41 __tablename__ = 'repositories'
42 __table_args__ = {'useexisting':True}
41 43 repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None, primary_key=True)
42 44 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
43 45 private = Column("private", BOOLEAN(), nullable=True, unique=None, default=None)
44 46 description = Column("description", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
47
45 48 user = relation('User')
49 repo2perm = relation('Repo2Perm', cascade='all')
46 50
47 51 class Permission(Base):
48 52 __tablename__ = 'permissions'
49 53 __table_args__ = {'useexisting':True}
50 permission_id = Column("id", INTEGER(), nullable=False, unique=True, default=None, primary_key=1)
54 permission_id = Column("permission_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
51 55 permission_name = Column("permission_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
52 56 permission_longname = Column("permission_longname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
53 57
54 58 def __repr__(self):
55 59 return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
60
61 class Repo2Perm(Base):
62 __tablename__ = 'repo_to_perm'
63 __table_args__ = (UniqueConstraint('user_id', 'permission_id', 'repository'), {'useexisting':True})
64 repo2perm_id = Column("repo2perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
65 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
66 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
67 repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_name'), nullable=False, unique=None, default=None)
68
69 user = relation('User')
70 permission = relation('Permission')
71
@@ -1,203 +1,238 b''
1 1 """ this is forms validation classes
2 2 http://formencode.org/module-formencode.validators.html
3 3 for list off all availible validators
4 4
5 5 we can create our own validators
6 6
7 7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 8 pre_validators [] These validators will be applied before the schema
9 9 chained_validators [] These validators will be applied after the schema
10 10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14 14
15 15
16 16 <name> = formencode.validators.<name of validator>
17 17 <name> must equal form name
18 18 list=[1,2,3,4,5]
19 19 for SELECT use formencode.All(OneOf(list), Int())
20 20
21 21 """
22 22 from formencode import All
23 23 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
24 24 Email, Bool, StringBoolean
25 25 from pylons import session
26 26 from pylons.i18n.translation import _
27 27 from pylons_app.lib.auth import get_crypt_password
28 28 import pylons_app.lib.helpers as h
29 29 from pylons_app.model import meta
30 30 from pylons_app.model.db import User, Repository
31 31 from sqlalchemy.exc import OperationalError
32 32 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
33 33 from webhelpers.pylonslib.secure_form import authentication_token
34 34 import datetime
35 35 import formencode
36 36 import logging
37 37 log = logging.getLogger(__name__)
38 38
39 39
40 40 #this is needed to translate the messages using _() in validators
41 41 class State_obj(object):
42 42 _ = staticmethod(_)
43 43
44 44 #===============================================================================
45 45 # VALIDATORS
46 46 #===============================================================================
47 47 class ValidAuthToken(formencode.validators.FancyValidator):
48 48 messages = {'invalid_token':_('Token mismatch')}
49 49
50 50 def validate_python(self, value, state):
51 51
52 52 if value != authentication_token():
53 53 raise formencode.Invalid(self.message('invalid_token', state,
54 54 search_number=value), value, state)
55 55 class ValidUsername(formencode.validators.FancyValidator):
56 56
57 57 def validate_python(self, value, state):
58 pass
58 if value in ['default', 'new_user']:
59 raise formencode.Invalid(_('Invalid username'), value, state)
59 60
60 61 class ValidPassword(formencode.validators.FancyValidator):
61 62
62 63 def to_python(self, value, state):
63 64 return get_crypt_password(value)
64 65
65 66 class ValidAuth(formencode.validators.FancyValidator):
66 67 messages = {
67 68 'invalid_password':_('invalid password'),
68 69 'invalid_login':_('invalid user name'),
69 70 'disabled_account':_('Your acccount is disabled')
70 71
71 72 }
72 73 #error mapping
73 74 e_dict = {'username':messages['invalid_login'],
74 75 'password':messages['invalid_password']}
75 76 e_dict_disable = {'username':messages['disabled_account']}
76 77
77 78 def validate_python(self, value, state):
78 79 sa = meta.Session
79 80 crypted_passwd = get_crypt_password(value['password'])
80 81 username = value['username']
81 82 try:
82 83 user = sa.query(User).filter(User.username == username).one()
83 84 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
84 85 log.error(e)
85 86 user = None
86 87 raise formencode.Invalid(self.message('invalid_password',
87 88 state=State_obj), value, state,
88 89 error_dict=self.e_dict)
89 90 if user:
90 91 if user.active:
91 92 if user.username == username and user.password == crypted_passwd:
92 93 from pylons_app.lib.auth import AuthUser
93 94 auth_user = AuthUser()
94 95 auth_user.username = username
95 96 auth_user.is_authenticated = True
96 97 auth_user.is_admin = user.admin
97 98 auth_user.user_id = user.user_id
98 99 session['hg_app_user'] = auth_user
99 100 session.save()
100 101 log.info('user %s is now authenticated', username)
101 102
102 103 try:
103 104 user.last_login = datetime.datetime.now()
104 105 sa.add(user)
105 106 sa.commit()
106 107 except (OperationalError) as e:
107 108 log.error(e)
108 109 sa.rollback()
109 110
110 111 return value
111 112 else:
112 113 log.warning('user %s not authenticated', username)
113 114 raise formencode.Invalid(self.message('invalid_password',
114 115 state=State_obj), value, state,
115 116 error_dict=self.e_dict)
116 117 else:
117 118 log.warning('user %s is disabled', username)
118 119 raise formencode.Invalid(self.message('disabled_account',
119 120 state=State_obj),
120 121 value, state,
121 122 error_dict=self.e_dict_disable)
122 123
123 124
124 125 class ValidRepoUser(formencode.validators.FancyValidator):
125 126
126 127 def to_python(self, value, state):
127 128 sa = meta.Session
128 129 try:
129 130 self.user_db = sa.query(User).filter(User.username == value).one()
130 131 except Exception:
131 132 raise formencode.Invalid(_('This username is not valid'),
132 133 value, state)
133 134 return self.user_db.user_id
134 135
135 136 def ValidRepoName(edit=False):
136 137 class _ValidRepoName(formencode.validators.FancyValidator):
137 138
138 139 def to_python(self, value, state):
139 140 slug = h.repo_name_slug(value)
140 141
141 142 sa = meta.Session
142 143 if sa.query(Repository).get(slug) and not edit:
143 144 raise formencode.Invalid(_('This repository already exists'),
144 145 value, state)
145 146
146 147 return slug
147 148 return _ValidRepoName
149
150 class ValidPerms(formencode.validators.FancyValidator):
151 messages = {'perm_new_user_name':_('This username is not valid')}
152
153 def to_python(self, value, state):
154 perms_update = []
155 perms_new = []
156 #build a list of permission to update and new permission to create
157 for k, v in value.items():
158 print k, v
159 if k.startswith('perm_'):
160 if k.startswith('perm_new_user'):
161 new_perm = value.get('perm_new_user', False)
162 new_user = value.get('perm_new_user_name', False)
163 if new_user and new_perm:
164 if (new_user, new_perm) not in perms_new:
165 perms_new.append((new_user, new_perm))
166 else:
167 perms_update.append((k[5:], v))
168 #clear from form list
169 #del value[k]
170 value['perms_updates'] = perms_update
171 value['perms_new'] = perms_new
172 sa = meta.Session
173 for k, v in perms_new:
174 try:
175 self.user_db = sa.query(User).filter(User.username == k).one()
176 except Exception:
177 msg = self.message('perm_new_user_name',
178 state=State_obj)
179 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
180 return value
181
148 182 #===============================================================================
149 183 # FORMS
150 184 #===============================================================================
151 185 class LoginForm(formencode.Schema):
152 186 allow_extra_fields = True
153 187 filter_extra_fields = True
154 188 username = UnicodeString(
155 189 strip=True,
156 190 min=3,
157 191 not_empty=True,
158 192 messages={
159 193 'empty':_('Please enter a login'),
160 194 'tooShort':_('Enter a value %(min)i characters long or more')}
161 195 )
162 196
163 197 password = UnicodeString(
164 198 strip=True,
165 199 min=3,
166 200 not_empty=True,
167 201 messages={
168 202 'empty':_('Please enter a password'),
169 203 'tooShort':_('Enter a value %(min)i characters long or more')}
170 204 )
171 205
172 206
173 207 #chained validators have access to all data
174 208 chained_validators = [ValidAuth]
175 209
176 210 def UserForm(edit=False):
177 211 class _UserForm(formencode.Schema):
178 212 allow_extra_fields = True
179 213 filter_extra_fields = True
180 214 username = All(UnicodeString(strip=True, min=3, not_empty=True), ValidUsername)
181 215 if edit:
182 216 new_password = All(UnicodeString(strip=True, min=3, not_empty=False), ValidPassword)
183 217 else:
184 218 password = All(UnicodeString(strip=True, min=3, not_empty=False), ValidPassword)
185 219 active = StringBoolean(if_missing=False)
186 220 name = UnicodeString(strip=True, min=3, not_empty=True)
187 221 lastname = UnicodeString(strip=True, min=3, not_empty=True)
188 222 email = Email(not_empty=True)
189 223
190 224 return _UserForm
191 225
192 226 def RepoForm(edit=False):
193 227 class _RepoForm(formencode.Schema):
194 228 allow_extra_fields = True
195 filter_extra_fields = True
229 filter_extra_fields = False
196 230 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit))
197 231 description = UnicodeString(strip=True, min=3, not_empty=True)
198 232 private = StringBoolean(if_missing=False)
199 233
200 234 if edit:
201 235 user = All(Int(not_empty=True), ValidRepoUser)
202 236
237 chained_validators = [ValidPerms]
203 238 return _RepoForm
@@ -1,106 +1,145 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # model for handling repositories actions
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 # This program is free software; you can redistribute it and/or
6 6 # modify it under the terms of the GNU General Public License
7 7 # as published by the Free Software Foundation; version 2
8 8 # of the License or (at your opinion) any later version of the license.
9 9 #
10 10 # This program is distributed in the hope that it will be useful,
11 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 # GNU General Public License for more details.
14 14 #
15 15 # You should have received a copy of the GNU General Public License
16 16 # along with this program; if not, write to the Free Software
17 17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 18 # MA 02110-1301, USA.
19 19
20 20 """
21 21 Created on Jun 5, 2010
22 22 model for handling repositories actions
23 23 @author: marcink
24 24 """
25 25 from pylons_app.model.meta import Session
26 from pylons_app.model.db import Repository
26 from pylons_app.model.db import Repository, Repo2Perm, User, Permission
27 27 import shutil
28 28 import os
29 29 from datetime import datetime
30 30 from pylons_app.lib.utils import check_repo
31 31 from pylons import app_globals as g
32 import traceback
32 33 import logging
33 34 log = logging.getLogger(__name__)
34 35
35 36 class RepoModel(object):
36 37
37 38 def __init__(self):
38 39 self.sa = Session()
39 40
40 41 def get(self, id):
41 42 return self.sa.query(Repository).get(id)
42 43
43 44
44 def update(self, id, form_data):
45 def update(self, repo_id, form_data):
45 46 try:
46 if id != form_data['repo_name']:
47 self.__rename_repo(id, form_data['repo_name'])
48 cur_repo = self.sa.query(Repository).get(id)
47 if repo_id != form_data['repo_name']:
48 self.__rename_repo(repo_id, form_data['repo_name'])
49 cur_repo = self.sa.query(Repository).get(repo_id)
49 50 for k, v in form_data.items():
50 51 if k == 'user':
51 52 cur_repo.user_id = v
52 53 else:
53 54 setattr(cur_repo, k, v)
55
56 #update permissions
57 for username, perm in form_data['perms_updates']:
58 r2p = self.sa.query(Repo2Perm)\
59 .filter(Repo2Perm.user == self.sa.query(User)\
60 .filter(User.username == username).one())\
61 .filter(Repo2Perm.repository == repo_id).one()
54 62
63 r2p.permission_id = self.sa.query(Permission).filter(
64 Permission.permission_name ==
65 perm).one().permission_id
66 self.sa.add(r2p)
67
68 for username, perm in form_data['perms_new']:
69 r2p = Repo2Perm()
70 r2p.repository = repo_id
71 r2p.user = self.sa.query(User)\
72 .filter(User.username == username).one()
73
74 r2p.permission_id = self.sa.query(Permission).filter(
75 Permission.permission_name ==
76 perm).one().permission_id
77 self.sa.add(r2p)
78
55 79 self.sa.add(cur_repo)
56 80 self.sa.commit()
57 except Exception as e:
58 log.error(e)
81 except:
82 log.error(traceback.format_exc())
59 83 self.sa.rollback()
60 84 raise
61 85
62 def create(self, form_data, cur_user):
86 def create(self, form_data, cur_user, just_db=False):
63 87 try:
88 repo_name = form_data['repo_name']
64 89 new_repo = Repository()
65 90 for k, v in form_data.items():
66 91 setattr(new_repo, k, v)
67 92
68 93 new_repo.user_id = cur_user.user_id
69 94 self.sa.add(new_repo)
95
96 #create default permission
97 repo2perm = Repo2Perm()
98 repo2perm.permission_id = self.sa.query(Permission)\
99 .filter(Permission.permission_name == 'repository.read')\
100 .one().permission_id
101
102 repo2perm.repository = repo_name
103 repo2perm.user_id = self.sa.query(User)\
104 .filter(User.username == 'default').one().user_id
105
106 self.sa.add(repo2perm)
70 107 self.sa.commit()
71 self.__create_repo(form_data['repo_name'])
72 except Exception as e:
73 log.error(e)
108 if not just_db:
109 self.__create_repo(repo_name)
110 except:
111 log.error(traceback.format_exc())
74 112 self.sa.rollback()
75 113 raise
76 114
77 115 def delete(self, repo):
78 116 try:
79 117 self.sa.delete(repo)
80 118 self.sa.commit()
81 119 self.__delete_repo(repo.repo_name)
82 except Exception as e:
83 log.error(e)
120 except:
121 log.error(traceback.format_exc())
84 122 self.sa.rollback()
85 123 raise
86 124
87 125 def __create_repo(self, repo_name):
88 126 repo_path = os.path.join(g.base_path, repo_name)
89 127 if check_repo(repo_name, g.base_path):
90 128 log.info('creating repo %s in %s', repo_name, repo_path)
91 129 from vcs.backends.hg import MercurialRepository
92 130 MercurialRepository(repo_path, create=True)
93 131
94 132 def __rename_repo(self, old, new):
95 133 log.info('renaming repoo from %s to %s', old, new)
96 134 old_path = os.path.join(g.base_path, old)
97 135 new_path = os.path.join(g.base_path, new)
98 136 shutil.move(old_path, new_path)
99 137
100 138 def __delete_repo(self, name):
101 139 rm_path = os.path.join(g.base_path, name)
102 140 log.info("Removing %s", rm_path)
103 141 #disable hg
104 142 shutil.move(os.path.join(rm_path, '.hg'), os.path.join(rm_path, 'rm__.hg'))
105 143 #disable repo
106 shutil.move(rm_path, os.path.join(g.base_path, 'rm__%s-%s' % (datetime.today(), id)))
144 shutil.move(rm_path, os.path.join(g.base_path, 'rm__%s__%s' \
145 % (datetime.today(), name)))
@@ -1,49 +1,109 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Repositories administration')}
6 6 </%def>
7 7 <%def name="breadcrumbs()">
8 8 ${h.link_to(u'Admin',h.url('admin_home'))}
9 9 /
10 10 ${_('Repos')}
11 11 </%def>
12 12 <%def name="page_nav()">
13 13 ${self.menu('admin')}
14 14 ${self.submenu('repos')}
15 15 </%def>
16 16 <%def name="main()">
17 17 <div>
18 18 <h2>${_('Repositories')} - ${_('edit')}</h2>
19 19 ${h.form(url('repo', id=c.repo_info.repo_name),method='put')}
20 20 <table>
21 21 <tr>
22 22 <td>${_('Name')}</td>
23 23 <td>${h.text('repo_name')}</td>
24 24 <td>${self.get_form_error('repo_name')}</td>
25 25 </tr>
26 26 <tr>
27 27 <td>${_('Description')}</td>
28 28 <td>${h.textarea('description',cols=23,rows=5)}</td>
29 29 <td>${self.get_form_error('description')}</td>
30 30 </tr>
31 31 <tr>
32 32 <td>${_('Private')}</td>
33 33 <td>${h.checkbox('private')}</td>
34 34 <td>${self.get_form_error('private')}</td>
35 35 </tr>
36 36 <tr>
37 37 <td>${_('Owner')}</td>
38 38 <td>${h.text('user')}</td>
39 39 <td>${self.get_form_error('user')}</td>
40 40 </tr>
41 41 <tr>
42 <td>${_('Permissions')}</td>
43 <td>
44 <table>
45 <tr>
46 <td>${_('none')}</td>
47 <td>${_('read')}</td>
48 <td>${_('write')}</td>
49 <td>${_('admin')}</td>
50 <td>${_('user')}</td>
51 </tr>
52
53 %for r2p in c.repo_info.repo2perm:
54 <tr>
55 <td>${h.radio('perm_%s' % r2p.user.username,'repository.none')}</td>
56 <td>${h.radio('perm_%s' % r2p.user.username,'repository.read')}</td>
57 <td>${h.radio('perm_%s' % r2p.user.username,'repository.write')}</td>
58 <td>${h.radio('perm_%s' % r2p.user.username,'repository.admin')}</td>
59 <td>${r2p.user.username}</td>
60 </tr>
61 %endfor
62
63
64 <%
65
66 if not hasattr(c,'form_errors'):
67 d = 'display:none;'
68 else:
69 d=''
70 %>
71
72 <tr id="add_perm_input" style="${d}">
73 <td>${h.radio('perm_new_user','repository.none')}</td>
74 <td>${h.radio('perm_new_user','repository.read')}</td>
75 <td>${h.radio('perm_new_user','repository.write')}</td>
76 <td>${h.radio('perm_new_user','repository.admin')}</td>
77 <td>${h.text('perm_new_user_name',size=10)}</td>
78 <td>${self.get_form_error('perm_new_user_name')}</td>
79 </tr>
80 <tr>
81 <td colspan="4">
82 <span id="add_perm" class="add_icon" style="cursor: pointer;">
83 ${_('Add another user')}
84 </span>
85 </td>
86 </tr>
87 </table>
88 </td>
89
90 </tr>
91 <tr>
42 92 <td></td>
43 93 <td>${h.submit('update','update')}</td>
44 94 </tr>
45 95
46 96 </table>
47 97 ${h.end_form()}
98 <script type="text/javascript">
99 YAHOO.util.Event.onDOMReady(function(){
100 var D = YAHOO.util.Dom;
101 YAHOO.util.Event.addListener('add_perm','click',function(){
102 D.setStyle('add_perm_input','display','');
103 D.setStyle('add_perm','opacity','0.6');
104 D.setStyle('add_perm','cursor','default');
105 });
106 });
107 </script>
48 108 </div>
49 109 </%def>
General Comments 0
You need to be logged in to leave comments. Login now