##// END OF EJS Templates
permission refactoring,...
marcink -
r417:3ed2d46a default
parent child Browse files
Show More
@@ -0,0 +1,51 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Model for permissions
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
21 Created on Aug 20, 2010
22 Model for permissions
23 @author: marcink
24 """
25
26 from pylons.i18n.translation import _
27 from pylons_app.model.db import User, Permission
28 from pylons_app.model.meta import Session
29 import logging
30 log = logging.getLogger(__name__)
31
32
33 class PermissionModel(object):
34
35 def __init__(self):
36 self.sa = Session()
37
38 def get_default(self):
39 return self.sa.query(User).filter(User.username == 'default').scalar()
40
41 def get_permission(self, id):
42 return self.sa.query(Permission).get(id)
43
44 def get_permission_by_name(self, name):
45 return self.sa.query(Permission)\
46 .filter(Permission.permission_name == name).scalar()
47
48
49 def update(self, form_result):
50 print form_result
51 pass
@@ -1,90 +1,162 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # permissions controller for pylons
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 Created on April 27, 2010
22 22 permissions controller for pylons
23 23 @author: marcink
24 24 """
25
25 26 from formencode import htmlfill
26 27 from pylons import request, session, tmpl_context as c, url
27 28 from pylons.controllers.util import abort, redirect
28 29 from pylons.i18n.translation import _
29 30 from pylons_app.lib import helpers as h
30 31 from pylons_app.lib.auth import LoginRequired, HasPermissionAllDecorator
31 32 from pylons_app.lib.base import BaseController, render
32 33 from pylons_app.model.db import User, UserLog
33 from pylons_app.model.forms import UserForm
34 from pylons_app.model.forms import UserForm, DefaultPermissionsForm
35 from pylons_app.model.permission_model import PermissionModel
34 36 from pylons_app.model.user_model import UserModel
35 37 import formencode
36 38 import logging
39 import traceback
37 40
38 41 log = logging.getLogger(__name__)
39 42
40 43 class PermissionsController(BaseController):
41 44 """REST Controller styled on the Atom Publishing Protocol"""
42 45 # To properly map this controller, ensure your config/routing.py
43 46 # file has a resource setup:
44 47 # map.resource('permission', 'permissions')
45 48
46 49 @LoginRequired()
47 #@HasPermissionAllDecorator('hg.admin')
50 @HasPermissionAllDecorator('hg.admin')
48 51 def __before__(self):
49 52 c.admin_user = session.get('admin_user')
50 53 c.admin_username = session.get('admin_username')
51 54 super(PermissionsController, self).__before__()
52 55
56 self.perms_choices = [('repository.none', _('None'),),
57 ('repository.read', _('Read'),),
58 ('repository.write', _('Write'),),
59 ('repository.admin', _('Admin'),)]
60 self.register_choices = [
61 ('hg.register.none', 'disabled'),
62 ('hg.register.manual_activate',
63 _('allowed with manual account activation')),
64 ('hg.register.auto_activate',
65 _('allowed with automatic account activation')), ]
66
67 self.create_choices = [('hg.create.none', _('Disabled')),
68 ('hg.create.repository', _('Enabled'))]
69
70
53 71 def index(self, format='html'):
54 72 """GET /permissions: All items in the collection"""
55 73 # url('permissions')
56 return render('admin/permissions/permissions.html')
57 74
58 75 def create(self):
59 76 """POST /permissions: Create a new item"""
60 77 # url('permissions')
61 78
62 79 def new(self, format='html'):
63 80 """GET /permissions/new: Form to create a new item"""
64 81 # url('new_permission')
65 82
66 83 def update(self, id):
67 84 """PUT /permissions/id: Update an existing item"""
68 85 # Forms posted to this method should contain a hidden field:
69 86 # <input type="hidden" name="_method" value="PUT" />
70 87 # Or using helpers:
71 88 # h.form(url('permission', id=ID),
72 89 # method='put')
73 90 # url('permission', id=ID)
74 91
92 permission_model = PermissionModel()
93
94 _form = DefaultPermissionsForm([x[0] for x in self.perms_choices],
95 [x[0] for x in self.register_choices],
96 [x[0] for x in self.create_choices])()
97
98 try:
99 form_result = _form.to_python(dict(request.POST))
100 permission_model.update(form_result)
101 h.flash(_('Default permissions updated succesfully'),
102 category='success')
103
104 except formencode.Invalid as errors:
105 c.perms_choices = self.perms_choices
106 c.register_choices = self.register_choices
107 c.create_choices = self.create_choices
108
109 return htmlfill.render(
110 render('admin/permissions/permissions.html'),
111 defaults=errors.value,
112 errors=errors.error_dict or {},
113 prefix_error=False,
114 encoding="UTF-8")
115 except Exception:
116 log.error(traceback.format_exc())
117 h.flash(_('error occured during update of permissions'),
118 category='error')
119
120 return redirect(url('edit_permission', id=id))
121
122
123
75 124 def delete(self, id):
76 125 """DELETE /permissions/id: Delete an existing item"""
77 126 # Forms posted to this method should contain a hidden field:
78 127 # <input type="hidden" name="_method" value="DELETE" />
79 128 # Or using helpers:
80 129 # h.form(url('permission', id=ID),
81 130 # method='delete')
82 131 # url('permission', id=ID)
83 132
84 133 def show(self, id, format='html'):
85 134 """GET /permissions/id: Show a specific item"""
86 135 # url('permission', id=ID)
87 136
88 137 def edit(self, id, format='html'):
89 138 """GET /permissions/id/edit: Form to edit an existing item"""
90 139 # url('edit_permission', id=ID)
140 c.perms_choices = self.perms_choices
141 c.register_choices = self.register_choices
142 c.create_choices = self.create_choices
143
144 if id == 'default':
145 defaults = {'_method':'put'}
146 for p in UserModel().get_default().user_perms:
147 if p.permission.permission_name.startswith('repository.'):
148 defaults['default_perm'] = p.permission.permission_name
149
150 if p.permission.permission_name.startswith('hg.register.'):
151 defaults['default_register'] = p.permission.permission_name
152
153 if p.permission.permission_name.startswith('hg.create.'):
154 defaults['default_create'] = p.permission.permission_name
155
156 return htmlfill.render(
157 render('admin/permissions/permissions.html'),
158 defaults=defaults,
159 encoding="UTF-8",
160 force_defaults=True,)
161 else:
162 return redirect(url('admin_home'))
@@ -1,234 +1,234 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 #
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 Created on April 7, 2010
22 22 admin controller for pylons
23 23 @author: marcink
24 24 """
25 25 from formencode import htmlfill
26 26 from operator import itemgetter
27 27 from paste.httpexceptions import HTTPInternalServerError
28 28 from pylons import request, response, session, tmpl_context as c, url
29 29 from pylons.controllers.util import abort, redirect
30 30 from pylons.i18n.translation import _
31 31 from pylons_app.lib import helpers as h
32 32 from pylons_app.lib.auth import LoginRequired, HasPermissionAllDecorator, \
33 33 HasPermissionAnyDecorator
34 34 from pylons_app.lib.base import BaseController, render
35 35 from pylons_app.lib.utils import invalidate_cache
36 36 from pylons_app.model.db import User
37 37 from pylons_app.model.forms import RepoForm
38 38 from pylons_app.model.hg_model import HgModel
39 39 from pylons_app.model.repo_model import RepoModel
40 40 import formencode
41 41 import logging
42 42 import traceback
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46 class ReposController(BaseController):
47 47 """REST Controller styled on the Atom Publishing Protocol"""
48 48 # To properly map this controller, ensure your config/routing.py
49 49 # file has a resource setup:
50 50 # map.resource('repo', 'repos')
51 51
52 52 @LoginRequired()
53 @HasPermissionAnyDecorator('hg.admin', 'repository.create')
53 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
54 54 def __before__(self):
55 55 c.admin_user = session.get('admin_user')
56 56 c.admin_username = session.get('admin_username')
57 57 super(ReposController, self).__before__()
58 58
59 59 @HasPermissionAllDecorator('hg.admin')
60 60 def index(self, format='html'):
61 61 """GET /repos: All items in the collection"""
62 62 # url('repos')
63 63 cached_repo_list = HgModel().get_repos()
64 64 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
65 65 return render('admin/repos/repos.html')
66 66
67 @HasPermissionAnyDecorator('hg.admin', 'repository.create')
67 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
68 68 def create(self):
69 69 """POST /repos: Create a new item"""
70 70 # url('repos')
71 71 repo_model = RepoModel()
72 72 _form = RepoForm()()
73 73 form_result = {}
74 74 try:
75 75 form_result = _form.to_python(dict(request.POST))
76 76 repo_model.create(form_result, c.hg_app_user)
77 77 invalidate_cache('cached_repo_list')
78 78 h.flash(_('created repository %s') % form_result['repo_name'],
79 79 category='success')
80 80
81 81 except formencode.Invalid as errors:
82 82 c.new_repo = errors.value['repo_name']
83 83
84 84 if request.POST.get('user_created'):
85 85 r = render('admin/repos/repo_add_create_repository.html')
86 86 else:
87 87 r = render('admin/repos/repo_add.html')
88 88
89 89 return htmlfill.render(
90 90 r,
91 91 defaults=errors.value,
92 92 errors=errors.error_dict or {},
93 93 prefix_error=False,
94 94 encoding="UTF-8")
95 95
96 96 except Exception:
97 97 log.error(traceback.format_exc())
98 98 msg = _('error occured during creation of repository %s') \
99 99 % form_result.get('repo_name')
100 100 h.flash(msg, category='error')
101 101 if request.POST.get('user_created'):
102 102 return redirect(url('hg_home'))
103 103 return redirect(url('repos'))
104 104
105 105 @HasPermissionAllDecorator('hg.admin')
106 106 def new(self, format='html'):
107 107 """GET /repos/new: Form to create a new item"""
108 108 new_repo = request.GET.get('repo', '')
109 109 c.new_repo = h.repo_name_slug(new_repo)
110 110
111 111 return render('admin/repos/repo_add.html')
112 112
113 113 @HasPermissionAllDecorator('hg.admin')
114 114 def update(self, repo_name):
115 115 """PUT /repos/repo_name: Update an existing item"""
116 116 # Forms posted to this method should contain a hidden field:
117 117 # <input type="hidden" name="_method" value="PUT" />
118 118 # Or using helpers:
119 119 # h.form(url('repo', repo_name=ID),
120 120 # method='put')
121 121 # url('repo', repo_name=ID)
122 122 repo_model = RepoModel()
123 123 changed_name = repo_name
124 124 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
125 125
126 126 try:
127 127 form_result = _form.to_python(dict(request.POST))
128 128 repo_model.update(repo_name, form_result)
129 129 invalidate_cache('cached_repo_list')
130 130 h.flash(_('Repository %s updated succesfully' % repo_name),
131 131 category='success')
132 132 changed_name = form_result['repo_name']
133 133 except formencode.Invalid as errors:
134 134 c.repo_info = repo_model.get(repo_name)
135 135 c.users_array = repo_model.get_users_js()
136 136 errors.value.update({'user':c.repo_info.user.username})
137 137 return htmlfill.render(
138 138 render('admin/repos/repo_edit.html'),
139 139 defaults=errors.value,
140 140 errors=errors.error_dict or {},
141 141 prefix_error=False,
142 142 encoding="UTF-8")
143 143
144 144 except Exception:
145 145 log.error(traceback.format_exc())
146 146 h.flash(_('error occured during update of repository %s') \
147 147 % repo_name, category='error')
148 148
149 149 return redirect(url('edit_repo', repo_name=changed_name))
150 150
151 151 @HasPermissionAllDecorator('hg.admin')
152 152 def delete(self, repo_name):
153 153 """DELETE /repos/repo_name: Delete an existing item"""
154 154 # Forms posted to this method should contain a hidden field:
155 155 # <input type="hidden" name="_method" value="DELETE" />
156 156 # Or using helpers:
157 157 # h.form(url('repo', repo_name=ID),
158 158 # method='delete')
159 159 # url('repo', repo_name=ID)
160 160
161 161 repo_model = RepoModel()
162 162 repo = repo_model.get(repo_name)
163 163 if not repo:
164 164 h.flash(_('%s repository is not mapped to db perhaps'
165 165 ' it was moved or renamed from the filesystem'
166 166 ' please run the application again'
167 167 ' in order to rescan repositories') % repo_name,
168 168 category='error')
169 169
170 170 return redirect(url('repos'))
171 171 try:
172 172 repo_model.delete(repo)
173 173 invalidate_cache('cached_repo_list')
174 174 h.flash(_('deleted repository %s') % repo_name, category='success')
175 175 except Exception:
176 176 h.flash(_('An error occured during deletion of %s') % repo_name,
177 177 category='error')
178 178
179 179 return redirect(url('repos'))
180 180
181 181 @HasPermissionAllDecorator('hg.admin')
182 182 def delete_perm_user(self, repo_name):
183 183 """
184 184 DELETE an existing repository permission user
185 185 @param repo_name:
186 186 """
187 187
188 188 try:
189 189 repo_model = RepoModel()
190 190 repo_model.delete_perm_user(request.POST, repo_name)
191 191 except Exception as e:
192 192 h.flash(_('An error occured during deletion of repository user'),
193 193 category='error')
194 194 raise HTTPInternalServerError()
195 195
196 196 @HasPermissionAllDecorator('hg.admin')
197 197 def show(self, repo_name, format='html'):
198 198 """GET /repos/repo_name: Show a specific item"""
199 199 # url('repo', repo_name=ID)
200 200
201 201 @HasPermissionAllDecorator('hg.admin')
202 202 def edit(self, repo_name, format='html'):
203 203 """GET /repos/repo_name/edit: Form to edit an existing item"""
204 204 # url('edit_repo', repo_name=ID)
205 205 repo_model = RepoModel()
206 206 c.repo_info = repo = repo_model.get(repo_name)
207 207 if not repo:
208 208 h.flash(_('%s repository is not mapped to db perhaps'
209 209 ' it was created or renamed from the filesystem'
210 210 ' please run the application again'
211 211 ' in order to rescan repositories') % repo_name,
212 212 category='error')
213 213
214 214 return redirect(url('repos'))
215 215 defaults = c.repo_info.__dict__
216 216 if c.repo_info.user:
217 217 defaults.update({'user':c.repo_info.user.username})
218 218 else:
219 219 replacement_user = self.sa.query(User)\
220 220 .filter(User.admin == True).first().username
221 221 defaults.update({'user':replacement_user})
222 222
223 223 c.users_array = repo_model.get_users_js()
224 224
225 225 for p in c.repo_info.repo_to_perm:
226 226 defaults.update({'perm_%s' % p.user.username:
227 227 p.permission.permission_name})
228 228
229 229 return htmlfill.render(
230 230 render('admin/repos/repo_edit.html'),
231 231 defaults=defaults,
232 232 encoding="UTF-8",
233 233 force_defaults=False
234 234 )
@@ -1,281 +1,281 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # settings controller for pylons
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 Created on July 14, 2010
22 22 settings controller for pylons
23 23 @author: marcink
24 24 """
25 25 from formencode import htmlfill
26 26 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
27 27 config
28 28 from pylons.controllers.util import abort, redirect
29 29 from pylons.i18n.translation import _
30 30 from pylons_app.lib import helpers as h
31 31 from pylons_app.lib.auth import LoginRequired, HasPermissionAllDecorator, \
32 32 HasPermissionAnyDecorator
33 33 from pylons_app.lib.base import BaseController, render
34 34 from pylons_app.lib.utils import repo2db_mapper, invalidate_cache, \
35 35 set_hg_app_config, get_hg_settings, get_hg_ui_settings, make_ui
36 36 from pylons_app.model.db import User, UserLog, HgAppSettings, HgAppUi
37 37 from pylons_app.model.forms import UserForm, ApplicationSettingsForm, \
38 38 ApplicationUiSettingsForm
39 39 from pylons_app.model.hg_model import HgModel
40 40 from pylons_app.model.user_model import UserModel
41 41 import formencode
42 42 import logging
43 43 import traceback
44 44
45 45 log = logging.getLogger(__name__)
46 46
47 47
48 48 class SettingsController(BaseController):
49 49 """REST Controller styled on the Atom Publishing Protocol"""
50 50 # To properly map this controller, ensure your config/routing.py
51 51 # file has a resource setup:
52 52 # map.resource('setting', 'settings', controller='admin/settings',
53 53 # path_prefix='/admin', name_prefix='admin_')
54 54
55 55
56 56 @LoginRequired()
57 57 def __before__(self):
58 58 c.admin_user = session.get('admin_user')
59 59 c.admin_username = session.get('admin_username')
60 60 super(SettingsController, self).__before__()
61 61
62 62
63 63 @HasPermissionAllDecorator('hg.admin')
64 64 def index(self, format='html'):
65 65 """GET /admin/settings: All items in the collection"""
66 66 # url('admin_settings')
67 67
68 68 defaults = get_hg_settings()
69 69 defaults.update(get_hg_ui_settings())
70 70 return htmlfill.render(
71 71 render('admin/settings/settings.html'),
72 72 defaults=defaults,
73 73 encoding="UTF-8",
74 74 force_defaults=False
75 75 )
76 76
77 77 @HasPermissionAllDecorator('hg.admin')
78 78 def create(self):
79 79 """POST /admin/settings: Create a new item"""
80 80 # url('admin_settings')
81 81
82 82 @HasPermissionAllDecorator('hg.admin')
83 83 def new(self, format='html'):
84 84 """GET /admin/settings/new: Form to create a new item"""
85 85 # url('admin_new_setting')
86 86
87 87 @HasPermissionAllDecorator('hg.admin')
88 88 def update(self, setting_id):
89 89 """PUT /admin/settings/setting_id: Update an existing item"""
90 90 # Forms posted to this method should contain a hidden field:
91 91 # <input type="hidden" name="_method" value="PUT" />
92 92 # Or using helpers:
93 93 # h.form(url('admin_setting', setting_id=ID),
94 94 # method='put')
95 95 # url('admin_setting', setting_id=ID)
96 96 if setting_id == 'mapping':
97 97 rm_obsolete = request.POST.get('destroy', False)
98 98 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
99 99
100 100 initial = HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
101 101 repo2db_mapper(initial, rm_obsolete)
102 102 invalidate_cache('cached_repo_list')
103 103 h.flash(_('Repositories sucessfully rescanned'), category='success')
104 104
105 105 if setting_id == 'global':
106 106
107 107 application_form = ApplicationSettingsForm()()
108 108 try:
109 109 form_result = application_form.to_python(dict(request.POST))
110 110
111 111 try:
112 112 hgsettings1 = self.sa.query(HgAppSettings)\
113 113 .filter(HgAppSettings.app_settings_name == 'title').one()
114 114 hgsettings1.app_settings_value = form_result['hg_app_title']
115 115
116 116 hgsettings2 = self.sa.query(HgAppSettings)\
117 117 .filter(HgAppSettings.app_settings_name == 'realm').one()
118 118 hgsettings2.app_settings_value = form_result['hg_app_realm']
119 119
120 120
121 121 self.sa.add(hgsettings1)
122 122 self.sa.add(hgsettings2)
123 123 self.sa.commit()
124 124 set_hg_app_config(config)
125 125 h.flash(_('Updated application settings'),
126 126 category='success')
127 127
128 128 except:
129 129 log.error(traceback.format_exc())
130 130 h.flash(_('error occured during updating application settings'),
131 131 category='error')
132 132
133 133 self.sa.rollback()
134 134
135 135
136 136 except formencode.Invalid as errors:
137 137 return htmlfill.render(
138 138 render('admin/settings/settings.html'),
139 139 defaults=errors.value,
140 140 errors=errors.error_dict or {},
141 141 prefix_error=False,
142 142 encoding="UTF-8")
143 143
144 144 if setting_id == 'mercurial':
145 145 application_form = ApplicationUiSettingsForm()()
146 146 try:
147 147 form_result = application_form.to_python(dict(request.POST))
148 148
149 149 try:
150 150
151 151 hgsettings1 = self.sa.query(HgAppUi)\
152 152 .filter(HgAppUi.ui_key == 'push_ssl').one()
153 153 hgsettings1.ui_value = form_result['web_push_ssl']
154 154
155 155 hgsettings2 = self.sa.query(HgAppUi)\
156 156 .filter(HgAppUi.ui_key == '/').one()
157 157 hgsettings2.ui_value = form_result['paths_root_path']
158 158
159 159
160 160 #HOOKS
161 161 hgsettings3 = self.sa.query(HgAppUi)\
162 162 .filter(HgAppUi.ui_key == 'changegroup.update').one()
163 163 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
164 164
165 165 hgsettings4 = self.sa.query(HgAppUi)\
166 166 .filter(HgAppUi.ui_key == 'changegroup.repo_size').one()
167 167 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
168 168
169 169
170 170
171 171
172 172 self.sa.add(hgsettings1)
173 173 self.sa.add(hgsettings2)
174 174 self.sa.add(hgsettings3)
175 175 self.sa.add(hgsettings4)
176 176 self.sa.commit()
177 177
178 178 h.flash(_('Updated mercurial settings'),
179 179 category='success')
180 180
181 181 except:
182 182 log.error(traceback.format_exc())
183 183 h.flash(_('error occured during updating application settings'),
184 184 category='error')
185 185
186 186 self.sa.rollback()
187 187
188 188
189 189 except formencode.Invalid as errors:
190 190 return htmlfill.render(
191 191 render('admin/settings/settings.html'),
192 192 defaults=errors.value,
193 193 errors=errors.error_dict or {},
194 194 prefix_error=False,
195 195 encoding="UTF-8")
196 196
197 197
198 198
199 199 return redirect(url('admin_settings'))
200 200
201 201 @HasPermissionAllDecorator('hg.admin')
202 202 def delete(self, setting_id):
203 203 """DELETE /admin/settings/setting_id: Delete an existing item"""
204 204 # Forms posted to this method should contain a hidden field:
205 205 # <input type="hidden" name="_method" value="DELETE" />
206 206 # Or using helpers:
207 207 # h.form(url('admin_setting', setting_id=ID),
208 208 # method='delete')
209 209 # url('admin_setting', setting_id=ID)
210 210
211 211 @HasPermissionAllDecorator('hg.admin')
212 212 def show(self, setting_id, format='html'):
213 213 """GET /admin/settings/setting_id: Show a specific item"""
214 214 # url('admin_setting', setting_id=ID)
215 215
216 216 @HasPermissionAllDecorator('hg.admin')
217 217 def edit(self, setting_id, format='html'):
218 218 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
219 219 # url('admin_edit_setting', setting_id=ID)
220 220
221 221
222 222 def my_account(self):
223 223 """
224 224 GET /_admin/my_account Displays info about my account
225 225 """
226 226 # url('admin_settings_my_account')
227 227 c.user = self.sa.query(User).get(c.hg_app_user.user_id)
228 228 if c.user.username == 'default':
229 229 h.flash(_("You can't edit this user since it's"
230 230 " crucial for entire application"), category='warning')
231 231 return redirect(url('users'))
232 232
233 233 defaults = c.user.__dict__
234 234 return htmlfill.render(
235 235 render('admin/users/user_edit_my_account.html'),
236 236 defaults=defaults,
237 237 encoding="UTF-8",
238 238 force_defaults=False
239 239 )
240 240
241 241 def my_account_update(self):
242 242 """PUT /_admin/my_account_update: Update an existing item"""
243 243 # Forms posted to this method should contain a hidden field:
244 244 # <input type="hidden" name="_method" value="PUT" />
245 245 # Or using helpers:
246 246 # h.form(url('admin_settings_my_account_update'),
247 247 # method='put')
248 248 # url('admin_settings_my_account_update', id=ID)
249 249 user_model = UserModel()
250 250 uid = c.hg_app_user.user_id
251 251 _form = UserForm(edit=True, old_data={'user_id':uid})()
252 252 form_result = {}
253 253 try:
254 254 form_result = _form.to_python(dict(request.POST))
255 255 user_model.update_my_account(uid, form_result)
256 256 h.flash(_('Your account was updated succesfully'),
257 257 category='success')
258 258
259 259 except formencode.Invalid as errors:
260 260 #c.user = self.sa.query(User).get(c.hg_app_user.user_id)
261 261 return htmlfill.render(
262 262 render('admin/users/user_edit_my_account.html'),
263 263 defaults=errors.value,
264 264 errors=errors.error_dict or {},
265 265 prefix_error=False,
266 266 encoding="UTF-8")
267 267 except Exception:
268 268 log.error(traceback.format_exc())
269 269 h.flash(_('error occured during update of user %s') \
270 270 % form_result.get('username'), category='error')
271 271
272 272 return redirect(url('my_account'))
273 273
274 @HasPermissionAnyDecorator('repository.create', 'hg.admin')
274 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
275 275 def create_repository(self):
276 276 """GET /_admin/create_repository: Form to create a new item"""
277 277 new_repo = request.GET.get('repo', '')
278 278 c.new_repo = h.repo_name_slug(new_repo)
279 279
280 280 return render('admin/repos/repo_add_create_repository.html')
281 281
@@ -1,163 +1,162 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # users controller for pylons
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 Created on April 4, 2010
22 22 users controller for pylons
23 23 @author: marcink
24 24 """
25 25
26 26 from formencode import htmlfill
27 27 from pylons import request, session, tmpl_context as c, url
28 28 from pylons.controllers.util import abort, redirect
29 29 from pylons.i18n.translation import _
30 30 from pylons_app.lib import helpers as h
31 31 from pylons_app.lib.auth import LoginRequired, HasPermissionAllDecorator
32 32 from pylons_app.lib.base import BaseController, render
33 33 from pylons_app.model.db import User, UserLog
34 34 from pylons_app.model.forms import UserForm
35 35 from pylons_app.model.user_model import UserModel, DefaultUserException
36 36 import formencode
37 37 import logging
38 38 import traceback
39 39
40
41 40 log = logging.getLogger(__name__)
42 41
43 42 class UsersController(BaseController):
44 43 """REST Controller styled on the Atom Publishing Protocol"""
45 44 # To properly map this controller, ensure your config/routing.py
46 45 # file has a resource setup:
47 46 # map.resource('user', 'users')
48 47
49 48 @LoginRequired()
50 49 @HasPermissionAllDecorator('hg.admin')
51 50 def __before__(self):
52 51 c.admin_user = session.get('admin_user')
53 52 c.admin_username = session.get('admin_username')
54 53 super(UsersController, self).__before__()
55 54
56 55
57 56 def index(self, format='html'):
58 57 """GET /users: All items in the collection"""
59 58 # url('users')
60 59
61 60 c.users_list = self.sa.query(User).all()
62 61 return render('admin/users/users.html')
63 62
64 63 def create(self):
65 64 """POST /users: Create a new item"""
66 65 # url('users')
67 66
68 67 user_model = UserModel()
69 68 login_form = UserForm()()
70 69 try:
71 70 form_result = login_form.to_python(dict(request.POST))
72 71 user_model.create(form_result)
73 72 h.flash(_('created user %s') % form_result['username'],
74 73 category='success')
75 74 except formencode.Invalid as errors:
76 75 return htmlfill.render(
77 76 render('admin/users/user_add.html'),
78 77 defaults=errors.value,
79 78 errors=errors.error_dict or {},
80 79 prefix_error=False,
81 80 encoding="UTF-8")
82 81 except Exception:
83 82 log.error(traceback.format_exc())
84 83 h.flash(_('error occured during creation of user %s') \
85 84 % request.POST.get('username'), category='error')
86 85 return redirect(url('users'))
87 86
88 87 def new(self, format='html'):
89 88 """GET /users/new: Form to create a new item"""
90 89 # url('new_user')
91 90 return render('admin/users/user_add.html')
92 91
93 92 def update(self, id):
94 93 """PUT /users/id: Update an existing item"""
95 94 # Forms posted to this method should contain a hidden field:
96 95 # <input type="hidden" name="_method" value="PUT" />
97 96 # Or using helpers:
98 97 # h.form(url('user', id=ID),
99 98 # method='put')
100 99 # url('user', id=ID)
101 100 user_model = UserModel()
102 101 _form = UserForm(edit=True, old_data={'user_id':id})()
103 102 form_result = {}
104 103 try:
105 104 form_result = _form.to_python(dict(request.POST))
106 105 user_model.update(id, form_result)
107 106 h.flash(_('User updated succesfully'), category='success')
108 107
109 108 except formencode.Invalid as errors:
110 109 c.user = user_model.get_user(id)
111 110 return htmlfill.render(
112 111 render('admin/users/user_edit.html'),
113 112 defaults=errors.value,
114 113 errors=errors.error_dict or {},
115 114 prefix_error=False,
116 115 encoding="UTF-8")
117 116 except Exception:
118 117 log.error(traceback.format_exc())
119 118 h.flash(_('error occured during update of user %s') \
120 119 % form_result.get('username'), category='error')
121 120
122 121 return redirect(url('users'))
123 122
124 123 def delete(self, id):
125 124 """DELETE /users/id: Delete an existing item"""
126 125 # Forms posted to this method should contain a hidden field:
127 126 # <input type="hidden" name="_method" value="DELETE" />
128 127 # Or using helpers:
129 128 # h.form(url('user', id=ID),
130 129 # method='delete')
131 130 # url('user', id=ID)
132 131 user_model = UserModel()
133 132 try:
134 133 user_model.delete(id)
135 134 h.flash(_('sucessfully deleted user'), category='success')
136 135 except DefaultUserException as e:
137 136 h.flash(str(e), category='warning')
138 137 except Exception:
139 138 h.flash(_('An error occured during deletion of user'),
140 139 category='error')
141 140 return redirect(url('users'))
142 141
143 142 def show(self, id, format='html'):
144 143 """GET /users/id: Show a specific item"""
145 144 # url('user', id=ID)
146 145
147 146
148 147 def edit(self, id, format='html'):
149 148 """GET /users/id/edit: Form to edit an existing item"""
150 149 # url('edit_user', id=ID)
151 150 c.user = self.sa.query(User).get(id)
152 151 if c.user.username == 'default':
153 152 h.flash(_("You can't edit this user since it's"
154 153 " crucial for entire application"), category='warning')
155 154 return redirect(url('users'))
156 155
157 156 defaults = c.user.__dict__
158 157 return htmlfill.render(
159 158 render('admin/users/user_edit.html'),
160 159 defaults=defaults,
161 160 encoding="UTF-8",
162 161 force_defaults=False
163 162 )
@@ -1,88 +1,97 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # login controller for pylons
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
21 """
22 Created on April 22, 2010
23 login controller for pylons
24 @author: marcink
25 """
20 26 from formencode import htmlfill
21 27 from pylons import request, response, session, tmpl_context as c, url
22 28 from pylons.controllers.util import abort, redirect
23 from pylons_app.lib.auth import AuthUser
29 from pylons_app.lib.auth import AuthUser, HasPermissionAnyDecorator
24 30 from pylons_app.lib.base import BaseController, render
25 31 from pylons_app.model.forms import LoginForm, RegisterForm
26 32 from pylons_app.model.user_model import UserModel
27 33 import formencode
28 34 import logging
29 """
30 Created on April 22, 2010
31 login controller for pylons
32 @author: marcink
33 """
34 35
35 36 log = logging.getLogger(__name__)
36 37
37 38 class LoginController(BaseController):
38 39
39 40 def __before__(self):
40 41 super(LoginController, self).__before__()
41 42
42 43 def index(self):
43 44 #redirect if already logged in
44 45 if c.hg_app_user.is_authenticated:
45 46 return redirect(url('hg_home'))
46 47
47 48 if request.POST:
48 49 #import Login Form validator class
49 50 login_form = LoginForm()
50 51 try:
51 52 c.form_result = login_form.to_python(dict(request.POST))
52 53 return redirect(url('hg_home'))
53 54
54 55 except formencode.Invalid as errors:
55 56 return htmlfill.render(
56 57 render('/login.html'),
57 58 defaults=errors.value,
58 59 errors=errors.error_dict or {},
59 60 prefix_error=False,
60 61 encoding="UTF-8")
61 62
62 63 return render('/login.html')
63 64
64
65 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
65 66 def register(self):
67 user_model = UserModel()
68 c.auto_active = False
69 for perm in user_model.get_default().user_perms:
70 if perm.permission.permission_name == 'hg.register.auto_activate':
71 c.auto_active = False
72 break
73
66 74 if request.POST:
67 user_model = UserModel()
75
68 76 register_form = RegisterForm()()
69 77 try:
70 78 form_result = register_form.to_python(dict(request.POST))
79 form_result['active'] = c.auto_active
71 80 user_model.create_registration(form_result)
72 81 return redirect(url('login_home'))
73 82
74 83 except formencode.Invalid as errors:
75 84 return htmlfill.render(
76 85 render('/register.html'),
77 86 defaults=errors.value,
78 87 errors=errors.error_dict or {},
79 88 prefix_error=False,
80 89 encoding="UTF-8")
81 90
82 91 return render('/register.html')
83 92
84 93 def logout(self):
85 94 session['hg_app_user'] = AuthUser()
86 95 session.save()
87 96 log.info('Logging out and setting user as Empty')
88 97 redirect(url('hg_home'))
@@ -1,433 +1,451 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # authentication and permission libraries
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 Created on April 4, 2010
22 22
23 23 @author: marcink
24 24 """
25 25 from beaker.cache import cache_region
26 26 from pylons import config, session, url, request
27 27 from pylons.controllers.util import abort, redirect
28 28 from pylons_app.lib.utils import get_repo_slug
29 29 from pylons_app.model import meta
30 from pylons_app.model.db import User, RepoToPerm, Repository, Permission
30 from pylons_app.model.db import User, RepoToPerm, Repository, Permission, \
31 UserToPerm
31 32 from sqlalchemy.exc import OperationalError
32 33 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
33 34 import bcrypt
34 35 from decorator import decorator
35 36 import logging
36 37
37 38 log = logging.getLogger(__name__)
38 39
39 40 def get_crypt_password(password):
40 41 """Cryptographic function used for password hashing based on sha1
41 42 @param password: password to hash
42 43 """
43 44 return bcrypt.hashpw(password, bcrypt.gensalt(10))
44 45
45 46 def check_password(password, hashed):
46 47 return bcrypt.hashpw(password, hashed) == hashed
47 48
48 49 @cache_region('super_short_term', 'cached_user')
49 50 def get_user_cached(username):
50 51 sa = meta.Session
51 52 try:
52 53 user = sa.query(User).filter(User.username == username).one()
53 54 finally:
54 55 meta.Session.remove()
55 56 return user
56 57
57 58 def authfunc(environ, username, password):
58 59 try:
59 60 user = get_user_cached(username)
60 61 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
61 62 log.error(e)
62 63 user = None
63 64
64 65 if user:
65 66 if user.active:
66 67 if user.username == username and check_password(password, user.password):
67 68 log.info('user %s authenticated correctly', username)
68 69 return True
69 70 else:
70 71 log.error('user %s is disabled', username)
71 72
72 73 return False
73 74
74 75 class AuthUser(object):
75 76 """
76 77 A simple object that handles a mercurial username for authentication
77 78 """
78 79 def __init__(self):
79 80 self.username = 'None'
80 81 self.name = ''
81 82 self.lastname = ''
82 83 self.email = ''
83 84 self.user_id = None
84 85 self.is_authenticated = False
85 86 self.is_admin = False
86 87 self.permissions = {}
87 88
88 89
89 90 def set_available_permissions(config):
90 91 """
91 92 This function will propagate pylons globals with all available defined
92 93 permission given in db. We don't wannt to check each time from db for new
93 94 permissions since adding a new permission also requires application restart
94 95 ie. to decorate new views with the newly created permission
95 96 @param config:
96 97 """
97 98 log.info('getting information about all available permissions')
98 99 try:
99 100 sa = meta.Session
100 101 all_perms = sa.query(Permission).all()
101 102 finally:
102 103 meta.Session.remove()
103 104
104 105 config['available_permissions'] = [x.permission_name for x in all_perms]
105 106
106 107 def set_base_path(config):
107 108 config['base_path'] = config['pylons.app_globals'].base_path
108 109
109 110 def fill_data(user):
110 111 """
111 112 Fills user data with those from database and log out user if not present
112 113 in database
113 114 @param user:
114 115 """
115 116 sa = meta.Session
116 117 dbuser = sa.query(User).get(user.user_id)
117 118 if dbuser:
118 119 user.username = dbuser.username
119 120 user.is_admin = dbuser.admin
120 121 user.name = dbuser.name
121 122 user.lastname = dbuser.lastname
122 123 user.email = dbuser.email
123 124 else:
124 125 user.is_authenticated = False
125 126 meta.Session.remove()
126 127 return user
127 128
128 129 def fill_perms(user):
129 130 """
130 131 Fills user permission attribute with permissions taken from database
131 132 @param user:
132 133 """
133 134
134 135 sa = meta.Session
135 136 user.permissions['repositories'] = {}
136 137 user.permissions['global'] = set()
137 138
138 #first fetch default permissions
139 default_perms = sa.query(RepoToPerm, Repository, Permission)\
139 #===========================================================================
140 # fetch default permissions
141 #===========================================================================
142 default_perms = sa.query(RepoToPerm, UserToPerm, Repository, Permission)\
143 .outerjoin((UserToPerm, RepoToPerm.user_id == UserToPerm.user_id))\
140 144 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
141 145 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
142 146 .filter(RepoToPerm.user_id == sa.query(User).filter(User.username ==
143 147 'default').one().user_id).all()
144 148
145 149 if user.is_admin:
150 #=======================================================================
151 # #admin have all rights set to admin
152 #=======================================================================
146 153 user.permissions['global'].add('hg.admin')
147 #admin have all rights set to admin
154
148 155 for perm in default_perms:
149 156 p = 'repository.admin'
150 157 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
151 158
152 159 else:
153 user.permissions['global'].add('repository.create')
154 user.permissions['global'].add('hg.register')
160 #=======================================================================
161 # set default permissions
162 #=======================================================================
155 163
164 #default global
165 for perm in default_perms:
166 user.permissions['global'].add(perm.UserToPerm.permission.permission_name)
167
168 # user.permissions['global'].add('hg.create.repository')
169 # user.permissions['global'].add('hg.register')
170
171 #default repositories
156 172 for perm in default_perms:
157 173 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
158 174 #disable defaults for private repos,
159 175 p = 'repository.none'
160 176 elif perm.Repository.user_id == user.user_id:
161 177 #set admin if owner
162 178 p = 'repository.admin'
163 179 else:
164 180 p = perm.Permission.permission_name
165 181
166 182 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
167 183
168
169 user_perms = sa.query(RepoToPerm, Permission, Repository)\
184 #=======================================================================
185 # #overwrite default with user permissions if any
186 #=======================================================================
187 user_perms = sa.query(RepoToPerm, UserToPerm, Permission, Repository)\
188 .outerjoin((UserToPerm, RepoToPerm.user_id == UserToPerm.user_id))\
170 189 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
171 190 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
172 191 .filter(RepoToPerm.user_id == user.user_id).all()
173 #overwrite userpermissions with defaults
192
174 193 for perm in user_perms:
175 #set write if owner
176 if perm.Repository.user_id == user.user_id:
177 p = 'repository.write'
194 if perm.Repository.user_id == user.user_id:#set admin if owner
195 p = 'repository.admin'
178 196 else:
179 197 p = perm.Permission.permission_name
180 198 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
181 199 meta.Session.remove()
182 200 return user
183 201
184 202 def get_user(session):
185 203 """
186 204 Gets user from session, and wraps permissions into user
187 205 @param session:
188 206 """
189 207 user = session.get('hg_app_user', AuthUser())
190 208 if user.is_authenticated:
191 209 user = fill_data(user)
192 210 user = fill_perms(user)
193 211 session['hg_app_user'] = user
194 212 session.save()
195 213 return user
196 214
197 215 #===============================================================================
198 216 # CHECK DECORATORS
199 217 #===============================================================================
200 218 class LoginRequired(object):
201 219 """Must be logged in to execute this function else redirect to login page"""
202 220
203 221 def __call__(self, func):
204 222 return decorator(self.__wrapper, func)
205 223
206 224 def __wrapper(self, func, *fargs, **fkwargs):
207 225 user = session.get('hg_app_user', AuthUser())
208 226 log.debug('Checking login required for user:%s', user.username)
209 227 if user.is_authenticated:
210 228 log.debug('user %s is authenticated', user.username)
211 229 return func(*fargs, **fkwargs)
212 230 else:
213 231 log.warn('user %s not authenticated', user.username)
214 232 log.debug('redirecting to login page')
215 233 return redirect(url('login_home'))
216 234
217 235 class PermsDecorator(object):
218 236 """Base class for decorators"""
219 237
220 238 def __init__(self, *required_perms):
221 239 available_perms = config['available_permissions']
222 240 for perm in required_perms:
223 241 if perm not in available_perms:
224 242 raise Exception("'%s' permission is not defined" % perm)
225 243 self.required_perms = set(required_perms)
226 244 self.user_perms = None
227 245
228 246 def __call__(self, func):
229 247 return decorator(self.__wrapper, func)
230 248
231 249
232 250 def __wrapper(self, func, *fargs, **fkwargs):
233 251 # _wrapper.__name__ = func.__name__
234 252 # _wrapper.__dict__.update(func.__dict__)
235 253 # _wrapper.__doc__ = func.__doc__
236 254
237 255 self.user_perms = session.get('hg_app_user', AuthUser()).permissions
238 256 log.debug('checking %s permissions %s for %s',
239 257 self.__class__.__name__, self.required_perms, func.__name__)
240 258
241 259 if self.check_permissions():
242 260 log.debug('Permission granted for %s', func.__name__)
243 261
244 262 return func(*fargs, **fkwargs)
245 263
246 264 else:
247 265 log.warning('Permission denied for %s', func.__name__)
248 266 #redirect with forbidden ret code
249 267 return abort(403)
250 268
251 269
252 270
253 271 def check_permissions(self):
254 272 """Dummy function for overriding"""
255 273 raise Exception('You have to write this function in child class')
256 274
257 275 class HasPermissionAllDecorator(PermsDecorator):
258 276 """Checks for access permission for all given predicates. All of them
259 277 have to be meet in order to fulfill the request
260 278 """
261 279
262 280 def check_permissions(self):
263 281 if self.required_perms.issubset(self.user_perms.get('global')):
264 282 return True
265 283 return False
266 284
267 285
268 286 class HasPermissionAnyDecorator(PermsDecorator):
269 287 """Checks for access permission for any of given predicates. In order to
270 288 fulfill the request any of predicates must be meet
271 289 """
272 290
273 291 def check_permissions(self):
274 292 if self.required_perms.intersection(self.user_perms.get('global')):
275 293 return True
276 294 return False
277 295
278 296 class HasRepoPermissionAllDecorator(PermsDecorator):
279 297 """Checks for access permission for all given predicates for specific
280 298 repository. All of them have to be meet in order to fulfill the request
281 299 """
282 300
283 301 def check_permissions(self):
284 302 repo_name = get_repo_slug(request)
285 303 try:
286 304 user_perms = set([self.user_perms['repositories'][repo_name]])
287 305 except KeyError:
288 306 return False
289 307 if self.required_perms.issubset(user_perms):
290 308 return True
291 309 return False
292 310
293 311
294 312 class HasRepoPermissionAnyDecorator(PermsDecorator):
295 313 """Checks for access permission for any of given predicates for specific
296 314 repository. In order to fulfill the request any of predicates must be meet
297 315 """
298 316
299 317 def check_permissions(self):
300 318 repo_name = get_repo_slug(request)
301 319
302 320 try:
303 321 user_perms = set([self.user_perms['repositories'][repo_name]])
304 322 except KeyError:
305 323 return False
306 324 if self.required_perms.intersection(user_perms):
307 325 return True
308 326 return False
309 327 #===============================================================================
310 328 # CHECK FUNCTIONS
311 329 #===============================================================================
312 330
313 331 class PermsFunction(object):
314 332 """Base function for other check functions"""
315 333
316 334 def __init__(self, *perms):
317 335 available_perms = config['available_permissions']
318 336
319 337 for perm in perms:
320 338 if perm not in available_perms:
321 339 raise Exception("'%s' permission in not defined" % perm)
322 340 self.required_perms = set(perms)
323 341 self.user_perms = None
324 342 self.granted_for = ''
325 343 self.repo_name = None
326 344
327 345 def __call__(self, check_Location=''):
328 346 user = session.get('hg_app_user', False)
329 347 if not user:
330 348 return False
331 349 self.user_perms = user.permissions
332 350 self.granted_for = user.username
333 351 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
334 352
335 353 if self.check_permissions():
336 354 log.debug('Permission granted for %s @%s', self.granted_for,
337 355 check_Location)
338 356 return True
339 357
340 358 else:
341 359 log.warning('Permission denied for %s @%s', self.granted_for,
342 360 check_Location)
343 361 return False
344 362
345 363 def check_permissions(self):
346 364 """Dummy function for overriding"""
347 365 raise Exception('You have to write this function in child class')
348 366
349 367 class HasPermissionAll(PermsFunction):
350 368 def check_permissions(self):
351 369 if self.required_perms.issubset(self.user_perms.get('global')):
352 370 return True
353 371 return False
354 372
355 373 class HasPermissionAny(PermsFunction):
356 374 def check_permissions(self):
357 375 if self.required_perms.intersection(self.user_perms.get('global')):
358 376 return True
359 377 return False
360 378
361 379 class HasRepoPermissionAll(PermsFunction):
362 380
363 381 def __call__(self, repo_name=None, check_Location=''):
364 382 self.repo_name = repo_name
365 383 return super(HasRepoPermissionAll, self).__call__(check_Location)
366 384
367 385 def check_permissions(self):
368 386 if not self.repo_name:
369 387 self.repo_name = get_repo_slug(request)
370 388
371 389 try:
372 390 self.user_perms = set([self.user_perms['repositories']\
373 391 [self.repo_name]])
374 392 except KeyError:
375 393 return False
376 394 self.granted_for = self.repo_name
377 395 if self.required_perms.issubset(self.user_perms):
378 396 return True
379 397 return False
380 398
381 399 class HasRepoPermissionAny(PermsFunction):
382 400
383 401 def __call__(self, repo_name=None, check_Location=''):
384 402 self.repo_name = repo_name
385 403 return super(HasRepoPermissionAny, self).__call__(check_Location)
386 404
387 405 def check_permissions(self):
388 406 if not self.repo_name:
389 407 self.repo_name = get_repo_slug(request)
390 408
391 409 try:
392 410 self.user_perms = set([self.user_perms['repositories']\
393 411 [self.repo_name]])
394 412 except KeyError:
395 413 return False
396 414 self.granted_for = self.repo_name
397 415 if self.required_perms.intersection(self.user_perms):
398 416 return True
399 417 return False
400 418
401 419 #===============================================================================
402 420 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
403 421 #===============================================================================
404 422
405 423 class HasPermissionAnyMiddleware(object):
406 424 def __init__(self, *perms):
407 425 self.required_perms = set(perms)
408 426
409 427 def __call__(self, user, repo_name):
410 428 usr = AuthUser()
411 429 usr.user_id = user.user_id
412 430 usr.username = user.username
413 431 usr.is_admin = user.admin
414 432
415 433 try:
416 434 self.user_perms = set([fill_perms(usr)\
417 435 .permissions['repositories'][repo_name]])
418 436 except:
419 437 self.user_perms = set()
420 438 self.granted_for = ''
421 439 self.username = user.username
422 440 self.repo_name = repo_name
423 441 return self.check_permissions()
424 442
425 443 def check_permissions(self):
426 444 log.debug('checking mercurial protocol '
427 445 'permissions for user:%s repository:%s',
428 446 self.username, self.repo_name)
429 447 if self.required_perms.intersection(self.user_perms):
430 448 log.debug('permission granted')
431 449 return True
432 450 log.debug('permission denied')
433 451 return False
@@ -1,205 +1,244 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 import uuid
31 31 ROOT = dn(dn(dn(os.path.realpath(__file__))))
32 32 sys.path.append(ROOT)
33 33
34 34 from pylons_app.lib.auth import get_crypt_password
35 35 from pylons_app.lib.utils import ask_ok
36 36 from pylons_app.model import init_model
37 from pylons_app.model.db import User, Permission, HgAppUi, HgAppSettings
37 from pylons_app.model.db import User, Permission, HgAppUi, HgAppSettings, \
38 UserToPerm
38 39 from pylons_app.model import meta
39 40 from sqlalchemy.engine import create_engine
40 41 import logging
41 42
42 43 log = logging.getLogger(__name__)
43 44
44 45 class DbManage(object):
45 46 def __init__(self, log_sql):
46 47 self.dbname = 'hg_app.db'
47 48 dburi = 'sqlite:////%s' % jn(ROOT, self.dbname)
48 49 engine = create_engine(dburi, echo=log_sql)
49 50 init_model(engine)
50 51 self.sa = meta.Session
51 52 self.db_exists = False
52 53
53 54 def check_for_db(self, override):
54 55 log.info('checking for exisiting db')
55 56 if os.path.isfile(jn(ROOT, self.dbname)):
56 57 self.db_exists = True
57 58 log.info('database exisist')
58 59 if not override:
59 60 raise Exception('database already exists')
60 61
61 62 def create_tables(self, override=False):
62 63 """
63 64 Create a auth database
64 65 """
65 66 self.check_for_db(override)
66 67 if override:
67 68 log.info("database exisist and it's going to be destroyed")
68 69 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
69 70 if not destroy:
70 71 sys.exit()
71 72 if self.db_exists and destroy:
72 73 os.remove(jn(ROOT, self.dbname))
73 74 checkfirst = not override
74 75 meta.Base.metadata.create_all(checkfirst=checkfirst)
75 76 log.info('Created tables for %s', self.dbname)
76 77
77 78 def admin_prompt(self):
78 79 import getpass
79 80 username = raw_input('Specify admin username:')
80 81 password = getpass.getpass('Specify admin password:')
81 82 self.create_user(username, password, True)
82 83
83 84 def config_prompt(self):
84 85 log.info('Setting up repositories config')
85 86
86 87 path = raw_input('Specify valid full path to your repositories'
87 88 ' you can change this later in application settings:')
88 89
89 90 if not os.path.isdir(path):
90 91 log.error('You entered wrong path')
91 92 sys.exit()
92 93
93 94 hooks1 = HgAppUi()
94 95 hooks1.ui_section = 'hooks'
95 96 hooks1.ui_key = 'changegroup.update'
96 97 hooks1.ui_value = 'hg update >&2'
97 98
98 99 hooks2 = HgAppUi()
99 100 hooks2.ui_section = 'hooks'
100 101 hooks2.ui_key = 'changegroup.repo_size'
101 102 hooks2.ui_value = 'python:pylons_app.lib.hooks.repo_size'
102 103
103 104 web1 = HgAppUi()
104 105 web1.ui_section = 'web'
105 106 web1.ui_key = 'push_ssl'
106 107 web1.ui_value = 'false'
107 108
108 109 web2 = HgAppUi()
109 110 web2.ui_section = 'web'
110 111 web2.ui_key = 'allow_archive'
111 112 web2.ui_value = 'gz zip bz2'
112 113
113 114 web3 = HgAppUi()
114 115 web3.ui_section = 'web'
115 116 web3.ui_key = 'allow_push'
116 117 web3.ui_value = '*'
117 118
118 119 web4 = HgAppUi()
119 120 web4.ui_section = 'web'
120 121 web4.ui_key = 'baseurl'
121 122 web4.ui_value = '/'
122 123
123 124 paths = HgAppUi()
124 125 paths.ui_section = 'paths'
125 126 paths.ui_key = '/'
126 127 paths.ui_value = os.path.join(path, '*')
127 128
128 129
129 130 hgsettings1 = HgAppSettings()
130 131
131 132 hgsettings1.app_settings_name = 'realm'
132 133 hgsettings1.app_settings_value = 'hg-app authentication'
133 134
134 135 hgsettings2 = HgAppSettings()
135 136 hgsettings2.app_settings_name = 'title'
136 137 hgsettings2.app_settings_value = 'hg-app'
137 138
138 139 try:
139 140 self.sa.add(hooks1)
140 141 self.sa.add(hooks2)
141 142 self.sa.add(web1)
142 143 self.sa.add(web2)
143 144 self.sa.add(web3)
144 145 self.sa.add(web4)
145 146 self.sa.add(paths)
146 147 self.sa.add(hgsettings1)
147 148 self.sa.add(hgsettings2)
148 149 self.sa.commit()
149 150 except:
150 151 self.sa.rollback()
151 152 raise
152 153 log.info('created ui config')
153 154
154 155 def create_user(self, username, password, admin=False):
155 156
156 157 log.info('creating default user')
157 158 #create default user for handling default permissions.
158 159 def_user = User()
159 160 def_user.username = 'default'
160 161 def_user.password = get_crypt_password(str(uuid.uuid1())[:8])
161 162 def_user.name = 'default'
162 163 def_user.lastname = 'default'
163 164 def_user.email = 'default@default.com'
164 165 def_user.admin = False
165 166 def_user.active = False
166 167
167 168 log.info('creating administrator user %s', username)
168 169 new_user = User()
169 170 new_user.username = username
170 171 new_user.password = get_crypt_password(password)
171 172 new_user.name = 'Hg'
172 173 new_user.lastname = 'Admin'
173 174 new_user.email = 'admin@localhost'
174 175 new_user.admin = admin
175 176 new_user.active = True
176 177
177 178 try:
178 179 self.sa.add(def_user)
179 180 self.sa.add(new_user)
180 181 self.sa.commit()
181 182 except:
182 183 self.sa.rollback()
183 184 raise
184 185
185 186 def create_permissions(self):
186 187 #module.(access|create|change|delete)_[name]
187 188 #module.(read|write|owner)
188 189 perms = [('repository.none', 'Repository no access'),
189 190 ('repository.read', 'Repository read access'),
190 191 ('repository.write', 'Repository write access'),
191 192 ('repository.admin', 'Repository admin access'),
192 ('repository.create', 'Repository create'),
193 193 ('hg.admin', 'Hg Administrator'),
194 ('hg.create.repository', 'Repository create'),
195 ('hg.create.none', 'Repository creation disabled'),
196 ('hg.register.none', 'Register disabled'),
197 ('hg.register.manual_activate', 'Register new user with hg-app without manual activation'),
198 ('hg.register.auto_activate', 'Register new user with hg-app without auto activation'),
194 199 ]
195 200
196 201 for p in perms:
197 202 new_perm = Permission()
198 203 new_perm.permission_name = p[0]
199 204 new_perm.permission_longname = p[1]
200 205 try:
201 206 self.sa.add(new_perm)
202 207 self.sa.commit()
203 208 except:
204 209 self.sa.rollback()
205 210 raise
211
212 def populate_default_permissions(self):
213 log.info('creating default user permissions')
214
215 default_user = self.sa.query(User)\
216 .filter(User.username == 'default').scalar()
217
218 reg_perm = UserToPerm()
219 reg_perm.user = default_user
220 reg_perm.permission = self.sa.query(Permission)\
221 .filter(Permission.permission_name == 'hg.register.manual_activate')\
222 .scalar()
223
224 create_repo_perm = UserToPerm()
225 create_repo_perm.user = default_user
226 create_repo_perm.permission = self.sa.query(Permission)\
227 .filter(Permission.permission_name == 'hg.create.repository')\
228 .scalar()
229
230 default_repo_perm = UserToPerm()
231 default_repo_perm.user = default_user
232 default_repo_perm.permission = self.sa.query(Permission)\
233 .filter(Permission.permission_name == 'repository.read')\
234 .scalar()
235
236 try:
237 self.sa.add(reg_perm)
238 self.sa.add(create_repo_perm)
239 self.sa.add(default_repo_perm)
240 self.sa.commit()
241 except:
242 self.sa.rollback()
243 raise
244
@@ -1,103 +1,107 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 HgAppSettings(Base):
7 7 __tablename__ = 'hg_app_settings'
8 8 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
9 9 app_settings_id = Column("app_settings_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
10 10 app_settings_name = Column("app_settings_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
11 11 app_settings_value = Column("app_settings_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
12 12
13 13 class HgAppUi(Base):
14 14 __tablename__ = 'hg_app_ui'
15 15 __table_args__ = {'useexisting':True}
16 16 ui_id = Column("ui_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
17 17 ui_section = Column("ui_section", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
18 18 ui_key = Column("ui_key", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
19 19 ui_value = Column("ui_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
20 20 ui_active = Column("ui_active", BOOLEAN(), nullable=True, unique=None, default=True)
21 21
22 22
23 23 class User(Base):
24 24 __tablename__ = 'users'
25 25 __table_args__ = {'useexisting':True}
26 26 user_id = Column("user_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
27 27 username = Column("username", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
28 28 password = Column("password", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
29 29 active = Column("active", BOOLEAN(), nullable=True, unique=None, default=None)
30 30 admin = Column("admin", BOOLEAN(), nullable=True, unique=None, default=False)
31 31 name = Column("name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
32 32 lastname = Column("lastname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
33 33 email = Column("email", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
34 34 last_login = Column("last_login", DATETIME(timezone=False), nullable=True, unique=None, default=None)
35 35
36 36 user_log = relation('UserLog')
37 user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id")
37 38
38 39 @LazyProperty
39 40 def full_contact(self):
40 41 return '%s %s <%s>' % (self.name, self.lastname, self.email)
41 42
42 43 def __repr__(self):
43 return "<User('%s:%s')>" % (self.user_id, self.username)
44 return "<User('id:%s:%s')>" % (self.user_id, self.username)
44 45
45 46 class UserLog(Base):
46 47 __tablename__ = 'user_logs'
47 48 __table_args__ = {'useexisting':True}
48 49 user_log_id = Column("user_log_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
49 50 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
50 51 user_ip = Column("user_ip", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
51 52 repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_name'), nullable=False, unique=None, default=None)
52 53 action = Column("action", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
53 54 action_date = Column("action_date", DATETIME(timezone=False), nullable=True, unique=None, default=None)
54 55
55 56 user = relation('User')
56 57
57 58 class Repository(Base):
58 59 __tablename__ = 'repositories'
59 60 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
60 61 repo_id = Column("repo_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
61 62 repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
62 63 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
63 64 private = Column("private", BOOLEAN(), nullable=True, unique=None, default=None)
64 65 description = Column("description", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
65 66
66 67 user = relation('User')
67 68 repo_to_perm = relation('RepoToPerm', cascade='all')
68 69
70 def __repr__(self):
71 return "<Repository('id:%s:%s')>" % (self.repo_id, self.repo_name)
72
69 73 class Permission(Base):
70 74 __tablename__ = 'permissions'
71 75 __table_args__ = {'useexisting':True}
72 76 permission_id = Column("permission_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
73 77 permission_name = Column("permission_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
74 78 permission_longname = Column("permission_longname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
75 79
76 80 def __repr__(self):
77 81 return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
78 82
79 83 class RepoToPerm(Base):
80 84 __tablename__ = 'repo_to_perm'
81 85 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
82 86 repo_to_perm_id = Column("repo_to_perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
83 87 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
84 88 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
85 89 repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
86 90
87 91 user = relation('User')
88 92 permission = relation('Permission')
89 93 repository = relation('Repository')
90 94
91 95 class UserToPerm(Base):
92 96 __tablename__ = 'user_to_perm'
93 97 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
94 98 user_to_perm_id = Column("user_to_perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
95 99 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
96 100 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
97 101
98 102 user = relation('User')
99 103 permission = relation('Permission')
100 104
101 105
102 106
103 107
@@ -1,330 +1,339 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 check_password, get_crypt_password
28 28 from pylons_app.model import meta
29 29 from pylons_app.model.db import User, Repository
30 30 from sqlalchemy.exc import OperationalError
31 31 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
32 32 from webhelpers.pylonslib.secure_form import authentication_token
33 33 import datetime
34 34 import formencode
35 35 import logging
36 36 import os
37 37 import pylons_app.lib.helpers as h
38 38 log = logging.getLogger(__name__)
39 39
40 40
41 41 #this is needed to translate the messages using _() in validators
42 42 class State_obj(object):
43 43 _ = staticmethod(_)
44 44
45 45 #===============================================================================
46 46 # VALIDATORS
47 47 #===============================================================================
48 48 class ValidAuthToken(formencode.validators.FancyValidator):
49 49 messages = {'invalid_token':_('Token mismatch')}
50 50
51 51 def validate_python(self, value, state):
52 52
53 53 if value != authentication_token():
54 54 raise formencode.Invalid(self.message('invalid_token', state,
55 55 search_number=value), value, state)
56 56
57 57 def ValidUsername(edit, old_data):
58 58 class _ValidUsername(formencode.validators.FancyValidator):
59 59
60 60 def validate_python(self, value, state):
61 61 if value in ['default', 'new_user']:
62 62 raise formencode.Invalid(_('Invalid username'), value, state)
63 63 #check if user is uniq
64 64 sa = meta.Session
65 65 old_un = None
66 66 if edit:
67 67 old_un = sa.query(User).get(old_data.get('user_id')).username
68 68
69 69 if old_un != value or not edit:
70 70 if sa.query(User).filter(User.username == value).scalar():
71 71 raise formencode.Invalid(_('This username already exists') ,
72 72 value, state)
73 73 meta.Session.remove()
74 74
75 75 return _ValidUsername
76 76
77 77 class ValidPassword(formencode.validators.FancyValidator):
78 78
79 79 def to_python(self, value, state):
80 80 if value:
81 81 return get_crypt_password(value)
82 82
83 83 class ValidAuth(formencode.validators.FancyValidator):
84 84 messages = {
85 85 'invalid_password':_('invalid password'),
86 86 'invalid_login':_('invalid user name'),
87 87 'disabled_account':_('Your acccount is disabled')
88 88
89 89 }
90 90 #error mapping
91 91 e_dict = {'username':messages['invalid_login'],
92 92 'password':messages['invalid_password']}
93 93 e_dict_disable = {'username':messages['disabled_account']}
94 94
95 95 def validate_python(self, value, state):
96 96 sa = meta.Session
97 97 password = value['password']
98 98 username = value['username']
99 99 try:
100 100 user = sa.query(User).filter(User.username == username).one()
101 101 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
102 102 log.error(e)
103 103 user = None
104 104 raise formencode.Invalid(self.message('invalid_password',
105 105 state=State_obj), value, state,
106 106 error_dict=self.e_dict)
107 107 if user:
108 108 if user.active:
109 109 if user.username == username and check_password(password, user.password):
110 110 from pylons_app.lib.auth import AuthUser
111 111 auth_user = AuthUser()
112 112 auth_user.username = username
113 113 auth_user.is_authenticated = True
114 114 auth_user.is_admin = user.admin
115 115 auth_user.user_id = user.user_id
116 116 auth_user.name = user.name
117 117 auth_user.lastname = user.lastname
118 118 session['hg_app_user'] = auth_user
119 119 session.save()
120 120 log.info('user %s is now authenticated', username)
121 121
122 122 try:
123 123 user.last_login = datetime.datetime.now()
124 124 sa.add(user)
125 125 sa.commit()
126 126 except (OperationalError) as e:
127 127 log.error(e)
128 128 sa.rollback()
129 129
130 130 return value
131 131 else:
132 132 log.warning('user %s not authenticated', username)
133 133 raise formencode.Invalid(self.message('invalid_password',
134 134 state=State_obj), value, state,
135 135 error_dict=self.e_dict)
136 136 else:
137 137 log.warning('user %s is disabled', username)
138 138 raise formencode.Invalid(self.message('disabled_account',
139 139 state=State_obj),
140 140 value, state,
141 141 error_dict=self.e_dict_disable)
142 142
143 143 meta.Session.remove()
144 144
145 145
146 146 class ValidRepoUser(formencode.validators.FancyValidator):
147 147
148 148 def to_python(self, value, state):
149 149 sa = meta.Session
150 150 try:
151 151 self.user_db = sa.query(User)\
152 152 .filter(User.active == True)\
153 153 .filter(User.username == value).one()
154 154 except Exception:
155 155 raise formencode.Invalid(_('This username is not valid'),
156 156 value, state)
157 157 meta.Session.remove()
158 158 return self.user_db.user_id
159 159
160 160 def ValidRepoName(edit, old_data):
161 161 class _ValidRepoName(formencode.validators.FancyValidator):
162 162
163 163 def to_python(self, value, state):
164 164 slug = h.repo_name_slug(value)
165 165 if slug in ['_admin']:
166 166 raise formencode.Invalid(_('This repository name is disallowed'),
167 167 value, state)
168 168 if old_data.get('repo_name') != value or not edit:
169 169 sa = meta.Session
170 170 if sa.query(Repository).filter(Repository.repo_name == slug).scalar():
171 171 raise formencode.Invalid(_('This repository already exists') ,
172 172 value, state)
173 173 meta.Session.remove()
174 174 return slug
175 175
176 176
177 177 return _ValidRepoName
178 178
179 179 class ValidPerms(formencode.validators.FancyValidator):
180 180 messages = {'perm_new_user_name':_('This username is not valid')}
181 181
182 182 def to_python(self, value, state):
183 183 perms_update = []
184 184 perms_new = []
185 185 #build a list of permission to update and new permission to create
186 186 for k, v in value.items():
187 187 if k.startswith('perm_'):
188 188 if k.startswith('perm_new_user'):
189 189 new_perm = value.get('perm_new_user', False)
190 190 new_user = value.get('perm_new_user_name', False)
191 191 if new_user and new_perm:
192 192 if (new_user, new_perm) not in perms_new:
193 193 perms_new.append((new_user, new_perm))
194 194 else:
195 195 usr = k[5:]
196 196 if usr == 'default':
197 197 if value['private']:
198 198 #set none for default when updating to private repo
199 199 v = 'repository.none'
200 200 perms_update.append((usr, v))
201 201 value['perms_updates'] = perms_update
202 202 value['perms_new'] = perms_new
203 203 sa = meta.Session
204 204 for k, v in perms_new:
205 205 try:
206 206 self.user_db = sa.query(User)\
207 207 .filter(User.active == True)\
208 208 .filter(User.username == k).one()
209 209 except Exception:
210 210 msg = self.message('perm_new_user_name',
211 211 state=State_obj)
212 212 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
213 213 return value
214 214
215 215 class ValidSettings(formencode.validators.FancyValidator):
216 216
217 217 def to_python(self, value, state):
218 218 #settings form can't edit user
219 219 if value.has_key('user'):
220 220 del['value']['user']
221 221
222 222 return value
223 223
224 224 class ValidPath(formencode.validators.FancyValidator):
225 225 def to_python(self, value, state):
226 226 isdir = os.path.isdir(value.replace('*', ''))
227 227 if (value.endswith('/*') or value.endswith('/**')) and isdir:
228 228 return value
229 229 elif not isdir:
230 230 msg = _('This is not a valid path')
231 231 else:
232 232 msg = _('You need to specify * or ** at the end of path (ie. /tmp/*)')
233 233
234 234 raise formencode.Invalid(msg, value, state,
235 235 error_dict={'paths_root_path':msg})
236 236
237 237 #===============================================================================
238 238 # FORMS
239 239 #===============================================================================
240 240 class LoginForm(formencode.Schema):
241 241 allow_extra_fields = True
242 242 filter_extra_fields = True
243 243 username = UnicodeString(
244 244 strip=True,
245 245 min=3,
246 246 not_empty=True,
247 247 messages={
248 248 'empty':_('Please enter a login'),
249 249 'tooShort':_('Enter a value %(min)i characters long or more')}
250 250 )
251 251
252 252 password = UnicodeString(
253 253 strip=True,
254 254 min=3,
255 255 not_empty=True,
256 256 messages={
257 257 'empty':_('Please enter a password'),
258 258 'tooShort':_('Enter a value %(min)i characters long or more')}
259 259 )
260 260
261 261
262 262 #chained validators have access to all data
263 263 chained_validators = [ValidAuth]
264 264
265 265 def UserForm(edit=False, old_data={}):
266 266 class _UserForm(formencode.Schema):
267 267 allow_extra_fields = True
268 268 filter_extra_fields = True
269 269 username = All(UnicodeString(strip=True, min=3, not_empty=True), ValidUsername(edit, old_data))
270 270 if edit:
271 271 new_password = All(UnicodeString(strip=True, min=3, not_empty=False), ValidPassword)
272 272 admin = StringBoolean(if_missing=False)
273 273 else:
274 274 password = All(UnicodeString(strip=True, min=8, not_empty=True), ValidPassword)
275 275 active = StringBoolean(if_missing=False)
276 276 name = UnicodeString(strip=True, min=3, not_empty=True)
277 277 lastname = UnicodeString(strip=True, min=3, not_empty=True)
278 278 email = Email(not_empty=True)
279 279
280 280 return _UserForm
281 281
282 282 RegisterForm = UserForm
283 283
284 284
285 285 def RepoForm(edit=False, old_data={}):
286 286 class _RepoForm(formencode.Schema):
287 287 allow_extra_fields = True
288 288 filter_extra_fields = False
289 289 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
290 290 description = UnicodeString(strip=True, min=3, not_empty=True)
291 291 private = StringBoolean(if_missing=False)
292 292
293 293 if edit:
294 294 user = All(Int(not_empty=True), ValidRepoUser)
295 295
296 296 chained_validators = [ValidPerms]
297 297 return _RepoForm
298 298
299 299 def RepoSettingsForm(edit=False, old_data={}):
300 300 class _RepoForm(formencode.Schema):
301 301 allow_extra_fields = True
302 302 filter_extra_fields = False
303 303 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
304 304 description = UnicodeString(strip=True, min=3, not_empty=True)
305 305 private = StringBoolean(if_missing=False)
306 306
307 307 chained_validators = [ValidPerms, ValidSettings]
308 308 return _RepoForm
309 309
310 310
311 311 def ApplicationSettingsForm():
312 312 class _ApplicationSettingsForm(formencode.Schema):
313 313 allow_extra_fields = True
314 314 filter_extra_fields = False
315 315 hg_app_title = UnicodeString(strip=True, min=3, not_empty=True)
316 316 hg_app_realm = UnicodeString(strip=True, min=3, not_empty=True)
317 317
318 318 return _ApplicationSettingsForm
319 319
320 320 def ApplicationUiSettingsForm():
321 321 class _ApplicationUiSettingsForm(formencode.Schema):
322 322 allow_extra_fields = True
323 323 filter_extra_fields = False
324 324 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
325 325 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=3, not_empty=True))
326 326 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
327 327 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
328 328
329 329 return _ApplicationUiSettingsForm
330 330
331 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
332 class _DefaultPermissionsForm(formencode.Schema):
333 allow_extra_fields = True
334 filter_extra_fields = True
335 default_perm = OneOf(perms_choices)
336 default_register = OneOf(register_choices)
337 default_create = OneOf(create_choices)
338
339 return _DefaultPermissionsForm
@@ -1,178 +1,185 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 Created on Jun 5, 2010
21 21 model for handling repositories actions
22 22 @author: marcink
23 23 """
24 24 from datetime import datetime
25 25 from pylons import app_globals as g
26 26 from pylons_app.lib.utils import check_repo
27 27 from pylons_app.model.db import Repository, RepoToPerm, User, Permission
28 28 from pylons_app.model.meta import Session
29 from pylons_app.model.user_model import UserModel
29 30 import logging
30 31 import os
31 32 import shutil
32 33 import traceback
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).filter(Repository.repo_name == id).scalar()
42 43
43 44 def get_users_js(self):
44 45
45 46 users = self.sa.query(User).filter(User.active == True).all()
46 47 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
47 48 users_array = '[%s];' % '\n'.join([u_tmpl % (u.user_id, u.name,
48 49 u.lastname, u.username)
49 50 for u in users])
50 51 return users_array
51 52
52 53
53 54 def update(self, repo_name, form_data):
54 55 try:
55 56
56 57 #update permissions
57 58 for username, perm in form_data['perms_updates']:
58 59 r2p = self.sa.query(RepoToPerm)\
59 60 .filter(RepoToPerm.user == self.sa.query(User)\
60 61 .filter(User.username == username).one())\
61 62 .filter(RepoToPerm.repository == self.get(repo_name))\
62 63 .one()
63 64
64 65 r2p.permission_id = self.sa.query(Permission).filter(
65 66 Permission.permission_name ==
66 67 perm).one().permission_id
67 68 self.sa.add(r2p)
68 69
69 70 #set new permissions
70 71 for username, perm in form_data['perms_new']:
71 72 r2p = RepoToPerm()
72 73 r2p.repository = self.get(repo_name)
73 74 r2p.user = self.sa.query(User)\
74 75 .filter(User.username == username).one()
75 76
76 77 r2p.permission_id = self.sa.query(Permission).filter(
77 78 Permission.permission_name == perm)\
78 79 .one().permission_id
79 80 self.sa.add(r2p)
80 81
81 82 #update current repo
82 83 cur_repo = self.get(repo_name)
83 84
84 85 for k, v in form_data.items():
85 86 if k == 'user':
86 87 cur_repo.user_id = v
87 88 else:
88 89 setattr(cur_repo, k, v)
89 90
90 91 self.sa.add(cur_repo)
91 92
92 93 if repo_name != form_data['repo_name']:
93 94 #rename our data
94 95 self.__rename_repo(repo_name, form_data['repo_name'])
95 96
96 97 self.sa.commit()
97 98 except:
98 99 log.error(traceback.format_exc())
99 100 self.sa.rollback()
100 101 raise
101 102
102 103 def create(self, form_data, cur_user, just_db=False):
103 104 try:
104 105 repo_name = form_data['repo_name']
105 106 new_repo = Repository()
106 107 for k, v in form_data.items():
107 108 setattr(new_repo, k, v)
108 109
109 110 new_repo.user_id = cur_user.user_id
110 111 self.sa.add(new_repo)
111 112
112 113 #create default permission
113 114 repo_to_perm = RepoToPerm()
114 default_perm = 'repository.none' if form_data['private'] \
115 else 'repository.read'
115 default = 'repository.read'
116 for p in UserModel().get_default().user_perms:
117 if p.permission.permission_name.startswith('repository.'):
118 default = p.permission.permission_name
119 break
120
121 default_perm = 'repository.none' if form_data['private'] else default
122
116 123 repo_to_perm.permission_id = self.sa.query(Permission)\
117 124 .filter(Permission.permission_name == default_perm)\
118 125 .one().permission_id
119 126
120 127 repo_to_perm.repository_id = new_repo.repo_id
121 128 repo_to_perm.user_id = self.sa.query(User)\
122 129 .filter(User.username == 'default').one().user_id
123 130
124 131 self.sa.add(repo_to_perm)
125 132 self.sa.commit()
126 133 if not just_db:
127 134 self.__create_repo(repo_name)
128 135 except:
129 136 log.error(traceback.format_exc())
130 137 self.sa.rollback()
131 138 raise
132 139
133 140 def delete(self, repo):
134 141 try:
135 142 self.sa.delete(repo)
136 143 self.sa.commit()
137 144 self.__delete_repo(repo.repo_name)
138 145 except:
139 146 log.error(traceback.format_exc())
140 147 self.sa.rollback()
141 148 raise
142 149
143 150 def delete_perm_user(self, form_data, repo_name):
144 151 try:
145 152 self.sa.query(RepoToPerm)\
146 153 .filter(RepoToPerm.repository == self.get(repo_name))\
147 154 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
148 155 self.sa.commit()
149 156 except:
150 157 log.error(traceback.format_exc())
151 158 self.sa.rollback()
152 159 raise
153 160
154 161 def __create_repo(self, repo_name):
155 162 repo_path = os.path.join(g.base_path, repo_name)
156 163 if check_repo(repo_name, g.base_path):
157 164 log.info('creating repo %s in %s', repo_name, repo_path)
158 165 from vcs.backends.hg import MercurialRepository
159 166 MercurialRepository(repo_path, create=True)
160 167
161 168 def __rename_repo(self, old, new):
162 169 log.info('renaming repo from %s to %s', old, new)
163 170
164 171 old_path = os.path.join(g.base_path, old)
165 172 new_path = os.path.join(g.base_path, new)
166 173 if os.path.isdir(new_path):
167 174 raise Exception('Was trying to rename to already existing dir %s',
168 175 new_path)
169 176 shutil.move(old_path, new_path)
170 177
171 178 def __delete_repo(self, name):
172 179 rm_path = os.path.join(g.base_path, name)
173 180 log.info("Removing %s", rm_path)
174 181 #disable hg
175 182 shutil.move(os.path.join(rm_path, '.hg'), os.path.join(rm_path, 'rm__.hg'))
176 183 #disable repo
177 184 shutil.move(rm_path, os.path.join(g.base_path, 'rm__%s__%s' \
178 185 % (datetime.today(), name)))
@@ -1,126 +1,128 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # Model for users
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 9, 2010
23 23 Model for users
24 24 @author: marcink
25 25 """
26 26
27 27 from pylons_app.model.db import User
28 28 from pylons_app.model.meta import Session
29 29 from pylons.i18n.translation import _
30 30 import logging
31 31 log = logging.getLogger(__name__)
32 32
33 33 class DefaultUserException(Exception):pass
34 34
35 35 class UserModel(object):
36 36
37 37 def __init__(self):
38 38 self.sa = Session()
39 39
40 def get_default(self):
41 return self.sa.query(User).filter(User.username == 'default').scalar()
42
40 43 def get_user(self, id):
41 44 return self.sa.query(User).get(id)
42 45
43 46 def create(self, form_data):
44 47 try:
45 48 new_user = User()
46 49 for k, v in form_data.items():
47 50 setattr(new_user, k, v)
48 51
49 52 self.sa.add(new_user)
50 53 self.sa.commit()
51 54 except Exception as e:
52 55 log.error(e)
53 56 self.sa.rollback()
54 57 raise
55 58
56 59 def create_registration(self, form_data):
57 60 try:
58 61 new_user = User()
59 62 for k, v in form_data.items():
60 if k != 'admin' or k != 'active':
63 if k != 'admin':
61 64 setattr(new_user, k, v)
62 setattr(new_user, 'active', True)
63 65
64 66 self.sa.add(new_user)
65 67 self.sa.commit()
66 68 except Exception as e:
67 69 log.error(e)
68 70 self.sa.rollback()
69 71 raise
70 72
71 73 def update(self, uid, form_data):
72 74 try:
73 75 new_user = self.sa.query(User).get(uid)
74 76 if new_user.username == 'default':
75 77 raise DefaultUserException(
76 78 _("You can't Edit this user since it's"
77 79 " crucial for entire application"))
78 80 for k, v in form_data.items():
79 81 if k == 'new_password' and v != '':
80 82 new_user.password = v
81 83 else:
82 84 setattr(new_user, k, v)
83 85
84 86 self.sa.add(new_user)
85 87 self.sa.commit()
86 88 except Exception as e:
87 89 log.error(e)
88 90 self.sa.rollback()
89 91 raise
90 92
91 93 def update_my_account(self, uid, form_data):
92 94 try:
93 95 new_user = self.sa.query(User).get(uid)
94 96 if new_user.username == 'default':
95 97 raise DefaultUserException(
96 98 _("You can't Edit this user since it's"
97 99 " crucial for entire application"))
98 100 for k, v in form_data.items():
99 101 if k == 'new_password' and v != '':
100 102 new_user.password = v
101 103 else:
102 104 if k not in ['admin', 'active']:
103 105 setattr(new_user, k, v)
104 106
105 107 self.sa.add(new_user)
106 108 self.sa.commit()
107 109 except Exception as e:
108 110 log.error(e)
109 111 self.sa.rollback()
110 112 raise
111 113
112 114 def delete(self, id):
113 115
114 116 try:
115 117
116 118 user = self.sa.query(User).get(id)
117 119 if user.username == 'default':
118 120 raise DefaultUserException(
119 121 _("You can't remove this user since it's"
120 122 " crucial for entire application"))
121 123 self.sa.delete(user)
122 124 self.sa.commit()
123 125 except Exception as e:
124 126 log.error(e)
125 127 self.sa.rollback()
126 128 raise
@@ -1,45 +1,62 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Permissions administration')}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 10 &raquo;
11 11 ${_('Permissions')}
12 12 </%def>
13 13
14 14 <%def name="page_nav()">
15 15 ${self.menu('admin')}
16 16 </%def>
17 17
18 18 <%def name="main()">
19 19 <div class="box">
20 20 <!-- box / title -->
21 21 <div class="title">
22 22 ${self.breadcrumbs()}
23 23 </div>
24 <h3>${_('Repositories permissions')}</h3>
25 ${h.form(url('permission', id='default_perm'),method='put')}
24 <h3>${_('Default permissions')}</h3>
25 ${h.form(url('permission', id='default'),method='put')}
26 26 <div class="form">
27 27 <!-- fields -->
28 28 <div class="fields">
29 29
30 30 <div class="field">
31 31 <div class="label">
32 <label for="default_perm">${_('default repository permission')}:</label>
32 <label for="default_perm">${_('Default repository permission')}:</label>
33 </div>
34 <div class="select">
35 ${h.select('default_perm','',c.perms_choices)}
36 </div>
37 </div>
38 <div class="field">
39 <div class="label">
40 <label for="default_register">${_('Registration')}:</label>
33 41 </div>
34 42 <div class="select">
35 ${h.select('default_perm','repository.read',['repository.none','repository.read','repository.write','repository.admin'])}
43 ${h.select('default_register','',c.register_choices)}
36 44 </div>
37 45 </div>
46 <div class="field">
47 <div class="label">
48 <label for="default_create">${_('Allow repository creation')}:</label>
49 </div>
50 <div class="select">
51 ${h.select('default_create','',c.create_choices)}
52 </div>
53 </div>
54
38 55 <div class="buttons">
39 56 ${h.submit('set','set',class_="ui-button ui-widget ui-state-default ui-corner-all")}
40 57 </div>
41 58 </div>
42 59 </div>
43 60 ${h.end_form()}
44 61 </div>
45 62 </%def>
@@ -1,255 +1,255 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
4 4 <head>
5 5 <title>${next.title()}</title>
6 6 <link rel="icon" href="/images/hgicon.png" type="image/png" />
7 7 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
8 8 <meta name="robots" content="index, nofollow"/>
9 9 <!-- stylesheets -->
10 10 ${self.css()}
11 11 <!-- scripts -->
12 12 ${self.js()}
13 13 </head>
14 14 <body>
15 15 <!-- header -->
16 16 <div id="header">
17 17 <!-- user -->
18 18 <ul id="logged-user">
19 19 <li class="first">
20 20 <div class="gravatar">
21 21 <img alt="gravatar" src="${h.gravatar_url(c.hg_app_user.email,24)}" />
22 22 </div>
23 23 <div class="account">
24 24 ${h.link_to('%s %s'%(c.hg_app_user.name,c.hg_app_user.lastname),h.url('admin_settings_my_account'))}<br/>
25 25 ${h.link_to(c.hg_app_user.username,h.url('admin_settings_my_account'))}
26 26 </div>
27 27 </li>
28 28 <li class="last highlight">${h.link_to(u'Logout',h.url('logout_home'))}</li>
29 29 </ul>
30 30 <!-- end user -->
31 31 <div id="header-inner">
32 32 <div id="home">
33 33 <a href="${h.url('hg_home')}"></a>
34 34 </div>
35 35 <!-- logo -->
36 36 <div id="logo">
37 37 <h1><a href="${h.url('hg_home')}">${c.hg_app_name}</a></h1>
38 38 </div>
39 39 <!-- end logo -->
40 40 <!-- quick menu -->
41 41 ${self.page_nav()}
42 42 <!-- end quick -->
43 43 <div class="corner tl"></div>
44 44 <div class="corner tr"></div>
45 45 </div>
46 46 </div>
47 47 <!-- end header -->
48 48
49 49 <!-- CONTENT -->
50 50 <div id="content">
51 51 <div class="flash_msg">
52 52 <% messages = h.flash.pop_messages() %>
53 53 % if messages:
54 54 <ul id="flash-messages">
55 55 % for message in messages:
56 56 <li class="${message.category}_msg">${message}</li>
57 57 % endfor
58 58 </ul>
59 59 % endif
60 60 </div>
61 61 <div id="main">
62 62 ${next.main()}
63 63 </div>
64 64 </div>
65 65 <!-- END CONTENT -->
66 66
67 67 <!-- footer -->
68 68 <div id="footer">
69 69 <p>Hg App ${c.hg_app_version} &copy; 2010 by Marcin Kuzminski</p>
70 70 <script type="text/javascript">${h.tooltip.activate()}</script>
71 71 </div>
72 72 <!-- end footer -->
73 73 </body>
74 74
75 75 </html>
76 76
77 77 ### MAKO DEFS ###
78 78 <%def name="page_nav()">
79 79 ${self.menu()}
80 80 </%def>
81 81
82 82 <%def name="menu(current=None)">
83 83 <%
84 84 def is_current(selected):
85 85 if selected == current:
86 86 return h.literal('class="current"')
87 87 %>
88 88 %if current not in ['home','admin']:
89 89 <script type="text/javascript">
90 90 YAHOO.util.Event.onDOMReady(function(){
91 91 YAHOO.util.Event.addListener('repo_switcher','click',function(){
92 92 if(YAHOO.util.Dom.hasClass('repo_switcher','selected')){
93 93 YAHOO.util.Dom.setStyle('switch_repos','display','none');
94 94 YAHOO.util.Dom.setStyle('repo_switcher','background','');
95 95 YAHOO.util.Dom.removeClass('repo_switcher','selected');
96 96 YAHOO.util.Dom.get('repo_switcher').removeAttribute('style');
97 97 }
98 98 else{
99 99 YAHOO.util.Dom.setStyle('switch_repos','display','');
100 100 YAHOO.util.Dom.addClass('repo_switcher','selected');
101 101 }
102 102 });
103 103 YAHOO.util.Event.addListener('repos_list','change',function(e){
104 104 var wa = YAHOO.util.Dom.get('repos_list').value;
105 105
106 106 var url = "${h.url('summary_home',repo_name='__REPO__')}".replace('__REPO__',wa);
107 107 window.location = url;
108 108 })
109 109 });
110 110 </script>
111 111
112 112 ##REGULAR MENU
113 113 <ul id="quick">
114 114 <!-- repo switcher -->
115 115 <li>
116 116 <a id="repo_switcher" title="${_('Switch repository')}" href="#">
117 117 <span class="icon">
118 118 <img src="/images/icons/database.png" alt="${_('Products')}" />
119 119 </span>
120 120 <span>&darr;</span>
121 121 </a>
122 122 <div id="switch_repos" style="display:none;">
123 123 <select id="repos_list" size="10">
124 124 %for repo in c.repo_switcher_list:
125 125 <option value="${repo}">${repo}</option>
126 126 %endfor
127 127 </select>
128 128 </div>
129 129 </li>
130 130
131 131 <li ${is_current('summary')}>
132 132 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=c.repo_name)}">
133 133 <span class="icon">
134 134 <img src="/images/icons/clipboard_16.png" alt="${_('Summary')}" />
135 135 </span>
136 136 <span>${_('Summary')}</span>
137 137 </a>
138 138 </li>
139 139 <li ${is_current('shortlog')}>
140 140 <a title="${_('Shortlog')}" href="${h.url('shortlog_home',repo_name=c.repo_name)}">
141 141 <span class="icon">
142 142 <img src="/images/icons/application_double.png" alt="${_('Shortlog')}" />
143 143 </span>
144 144 <span>${_('Shortlog')}</span>
145 145 </a>
146 146 </li>
147 147 <li ${is_current('changelog')}>
148 148 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=c.repo_name)}">
149 149 <span class="icon">
150 150 <img src="/images/icons/time.png" alt="${_('Changelog')}" />
151 151 </span>
152 152 <span>${_('Changelog')}</span>
153 153 </a>
154 154 </li>
155 155 <li ${is_current('branches')}>
156 156 <a title="${_('Branches')}" href="${h.url('branches_home',repo_name=c.repo_name)}">
157 157 <span class="icon">
158 158 <img src="/images/icons/arrow_branch.png" alt="${_('Branches')}" />
159 159 </span>
160 160 <span>${_('Branches')}</span>
161 161 </a>
162 162 </li>
163 163 <li ${is_current('tags')}>
164 164 <a title="${_('Tags')}" href="${h.url('tags_home',repo_name=c.repo_name)}">
165 165 <span class="icon">
166 166 <img src="/images/icons/tag_blue.png" alt="${_('Tags')}" />
167 167 </span>
168 168 <span>${_('Tags')}</span>
169 169 </a>
170 170 </li>
171 171 <li ${is_current('files')}>
172 172 <a title="${_('Files')}" href="${h.url('files_home',repo_name=c.repo_name)}">
173 173 <span class="icon">
174 174 <img src="/images/icons/file.png" alt="${_('Files')}" />
175 175 </span>
176 176 <span>${_('Files')}</span>
177 177 </a>
178 178 </li>
179 179 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
180 180 <li ${is_current('settings')}>
181 181 <a title="${_('Settings')}" href="${h.url('repo_settings_home',repo_name=c.repo_name)}">
182 182 <span class="icon">
183 183 <img src="/images/icons/cog_edit.png" alt="${_('Settings')}" />
184 184 </span>
185 185 <span>${_('Settings')}</span>
186 186 </a>
187 187 </li>
188 188 %endif
189 189 </ul>
190 190 %else:
191 191 ##ROOT MENU
192 192 <ul id="quick">
193 193 <li>
194 194 <a title="${_('Home')}" href="${h.url('hg_home')}">
195 195 <span class="icon">
196 196 <img src="/images/icons/home_16.png" alt="${_('Home')}" />
197 197 </span>
198 198 <span>${_('Home')}</span>
199 199 </a>
200 200 </li>
201 201
202 202 <li>
203 203 <a title="${_('Search')}" href="${h.url('search')}">
204 204 <span class="icon">
205 205 <img src="/images/icons/search_16.png" alt="${_('Search')}" />
206 206 </span>
207 207 <span>${_('Search')}</span>
208 208 </a>
209 209 </li>
210 210
211 211 %if h.HasPermissionAll('hg.admin')('access admin main page'):
212 212 <li ${is_current('admin')}>
213 213 <a title="${_('Admin')}" href="${h.url('admin_home')}">
214 214 <span class="icon">
215 215 <img src="/images/icons/cog_edit.png" alt="${_('Admin')}" />
216 216 </span>
217 217 <span>${_('Admin')}</span>
218 218 </a>
219 219 <ul>
220 220 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
221 221 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
222 <li>${h.link_to(_('permissions'),h.url('permissions'),class_='permissions')}</li>
222 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
223 223 <li>${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
224 224 </ul>
225 225 </li>
226 226 %endif
227 227
228 228 </ul>
229 229 %endif
230 230 </%def>
231 231
232 232
233 233 <%def name="css()">
234 234 <link rel="stylesheet" type="text/css" href="/css/reset.css" />
235 235 <link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
236 236 <link rel="stylesheet" type="text/css" href="/css/style_full.css" />
237 237 <link id="color" rel="stylesheet" type="text/css" href="/css/colors/blue.css" />
238 238 <link rel="stylesheet" type="text/css" href="/css/pygments.css" />
239 239 <link rel="stylesheet" type="text/css" href="/css/diff.css" />
240 240 </%def>
241 241
242 242 <%def name="js()">
243 243 <script type="text/javascript" src="/js/yui/utilities/utilities.js"></script>
244 244 <!--[if IE]><script language="javascript" type="text/javascript" src="/js/excanvas.min.js"></script><![endif]-->
245 245 <script type="text/javascript" src="/js/yui/container/container-min.js"></script>
246 246 <script type="text/javascript" src="/js/yui/datasource/datasource-min.js"></script>
247 247 <script type="text/javascript" src="/js/yui/autocomplete/autocomplete-min.js"></script>
248 248 <script type="text/javascript" src="/js/yui.flot.js"></script>
249 249 </%def>
250 250
251 251 <%def name="breadcrumbs()">
252 252 <div class="breadcrumbs">
253 253 ${self.breadcrumbs_links()}
254 254 </div>
255 255 </%def> No newline at end of file
@@ -1,84 +1,84 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="base/base.html"/>
3 3 <%def name="title()">
4 4 ${c.hg_app_name}
5 5 </%def>
6 6 <%def name="breadcrumbs()">
7 7 ${c.hg_app_name}
8 8 </%def>
9 9 <%def name="page_nav()">
10 10 ${self.menu('home')}
11 11 </%def>
12 12 <%def name="main()">
13 13 <%def name="get_sort(name)">
14 14 <%name_slug = name.lower().replace(' ','_') %>
15 15 %if name_slug == c.cs_slug:
16 16 <span style="font-weight: bold;text-decoration: underline;">${name}</span>
17 17 %else:
18 18 <span style="font-weight: bold">${name}</span>
19 19 %endif
20 20 <a href="?sort=${name_slug}">&darr;</a>
21 21 <a href="?sort=-${name_slug}">&uarr;</a>
22 22 </%def>
23 23
24 24
25 25
26 26 <div class="box">
27 27 <!-- box / title -->
28 28 <div class="title">
29 29 <h5>${_('Dashboard')}</h5>
30 %if h.HasPermissionAny('repository.create','hg.admin')():
30 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
31 31 <ul class="links">
32 32 <li>
33 33 <span>${h.link_to(u'ADD NEW REPOSITORY',h.url('admin_settings_create_repository'),class_="add_icon")}</span>
34 34 </li>
35 35 </ul>
36 36 %endif
37 37 </div>
38 38 <!-- end box / title -->
39 39 <div class="table">
40 40 <table>
41 41 <thead>
42 42 <tr>
43 43 <th class="left">${get_sort(_('Name'))}</th>
44 44 <th class="left">${get_sort(_('Description'))}</th>
45 45 <th class="left">${get_sort(_('Last change'))}</th>
46 46 <th class="left">${get_sort(_('Tip'))}</th>
47 47 <th class="left">${get_sort(_('Contact'))}</th>
48 48 <th class="left">${_('RSS')}</th>
49 49 <th class="left">${_('Atom')}</th>
50 50 </tr>
51 51 </thead>
52 52 <tbody>
53 53 %for cnt,repo in enumerate(c.repos_list):
54 54 %if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(repo['name'],'main page check'):
55 55 <tr class="parity${cnt%2}">
56 56 <td>
57 57 %if repo['repo'].dbrepo.private:
58 58 <img alt="${_('private')}" src="/images/icons/lock.png"/>
59 59 %else:
60 60 <img alt="${_('public')}" src="/images/icons/lock_open.png"/>
61 61 %endif
62 62 ${h.link_to(repo['name'],
63 63 h.url('summary_home',repo_name=repo['name']))}</td>
64 64 <td title="${repo['description']}">${h.truncate(repo['description'],60)}</td>
65 65 <td>${h.age(repo['last_change'])}</td>
66 66 <td>${h.link_to_if(repo['rev']>=0,'r%s:%s' % (repo['rev'],repo['tip']),
67 67 h.url('changeset_home',repo_name=repo['name'],revision=repo['tip']),
68 68 class_="tooltip",
69 69 tooltip_title=h.tooltip(repo['last_msg']))}</td>
70 70 <td title="${repo['contact']}">${h.person(repo['contact'])}</td>
71 71 <td>
72 72 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon" href="${h.url('rss_feed_home',repo_name=repo['name'])}"></a>
73 73 </td>
74 74 <td>
75 75 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'])}"></a>
76 76 </td>
77 77 </tr>
78 78 %endif
79 79 %endfor
80 80 </tbody>
81 81 </table>
82 82 </div>
83 83 </div>
84 84 </%def>
@@ -1,76 +1,78 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
4 4 <head>
5 5 <title>${_('Sign In to hg-app')}</title>
6 6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 7 <link rel="icon" href="/images/hgicon.png" type="image/png" />
8 8 <meta name="robots" content="index, nofollow"/>
9 9
10 10 <!-- stylesheets -->
11 11 <link rel="stylesheet" type="text/css" href="/css/reset.css" />
12 12 <link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
13 13 <link id="color" rel="stylesheet" type="text/css" href="/css/colors/blue.css" />
14 14
15 15 <!-- scripts -->
16 16
17 17 </head>
18 18 <body>
19 19 <div id="login">
20 20 <!-- login -->
21 21 <div class="title">
22 22 <h5>${_('Sign In to hg-app')}</h5>
23 23 <div class="corner tl"></div>
24 24 <div class="corner tr"></div>
25 25 </div>
26 26 <div class="inner">
27 27 ${h.form(h.url.current())}
28 28 <div class="form">
29 29 <!-- fields -->
30 30
31 31 <div class="fields">
32 32 <div class="field">
33 33 <div class="label">
34 34 <label for="username">${_('Username')}:</label>
35 35 </div>
36 36 <div class="input">
37 37 ${h.text('username',class_='focus',size=40)}
38 38 </div>
39 39
40 40 </div>
41 41 <div class="field">
42 42 <div class="label">
43 43 <label for="password">${_('Password')}:</label>
44 44 </div>
45 45 <div class="input">
46 46 ${h.password('password',class_='focus',size=40)}
47 47 </div>
48 48
49 49 </div>
50 50 ##<div class="field">
51 51 ## <div class="checkbox">
52 52 ## <input type="checkbox" id="remember" name="remember" />
53 53 ## <label for="remember">Remember me</label>
54 54 ## </div>
55 55 ##</div>
56 56 <div class="buttons">
57 57 ${h.submit('sign_in','Sign In',class_="ui-button ui-widget ui-state-default ui-corner-all")}
58 58 </div>
59 59 </div>
60 60 <!-- end fields -->
61 61 <!-- links -->
62 62 <div class="links">
63 63 ${h.link_to(_('Forgot your password ?'),h.url('#'))}
64 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
64 65 /
65 66 ${h.link_to(_("Don't have an account ?"),h.url('register'))}
67 %endif
66 68 </div>
67 69
68 70 <!-- end links -->
69 71 </div>
70 72 ${h.end_form()}
71 73 </div>
72 74 <!-- end login -->
73 75 </div>
74 76 </body>
75 77 </html>
76 78
@@ -1,88 +1,93 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
4 4 <head>
5 5 <title>${_('Sign Up to hg-app')}</title>
6 6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 7 <link rel="icon" href="/images/hgicon.png" type="image/png" />
8 8 <meta name="robots" content="index, nofollow"/>
9 9
10 10 <!-- stylesheets -->
11 11 <link rel="stylesheet" type="text/css" href="/css/reset.css" />
12 12 <link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
13 13 <link id="color" rel="stylesheet" type="text/css" href="/css/colors/blue.css" />
14 14
15 15 <!-- scripts -->
16 16
17 17 </head>
18 18 <body>
19 19 <div id="register">
20 20
21 21 <div class="title">
22 22 <h5>${_('Sign Up to hg-app')}</h5>
23 23 <div class="corner tl"></div>
24 24 <div class="corner tr"></div>
25 25 </div>
26 26 <div class="inner">
27 27 ${h.form(url('register'))}
28 28 <div class="form">
29 29 <!-- fields -->
30 30 <div class="fields">
31 31 <div class="field">
32 32 <div class="label">
33 33 <label for="username">${_('Username')}:</label>
34 34 </div>
35 35 <div class="input">
36 36 ${h.text('username')}
37 37 </div>
38 38 </div>
39 39
40 40 <div class="field">
41 41 <div class="label">
42 42 <label for="password">${_('New Password')}:</label>
43 43 </div>
44 44 <div class="input">
45 45 ${h.password('password')}
46 46 </div>
47 47 </div>
48 48
49 49 <div class="field">
50 50 <div class="label">
51 51 <label for="name">${_('First Name')}:</label>
52 52 </div>
53 53 <div class="input">
54 54 ${h.text('name')}
55 55 </div>
56 56 </div>
57 57
58 58 <div class="field">
59 59 <div class="label">
60 60 <label for="lastname">${_('Last Name')}:</label>
61 61 </div>
62 62 <div class="input">
63 63 ${h.text('lastname')}
64 64 </div>
65 65 </div>
66 66
67 67 <div class="field">
68 68 <div class="label">
69 69 <label for="email">${_('Email')}:</label>
70 70 </div>
71 71 <div class="input">
72 72 ${h.text('email')}
73 73 </div>
74 74 </div>
75 75
76 76 <div class="buttons">
77 77 <div class="nohighlight">
78 78 ${h.submit('sign_up','Sign Up',class_="ui-button ui-widget ui-state-default ui-corner-all")}
79 %if c.auto_active:
80 <div class="activation_msg">${_('Your account will be activated right after registration')}</div>
81 %else:
82 <div class="activation_msg">${_('Your account must wait for activation by administrator')}</div>
83 %endif
79 84 </div>
80 85 </div>
81 86 </div>
82 87 </div>
83 88 ${h.end_form()}
84 89 </div>
85 90 </div>
86 91 </body>
87 92 </html>
88 93
@@ -1,23 +1,24 b''
1 1 """Setup the pylons_app application"""
2 2
3 3 from os.path import dirname as dn, join as jn
4 4 from pylons_app.config.environment import load_environment
5 5 from pylons_app.lib.db_manage import DbManage
6 6 import logging
7 7 import os
8 8 import sys
9 9
10 10 log = logging.getLogger(__name__)
11 11
12 12 ROOT = dn(dn(os.path.realpath(__file__)))
13 13 sys.path.append(ROOT)
14 14
15 15 def setup_app(command, conf, vars):
16 16 """Place any commands to setup pylons_app here"""
17 17 dbmanage = DbManage(log_sql=True)
18 18 dbmanage.create_tables(override=True)
19 19 dbmanage.config_prompt()
20 20 dbmanage.admin_prompt()
21 21 dbmanage.create_permissions()
22 dbmanage.populate_default_permissions()
22 23 load_environment(conf.global_conf, conf.local_conf, initial=True)
23 24
General Comments 0
You need to be logged in to leave comments. Login now