##// END OF EJS Templates
implements #60, ldap configuration and authentication....
marcink -
r705:9e9f1b91 beta
parent child Browse files
Show More
@@ -1,190 +1,192 b''
1 """
1 """
2 Routes configuration
2 Routes configuration
3
3
4 The more specific and detailed routes should be defined first so they
4 The more specific and detailed routes should be defined first so they
5 may take precedent over the more generic routes. For more information
5 may take precedent over the more generic routes. For more information
6 refer to the routes manual at http://routes.groovie.org/docs/
6 refer to the routes manual at http://routes.groovie.org/docs/
7 """
7 """
8 from __future__ import with_statement
8 from __future__ import with_statement
9 from routes import Mapper
9 from routes import Mapper
10 from rhodecode.lib.utils import check_repo_fast as cr
10 from rhodecode.lib.utils import check_repo_fast as cr
11
11
12 def make_map(config):
12 def make_map(config):
13 """Create, configure and return the routes Mapper"""
13 """Create, configure and return the routes Mapper"""
14 map = Mapper(directory=config['pylons.paths']['controllers'],
14 map = Mapper(directory=config['pylons.paths']['controllers'],
15 always_scan=config['debug'])
15 always_scan=config['debug'])
16 map.minimization = False
16 map.minimization = False
17 map.explicit = False
17 map.explicit = False
18
18
19 def check_repo(environ, match_dict):
19 def check_repo(environ, match_dict):
20 """
20 """
21 check for valid repository for proper 404 handling
21 check for valid repository for proper 404 handling
22 :param environ:
22 :param environ:
23 :param match_dict:
23 :param match_dict:
24 """
24 """
25 repo_name = match_dict.get('repo_name')
25 repo_name = match_dict.get('repo_name')
26 return not cr(repo_name, config['base_path'])
26 return not cr(repo_name, config['base_path'])
27
27
28 # The ErrorController route (handles 404/500 error pages); it should
28 # The ErrorController route (handles 404/500 error pages); it should
29 # likely stay at the top, ensuring it can always be resolved
29 # likely stay at the top, ensuring it can always be resolved
30 map.connect('/error/{action}', controller='error')
30 map.connect('/error/{action}', controller='error')
31 map.connect('/error/{action}/{id}', controller='error')
31 map.connect('/error/{action}/{id}', controller='error')
32
32
33 #==========================================================================
33 #==========================================================================
34 # CUSTOM ROUTES HERE
34 # CUSTOM ROUTES HERE
35 #==========================================================================
35 #==========================================================================
36
36
37 #MAIN PAGE
37 #MAIN PAGE
38 map.connect('home', '/', controller='home', action='index')
38 map.connect('home', '/', controller='home', action='index')
39 map.connect('bugtracker', "http://bitbucket.org/marcinkuzminski/rhodecode/issues", _static=True)
39 map.connect('bugtracker', "http://bitbucket.org/marcinkuzminski/rhodecode/issues", _static=True)
40 map.connect('gpl_license', "http://www.gnu.org/licenses/gpl.html", _static=True)
40 map.connect('gpl_license', "http://www.gnu.org/licenses/gpl.html", _static=True)
41 #ADMIN REPOSITORY REST ROUTES
41 #ADMIN REPOSITORY REST ROUTES
42 with map.submapper(path_prefix='/_admin', controller='admin/repos') as m:
42 with map.submapper(path_prefix='/_admin', controller='admin/repos') as m:
43 m.connect("repos", "/repos",
43 m.connect("repos", "/repos",
44 action="create", conditions=dict(method=["POST"]))
44 action="create", conditions=dict(method=["POST"]))
45 m.connect("repos", "/repos",
45 m.connect("repos", "/repos",
46 action="index", conditions=dict(method=["GET"]))
46 action="index", conditions=dict(method=["GET"]))
47 m.connect("formatted_repos", "/repos.{format}",
47 m.connect("formatted_repos", "/repos.{format}",
48 action="index",
48 action="index",
49 conditions=dict(method=["GET"]))
49 conditions=dict(method=["GET"]))
50 m.connect("new_repo", "/repos/new",
50 m.connect("new_repo", "/repos/new",
51 action="new", conditions=dict(method=["GET"]))
51 action="new", conditions=dict(method=["GET"]))
52 m.connect("formatted_new_repo", "/repos/new.{format}",
52 m.connect("formatted_new_repo", "/repos/new.{format}",
53 action="new", conditions=dict(method=["GET"]))
53 action="new", conditions=dict(method=["GET"]))
54 m.connect("/repos/{repo_name:.*}",
54 m.connect("/repos/{repo_name:.*}",
55 action="update", conditions=dict(method=["PUT"],
55 action="update", conditions=dict(method=["PUT"],
56 function=check_repo))
56 function=check_repo))
57 m.connect("/repos/{repo_name:.*}",
57 m.connect("/repos/{repo_name:.*}",
58 action="delete", conditions=dict(method=["DELETE"],
58 action="delete", conditions=dict(method=["DELETE"],
59 function=check_repo))
59 function=check_repo))
60 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
60 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
61 action="edit", conditions=dict(method=["GET"],
61 action="edit", conditions=dict(method=["GET"],
62 function=check_repo))
62 function=check_repo))
63 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
63 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
64 action="edit", conditions=dict(method=["GET"],
64 action="edit", conditions=dict(method=["GET"],
65 function=check_repo))
65 function=check_repo))
66 m.connect("repo", "/repos/{repo_name:.*}",
66 m.connect("repo", "/repos/{repo_name:.*}",
67 action="show", conditions=dict(method=["GET"],
67 action="show", conditions=dict(method=["GET"],
68 function=check_repo))
68 function=check_repo))
69 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
69 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
70 action="show", conditions=dict(method=["GET"],
70 action="show", conditions=dict(method=["GET"],
71 function=check_repo))
71 function=check_repo))
72 #ajax delete repo perm user
72 #ajax delete repo perm user
73 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
73 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
74 action="delete_perm_user", conditions=dict(method=["DELETE"],
74 action="delete_perm_user", conditions=dict(method=["DELETE"],
75 function=check_repo))
75 function=check_repo))
76
76
77 #ADMIN USER REST ROUTES
77 #ADMIN USER REST ROUTES
78 map.resource('user', 'users', controller='admin/users', path_prefix='/_admin')
78 map.resource('user', 'users', controller='admin/users', path_prefix='/_admin')
79
79
80 #ADMIN PERMISSIONS REST ROUTES
80 #ADMIN PERMISSIONS REST ROUTES
81 map.resource('permission', 'permissions', controller='admin/permissions', path_prefix='/_admin')
81 map.resource('permission', 'permissions', controller='admin/permissions', path_prefix='/_admin')
82 map.connect('permissions_ldap', '/_admin/permissions_ldap', controller='admin/permissions', action='ldap')
83
82
84
83 #ADMIN SETTINGS REST ROUTES
85 #ADMIN SETTINGS REST ROUTES
84 with map.submapper(path_prefix='/_admin', controller='admin/settings') as m:
86 with map.submapper(path_prefix='/_admin', controller='admin/settings') as m:
85 m.connect("admin_settings", "/settings",
87 m.connect("admin_settings", "/settings",
86 action="create", conditions=dict(method=["POST"]))
88 action="create", conditions=dict(method=["POST"]))
87 m.connect("admin_settings", "/settings",
89 m.connect("admin_settings", "/settings",
88 action="index", conditions=dict(method=["GET"]))
90 action="index", conditions=dict(method=["GET"]))
89 m.connect("formatted_admin_settings", "/settings.{format}",
91 m.connect("formatted_admin_settings", "/settings.{format}",
90 action="index", conditions=dict(method=["GET"]))
92 action="index", conditions=dict(method=["GET"]))
91 m.connect("admin_new_setting", "/settings/new",
93 m.connect("admin_new_setting", "/settings/new",
92 action="new", conditions=dict(method=["GET"]))
94 action="new", conditions=dict(method=["GET"]))
93 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
95 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
94 action="new", conditions=dict(method=["GET"]))
96 action="new", conditions=dict(method=["GET"]))
95 m.connect("/settings/{setting_id}",
97 m.connect("/settings/{setting_id}",
96 action="update", conditions=dict(method=["PUT"]))
98 action="update", conditions=dict(method=["PUT"]))
97 m.connect("/settings/{setting_id}",
99 m.connect("/settings/{setting_id}",
98 action="delete", conditions=dict(method=["DELETE"]))
100 action="delete", conditions=dict(method=["DELETE"]))
99 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
101 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
100 action="edit", conditions=dict(method=["GET"]))
102 action="edit", conditions=dict(method=["GET"]))
101 m.connect("formatted_admin_edit_setting", "/settings/{setting_id}.{format}/edit",
103 m.connect("formatted_admin_edit_setting", "/settings/{setting_id}.{format}/edit",
102 action="edit", conditions=dict(method=["GET"]))
104 action="edit", conditions=dict(method=["GET"]))
103 m.connect("admin_setting", "/settings/{setting_id}",
105 m.connect("admin_setting", "/settings/{setting_id}",
104 action="show", conditions=dict(method=["GET"]))
106 action="show", conditions=dict(method=["GET"]))
105 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
107 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
106 action="show", conditions=dict(method=["GET"]))
108 action="show", conditions=dict(method=["GET"]))
107 m.connect("admin_settings_my_account", "/my_account",
109 m.connect("admin_settings_my_account", "/my_account",
108 action="my_account", conditions=dict(method=["GET"]))
110 action="my_account", conditions=dict(method=["GET"]))
109 m.connect("admin_settings_my_account_update", "/my_account_update",
111 m.connect("admin_settings_my_account_update", "/my_account_update",
110 action="my_account_update", conditions=dict(method=["PUT"]))
112 action="my_account_update", conditions=dict(method=["PUT"]))
111 m.connect("admin_settings_create_repository", "/create_repository",
113 m.connect("admin_settings_create_repository", "/create_repository",
112 action="create_repository", conditions=dict(method=["GET"]))
114 action="create_repository", conditions=dict(method=["GET"]))
113
115
114 #ADMIN MAIN PAGES
116 #ADMIN MAIN PAGES
115 with map.submapper(path_prefix='/_admin', controller='admin/admin') as m:
117 with map.submapper(path_prefix='/_admin', controller='admin/admin') as m:
116 m.connect('admin_home', '', action='index')#main page
118 m.connect('admin_home', '', action='index')#main page
117 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
119 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
118 action='add_repo')
120 action='add_repo')
119 #SEARCH
121 #SEARCH
120 map.connect('search', '/_admin/search', controller='search',)
122 map.connect('search', '/_admin/search', controller='search',)
121 map.connect('search_repo', '/_admin/search/{search_repo:.*}', controller='search')
123 map.connect('search_repo', '/_admin/search/{search_repo:.*}', controller='search')
122
124
123 #LOGIN/LOGOUT/REGISTER/SIGN IN
125 #LOGIN/LOGOUT/REGISTER/SIGN IN
124 map.connect('login_home', '/_admin/login', controller='login')
126 map.connect('login_home', '/_admin/login', controller='login')
125 map.connect('logout_home', '/_admin/logout', controller='login', action='logout')
127 map.connect('logout_home', '/_admin/logout', controller='login', action='logout')
126 map.connect('register', '/_admin/register', controller='login', action='register')
128 map.connect('register', '/_admin/register', controller='login', action='register')
127 map.connect('reset_password', '/_admin/password_reset', controller='login', action='password_reset')
129 map.connect('reset_password', '/_admin/password_reset', controller='login', action='password_reset')
128
130
129 #FEEDS
131 #FEEDS
130 map.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
132 map.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
131 controller='feed', action='rss',
133 controller='feed', action='rss',
132 conditions=dict(function=check_repo))
134 conditions=dict(function=check_repo))
133 map.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
135 map.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
134 controller='feed', action='atom',
136 controller='feed', action='atom',
135 conditions=dict(function=check_repo))
137 conditions=dict(function=check_repo))
136
138
137
139
138 #REPOSITORY ROUTES
140 #REPOSITORY ROUTES
139 map.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
141 map.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
140 controller='changeset', revision='tip',
142 controller='changeset', revision='tip',
141 conditions=dict(function=check_repo))
143 conditions=dict(function=check_repo))
142 map.connect('raw_changeset_home', '/{repo_name:.*}/raw-changeset/{revision}',
144 map.connect('raw_changeset_home', '/{repo_name:.*}/raw-changeset/{revision}',
143 controller='changeset', action='raw_changeset', revision='tip',
145 controller='changeset', action='raw_changeset', revision='tip',
144 conditions=dict(function=check_repo))
146 conditions=dict(function=check_repo))
145 map.connect('summary_home', '/{repo_name:.*}/summary',
147 map.connect('summary_home', '/{repo_name:.*}/summary',
146 controller='summary', conditions=dict(function=check_repo))
148 controller='summary', conditions=dict(function=check_repo))
147 map.connect('shortlog_home', '/{repo_name:.*}/shortlog',
149 map.connect('shortlog_home', '/{repo_name:.*}/shortlog',
148 controller='shortlog', conditions=dict(function=check_repo))
150 controller='shortlog', conditions=dict(function=check_repo))
149 map.connect('branches_home', '/{repo_name:.*}/branches',
151 map.connect('branches_home', '/{repo_name:.*}/branches',
150 controller='branches', conditions=dict(function=check_repo))
152 controller='branches', conditions=dict(function=check_repo))
151 map.connect('tags_home', '/{repo_name:.*}/tags',
153 map.connect('tags_home', '/{repo_name:.*}/tags',
152 controller='tags', conditions=dict(function=check_repo))
154 controller='tags', conditions=dict(function=check_repo))
153 map.connect('changelog_home', '/{repo_name:.*}/changelog',
155 map.connect('changelog_home', '/{repo_name:.*}/changelog',
154 controller='changelog', conditions=dict(function=check_repo))
156 controller='changelog', conditions=dict(function=check_repo))
155 map.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
157 map.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
156 controller='files', revision='tip', f_path='',
158 controller='files', revision='tip', f_path='',
157 conditions=dict(function=check_repo))
159 conditions=dict(function=check_repo))
158 map.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
160 map.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
159 controller='files', action='diff', revision='tip', f_path='',
161 controller='files', action='diff', revision='tip', f_path='',
160 conditions=dict(function=check_repo))
162 conditions=dict(function=check_repo))
161 map.connect('files_rawfile_home', '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
163 map.connect('files_rawfile_home', '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
162 controller='files', action='rawfile', revision='tip', f_path='',
164 controller='files', action='rawfile', revision='tip', f_path='',
163 conditions=dict(function=check_repo))
165 conditions=dict(function=check_repo))
164 map.connect('files_raw_home', '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
166 map.connect('files_raw_home', '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
165 controller='files', action='raw', revision='tip', f_path='',
167 controller='files', action='raw', revision='tip', f_path='',
166 conditions=dict(function=check_repo))
168 conditions=dict(function=check_repo))
167 map.connect('files_annotate_home', '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
169 map.connect('files_annotate_home', '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
168 controller='files', action='annotate', revision='tip', f_path='',
170 controller='files', action='annotate', revision='tip', f_path='',
169 conditions=dict(function=check_repo))
171 conditions=dict(function=check_repo))
170 map.connect('files_archive_home', '/{repo_name:.*}/archive/{revision}/{fileformat}',
172 map.connect('files_archive_home', '/{repo_name:.*}/archive/{revision}/{fileformat}',
171 controller='files', action='archivefile', revision='tip',
173 controller='files', action='archivefile', revision='tip',
172 conditions=dict(function=check_repo))
174 conditions=dict(function=check_repo))
173 map.connect('repo_settings_delete', '/{repo_name:.*}/settings',
175 map.connect('repo_settings_delete', '/{repo_name:.*}/settings',
174 controller='settings', action="delete",
176 controller='settings', action="delete",
175 conditions=dict(method=["DELETE"], function=check_repo))
177 conditions=dict(method=["DELETE"], function=check_repo))
176 map.connect('repo_settings_update', '/{repo_name:.*}/settings',
178 map.connect('repo_settings_update', '/{repo_name:.*}/settings',
177 controller='settings', action="update",
179 controller='settings', action="update",
178 conditions=dict(method=["PUT"], function=check_repo))
180 conditions=dict(method=["PUT"], function=check_repo))
179 map.connect('repo_settings_home', '/{repo_name:.*}/settings',
181 map.connect('repo_settings_home', '/{repo_name:.*}/settings',
180 controller='settings', action='index',
182 controller='settings', action='index',
181 conditions=dict(function=check_repo))
183 conditions=dict(function=check_repo))
182
184
183 map.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
185 map.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
184 controller='settings', action='fork_create',
186 controller='settings', action='fork_create',
185 conditions=dict(function=check_repo, method=["POST"]))
187 conditions=dict(function=check_repo, method=["POST"]))
186 map.connect('repo_fork_home', '/{repo_name:.*}/fork',
188 map.connect('repo_fork_home', '/{repo_name:.*}/fork',
187 controller='settings', action='fork',
189 controller='settings', action='fork',
188 conditions=dict(function=check_repo))
190 conditions=dict(function=check_repo))
189
191
190 return map
192 return map
@@ -1,165 +1,217 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # permissions controller for pylons
3 # permissions controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
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
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 27, 2010
21 Created on April 27, 2010
22 permissions controller for pylons
22 permissions controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25
25
26 from formencode import htmlfill
26 from formencode import htmlfill
27 from pylons import request, session, tmpl_context as c, url
27 from pylons import request, session, tmpl_context as c, url
28 from pylons.controllers.util import abort, redirect
28 from pylons.controllers.util import abort, redirect
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30 from rhodecode.lib import helpers as h
30 from rhodecode.lib import helpers as h
31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
32 from rhodecode.lib.auth_ldap import LdapImportError
32 from rhodecode.lib.base import BaseController, render
33 from rhodecode.lib.base import BaseController, render
33 from rhodecode.model.forms import UserForm, DefaultPermissionsForm
34 from rhodecode.model.forms import LdapSettingsForm, DefaultPermissionsForm
34 from rhodecode.model.permission import PermissionModel
35 from rhodecode.model.permission import PermissionModel
36 from rhodecode.model.settings import SettingsModel
35 from rhodecode.model.user import UserModel
37 from rhodecode.model.user import UserModel
36 import formencode
38 import formencode
37 import logging
39 import logging
38 import traceback
40 import traceback
39
41
40 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
41
43
42 class PermissionsController(BaseController):
44 class PermissionsController(BaseController):
43 """REST Controller styled on the Atom Publishing Protocol"""
45 """REST Controller styled on the Atom Publishing Protocol"""
44 # To properly map this controller, ensure your config/routing.py
46 # To properly map this controller, ensure your config/routing.py
45 # file has a resource setup:
47 # file has a resource setup:
46 # map.resource('permission', 'permissions')
48 # map.resource('permission', 'permissions')
47
49
48 @LoginRequired()
50 @LoginRequired()
49 @HasPermissionAllDecorator('hg.admin')
51 @HasPermissionAllDecorator('hg.admin')
50 def __before__(self):
52 def __before__(self):
51 c.admin_user = session.get('admin_user')
53 c.admin_user = session.get('admin_user')
52 c.admin_username = session.get('admin_username')
54 c.admin_username = session.get('admin_username')
53 super(PermissionsController, self).__before__()
55 super(PermissionsController, self).__before__()
54
56
55 self.perms_choices = [('repository.none', _('None'),),
57 self.perms_choices = [('repository.none', _('None'),),
56 ('repository.read', _('Read'),),
58 ('repository.read', _('Read'),),
57 ('repository.write', _('Write'),),
59 ('repository.write', _('Write'),),
58 ('repository.admin', _('Admin'),)]
60 ('repository.admin', _('Admin'),)]
59 self.register_choices = [
61 self.register_choices = [
60 ('hg.register.none',
62 ('hg.register.none',
61 _('disabled')),
63 _('disabled')),
62 ('hg.register.manual_activate',
64 ('hg.register.manual_activate',
63 _('allowed with manual account activation')),
65 _('allowed with manual account activation')),
64 ('hg.register.auto_activate',
66 ('hg.register.auto_activate',
65 _('allowed with automatic account activation')), ]
67 _('allowed with automatic account activation')), ]
66
68
67 self.create_choices = [('hg.create.none', _('Disabled')),
69 self.create_choices = [('hg.create.none', _('Disabled')),
68 ('hg.create.repository', _('Enabled'))]
70 ('hg.create.repository', _('Enabled'))]
69
71
70
72
71 def index(self, format='html'):
73 def index(self, format='html'):
72 """GET /permissions: All items in the collection"""
74 """GET /permissions: All items in the collection"""
73 # url('permissions')
75 # url('permissions')
74
76
75 def create(self):
77 def create(self):
76 """POST /permissions: Create a new item"""
78 """POST /permissions: Create a new item"""
77 # url('permissions')
79 # url('permissions')
78
80
79 def new(self, format='html'):
81 def new(self, format='html'):
80 """GET /permissions/new: Form to create a new item"""
82 """GET /permissions/new: Form to create a new item"""
81 # url('new_permission')
83 # url('new_permission')
82
84
83 def update(self, id):
85 def update(self, id):
84 """PUT /permissions/id: Update an existing item"""
86 """PUT /permissions/id: Update an existing item"""
85 # Forms posted to this method should contain a hidden field:
87 # Forms posted to this method should contain a hidden field:
86 # <input type="hidden" name="_method" value="PUT" />
88 # <input type="hidden" name="_method" value="PUT" />
87 # Or using helpers:
89 # Or using helpers:
88 # h.form(url('permission', id=ID),
90 # h.form(url('permission', id=ID),
89 # method='put')
91 # method='put')
90 # url('permission', id=ID)
92 # url('permission', id=ID)
91
93
92 permission_model = PermissionModel()
94 permission_model = PermissionModel()
93
95
94 _form = DefaultPermissionsForm([x[0] for x in self.perms_choices],
96 _form = DefaultPermissionsForm([x[0] for x in self.perms_choices],
95 [x[0] for x in self.register_choices],
97 [x[0] for x in self.register_choices],
96 [x[0] for x in self.create_choices])()
98 [x[0] for x in self.create_choices])()
97
99
98 try:
100 try:
99 form_result = _form.to_python(dict(request.POST))
101 form_result = _form.to_python(dict(request.POST))
100 form_result.update({'perm_user_name':id})
102 form_result.update({'perm_user_name':id})
101 permission_model.update(form_result)
103 permission_model.update(form_result)
102 h.flash(_('Default permissions updated succesfully'),
104 h.flash(_('Default permissions updated successfully'),
103 category='success')
105 category='success')
104
106
105 except formencode.Invalid, errors:
107 except formencode.Invalid, errors:
106 c.perms_choices = self.perms_choices
108 c.perms_choices = self.perms_choices
107 c.register_choices = self.register_choices
109 c.register_choices = self.register_choices
108 c.create_choices = self.create_choices
110 c.create_choices = self.create_choices
111 defaults = errors.value
112 defaults.update(SettingsModel().get_ldap_settings())
109
113
110 return htmlfill.render(
114 return htmlfill.render(
111 render('admin/permissions/permissions.html'),
115 render('admin/permissions/permissions.html'),
112 defaults=errors.value,
116 defaults=defaults,
113 errors=errors.error_dict or {},
117 errors=errors.error_dict or {},
114 prefix_error=False,
118 prefix_error=False,
115 encoding="UTF-8")
119 encoding="UTF-8")
116 except Exception:
120 except Exception:
117 log.error(traceback.format_exc())
121 log.error(traceback.format_exc())
118 h.flash(_('error occured during update of permissions'),
122 h.flash(_('error occured during update of permissions'),
119 category='error')
123 category='error')
120
124
121 return redirect(url('edit_permission', id=id))
125 return redirect(url('edit_permission', id=id))
122
126
123
127
124
128
125 def delete(self, id):
129 def delete(self, id):
126 """DELETE /permissions/id: Delete an existing item"""
130 """DELETE /permissions/id: Delete an existing item"""
127 # Forms posted to this method should contain a hidden field:
131 # Forms posted to this method should contain a hidden field:
128 # <input type="hidden" name="_method" value="DELETE" />
132 # <input type="hidden" name="_method" value="DELETE" />
129 # Or using helpers:
133 # Or using helpers:
130 # h.form(url('permission', id=ID),
134 # h.form(url('permission', id=ID),
131 # method='delete')
135 # method='delete')
132 # url('permission', id=ID)
136 # url('permission', id=ID)
133
137
134 def show(self, id, format='html'):
138 def show(self, id, format='html'):
135 """GET /permissions/id: Show a specific item"""
139 """GET /permissions/id: Show a specific item"""
136 # url('permission', id=ID)
140 # url('permission', id=ID)
137
141
138 def edit(self, id, format='html'):
142 def edit(self, id, format='html'):
139 """GET /permissions/id/edit: Form to edit an existing item"""
143 """GET /permissions/id/edit: Form to edit an existing item"""
140 #url('edit_permission', id=ID)
144 #url('edit_permission', id=ID)
141 c.perms_choices = self.perms_choices
145 c.perms_choices = self.perms_choices
142 c.register_choices = self.register_choices
146 c.register_choices = self.register_choices
143 c.create_choices = self.create_choices
147 c.create_choices = self.create_choices
144
148
145 if id == 'default':
149 if id == 'default':
146 default_user = UserModel().get_by_username('default')
150 default_user = UserModel().get_by_username('default')
147 defaults = {'_method':'put',
151 defaults = {'_method':'put',
148 'anonymous':default_user.active}
152 'anonymous':default_user.active}
153 defaults.update(SettingsModel().get_ldap_settings())
149 for p in default_user.user_perms:
154 for p in default_user.user_perms:
150 if p.permission.permission_name.startswith('repository.'):
155 if p.permission.permission_name.startswith('repository.'):
151 defaults['default_perm'] = p.permission.permission_name
156 defaults['default_perm'] = p.permission.permission_name
152
157
153 if p.permission.permission_name.startswith('hg.register.'):
158 if p.permission.permission_name.startswith('hg.register.'):
154 defaults['default_register'] = p.permission.permission_name
159 defaults['default_register'] = p.permission.permission_name
155
160
156 if p.permission.permission_name.startswith('hg.create.'):
161 if p.permission.permission_name.startswith('hg.create.'):
157 defaults['default_create'] = p.permission.permission_name
162 defaults['default_create'] = p.permission.permission_name
158
163
159 return htmlfill.render(
164 return htmlfill.render(
160 render('admin/permissions/permissions.html'),
165 render('admin/permissions/permissions.html'),
161 defaults=defaults,
166 defaults=defaults,
162 encoding="UTF-8",
167 encoding="UTF-8",
163 force_defaults=True,)
168 force_defaults=True,)
164 else:
169 else:
165 return redirect(url('admin_home'))
170 return redirect(url('admin_home'))
171
172
173 def ldap(self, id_user='default'):
174 """
175 POST ldap create and store ldap settings
176 """
177
178 settings_model = SettingsModel()
179 _form = LdapSettingsForm()()
180
181 try:
182 form_result = _form.to_python(dict(request.POST))
183 try:
184
185 for k, v in form_result.items():
186 if k.startswith('ldap_'):
187 setting = settings_model.get(k)
188 setting.app_settings_value = v
189 self.sa.add(setting)
190
191 self.sa.commit()
192 h.flash(_('Ldap settings updated successfully'),
193 category='success')
194 except:
195 raise
196 except LdapImportError:
197 h.flash(_('Unable to activate ldap. The "ldap-python" library '
198 'is missing.'),
199 category='warning')
200
201 except formencode.Invalid, errors:
202 c.perms_choices = self.perms_choices
203 c.register_choices = self.register_choices
204 c.create_choices = self.create_choices
205
206 return htmlfill.render(
207 render('admin/permissions/permissions.html'),
208 defaults=errors.value,
209 errors=errors.error_dict or {},
210 prefix_error=False,
211 encoding="UTF-8")
212 except Exception:
213 log.error(traceback.format_exc())
214 h.flash(_('error occured during update of ldap settings'),
215 category='error')
216
217 return redirect(url('edit_permission', id=id_user))
@@ -1,319 +1,314 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # settings controller for pylons
3 # settings controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
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
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on July 14, 2010
21 Created on July 14, 2010
22 settings controller for pylons
22 settings controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from formencode import htmlfill
25 from formencode import htmlfill
26 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
26 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
27 config
27 config
28 from pylons.controllers.util import abort, redirect
28 from pylons.controllers.util import abort, redirect
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30 from rhodecode.lib import helpers as h
30 from rhodecode.lib import helpers as h
31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
32 HasPermissionAnyDecorator
32 HasPermissionAnyDecorator
33 from rhodecode.lib.base import BaseController, render
33 from rhodecode.lib.base import BaseController, render
34 from rhodecode.lib.celerylib import tasks, run_task
34 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
35 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
35 set_rhodecode_config, get_hg_settings, get_hg_ui_settings
36 set_rhodecode_config, get_hg_settings, get_hg_ui_settings
36 from rhodecode.model.db import RhodeCodeSettings, RhodeCodeUi, Repository
37 from rhodecode.model.db import RhodeCodeUi, Repository
37 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
38 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
38 ApplicationUiSettingsForm
39 ApplicationUiSettingsForm
39 from rhodecode.model.scm import ScmModel
40 from rhodecode.model.scm import ScmModel
41 from rhodecode.model.settings import SettingsModel
40 from rhodecode.model.user import UserModel
42 from rhodecode.model.user import UserModel
41 from rhodecode.lib.celerylib import tasks, run_task
42 from sqlalchemy import func
43 from sqlalchemy import func
43 import formencode
44 import formencode
44 import logging
45 import logging
45 import traceback
46 import traceback
46
47
47 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
48
49
49
50
50 class SettingsController(BaseController):
51 class SettingsController(BaseController):
51 """REST Controller styled on the Atom Publishing Protocol"""
52 """REST Controller styled on the Atom Publishing Protocol"""
52 # To properly map this controller, ensure your config/routing.py
53 # To properly map this controller, ensure your config/routing.py
53 # file has a resource setup:
54 # file has a resource setup:
54 # map.resource('setting', 'settings', controller='admin/settings',
55 # map.resource('setting', 'settings', controller='admin/settings',
55 # path_prefix='/admin', name_prefix='admin_')
56 # path_prefix='/admin', name_prefix='admin_')
56
57
57
58
58 @LoginRequired()
59 @LoginRequired()
59 def __before__(self):
60 def __before__(self):
60 c.admin_user = session.get('admin_user')
61 c.admin_user = session.get('admin_user')
61 c.admin_username = session.get('admin_username')
62 c.admin_username = session.get('admin_username')
62 super(SettingsController, self).__before__()
63 super(SettingsController, self).__before__()
63
64
64
65
65 @HasPermissionAllDecorator('hg.admin')
66 @HasPermissionAllDecorator('hg.admin')
66 def index(self, format='html'):
67 def index(self, format='html'):
67 """GET /admin/settings: All items in the collection"""
68 """GET /admin/settings: All items in the collection"""
68 # url('admin_settings')
69 # url('admin_settings')
69
70
70 defaults = get_hg_settings()
71 defaults = get_hg_settings()
71 defaults.update(get_hg_ui_settings())
72 defaults.update(get_hg_ui_settings())
72 return htmlfill.render(
73 return htmlfill.render(
73 render('admin/settings/settings.html'),
74 render('admin/settings/settings.html'),
74 defaults=defaults,
75 defaults=defaults,
75 encoding="UTF-8",
76 encoding="UTF-8",
76 force_defaults=False
77 force_defaults=False
77 )
78 )
78
79
79 @HasPermissionAllDecorator('hg.admin')
80 @HasPermissionAllDecorator('hg.admin')
80 def create(self):
81 def create(self):
81 """POST /admin/settings: Create a new item"""
82 """POST /admin/settings: Create a new item"""
82 # url('admin_settings')
83 # url('admin_settings')
83
84
84 @HasPermissionAllDecorator('hg.admin')
85 @HasPermissionAllDecorator('hg.admin')
85 def new(self, format='html'):
86 def new(self, format='html'):
86 """GET /admin/settings/new: Form to create a new item"""
87 """GET /admin/settings/new: Form to create a new item"""
87 # url('admin_new_setting')
88 # url('admin_new_setting')
88
89
89 @HasPermissionAllDecorator('hg.admin')
90 @HasPermissionAllDecorator('hg.admin')
90 def update(self, setting_id):
91 def update(self, setting_id):
91 """PUT /admin/settings/setting_id: Update an existing item"""
92 """PUT /admin/settings/setting_id: Update an existing item"""
92 # Forms posted to this method should contain a hidden field:
93 # Forms posted to this method should contain a hidden field:
93 # <input type="hidden" name="_method" value="PUT" />
94 # <input type="hidden" name="_method" value="PUT" />
94 # Or using helpers:
95 # Or using helpers:
95 # h.form(url('admin_setting', setting_id=ID),
96 # h.form(url('admin_setting', setting_id=ID),
96 # method='put')
97 # method='put')
97 # url('admin_setting', setting_id=ID)
98 # url('admin_setting', setting_id=ID)
98 if setting_id == 'mapping':
99 if setting_id == 'mapping':
99 rm_obsolete = request.POST.get('destroy', False)
100 rm_obsolete = request.POST.get('destroy', False)
100 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
101 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
101
102
102 initial = ScmModel().repo_scan(g.paths[0][1], g.baseui)
103 initial = ScmModel().repo_scan(g.paths[0][1], g.baseui)
103 for repo_name in initial.keys():
104 for repo_name in initial.keys():
104 invalidate_cache('get_repo_cached_%s' % repo_name)
105 invalidate_cache('get_repo_cached_%s' % repo_name)
105
106
106 repo2db_mapper(initial, rm_obsolete)
107 repo2db_mapper(initial, rm_obsolete)
107
108
108 h.flash(_('Repositories successfully rescanned'), category='success')
109 h.flash(_('Repositories successfully rescanned'), category='success')
109
110
110 if setting_id == 'whoosh':
111 if setting_id == 'whoosh':
111 repo_location = get_hg_ui_settings()['paths_root_path']
112 repo_location = get_hg_ui_settings()['paths_root_path']
112 full_index = request.POST.get('full_index', False)
113 full_index = request.POST.get('full_index', False)
113 task = run_task(tasks.whoosh_index, repo_location, full_index)
114 task = run_task(tasks.whoosh_index, repo_location, full_index)
114
115
115 h.flash(_('Whoosh reindex task scheduled'), category='success')
116 h.flash(_('Whoosh reindex task scheduled'), category='success')
116 if setting_id == 'global':
117 if setting_id == 'global':
117
118
118 application_form = ApplicationSettingsForm()()
119 application_form = ApplicationSettingsForm()()
119 try:
120 try:
120 form_result = application_form.to_python(dict(request.POST))
121 form_result = application_form.to_python(dict(request.POST))
121
122 settings_model = SettingsModel()
122 try:
123 try:
123 hgsettings1 = self.sa.query(RhodeCodeSettings)\
124 hgsettings1 = settings_model.get('title')
124 .filter(RhodeCodeSettings.app_settings_name \
125 == 'title').one()
126
127 hgsettings1.app_settings_value = form_result['rhodecode_title']
125 hgsettings1.app_settings_value = form_result['rhodecode_title']
128
126
129 hgsettings2 = self.sa.query(RhodeCodeSettings)\
127 hgsettings2 = settings_model('realm')
130 .filter(RhodeCodeSettings.app_settings_name \
131 == 'realm').one()
132
133 hgsettings2.app_settings_value = form_result['rhodecode_realm']
128 hgsettings2.app_settings_value = form_result['rhodecode_realm']
134
129
135
130
136 self.sa.add(hgsettings1)
131 self.sa.add(hgsettings1)
137 self.sa.add(hgsettings2)
132 self.sa.add(hgsettings2)
138 self.sa.commit()
133 self.sa.commit()
139 set_rhodecode_config(config)
134 set_rhodecode_config(config)
140 h.flash(_('Updated application settings'),
135 h.flash(_('Updated application settings'),
141 category='success')
136 category='success')
142
137
143 except:
138 except:
144 log.error(traceback.format_exc())
139 log.error(traceback.format_exc())
145 h.flash(_('error occurred during updating application settings'),
140 h.flash(_('error occurred during updating application settings'),
146 category='error')
141 category='error')
147
142
148 self.sa.rollback()
143 self.sa.rollback()
149
144
150
145
151 except formencode.Invalid, errors:
146 except formencode.Invalid, errors:
152 return htmlfill.render(
147 return htmlfill.render(
153 render('admin/settings/settings.html'),
148 render('admin/settings/settings.html'),
154 defaults=errors.value,
149 defaults=errors.value,
155 errors=errors.error_dict or {},
150 errors=errors.error_dict or {},
156 prefix_error=False,
151 prefix_error=False,
157 encoding="UTF-8")
152 encoding="UTF-8")
158
153
159 if setting_id == 'mercurial':
154 if setting_id == 'mercurial':
160 application_form = ApplicationUiSettingsForm()()
155 application_form = ApplicationUiSettingsForm()()
161 try:
156 try:
162 form_result = application_form.to_python(dict(request.POST))
157 form_result = application_form.to_python(dict(request.POST))
163
158
164 try:
159 try:
165
160
166 hgsettings1 = self.sa.query(RhodeCodeUi)\
161 hgsettings1 = self.sa.query(RhodeCodeUi)\
167 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
162 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
168 hgsettings1.ui_value = form_result['web_push_ssl']
163 hgsettings1.ui_value = form_result['web_push_ssl']
169
164
170 hgsettings2 = self.sa.query(RhodeCodeUi)\
165 hgsettings2 = self.sa.query(RhodeCodeUi)\
171 .filter(RhodeCodeUi.ui_key == '/').one()
166 .filter(RhodeCodeUi.ui_key == '/').one()
172 hgsettings2.ui_value = form_result['paths_root_path']
167 hgsettings2.ui_value = form_result['paths_root_path']
173
168
174
169
175 #HOOKS
170 #HOOKS
176 hgsettings3 = self.sa.query(RhodeCodeUi)\
171 hgsettings3 = self.sa.query(RhodeCodeUi)\
177 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
172 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
178 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
173 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
179
174
180 hgsettings4 = self.sa.query(RhodeCodeUi)\
175 hgsettings4 = self.sa.query(RhodeCodeUi)\
181 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
176 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
182 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
177 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
183
178
184 hgsettings5 = self.sa.query(RhodeCodeUi)\
179 hgsettings5 = self.sa.query(RhodeCodeUi)\
185 .filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
180 .filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
186 hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
181 hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
187
182
188 hgsettings6 = self.sa.query(RhodeCodeUi)\
183 hgsettings6 = self.sa.query(RhodeCodeUi)\
189 .filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
184 .filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
190 hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
185 hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
191
186
192
187
193 self.sa.add(hgsettings1)
188 self.sa.add(hgsettings1)
194 self.sa.add(hgsettings2)
189 self.sa.add(hgsettings2)
195 self.sa.add(hgsettings3)
190 self.sa.add(hgsettings3)
196 self.sa.add(hgsettings4)
191 self.sa.add(hgsettings4)
197 self.sa.add(hgsettings5)
192 self.sa.add(hgsettings5)
198 self.sa.add(hgsettings6)
193 self.sa.add(hgsettings6)
199 self.sa.commit()
194 self.sa.commit()
200
195
201 h.flash(_('Updated mercurial settings'),
196 h.flash(_('Updated mercurial settings'),
202 category='success')
197 category='success')
203
198
204 except:
199 except:
205 log.error(traceback.format_exc())
200 log.error(traceback.format_exc())
206 h.flash(_('error occurred during updating application settings'),
201 h.flash(_('error occurred during updating application settings'),
207 category='error')
202 category='error')
208
203
209 self.sa.rollback()
204 self.sa.rollback()
210
205
211
206
212 except formencode.Invalid, errors:
207 except formencode.Invalid, errors:
213 return htmlfill.render(
208 return htmlfill.render(
214 render('admin/settings/settings.html'),
209 render('admin/settings/settings.html'),
215 defaults=errors.value,
210 defaults=errors.value,
216 errors=errors.error_dict or {},
211 errors=errors.error_dict or {},
217 prefix_error=False,
212 prefix_error=False,
218 encoding="UTF-8")
213 encoding="UTF-8")
219
214
220
215
221
216
222 return redirect(url('admin_settings'))
217 return redirect(url('admin_settings'))
223
218
224 @HasPermissionAllDecorator('hg.admin')
219 @HasPermissionAllDecorator('hg.admin')
225 def delete(self, setting_id):
220 def delete(self, setting_id):
226 """DELETE /admin/settings/setting_id: Delete an existing item"""
221 """DELETE /admin/settings/setting_id: Delete an existing item"""
227 # Forms posted to this method should contain a hidden field:
222 # Forms posted to this method should contain a hidden field:
228 # <input type="hidden" name="_method" value="DELETE" />
223 # <input type="hidden" name="_method" value="DELETE" />
229 # Or using helpers:
224 # Or using helpers:
230 # h.form(url('admin_setting', setting_id=ID),
225 # h.form(url('admin_setting', setting_id=ID),
231 # method='delete')
226 # method='delete')
232 # url('admin_setting', setting_id=ID)
227 # url('admin_setting', setting_id=ID)
233
228
234 @HasPermissionAllDecorator('hg.admin')
229 @HasPermissionAllDecorator('hg.admin')
235 def show(self, setting_id, format='html'):
230 def show(self, setting_id, format='html'):
236 """GET /admin/settings/setting_id: Show a specific item"""
231 """GET /admin/settings/setting_id: Show a specific item"""
237 # url('admin_setting', setting_id=ID)
232 # url('admin_setting', setting_id=ID)
238
233
239 @HasPermissionAllDecorator('hg.admin')
234 @HasPermissionAllDecorator('hg.admin')
240 def edit(self, setting_id, format='html'):
235 def edit(self, setting_id, format='html'):
241 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
236 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
242 # url('admin_edit_setting', setting_id=ID)
237 # url('admin_edit_setting', setting_id=ID)
243
238
244
239
245 def my_account(self):
240 def my_account(self):
246 """
241 """
247 GET /_admin/my_account Displays info about my account
242 GET /_admin/my_account Displays info about my account
248 """
243 """
249
244
250 # url('admin_settings_my_account')
245 # url('admin_settings_my_account')
251 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
246 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
252 all_repos = self.sa.query(Repository)\
247 all_repos = self.sa.query(Repository)\
253 .filter(Repository.user_id == c.user.user_id)\
248 .filter(Repository.user_id == c.user.user_id)\
254 .order_by(func.lower(Repository.repo_name))\
249 .order_by(func.lower(Repository.repo_name))\
255 .all()
250 .all()
256 c.user_repos = ScmModel().get_repos(all_repos)
251 c.user_repos = ScmModel().get_repos(all_repos)
257
252
258 if c.user.username == 'default':
253 if c.user.username == 'default':
259 h.flash(_("You can't edit this user since it's"
254 h.flash(_("You can't edit this user since it's"
260 " crucial for entire application"), category='warning')
255 " crucial for entire application"), category='warning')
261 return redirect(url('users'))
256 return redirect(url('users'))
262
257
263 defaults = c.user.__dict__
258 defaults = c.user.__dict__
264 return htmlfill.render(
259 return htmlfill.render(
265 render('admin/users/user_edit_my_account.html'),
260 render('admin/users/user_edit_my_account.html'),
266 defaults=defaults,
261 defaults=defaults,
267 encoding="UTF-8",
262 encoding="UTF-8",
268 force_defaults=False
263 force_defaults=False
269 )
264 )
270
265
271 def my_account_update(self):
266 def my_account_update(self):
272 """PUT /_admin/my_account_update: Update an existing item"""
267 """PUT /_admin/my_account_update: Update an existing item"""
273 # Forms posted to this method should contain a hidden field:
268 # Forms posted to this method should contain a hidden field:
274 # <input type="hidden" name="_method" value="PUT" />
269 # <input type="hidden" name="_method" value="PUT" />
275 # Or using helpers:
270 # Or using helpers:
276 # h.form(url('admin_settings_my_account_update'),
271 # h.form(url('admin_settings_my_account_update'),
277 # method='put')
272 # method='put')
278 # url('admin_settings_my_account_update', id=ID)
273 # url('admin_settings_my_account_update', id=ID)
279 user_model = UserModel()
274 user_model = UserModel()
280 uid = c.rhodecode_user.user_id
275 uid = c.rhodecode_user.user_id
281 _form = UserForm(edit=True, old_data={'user_id':uid,
276 _form = UserForm(edit=True, old_data={'user_id':uid,
282 'email':c.rhodecode_user.email})()
277 'email':c.rhodecode_user.email})()
283 form_result = {}
278 form_result = {}
284 try:
279 try:
285 form_result = _form.to_python(dict(request.POST))
280 form_result = _form.to_python(dict(request.POST))
286 user_model.update_my_account(uid, form_result)
281 user_model.update_my_account(uid, form_result)
287 h.flash(_('Your account was updated succesfully'),
282 h.flash(_('Your account was updated succesfully'),
288 category='success')
283 category='success')
289
284
290 except formencode.Invalid, errors:
285 except formencode.Invalid, errors:
291 c.user = user_model.get(c.rhodecode_user.user_id, cache=False)
286 c.user = user_model.get(c.rhodecode_user.user_id, cache=False)
292 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
287 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
293 all_repos = self.sa.query(Repository)\
288 all_repos = self.sa.query(Repository)\
294 .filter(Repository.user_id == c.user.user_id)\
289 .filter(Repository.user_id == c.user.user_id)\
295 .order_by(func.lower(Repository.repo_name))\
290 .order_by(func.lower(Repository.repo_name))\
296 .all()
291 .all()
297 c.user_repos = ScmModel().get_repos(all_repos)
292 c.user_repos = ScmModel().get_repos(all_repos)
298
293
299 return htmlfill.render(
294 return htmlfill.render(
300 render('admin/users/user_edit_my_account.html'),
295 render('admin/users/user_edit_my_account.html'),
301 defaults=errors.value,
296 defaults=errors.value,
302 errors=errors.error_dict or {},
297 errors=errors.error_dict or {},
303 prefix_error=False,
298 prefix_error=False,
304 encoding="UTF-8")
299 encoding="UTF-8")
305 except Exception:
300 except Exception:
306 log.error(traceback.format_exc())
301 log.error(traceback.format_exc())
307 h.flash(_('error occured during update of user %s') \
302 h.flash(_('error occured during update of user %s') \
308 % form_result.get('username'), category='error')
303 % form_result.get('username'), category='error')
309
304
310 return redirect(url('my_account'))
305 return redirect(url('my_account'))
311
306
312 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
307 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
313 def create_repository(self):
308 def create_repository(self):
314 """GET /_admin/create_repository: Form to create a new item"""
309 """GET /_admin/create_repository: Form to create a new item"""
315 new_repo = request.GET.get('repo', '')
310 new_repo = request.GET.get('repo', '')
316 c.new_repo = h.repo_name_slug(new_repo)
311 c.new_repo = h.repo_name_slug(new_repo)
317
312
318 return render('admin/repos/repo_add_create_repository.html')
313 return render('admin/repos/repo_add_create_repository.html')
319
314
@@ -1,488 +1,525 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # authentication and permission libraries
3 # authentication and permission libraries
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
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
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 4, 2010
21 Created on April 4, 2010
22
22
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from pylons import config, session, url, request
25 from pylons import config, session, url, request
26 from pylons.controllers.util import abort, redirect
26 from pylons.controllers.util import abort, redirect
27 from rhodecode.lib.utils import get_repo_slug
27 from rhodecode.lib.utils import get_repo_slug
28 from rhodecode.lib.auth_ldap import AuthLdap, UsernameError, PasswordError
28 from rhodecode.model import meta
29 from rhodecode.model import meta
29 from rhodecode.model.user import UserModel
30 from rhodecode.model.user import UserModel
30 from rhodecode.model.caching_query import FromCache
31 from rhodecode.model.caching_query import FromCache
31 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
32 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
32 UserToPerm
33 UserToPerm
33 import bcrypt
34 import bcrypt
34 from decorator import decorator
35 from decorator import decorator
35 import logging
36 import logging
36 import random
37 import random
38 import traceback
37
39
38 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
39
41
40 class PasswordGenerator(object):
42 class PasswordGenerator(object):
41 """This is a simple class for generating password from
43 """This is a simple class for generating password from
42 different sets of characters
44 different sets of characters
43 usage:
45 usage:
44 passwd_gen = PasswordGenerator()
46 passwd_gen = PasswordGenerator()
45 #print 8-letter password containing only big and small letters of alphabet
47 #print 8-letter password containing only big and small letters of alphabet
46 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
48 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
47 """
49 """
48 ALPHABETS_NUM = r'''1234567890'''#[0]
50 ALPHABETS_NUM = r'''1234567890'''#[0]
49 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
51 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
50 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
52 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
51 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
53 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
52 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
54 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
53 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
55 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
54 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
56 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
55 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
57 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
56 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
58 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
57
59
58 def __init__(self, passwd=''):
60 def __init__(self, passwd=''):
59 self.passwd = passwd
61 self.passwd = passwd
60
62
61 def gen_password(self, len, type):
63 def gen_password(self, len, type):
62 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
64 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
63 return self.passwd
65 return self.passwd
64
66
65
67
66 def get_crypt_password(password):
68 def get_crypt_password(password):
67 """Cryptographic function used for password hashing based on sha1
69 """Cryptographic function used for password hashing based on sha1
68 :param password: password to hash
70 :param password: password to hash
69 """
71 """
70 return bcrypt.hashpw(password, bcrypt.gensalt(10))
72 return bcrypt.hashpw(password, bcrypt.gensalt(10))
71
73
72 def check_password(password, hashed):
74 def check_password(password, hashed):
73 return bcrypt.hashpw(password, hashed) == hashed
75 return bcrypt.hashpw(password, hashed) == hashed
74
76
75 def authfunc(environ, username, password):
77 def authfunc(environ, username, password):
76 """
78 """
77 Authentication function used in Mercurial/Git/ and access controll,
79 Authentication function used in Mercurial/Git/ and access control,
78 firstly checks for db authentication then if ldap is enabled for ldap
80 firstly checks for db authentication then if ldap is enabled for ldap
79 authentication
81 authentication, also creates ldap user if not in database
82
80 :param environ: needed only for using in Basic auth, can be None
83 :param environ: needed only for using in Basic auth, can be None
81 :param username: username
84 :param username: username
82 :param password: password
85 :param password: password
83 """
86 """
87 user_model = UserModel()
88 user = user_model.get_by_username(username, cache=False)
84
89
85 user = UserModel().get_by_username(username, cache=False)
90 if user is not None and user.is_ldap is False:
86
87 if user:
88 if user.active:
91 if user.active:
89
92
90 if user.username == 'default' and user.active:
93 if user.username == 'default' and user.active:
91 log.info('user %s authenticated correctly', username)
94 log.info('user %s authenticated correctly', username)
92 return True
95 return True
93
96
94 elif user.username == username and check_password(password, user.password):
97 elif user.username == username and check_password(password, user.password):
95 log.info('user %s authenticated correctly', username)
98 log.info('user %s authenticated correctly', username)
96 return True
99 return True
97 else:
100 else:
98 log.error('user %s is disabled', username)
101 log.error('user %s is disabled', username)
99
102
103
104 else:
105 from rhodecode.model.settings import SettingsModel
106 ldap_settings = SettingsModel().get_ldap_settings()
107
108 #======================================================================
109 # FALLBACK TO LDAP AUTH IN ENABLE
110 #======================================================================
111 if ldap_settings.get('ldap_active', False):
112 kwargs = {
113 'server':ldap_settings.get('ldap_host', ''),
114 'base_dn':ldap_settings.get('ldap_base_dn', ''),
115 'port':ldap_settings.get('ldap_port'),
116 'bind_dn':ldap_settings.get('ldap_dn_user'),
117 'bind_pass':ldap_settings.get('ldap_dn_pass'),
118 'use_ldaps':ldap_settings.get('ldap_ldaps'),
119 'ldap_version':3,
120 }
121 log.debug('Checking for ldap authentication')
122 try:
123 aldap = AuthLdap(**kwargs)
124 res = aldap.authenticate_ldap(username, password)
125
126 authenticated = res[1]['uid'][0] == username
127
128 if authenticated and user_model.create_ldap(username, password):
129 log.info('created new ldap user')
130
131 return authenticated
132 except (UsernameError, PasswordError):
133 return False
134 except:
135 log.error(traceback.format_exc())
136 return False
100 return False
137 return False
101
138
102 class AuthUser(object):
139 class AuthUser(object):
103 """
140 """
104 A simple object that handles a mercurial username for authentication
141 A simple object that handles a mercurial username for authentication
105 """
142 """
106 def __init__(self):
143 def __init__(self):
107 self.username = 'None'
144 self.username = 'None'
108 self.name = ''
145 self.name = ''
109 self.lastname = ''
146 self.lastname = ''
110 self.email = ''
147 self.email = ''
111 self.user_id = None
148 self.user_id = None
112 self.is_authenticated = False
149 self.is_authenticated = False
113 self.is_admin = False
150 self.is_admin = False
114 self.permissions = {}
151 self.permissions = {}
115
152
116 def __repr__(self):
153 def __repr__(self):
117 return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
154 return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
118
155
119 def set_available_permissions(config):
156 def set_available_permissions(config):
120 """
157 """
121 This function will propagate pylons globals with all available defined
158 This function will propagate pylons globals with all available defined
122 permission given in db. We don't wannt to check each time from db for new
159 permission given in db. We don't wannt to check each time from db for new
123 permissions since adding a new permission also requires application restart
160 permissions since adding a new permission also requires application restart
124 ie. to decorate new views with the newly created permission
161 ie. to decorate new views with the newly created permission
125 :param config:
162 :param config:
126 """
163 """
127 log.info('getting information about all available permissions')
164 log.info('getting information about all available permissions')
128 try:
165 try:
129 sa = meta.Session()
166 sa = meta.Session()
130 all_perms = sa.query(Permission).all()
167 all_perms = sa.query(Permission).all()
131 except:
168 except:
132 pass
169 pass
133 finally:
170 finally:
134 meta.Session.remove()
171 meta.Session.remove()
135
172
136 config['available_permissions'] = [x.permission_name for x in all_perms]
173 config['available_permissions'] = [x.permission_name for x in all_perms]
137
174
138 def set_base_path(config):
175 def set_base_path(config):
139 config['base_path'] = config['pylons.app_globals'].base_path
176 config['base_path'] = config['pylons.app_globals'].base_path
140
177
141
178
142 def fill_perms(user):
179 def fill_perms(user):
143 """
180 """
144 Fills user permission attribute with permissions taken from database
181 Fills user permission attribute with permissions taken from database
145 :param user:
182 :param user:
146 """
183 """
147
184
148 sa = meta.Session()
185 sa = meta.Session()
149 user.permissions['repositories'] = {}
186 user.permissions['repositories'] = {}
150 user.permissions['global'] = set()
187 user.permissions['global'] = set()
151
188
152 #===========================================================================
189 #===========================================================================
153 # fetch default permissions
190 # fetch default permissions
154 #===========================================================================
191 #===========================================================================
155 default_user = UserModel().get_by_username('default', cache=True)
192 default_user = UserModel().get_by_username('default', cache=True)
156
193
157 default_perms = sa.query(RepoToPerm, Repository, Permission)\
194 default_perms = sa.query(RepoToPerm, Repository, Permission)\
158 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
195 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
159 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
196 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
160 .filter(RepoToPerm.user == default_user).all()
197 .filter(RepoToPerm.user == default_user).all()
161
198
162 if user.is_admin:
199 if user.is_admin:
163 #=======================================================================
200 #=======================================================================
164 # #admin have all default rights set to admin
201 # #admin have all default rights set to admin
165 #=======================================================================
202 #=======================================================================
166 user.permissions['global'].add('hg.admin')
203 user.permissions['global'].add('hg.admin')
167
204
168 for perm in default_perms:
205 for perm in default_perms:
169 p = 'repository.admin'
206 p = 'repository.admin'
170 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
207 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
171
208
172 else:
209 else:
173 #=======================================================================
210 #=======================================================================
174 # set default permissions
211 # set default permissions
175 #=======================================================================
212 #=======================================================================
176
213
177 #default global
214 #default global
178 default_global_perms = sa.query(UserToPerm)\
215 default_global_perms = sa.query(UserToPerm)\
179 .filter(UserToPerm.user == sa.query(User).filter(User.username ==
216 .filter(UserToPerm.user == sa.query(User).filter(User.username ==
180 'default').one())
217 'default').one())
181
218
182 for perm in default_global_perms:
219 for perm in default_global_perms:
183 user.permissions['global'].add(perm.permission.permission_name)
220 user.permissions['global'].add(perm.permission.permission_name)
184
221
185 #default repositories
222 #default repositories
186 for perm in default_perms:
223 for perm in default_perms:
187 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
224 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
188 #disable defaults for private repos,
225 #disable defaults for private repos,
189 p = 'repository.none'
226 p = 'repository.none'
190 elif perm.Repository.user_id == user.user_id:
227 elif perm.Repository.user_id == user.user_id:
191 #set admin if owner
228 #set admin if owner
192 p = 'repository.admin'
229 p = 'repository.admin'
193 else:
230 else:
194 p = perm.Permission.permission_name
231 p = perm.Permission.permission_name
195
232
196 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
233 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
197
234
198 #=======================================================================
235 #=======================================================================
199 # #overwrite default with user permissions if any
236 # #overwrite default with user permissions if any
200 #=======================================================================
237 #=======================================================================
201 user_perms = sa.query(RepoToPerm, Permission, Repository)\
238 user_perms = sa.query(RepoToPerm, Permission, Repository)\
202 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
239 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
203 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
240 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
204 .filter(RepoToPerm.user_id == user.user_id).all()
241 .filter(RepoToPerm.user_id == user.user_id).all()
205
242
206 for perm in user_perms:
243 for perm in user_perms:
207 if perm.Repository.user_id == user.user_id:#set admin if owner
244 if perm.Repository.user_id == user.user_id:#set admin if owner
208 p = 'repository.admin'
245 p = 'repository.admin'
209 else:
246 else:
210 p = perm.Permission.permission_name
247 p = perm.Permission.permission_name
211 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
248 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
212 meta.Session.remove()
249 meta.Session.remove()
213 return user
250 return user
214
251
215 def get_user(session):
252 def get_user(session):
216 """
253 """
217 Gets user from session, and wraps permissions into user
254 Gets user from session, and wraps permissions into user
218 :param session:
255 :param session:
219 """
256 """
220 user = session.get('rhodecode_user', AuthUser())
257 user = session.get('rhodecode_user', AuthUser())
221 #if the user is not logged in we check for anonymous access
258 #if the user is not logged in we check for anonymous access
222 #if user is logged and it's a default user check if we still have anonymous
259 #if user is logged and it's a default user check if we still have anonymous
223 #access enabled
260 #access enabled
224 if user.user_id is None or user.username == 'default':
261 if user.user_id is None or user.username == 'default':
225 anonymous_user = UserModel().get_by_username('default', cache=True)
262 anonymous_user = UserModel().get_by_username('default', cache=True)
226 if anonymous_user.active is True:
263 if anonymous_user.active is True:
227 #then we set this user is logged in
264 #then we set this user is logged in
228 user.is_authenticated = True
265 user.is_authenticated = True
229 user.user_id = anonymous_user.user_id
266 user.user_id = anonymous_user.user_id
230 else:
267 else:
231 user.is_authenticated = False
268 user.is_authenticated = False
232
269
233 if user.is_authenticated:
270 if user.is_authenticated:
234 user = UserModel().fill_data(user)
271 user = UserModel().fill_data(user)
235
272
236 user = fill_perms(user)
273 user = fill_perms(user)
237 session['rhodecode_user'] = user
274 session['rhodecode_user'] = user
238 session.save()
275 session.save()
239 return user
276 return user
240
277
241 #===============================================================================
278 #===============================================================================
242 # CHECK DECORATORS
279 # CHECK DECORATORS
243 #===============================================================================
280 #===============================================================================
244 class LoginRequired(object):
281 class LoginRequired(object):
245 """Must be logged in to execute this function else redirect to login page"""
282 """Must be logged in to execute this function else redirect to login page"""
246
283
247 def __call__(self, func):
284 def __call__(self, func):
248 return decorator(self.__wrapper, func)
285 return decorator(self.__wrapper, func)
249
286
250 def __wrapper(self, func, *fargs, **fkwargs):
287 def __wrapper(self, func, *fargs, **fkwargs):
251 user = session.get('rhodecode_user', AuthUser())
288 user = session.get('rhodecode_user', AuthUser())
252 log.debug('Checking login required for user:%s', user.username)
289 log.debug('Checking login required for user:%s', user.username)
253 if user.is_authenticated:
290 if user.is_authenticated:
254 log.debug('user %s is authenticated', user.username)
291 log.debug('user %s is authenticated', user.username)
255 return func(*fargs, **fkwargs)
292 return func(*fargs, **fkwargs)
256 else:
293 else:
257 log.warn('user %s not authenticated', user.username)
294 log.warn('user %s not authenticated', user.username)
258
295
259 p = ''
296 p = ''
260 if request.environ.get('SCRIPT_NAME') != '/':
297 if request.environ.get('SCRIPT_NAME') != '/':
261 p += request.environ.get('SCRIPT_NAME')
298 p += request.environ.get('SCRIPT_NAME')
262
299
263 p += request.environ.get('PATH_INFO')
300 p += request.environ.get('PATH_INFO')
264 if request.environ.get('QUERY_STRING'):
301 if request.environ.get('QUERY_STRING'):
265 p += '?' + request.environ.get('QUERY_STRING')
302 p += '?' + request.environ.get('QUERY_STRING')
266
303
267 log.debug('redirecting to login page with %s', p)
304 log.debug('redirecting to login page with %s', p)
268 return redirect(url('login_home', came_from=p))
305 return redirect(url('login_home', came_from=p))
269
306
270 class PermsDecorator(object):
307 class PermsDecorator(object):
271 """Base class for decorators"""
308 """Base class for decorators"""
272
309
273 def __init__(self, *required_perms):
310 def __init__(self, *required_perms):
274 available_perms = config['available_permissions']
311 available_perms = config['available_permissions']
275 for perm in required_perms:
312 for perm in required_perms:
276 if perm not in available_perms:
313 if perm not in available_perms:
277 raise Exception("'%s' permission is not defined" % perm)
314 raise Exception("'%s' permission is not defined" % perm)
278 self.required_perms = set(required_perms)
315 self.required_perms = set(required_perms)
279 self.user_perms = None
316 self.user_perms = None
280
317
281 def __call__(self, func):
318 def __call__(self, func):
282 return decorator(self.__wrapper, func)
319 return decorator(self.__wrapper, func)
283
320
284
321
285 def __wrapper(self, func, *fargs, **fkwargs):
322 def __wrapper(self, func, *fargs, **fkwargs):
286 # _wrapper.__name__ = func.__name__
323 # _wrapper.__name__ = func.__name__
287 # _wrapper.__dict__.update(func.__dict__)
324 # _wrapper.__dict__.update(func.__dict__)
288 # _wrapper.__doc__ = func.__doc__
325 # _wrapper.__doc__ = func.__doc__
289 self.user = session.get('rhodecode_user', AuthUser())
326 self.user = session.get('rhodecode_user', AuthUser())
290 self.user_perms = self.user.permissions
327 self.user_perms = self.user.permissions
291 log.debug('checking %s permissions %s for %s %s',
328 log.debug('checking %s permissions %s for %s %s',
292 self.__class__.__name__, self.required_perms, func.__name__,
329 self.__class__.__name__, self.required_perms, func.__name__,
293 self.user)
330 self.user)
294
331
295 if self.check_permissions():
332 if self.check_permissions():
296 log.debug('Permission granted for %s %s', func.__name__, self.user)
333 log.debug('Permission granted for %s %s', func.__name__, self.user)
297
334
298 return func(*fargs, **fkwargs)
335 return func(*fargs, **fkwargs)
299
336
300 else:
337 else:
301 log.warning('Permission denied for %s %s', func.__name__, self.user)
338 log.warning('Permission denied for %s %s', func.__name__, self.user)
302 #redirect with forbidden ret code
339 #redirect with forbidden ret code
303 return abort(403)
340 return abort(403)
304
341
305
342
306
343
307 def check_permissions(self):
344 def check_permissions(self):
308 """Dummy function for overriding"""
345 """Dummy function for overriding"""
309 raise Exception('You have to write this function in child class')
346 raise Exception('You have to write this function in child class')
310
347
311 class HasPermissionAllDecorator(PermsDecorator):
348 class HasPermissionAllDecorator(PermsDecorator):
312 """Checks for access permission for all given predicates. All of them
349 """Checks for access permission for all given predicates. All of them
313 have to be meet in order to fulfill the request
350 have to be meet in order to fulfill the request
314 """
351 """
315
352
316 def check_permissions(self):
353 def check_permissions(self):
317 if self.required_perms.issubset(self.user_perms.get('global')):
354 if self.required_perms.issubset(self.user_perms.get('global')):
318 return True
355 return True
319 return False
356 return False
320
357
321
358
322 class HasPermissionAnyDecorator(PermsDecorator):
359 class HasPermissionAnyDecorator(PermsDecorator):
323 """Checks for access permission for any of given predicates. In order to
360 """Checks for access permission for any of given predicates. In order to
324 fulfill the request any of predicates must be meet
361 fulfill the request any of predicates must be meet
325 """
362 """
326
363
327 def check_permissions(self):
364 def check_permissions(self):
328 if self.required_perms.intersection(self.user_perms.get('global')):
365 if self.required_perms.intersection(self.user_perms.get('global')):
329 return True
366 return True
330 return False
367 return False
331
368
332 class HasRepoPermissionAllDecorator(PermsDecorator):
369 class HasRepoPermissionAllDecorator(PermsDecorator):
333 """Checks for access permission for all given predicates for specific
370 """Checks for access permission for all given predicates for specific
334 repository. All of them have to be meet in order to fulfill the request
371 repository. All of them have to be meet in order to fulfill the request
335 """
372 """
336
373
337 def check_permissions(self):
374 def check_permissions(self):
338 repo_name = get_repo_slug(request)
375 repo_name = get_repo_slug(request)
339 try:
376 try:
340 user_perms = set([self.user_perms['repositories'][repo_name]])
377 user_perms = set([self.user_perms['repositories'][repo_name]])
341 except KeyError:
378 except KeyError:
342 return False
379 return False
343 if self.required_perms.issubset(user_perms):
380 if self.required_perms.issubset(user_perms):
344 return True
381 return True
345 return False
382 return False
346
383
347
384
348 class HasRepoPermissionAnyDecorator(PermsDecorator):
385 class HasRepoPermissionAnyDecorator(PermsDecorator):
349 """Checks for access permission for any of given predicates for specific
386 """Checks for access permission for any of given predicates for specific
350 repository. In order to fulfill the request any of predicates must be meet
387 repository. In order to fulfill the request any of predicates must be meet
351 """
388 """
352
389
353 def check_permissions(self):
390 def check_permissions(self):
354 repo_name = get_repo_slug(request)
391 repo_name = get_repo_slug(request)
355
392
356 try:
393 try:
357 user_perms = set([self.user_perms['repositories'][repo_name]])
394 user_perms = set([self.user_perms['repositories'][repo_name]])
358 except KeyError:
395 except KeyError:
359 return False
396 return False
360 if self.required_perms.intersection(user_perms):
397 if self.required_perms.intersection(user_perms):
361 return True
398 return True
362 return False
399 return False
363 #===============================================================================
400 #===============================================================================
364 # CHECK FUNCTIONS
401 # CHECK FUNCTIONS
365 #===============================================================================
402 #===============================================================================
366
403
367 class PermsFunction(object):
404 class PermsFunction(object):
368 """Base function for other check functions"""
405 """Base function for other check functions"""
369
406
370 def __init__(self, *perms):
407 def __init__(self, *perms):
371 available_perms = config['available_permissions']
408 available_perms = config['available_permissions']
372
409
373 for perm in perms:
410 for perm in perms:
374 if perm not in available_perms:
411 if perm not in available_perms:
375 raise Exception("'%s' permission in not defined" % perm)
412 raise Exception("'%s' permission in not defined" % perm)
376 self.required_perms = set(perms)
413 self.required_perms = set(perms)
377 self.user_perms = None
414 self.user_perms = None
378 self.granted_for = ''
415 self.granted_for = ''
379 self.repo_name = None
416 self.repo_name = None
380
417
381 def __call__(self, check_Location=''):
418 def __call__(self, check_Location=''):
382 user = session.get('rhodecode_user', False)
419 user = session.get('rhodecode_user', False)
383 if not user:
420 if not user:
384 return False
421 return False
385 self.user_perms = user.permissions
422 self.user_perms = user.permissions
386 self.granted_for = user.username
423 self.granted_for = user.username
387 log.debug('checking %s %s %s', self.__class__.__name__,
424 log.debug('checking %s %s %s', self.__class__.__name__,
388 self.required_perms, user)
425 self.required_perms, user)
389
426
390 if self.check_permissions():
427 if self.check_permissions():
391 log.debug('Permission granted for %s @ %s %s', self.granted_for,
428 log.debug('Permission granted for %s @ %s %s', self.granted_for,
392 check_Location, user)
429 check_Location, user)
393 return True
430 return True
394
431
395 else:
432 else:
396 log.warning('Permission denied for %s @ %s %s', self.granted_for,
433 log.warning('Permission denied for %s @ %s %s', self.granted_for,
397 check_Location, user)
434 check_Location, user)
398 return False
435 return False
399
436
400 def check_permissions(self):
437 def check_permissions(self):
401 """Dummy function for overriding"""
438 """Dummy function for overriding"""
402 raise Exception('You have to write this function in child class')
439 raise Exception('You have to write this function in child class')
403
440
404 class HasPermissionAll(PermsFunction):
441 class HasPermissionAll(PermsFunction):
405 def check_permissions(self):
442 def check_permissions(self):
406 if self.required_perms.issubset(self.user_perms.get('global')):
443 if self.required_perms.issubset(self.user_perms.get('global')):
407 return True
444 return True
408 return False
445 return False
409
446
410 class HasPermissionAny(PermsFunction):
447 class HasPermissionAny(PermsFunction):
411 def check_permissions(self):
448 def check_permissions(self):
412 if self.required_perms.intersection(self.user_perms.get('global')):
449 if self.required_perms.intersection(self.user_perms.get('global')):
413 return True
450 return True
414 return False
451 return False
415
452
416 class HasRepoPermissionAll(PermsFunction):
453 class HasRepoPermissionAll(PermsFunction):
417
454
418 def __call__(self, repo_name=None, check_Location=''):
455 def __call__(self, repo_name=None, check_Location=''):
419 self.repo_name = repo_name
456 self.repo_name = repo_name
420 return super(HasRepoPermissionAll, self).__call__(check_Location)
457 return super(HasRepoPermissionAll, self).__call__(check_Location)
421
458
422 def check_permissions(self):
459 def check_permissions(self):
423 if not self.repo_name:
460 if not self.repo_name:
424 self.repo_name = get_repo_slug(request)
461 self.repo_name = get_repo_slug(request)
425
462
426 try:
463 try:
427 self.user_perms = set([self.user_perms['repositories']\
464 self.user_perms = set([self.user_perms['repositories']\
428 [self.repo_name]])
465 [self.repo_name]])
429 except KeyError:
466 except KeyError:
430 return False
467 return False
431 self.granted_for = self.repo_name
468 self.granted_for = self.repo_name
432 if self.required_perms.issubset(self.user_perms):
469 if self.required_perms.issubset(self.user_perms):
433 return True
470 return True
434 return False
471 return False
435
472
436 class HasRepoPermissionAny(PermsFunction):
473 class HasRepoPermissionAny(PermsFunction):
437
474
438 def __call__(self, repo_name=None, check_Location=''):
475 def __call__(self, repo_name=None, check_Location=''):
439 self.repo_name = repo_name
476 self.repo_name = repo_name
440 return super(HasRepoPermissionAny, self).__call__(check_Location)
477 return super(HasRepoPermissionAny, self).__call__(check_Location)
441
478
442 def check_permissions(self):
479 def check_permissions(self):
443 if not self.repo_name:
480 if not self.repo_name:
444 self.repo_name = get_repo_slug(request)
481 self.repo_name = get_repo_slug(request)
445
482
446 try:
483 try:
447 self.user_perms = set([self.user_perms['repositories']\
484 self.user_perms = set([self.user_perms['repositories']\
448 [self.repo_name]])
485 [self.repo_name]])
449 except KeyError:
486 except KeyError:
450 return False
487 return False
451 self.granted_for = self.repo_name
488 self.granted_for = self.repo_name
452 if self.required_perms.intersection(self.user_perms):
489 if self.required_perms.intersection(self.user_perms):
453 return True
490 return True
454 return False
491 return False
455
492
456 #===============================================================================
493 #===============================================================================
457 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
494 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
458 #===============================================================================
495 #===============================================================================
459
496
460 class HasPermissionAnyMiddleware(object):
497 class HasPermissionAnyMiddleware(object):
461 def __init__(self, *perms):
498 def __init__(self, *perms):
462 self.required_perms = set(perms)
499 self.required_perms = set(perms)
463
500
464 def __call__(self, user, repo_name):
501 def __call__(self, user, repo_name):
465 usr = AuthUser()
502 usr = AuthUser()
466 usr.user_id = user.user_id
503 usr.user_id = user.user_id
467 usr.username = user.username
504 usr.username = user.username
468 usr.is_admin = user.admin
505 usr.is_admin = user.admin
469
506
470 try:
507 try:
471 self.user_perms = set([fill_perms(usr)\
508 self.user_perms = set([fill_perms(usr)\
472 .permissions['repositories'][repo_name]])
509 .permissions['repositories'][repo_name]])
473 except:
510 except:
474 self.user_perms = set()
511 self.user_perms = set()
475 self.granted_for = ''
512 self.granted_for = ''
476 self.username = user.username
513 self.username = user.username
477 self.repo_name = repo_name
514 self.repo_name = repo_name
478 return self.check_permissions()
515 return self.check_permissions()
479
516
480 def check_permissions(self):
517 def check_permissions(self):
481 log.debug('checking mercurial protocol '
518 log.debug('checking mercurial protocol '
482 'permissions for user:%s repository:%s',
519 'permissions for user:%s repository:%s',
483 self.username, self.repo_name)
520 self.username, self.repo_name)
484 if self.required_perms.intersection(self.user_perms):
521 if self.required_perms.intersection(self.user_perms):
485 log.debug('permission granted')
522 log.debug('permission granted')
486 return True
523 return True
487 log.debug('permission denied')
524 log.debug('permission denied')
488 return False
525 return False
@@ -1,86 +1,93 b''
1 import logging
2 logging.basicConfig(level=logging.DEBUG)
3 log = logging.getLogger('ldap')
4
5 #==============================================================================
1 #==============================================================================
6 # LDAP
2 # LDAP
7 #Name = Just a description for the auth modes page
3 #Name = Just a description for the auth modes page
8 #Host = DepartmentName.OrganizationName.local/ IP
4 #Host = DepartmentName.OrganizationName.local/ IP
9 #Port = 389 default for ldap
5 #Port = 389 default for ldap
10 #LDAPS = no set True if You need to use ldaps
6 #LDAPS = no set True if You need to use ldaps
11 #Account = DepartmentName\UserName (or UserName@MyDomain depending on AD server)
7 #Account = DepartmentName\UserName (or UserName@MyDomain depending on AD server)
12 #Password = <password>
8 #Password = <password>
13 #Base DN = DC=DepartmentName,DC=OrganizationName,DC=local
9 #Base DN = DC=DepartmentName,DC=OrganizationName,DC=local
14 #
15 #On-the-fly user creation = yes
16 #Attributes
17 # Login = sAMAccountName
18 # Firstname = givenName
19 # Lastname = sN
20 # Email = mail
21
10
22 #==============================================================================
11 #==============================================================================
23 class UsernameError(Exception):pass
12
24 class PasswordError(Exception):pass
13 from rhodecode.lib.exceptions import LdapImportError, UsernameError, \
14 PasswordError, ConnectionError
15 import logging
16
17 log = logging.getLogger(__name__)
25
18
26 LDAP_USE_LDAPS = False
19 try:
27 ldap_server_type = 'ldap'
20 import ldap
28 LDAP_SERVER_ADDRESS = 'myldap.com'
21 except ImportError:
29 LDAP_SERVER_PORT = '389'
22 pass
30
23
31 #USE FOR READ ONLY BIND TO LDAP SERVER
24 class AuthLdap(object):
32 LDAP_BIND_DN = ''
33 LDAP_BIND_PASS = ''
34
25
35 if LDAP_USE_LDAPS:ldap_server_type = ldap_server_type + 's'
26 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
36 LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
27 use_ldaps=False, ldap_version=3):
37 LDAP_SERVER_ADDRESS,
28 self.ldap_version = ldap_version
38 LDAP_SERVER_PORT)
29 if use_ldaps:
39
30 port = port or 689
40 BASE_DN = "ou=people,dc=server,dc=com"
31 self.LDAP_USE_LDAPS = use_ldaps
41 AUTH_DN = "uid=%s,%s"
32 self.LDAP_SERVER_ADDRESS = server
33 self.LDAP_SERVER_PORT = port
42
34
43 def authenticate_ldap(username, password):
35 #USE FOR READ ONLY BIND TO LDAP SERVER
44 """Authenticate a user via LDAP and return his/her LDAP properties.
36 self.LDAP_BIND_DN = bind_dn
37 self.LDAP_BIND_PASS = bind_pass
45
38
46 Raises AuthenticationError if the credentials are rejected, or
39 ldap_server_type = 'ldap'
47 EnvironmentError if the LDAP server can't be reached.
40 if self.LDAP_USE_LDAPS:ldap_server_type = ldap_server_type + 's'
48 """
41 self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
49 try:
42 self.LDAP_SERVER_ADDRESS,
50 import ldap
43 self.LDAP_SERVER_PORT)
51 except ImportError:
44
52 raise Exception('Could not import ldap make sure You install python-ldap')
45 self.BASE_DN = base_dn
46 self.AUTH_DN = "uid=%s,%s"
53
47
54 from rhodecode.lib.helpers import chop_at
48 def authenticate_ldap(self, username, password):
55
49 """Authenticate a user via LDAP and return his/her LDAP properties.
56 uid = chop_at(username, "@%s" % LDAP_SERVER_ADDRESS)
50
57 dn = AUTH_DN % (uid, BASE_DN)
51 Raises AuthenticationError if the credentials are rejected, or
58 log.debug("Authenticating %r at %s", dn, LDAP_SERVER)
52 EnvironmentError if the LDAP server can't be reached.
59 if "," in username:
60 raise UsernameError("invalid character in username: ,")
61 try:
62 #ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, '/etc/openldap/cacerts')
63 server = ldap.initialize(LDAP_SERVER)
64 server.protocol = ldap.VERSION3
65
66 if LDAP_BIND_DN and LDAP_BIND_PASS:
67 server.simple_bind_s(AUTH_DN % (LDAP_BIND_DN,
68 LDAP_BIND_PASS),
69 password)
70
53
71 server.simple_bind_s(dn, password)
54 :param username: username
72 properties = server.search_s(dn, ldap.SCOPE_SUBTREE)
55 :param password: password
73 if not properties:
56 """
74 raise ldap.NO_SUCH_OBJECT()
57
75 except ldap.NO_SUCH_OBJECT, e:
58 from rhodecode.lib.helpers import chop_at
76 log.debug("LDAP says no such user '%s' (%s)", uid, username)
59
77 raise UsernameError()
60 uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
78 except ldap.INVALID_CREDENTIALS, e:
61 dn = self.AUTH_DN % (uid, self.BASE_DN)
79 log.debug("LDAP rejected password for user '%s' (%s)", uid, username)
62 log.debug("Authenticating %r at %s", dn, self.LDAP_SERVER)
80 raise PasswordError()
63 if "," in username:
81 except ldap.SERVER_DOWN, e:
64 raise UsernameError("invalid character in username: ,")
82 raise EnvironmentError("can't access authentication server")
65 try:
83 return properties
66 ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, '/etc/openldap/cacerts')
67 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
68 server = ldap.initialize(self.LDAP_SERVER)
69 if self.ldap_version == 2:
70 server.protocol = ldap.VERSION2
71 else:
72 server.protocol = ldap.VERSION3
84
73
74 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
75 server.simple_bind_s(self.AUTH_DN % (self.LDAP_BIND_DN,
76 self.BASE_DN),
77 self.LDAP_BIND_PASS)
85
78
86 print authenticate_ldap('test', 'test')
79 server.simple_bind_s(dn, password)
80 properties = server.search_s(dn, ldap.SCOPE_SUBTREE)
81 if not properties:
82 raise ldap.NO_SUCH_OBJECT()
83 except ldap.NO_SUCH_OBJECT, e:
84 log.debug("LDAP says no such user '%s' (%s)", uid, username)
85 raise UsernameError()
86 except ldap.INVALID_CREDENTIALS, e:
87 log.debug("LDAP rejected password for user '%s' (%s)", uid, username)
88 raise PasswordError()
89 except ldap.SERVER_DOWN, e:
90 raise ConnectionError("LDAP can't access authentication server")
91
92 return properties[0]
93
@@ -1,361 +1,388 b''
1 """ this is forms validation classes
1 """ this is forms validation classes
2 http://formencode.org/module-formencode.validators.html
2 http://formencode.org/module-formencode.validators.html
3 for list off all availible validators
3 for list off all availible validators
4
4
5 we can create our own validators
5 we can create our own validators
6
6
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 pre_validators [] These validators will be applied before the schema
8 pre_validators [] These validators will be applied before the schema
9 chained_validators [] These validators will be applied after the schema
9 chained_validators [] These validators will be applied after the schema
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
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 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
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.
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 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
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 <name> = formencode.validators.<name of validator>
16 <name> = formencode.validators.<name of validator>
17 <name> must equal form name
17 <name> must equal form name
18 list=[1,2,3,4,5]
18 list=[1,2,3,4,5]
19 for SELECT use formencode.All(OneOf(list), Int())
19 for SELECT use formencode.All(OneOf(list), Int())
20
20
21 """
21 """
22 from formencode import All
22 from formencode import All
23 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
23 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
24 Email, Bool, StringBoolean
24 Email, Bool, StringBoolean
25 from pylons import session
25 from pylons import session
26 from pylons.i18n.translation import _
26 from pylons.i18n.translation import _
27 from rhodecode.lib.auth import authfunc, get_crypt_password
27 from rhodecode.lib.auth import authfunc, get_crypt_password
28 from rhodecode.lib.exceptions import LdapImportError
28 from rhodecode.model import meta
29 from rhodecode.model import meta
29 from rhodecode.model.user import UserModel
30 from rhodecode.model.user import UserModel
30 from rhodecode.model.repo import RepoModel
31 from rhodecode.model.repo import RepoModel
31 from rhodecode.model.db import User
32 from rhodecode.model.db import User
32 from webhelpers.pylonslib.secure_form import authentication_token
33 from webhelpers.pylonslib.secure_form import authentication_token
33 from vcs import BACKENDS
34 from vcs import BACKENDS
34 import formencode
35 import formencode
35 import logging
36 import logging
36 import os
37 import os
37 import rhodecode.lib.helpers as h
38 import rhodecode.lib.helpers as h
38
39
39 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
40
41
41 #this is needed to translate the messages using _() in validators
42 #this is needed to translate the messages using _() in validators
42 class State_obj(object):
43 class State_obj(object):
43 _ = staticmethod(_)
44 _ = staticmethod(_)
44
45
45 #===============================================================================
46 #===============================================================================
46 # VALIDATORS
47 # VALIDATORS
47 #===============================================================================
48 #===============================================================================
48 class ValidAuthToken(formencode.validators.FancyValidator):
49 class ValidAuthToken(formencode.validators.FancyValidator):
49 messages = {'invalid_token':_('Token mismatch')}
50 messages = {'invalid_token':_('Token mismatch')}
50
51
51 def validate_python(self, value, state):
52 def validate_python(self, value, state):
52
53
53 if value != authentication_token():
54 if value != authentication_token():
54 raise formencode.Invalid(self.message('invalid_token', state,
55 raise formencode.Invalid(self.message('invalid_token', state,
55 search_number=value), value, state)
56 search_number=value), value, state)
56
57
57 def ValidUsername(edit, old_data):
58 def ValidUsername(edit, old_data):
58 class _ValidUsername(formencode.validators.FancyValidator):
59 class _ValidUsername(formencode.validators.FancyValidator):
59
60
60 def validate_python(self, value, state):
61 def validate_python(self, value, state):
61 if value in ['default', 'new_user']:
62 if value in ['default', 'new_user']:
62 raise formencode.Invalid(_('Invalid username'), value, state)
63 raise formencode.Invalid(_('Invalid username'), value, state)
63 #check if user is unique
64 #check if user is unique
64 old_un = None
65 old_un = None
65 if edit:
66 if edit:
66 old_un = UserModel().get(old_data.get('user_id')).username
67 old_un = UserModel().get(old_data.get('user_id')).username
67
68
68 if old_un != value or not edit:
69 if old_un != value or not edit:
69 if UserModel().get_by_username(value, cache=False):
70 if UserModel().get_by_username(value, cache=False):
70 raise formencode.Invalid(_('This username already exists') ,
71 raise formencode.Invalid(_('This username already exists') ,
71 value, state)
72 value, state)
72
73
73 return _ValidUsername
74 return _ValidUsername
74
75
75 class ValidPassword(formencode.validators.FancyValidator):
76 class ValidPassword(formencode.validators.FancyValidator):
76
77
77 def to_python(self, value, state):
78 def to_python(self, value, state):
78 if value:
79 if value:
79 return get_crypt_password(value)
80 return get_crypt_password(value)
80
81
81 class ValidAuth(formencode.validators.FancyValidator):
82 class ValidAuth(formencode.validators.FancyValidator):
82 messages = {
83 messages = {
83 'invalid_password':_('invalid password'),
84 'invalid_password':_('invalid password'),
84 'invalid_login':_('invalid user name'),
85 'invalid_login':_('invalid user name'),
85 'disabled_account':_('Your acccount is disabled')
86 'disabled_account':_('Your account is disabled')
86
87
87 }
88 }
88 #error mapping
89 #error mapping
89 e_dict = {'username':messages['invalid_login'],
90 e_dict = {'username':messages['invalid_login'],
90 'password':messages['invalid_password']}
91 'password':messages['invalid_password']}
91 e_dict_disable = {'username':messages['disabled_account']}
92 e_dict_disable = {'username':messages['disabled_account']}
92
93
93 def validate_python(self, value, state):
94 def validate_python(self, value, state):
94 password = value['password']
95 password = value['password']
95 username = value['username']
96 username = value['username']
96 user = UserModel().get_by_username(username)
97 user = UserModel().get_by_username(username)
97
98
98 if authfunc(None, username, password):
99 if authfunc(None, username, password):
99 return value
100 return value
100 else:
101 else:
101 if user and user.active is False:
102 if user and user.active is False:
102 log.warning('user %s is disabled', username)
103 log.warning('user %s is disabled', username)
103 raise formencode.Invalid(self.message('disabled_account',
104 raise formencode.Invalid(self.message('disabled_account',
104 state=State_obj),
105 state=State_obj),
105 value, state,
106 value, state,
106 error_dict=self.e_dict_disable)
107 error_dict=self.e_dict_disable)
107 else:
108 else:
108 log.warning('user %s not authenticated', username)
109 log.warning('user %s not authenticated', username)
109 raise formencode.Invalid(self.message('invalid_password',
110 raise formencode.Invalid(self.message('invalid_password',
110 state=State_obj), value, state,
111 state=State_obj), value, state,
111 error_dict=self.e_dict)
112 error_dict=self.e_dict)
112
113
113 class ValidRepoUser(formencode.validators.FancyValidator):
114 class ValidRepoUser(formencode.validators.FancyValidator):
114
115
115 def to_python(self, value, state):
116 def to_python(self, value, state):
116 sa = meta.Session()
117 sa = meta.Session()
117 try:
118 try:
118 self.user_db = sa.query(User)\
119 self.user_db = sa.query(User)\
119 .filter(User.active == True)\
120 .filter(User.active == True)\
120 .filter(User.username == value).one()
121 .filter(User.username == value).one()
121 except Exception:
122 except Exception:
122 raise formencode.Invalid(_('This username is not valid'),
123 raise formencode.Invalid(_('This username is not valid'),
123 value, state)
124 value, state)
124 finally:
125 finally:
125 meta.Session.remove()
126 meta.Session.remove()
126
127
127 return self.user_db.user_id
128 return self.user_db.user_id
128
129
129 def ValidRepoName(edit, old_data):
130 def ValidRepoName(edit, old_data):
130 class _ValidRepoName(formencode.validators.FancyValidator):
131 class _ValidRepoName(formencode.validators.FancyValidator):
131
132
132 def to_python(self, value, state):
133 def to_python(self, value, state):
133 slug = h.repo_name_slug(value)
134 slug = h.repo_name_slug(value)
134 if slug in ['_admin']:
135 if slug in ['_admin']:
135 raise formencode.Invalid(_('This repository name is disallowed'),
136 raise formencode.Invalid(_('This repository name is disallowed'),
136 value, state)
137 value, state)
137 if old_data.get('repo_name') != value or not edit:
138 if old_data.get('repo_name') != value or not edit:
138 if RepoModel().get(slug, cache=False):
139 if RepoModel().get(slug, cache=False):
139 raise formencode.Invalid(_('This repository already exists') ,
140 raise formencode.Invalid(_('This repository already exists') ,
140 value, state)
141 value, state)
141 return slug
142 return slug
142
143
143
144
144 return _ValidRepoName
145 return _ValidRepoName
145
146
146 def ValidForkType(old_data):
147 def ValidForkType(old_data):
147 class _ValidForkType(formencode.validators.FancyValidator):
148 class _ValidForkType(formencode.validators.FancyValidator):
148
149
149 def to_python(self, value, state):
150 def to_python(self, value, state):
150 if old_data['repo_type'] != value:
151 if old_data['repo_type'] != value:
151 raise formencode.Invalid(_('Fork have to be the same type as original'), value, state)
152 raise formencode.Invalid(_('Fork have to be the same type as original'), value, state)
152 return value
153 return value
153 return _ValidForkType
154 return _ValidForkType
154
155
155 class ValidPerms(formencode.validators.FancyValidator):
156 class ValidPerms(formencode.validators.FancyValidator):
156 messages = {'perm_new_user_name':_('This username is not valid')}
157 messages = {'perm_new_user_name':_('This username is not valid')}
157
158
158 def to_python(self, value, state):
159 def to_python(self, value, state):
159 perms_update = []
160 perms_update = []
160 perms_new = []
161 perms_new = []
161 #build a list of permission to update and new permission to create
162 #build a list of permission to update and new permission to create
162 for k, v in value.items():
163 for k, v in value.items():
163 if k.startswith('perm_'):
164 if k.startswith('perm_'):
164 if k.startswith('perm_new_user'):
165 if k.startswith('perm_new_user'):
165 new_perm = value.get('perm_new_user', False)
166 new_perm = value.get('perm_new_user', False)
166 new_user = value.get('perm_new_user_name', False)
167 new_user = value.get('perm_new_user_name', False)
167 if new_user and new_perm:
168 if new_user and new_perm:
168 if (new_user, new_perm) not in perms_new:
169 if (new_user, new_perm) not in perms_new:
169 perms_new.append((new_user, new_perm))
170 perms_new.append((new_user, new_perm))
170 else:
171 else:
171 usr = k[5:]
172 usr = k[5:]
172 if usr == 'default':
173 if usr == 'default':
173 if value['private']:
174 if value['private']:
174 #set none for default when updating to private repo
175 #set none for default when updating to private repo
175 v = 'repository.none'
176 v = 'repository.none'
176 perms_update.append((usr, v))
177 perms_update.append((usr, v))
177 value['perms_updates'] = perms_update
178 value['perms_updates'] = perms_update
178 value['perms_new'] = perms_new
179 value['perms_new'] = perms_new
179 sa = meta.Session
180 sa = meta.Session
180 for k, v in perms_new:
181 for k, v in perms_new:
181 try:
182 try:
182 self.user_db = sa.query(User)\
183 self.user_db = sa.query(User)\
183 .filter(User.active == True)\
184 .filter(User.active == True)\
184 .filter(User.username == k).one()
185 .filter(User.username == k).one()
185 except Exception:
186 except Exception:
186 msg = self.message('perm_new_user_name',
187 msg = self.message('perm_new_user_name',
187 state=State_obj)
188 state=State_obj)
188 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
189 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
189 return value
190 return value
190
191
191 class ValidSettings(formencode.validators.FancyValidator):
192 class ValidSettings(formencode.validators.FancyValidator):
192
193
193 def to_python(self, value, state):
194 def to_python(self, value, state):
194 #settings form can't edit user
195 #settings form can't edit user
195 if value.has_key('user'):
196 if value.has_key('user'):
196 del['value']['user']
197 del['value']['user']
197
198
198 return value
199 return value
199
200
200 class ValidPath(formencode.validators.FancyValidator):
201 class ValidPath(formencode.validators.FancyValidator):
201 def to_python(self, value, state):
202 def to_python(self, value, state):
202
203
203 if not os.path.isdir(value):
204 if not os.path.isdir(value):
204 msg = _('This is not a valid path')
205 msg = _('This is not a valid path')
205 raise formencode.Invalid(msg, value, state,
206 raise formencode.Invalid(msg, value, state,
206 error_dict={'paths_root_path':msg})
207 error_dict={'paths_root_path':msg})
207 return value
208 return value
208
209
209 def UniqSystemEmail(old_data):
210 def UniqSystemEmail(old_data):
210 class _UniqSystemEmail(formencode.validators.FancyValidator):
211 class _UniqSystemEmail(formencode.validators.FancyValidator):
211 def to_python(self, value, state):
212 def to_python(self, value, state):
212 if old_data.get('email') != value:
213 if old_data.get('email') != value:
213 sa = meta.Session()
214 sa = meta.Session()
214 try:
215 try:
215 user = sa.query(User).filter(User.email == value).scalar()
216 user = sa.query(User).filter(User.email == value).scalar()
216 if user:
217 if user:
217 raise formencode.Invalid(_("That e-mail address is already taken") ,
218 raise formencode.Invalid(_("That e-mail address is already taken") ,
218 value, state)
219 value, state)
219 finally:
220 finally:
220 meta.Session.remove()
221 meta.Session.remove()
221
222
222 return value
223 return value
223
224
224 return _UniqSystemEmail
225 return _UniqSystemEmail
225
226
226 class ValidSystemEmail(formencode.validators.FancyValidator):
227 class ValidSystemEmail(formencode.validators.FancyValidator):
227 def to_python(self, value, state):
228 def to_python(self, value, state):
228 sa = meta.Session
229 sa = meta.Session
229 try:
230 try:
230 user = sa.query(User).filter(User.email == value).scalar()
231 user = sa.query(User).filter(User.email == value).scalar()
231 if user is None:
232 if user is None:
232 raise formencode.Invalid(_("That e-mail address doesn't exist.") ,
233 raise formencode.Invalid(_("That e-mail address doesn't exist.") ,
233 value, state)
234 value, state)
234 finally:
235 finally:
235 meta.Session.remove()
236 meta.Session.remove()
236
237
237 return value
238 return value
238
239
240 class LdapLibValidator(formencode.validators.FancyValidator):
241
242 def to_python(self, value, state):
243
244 try:
245 import ldap
246 except ImportError:
247 raise LdapImportError
248 return value
249
239 #===============================================================================
250 #===============================================================================
240 # FORMS
251 # FORMS
241 #===============================================================================
252 #===============================================================================
242 class LoginForm(formencode.Schema):
253 class LoginForm(formencode.Schema):
243 allow_extra_fields = True
254 allow_extra_fields = True
244 filter_extra_fields = True
255 filter_extra_fields = True
245 username = UnicodeString(
256 username = UnicodeString(
246 strip=True,
257 strip=True,
247 min=1,
258 min=1,
248 not_empty=True,
259 not_empty=True,
249 messages={
260 messages={
250 'empty':_('Please enter a login'),
261 'empty':_('Please enter a login'),
251 'tooShort':_('Enter a value %(min)i characters long or more')}
262 'tooShort':_('Enter a value %(min)i characters long or more')}
252 )
263 )
253
264
254 password = UnicodeString(
265 password = UnicodeString(
255 strip=True,
266 strip=True,
256 min=6,
267 min=6,
257 not_empty=True,
268 not_empty=True,
258 messages={
269 messages={
259 'empty':_('Please enter a password'),
270 'empty':_('Please enter a password'),
260 'tooShort':_('Enter %(min)i characters or more')}
271 'tooShort':_('Enter %(min)i characters or more')}
261 )
272 )
262
273
263
274
264 #chained validators have access to all data
275 #chained validators have access to all data
265 chained_validators = [ValidAuth]
276 chained_validators = [ValidAuth]
266
277
267 def UserForm(edit=False, old_data={}):
278 def UserForm(edit=False, old_data={}):
268 class _UserForm(formencode.Schema):
279 class _UserForm(formencode.Schema):
269 allow_extra_fields = True
280 allow_extra_fields = True
270 filter_extra_fields = True
281 filter_extra_fields = True
271 username = All(UnicodeString(strip=True, min=1, not_empty=True), ValidUsername(edit, old_data))
282 username = All(UnicodeString(strip=True, min=1, not_empty=True), ValidUsername(edit, old_data))
272 if edit:
283 if edit:
273 new_password = All(UnicodeString(strip=True, min=6, not_empty=False), ValidPassword)
284 new_password = All(UnicodeString(strip=True, min=6, not_empty=False), ValidPassword)
274 admin = StringBoolean(if_missing=False)
285 admin = StringBoolean(if_missing=False)
275 else:
286 else:
276 password = All(UnicodeString(strip=True, min=6, not_empty=True), ValidPassword)
287 password = All(UnicodeString(strip=True, min=6, not_empty=True), ValidPassword)
277 active = StringBoolean(if_missing=False)
288 active = StringBoolean(if_missing=False)
278 name = UnicodeString(strip=True, min=1, not_empty=True)
289 name = UnicodeString(strip=True, min=1, not_empty=True)
279 lastname = UnicodeString(strip=True, min=1, not_empty=True)
290 lastname = UnicodeString(strip=True, min=1, not_empty=True)
280 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
291 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
281
292
282 return _UserForm
293 return _UserForm
283
294
284 RegisterForm = UserForm
295 RegisterForm = UserForm
285
296
286 def PasswordResetForm():
297 def PasswordResetForm():
287 class _PasswordResetForm(formencode.Schema):
298 class _PasswordResetForm(formencode.Schema):
288 allow_extra_fields = True
299 allow_extra_fields = True
289 filter_extra_fields = True
300 filter_extra_fields = True
290 email = All(ValidSystemEmail(), Email(not_empty=True))
301 email = All(ValidSystemEmail(), Email(not_empty=True))
291 return _PasswordResetForm
302 return _PasswordResetForm
292
303
293 def RepoForm(edit=False, old_data={}):
304 def RepoForm(edit=False, old_data={}):
294 class _RepoForm(formencode.Schema):
305 class _RepoForm(formencode.Schema):
295 allow_extra_fields = True
306 allow_extra_fields = True
296 filter_extra_fields = False
307 filter_extra_fields = False
297 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
308 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
298 description = UnicodeString(strip=True, min=1, not_empty=True)
309 description = UnicodeString(strip=True, min=1, not_empty=True)
299 private = StringBoolean(if_missing=False)
310 private = StringBoolean(if_missing=False)
300 repo_type = OneOf(BACKENDS.keys())
311 repo_type = OneOf(BACKENDS.keys())
301 if edit:
312 if edit:
302 user = All(Int(not_empty=True), ValidRepoUser)
313 user = All(Int(not_empty=True), ValidRepoUser)
303
314
304 chained_validators = [ValidPerms]
315 chained_validators = [ValidPerms]
305 return _RepoForm
316 return _RepoForm
306
317
307 def RepoForkForm(edit=False, old_data={}):
318 def RepoForkForm(edit=False, old_data={}):
308 class _RepoForkForm(formencode.Schema):
319 class _RepoForkForm(formencode.Schema):
309 allow_extra_fields = True
320 allow_extra_fields = True
310 filter_extra_fields = False
321 filter_extra_fields = False
311 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
322 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
312 description = UnicodeString(strip=True, min=1, not_empty=True)
323 description = UnicodeString(strip=True, min=1, not_empty=True)
313 private = StringBoolean(if_missing=False)
324 private = StringBoolean(if_missing=False)
314 repo_type = All(ValidForkType(old_data), OneOf(BACKENDS.keys()))
325 repo_type = All(ValidForkType(old_data), OneOf(BACKENDS.keys()))
315 return _RepoForkForm
326 return _RepoForkForm
316
327
317 def RepoSettingsForm(edit=False, old_data={}):
328 def RepoSettingsForm(edit=False, old_data={}):
318 class _RepoForm(formencode.Schema):
329 class _RepoForm(formencode.Schema):
319 allow_extra_fields = True
330 allow_extra_fields = True
320 filter_extra_fields = False
331 filter_extra_fields = False
321 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
332 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
322 description = UnicodeString(strip=True, min=1, not_empty=True)
333 description = UnicodeString(strip=True, min=1, not_empty=True)
323 private = StringBoolean(if_missing=False)
334 private = StringBoolean(if_missing=False)
324
335
325 chained_validators = [ValidPerms, ValidSettings]
336 chained_validators = [ValidPerms, ValidSettings]
326 return _RepoForm
337 return _RepoForm
327
338
328
339
329 def ApplicationSettingsForm():
340 def ApplicationSettingsForm():
330 class _ApplicationSettingsForm(formencode.Schema):
341 class _ApplicationSettingsForm(formencode.Schema):
331 allow_extra_fields = True
342 allow_extra_fields = True
332 filter_extra_fields = False
343 filter_extra_fields = False
333 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
344 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
334 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
345 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
335
346
336 return _ApplicationSettingsForm
347 return _ApplicationSettingsForm
337
348
338 def ApplicationUiSettingsForm():
349 def ApplicationUiSettingsForm():
339 class _ApplicationUiSettingsForm(formencode.Schema):
350 class _ApplicationUiSettingsForm(formencode.Schema):
340 allow_extra_fields = True
351 allow_extra_fields = True
341 filter_extra_fields = False
352 filter_extra_fields = False
342 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
353 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
343 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
354 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
344 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
355 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
345 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
356 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
346 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
357 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
347 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
358 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
348
359
349 return _ApplicationUiSettingsForm
360 return _ApplicationUiSettingsForm
350
361
351 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
362 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
352 class _DefaultPermissionsForm(formencode.Schema):
363 class _DefaultPermissionsForm(formencode.Schema):
353 allow_extra_fields = True
364 allow_extra_fields = True
354 filter_extra_fields = True
365 filter_extra_fields = True
355 overwrite_default = OneOf(['true', 'false'], if_missing='false')
366 overwrite_default = StringBoolean(if_missing=False)
356 anonymous = OneOf(['True', 'False'], if_missing=False)
367 anonymous = OneOf(['True', 'False'], if_missing=False)
357 default_perm = OneOf(perms_choices)
368 default_perm = OneOf(perms_choices)
358 default_register = OneOf(register_choices)
369 default_register = OneOf(register_choices)
359 default_create = OneOf(create_choices)
370 default_create = OneOf(create_choices)
360
371
361 return _DefaultPermissionsForm
372 return _DefaultPermissionsForm
373
374
375 def LdapSettingsForm():
376 class _LdapSettingsForm(formencode.Schema):
377 allow_extra_fields = True
378 filter_extra_fields = True
379 pre_validators = [LdapLibValidator]
380 ldap_active = StringBoolean(if_missing=False)
381 ldap_host = UnicodeString(strip=True,)
382 ldap_port = Number(strip=True,)
383 ldap_ldaps = StringBoolean(if_missing=False)
384 ldap_dn_user = UnicodeString(strip=True,)
385 ldap_dn_pass = UnicodeString(strip=True,)
386 ldap_base_dn = UnicodeString(strip=True,)
387
388 return _LdapSettingsForm
@@ -1,103 +1,98 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Model for permissions
3 # Model for permissions
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
5
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
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
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on Aug 20, 2010
21 Created on Aug 20, 2010
22 Model for permissions
22 Model for permissions
23 @author: marcink
23 @author: marcink
24 """
24 """
25
25
26 from rhodecode.model.db import User, Permission, UserToPerm, RepoToPerm
26 from rhodecode.model.db import User, Permission, UserToPerm, RepoToPerm
27 from rhodecode.model.caching_query import FromCache
27 from rhodecode.model.caching_query import FromCache
28 from rhodecode.model.meta import Session
28 from rhodecode.model.meta import Session
29 import logging
29 import logging
30 import traceback
30 import traceback
31 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
32
32
33
33
34 class PermissionModel(object):
34 class PermissionModel(object):
35
35
36 def __init__(self):
36 def __init__(self):
37 self.sa = Session()
37 self.sa = Session()
38
38
39 def get_permission(self, permission_id, cache=False):
39 def get_permission(self, permission_id, cache=False):
40 perm = self.sa.query(Permission)
40 perm = self.sa.query(Permission)
41 if cache:
41 if cache:
42 perm = perm.options(FromCache("sql_cache_short",
42 perm = perm.options(FromCache("sql_cache_short",
43 "get_permission_%s" % permission_id))
43 "get_permission_%s" % permission_id))
44 return perm.get(permission_id)
44 return perm.get(permission_id)
45
45
46 def get_permission_by_name(self, name, cache=False):
46 def get_permission_by_name(self, name, cache=False):
47 perm = self.sa.query(Permission)\
47 perm = self.sa.query(Permission)\
48 .filter(Permission.permission_name == name)
48 .filter(Permission.permission_name == name)
49 if cache:
49 if cache:
50 perm = perm.options(FromCache("sql_cache_short",
50 perm = perm.options(FromCache("sql_cache_short",
51 "get_permission_%s" % name))
51 "get_permission_%s" % name))
52 return perm.scalar()
52 return perm.scalar()
53
53
54 def update(self, form_result):
54 def update(self, form_result):
55 perm_user = self.sa.query(User)\
55 perm_user = self.sa.query(User)\
56 .filter(User.username == form_result['perm_user_name']).scalar()
56 .filter(User.username == form_result['perm_user_name']).scalar()
57 u2p = self.sa.query(UserToPerm).filter(UserToPerm.user == perm_user).all()
57 u2p = self.sa.query(UserToPerm).filter(UserToPerm.user == perm_user).all()
58 if len(u2p) != 3:
58 if len(u2p) != 3:
59 raise Exception('Defined: %s should be 3 permissions for default'
59 raise Exception('Defined: %s should be 3 permissions for default'
60 ' user. This should not happen please verify'
60 ' user. This should not happen please verify'
61 ' your database' % len(u2p))
61 ' your database' % len(u2p))
62
62
63 try:
63 try:
64 #stage 1 change defaults
64 #stage 1 change defaults
65 for p in u2p:
65 for p in u2p:
66 if p.permission.permission_name.startswith('repository.'):
66 if p.permission.permission_name.startswith('repository.'):
67 p.permission = self.get_permission_by_name(
67 p.permission = self.get_permission_by_name(
68 form_result['default_perm'])
68 form_result['default_perm'])
69 self.sa.add(p)
69 self.sa.add(p)
70
70
71 if p.permission.permission_name.startswith('hg.register.'):
71 if p.permission.permission_name.startswith('hg.register.'):
72 p.permission = self.get_permission_by_name(
72 p.permission = self.get_permission_by_name(
73 form_result['default_register'])
73 form_result['default_register'])
74 self.sa.add(p)
74 self.sa.add(p)
75
75
76 if p.permission.permission_name.startswith('hg.create.'):
76 if p.permission.permission_name.startswith('hg.create.'):
77 p.permission = self.get_permission_by_name(
77 p.permission = self.get_permission_by_name(
78 form_result['default_create'])
78 form_result['default_create'])
79 self.sa.add(p)
79 self.sa.add(p)
80 #stage 2 update all default permissions for repos if checked
80 #stage 2 update all default permissions for repos if checked
81 if form_result['overwrite_default'] == 'true':
81 if form_result['overwrite_default'] == 'true':
82 for r2p in self.sa.query(RepoToPerm)\
82 for r2p in self.sa.query(RepoToPerm)\
83 .filter(RepoToPerm.user == perm_user).all():
83 .filter(RepoToPerm.user == perm_user).all():
84 r2p.permission = self.get_permission_by_name(
84 r2p.permission = self.get_permission_by_name(
85 form_result['default_perm'])
85 form_result['default_perm'])
86 self.sa.add(r2p)
86 self.sa.add(r2p)
87
87
88 #stage 3 set anonymous access
88 #stage 3 set anonymous access
89 if perm_user.username == 'default':
89 if perm_user.username == 'default':
90 perm_user.active = bool(form_result['anonymous'])
90 perm_user.active = bool(form_result['anonymous'])
91 self.sa.add(perm_user)
91 self.sa.add(perm_user)
92
92
93
93
94 self.sa.commit()
94 self.sa.commit()
95 except:
95 except:
96 log.error(traceback.format_exc())
96 log.error(traceback.format_exc())
97 self.sa.rollback()
97 self.sa.rollback()
98 raise
98 raise
99
100
101
102
103
@@ -1,68 +1,79 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Model for RhodeCode settings
3 # Model for RhodeCode settings
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
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
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on Nov 17, 2010
21 Created on Nov 17, 2010
22 Model for RhodeCode
22 Model for RhodeCode
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from rhodecode.lib import helpers as h
25 from rhodecode.lib import helpers as h
26 from rhodecode.model import meta
26 from rhodecode.model import meta
27 from rhodecode.model.caching_query import FromCache
27 from rhodecode.model.caching_query import FromCache
28 from rhodecode.model.db import RhodeCodeSettings
28 from rhodecode.model.db import RhodeCodeSettings
29 from sqlalchemy.orm import joinedload
29 from sqlalchemy.orm import joinedload
30 from sqlalchemy.orm.session import make_transient
30 from sqlalchemy.orm.session import make_transient
31 import logging
31 import logging
32
32
33 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
34
34
35 class SettingsModel(object):
35 class SettingsModel(object):
36 """
36 """
37 Settings model
37 Settings model
38 """
38 """
39
39
40 def __init__(self):
40 def __init__(self):
41 self.sa = meta.Session()
41 self.sa = meta.Session()
42
42
43
43
44 def get(self, settings_key, cache=False):
44 def get(self, settings_key, cache=False):
45 r = self.sa.query(RhodeCodeSettings)\
45 r = self.sa.query(RhodeCodeSettings)\
46 .filter(RhodeCodeSettings.app_settings_name == settings_key).scalar()
46 .filter(RhodeCodeSettings.app_settings_name == settings_key).scalar()
47 if cache:
47 if cache:
48 r = r.options(FromCache("sql_cache_short",
48 r = r.options(FromCache("sql_cache_short",
49 "get_setting_%s" % settings_key))
49 "get_setting_%s" % settings_key))
50 return r
50 return r
51
51
52
52
53 def get_ldap_settings(self):
53 def get_ldap_settings(self):
54 """
55 Returns ldap settings from database
56 :returns:
57 ldap_active
58 ldap_host
59 ldap_port
60 ldap_ldaps
61 ldap_dn_user
62 ldap_dn_pass
63 ldap_base_dn
64 """
54
65
55 r = self.sa.query(RhodeCodeSettings)\
66 r = self.sa.query(RhodeCodeSettings)\
56 .filter(RhodeCodeSettings.app_settings_name\
67 .filter(RhodeCodeSettings.app_settings_name\
57 .startswith('ldap_'))\
68 .startswith('ldap_'))\
58 .all()
69 .all()
59
70
60 fd = {}
71 fd = {}
61
72
62 for row in r:
73 for row in r:
63 v = row.app_settings_value
74 v = row.app_settings_value
64 if v in ['0', '1']:
75 if v in ['0', '1']:
65 v = v == '1'
76 v = v == '1'
66 fd.update({row.app_settings_name:v})
77 fd.update({row.app_settings_name:v})
67
78
68 return fd
79 return fd
@@ -1,177 +1,207 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Model for users
3 # Model for users
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
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
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 9, 2010
21 Created on April 9, 2010
22 Model for users
22 Model for users
23 :author: marcink
23 :author: marcink
24 """
24 """
25
25
26 from pylons.i18n.translation import _
26 from pylons.i18n.translation import _
27 from rhodecode.model.caching_query import FromCache
27 from rhodecode.model.caching_query import FromCache
28 from rhodecode.model.db import User
28 from rhodecode.model.db import User
29 from rhodecode.model.meta import Session
29 from rhodecode.model.meta import Session
30 import logging
30 import logging
31 import traceback
31 import traceback
32
32
33 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
34
34
35 class DefaultUserException(Exception):pass
35 class DefaultUserException(Exception):pass
36
36
37 class UserModel(object):
37 class UserModel(object):
38
38
39 def __init__(self):
39 def __init__(self):
40 self.sa = Session()
40 self.sa = Session()
41
41
42 def get(self, user_id, cache=False):
42 def get(self, user_id, cache=False):
43 user = self.sa.query(User)
43 user = self.sa.query(User)
44 if cache:
44 if cache:
45 user = user.options(FromCache("sql_cache_short",
45 user = user.options(FromCache("sql_cache_short",
46 "get_user_%s" % user_id))
46 "get_user_%s" % user_id))
47 return user.get(user_id)
47 return user.get(user_id)
48
48
49
49
50 def get_by_username(self, username, cache=False):
50 def get_by_username(self, username, cache=False):
51 user = self.sa.query(User)\
51 user = self.sa.query(User)\
52 .filter(User.username == username)
52 .filter(User.username == username)
53 if cache:
53 if cache:
54 user = user.options(FromCache("sql_cache_short",
54 user = user.options(FromCache("sql_cache_short",
55 "get_user_%s" % username))
55 "get_user_%s" % username))
56 return user.scalar()
56 return user.scalar()
57
57
58 def create(self, form_data):
58 def create(self, form_data):
59 try:
59 try:
60 new_user = User()
60 new_user = User()
61 for k, v in form_data.items():
61 for k, v in form_data.items():
62 setattr(new_user, k, v)
62 setattr(new_user, k, v)
63
63
64 self.sa.add(new_user)
64 self.sa.add(new_user)
65 self.sa.commit()
65 self.sa.commit()
66 except:
66 except:
67 log.error(traceback.format_exc())
67 log.error(traceback.format_exc())
68 self.sa.rollback()
68 self.sa.rollback()
69 raise
69 raise
70
70
71 def create_ldap(self, username, password):
72 """
73 Checks if user is in database, if not creates this user marked
74 as ldap user
75 :param username:
76 :param password:
77 """
78
79 if self.get_by_username(username) is None:
80 try:
81 new_user = User()
82 new_user.username = username
83 new_user.password = password
84 new_user.email = '%s@ldap.server' % username
85 new_user.active = True
86 new_user.is_ldap = True
87 new_user.name = '%s@ldap' % username
88 new_user.lastname = ''
89
90
91 self.sa.add(new_user)
92 self.sa.commit()
93 return True
94 except:
95 log.error(traceback.format_exc())
96 self.sa.rollback()
97 raise
98
99 return False
100
71 def create_registration(self, form_data):
101 def create_registration(self, form_data):
72 from rhodecode.lib.celerylib import tasks, run_task
102 from rhodecode.lib.celerylib import tasks, run_task
73 try:
103 try:
74 new_user = User()
104 new_user = User()
75 for k, v in form_data.items():
105 for k, v in form_data.items():
76 if k != 'admin':
106 if k != 'admin':
77 setattr(new_user, k, v)
107 setattr(new_user, k, v)
78
108
79 self.sa.add(new_user)
109 self.sa.add(new_user)
80 self.sa.commit()
110 self.sa.commit()
81 body = ('New user registration\n'
111 body = ('New user registration\n'
82 'username: %s\n'
112 'username: %s\n'
83 'email: %s\n')
113 'email: %s\n')
84 body = body % (form_data['username'], form_data['email'])
114 body = body % (form_data['username'], form_data['email'])
85
115
86 run_task(tasks.send_email, None,
116 run_task(tasks.send_email, None,
87 _('[RhodeCode] New User registration'),
117 _('[RhodeCode] New User registration'),
88 body)
118 body)
89 except:
119 except:
90 log.error(traceback.format_exc())
120 log.error(traceback.format_exc())
91 self.sa.rollback()
121 self.sa.rollback()
92 raise
122 raise
93
123
94 def update(self, user_id, form_data):
124 def update(self, user_id, form_data):
95 try:
125 try:
96 new_user = self.get(user_id, cache=False)
126 new_user = self.get(user_id, cache=False)
97 if new_user.username == 'default':
127 if new_user.username == 'default':
98 raise DefaultUserException(
128 raise DefaultUserException(
99 _("You can't Edit this user since it's"
129 _("You can't Edit this user since it's"
100 " crucial for entire application"))
130 " crucial for entire application"))
101 for k, v in form_data.items():
131 for k, v in form_data.items():
102 if k == 'new_password' and v != '':
132 if k == 'new_password' and v != '':
103 new_user.password = v
133 new_user.password = v
104 else:
134 else:
105 setattr(new_user, k, v)
135 setattr(new_user, k, v)
106
136
107 self.sa.add(new_user)
137 self.sa.add(new_user)
108 self.sa.commit()
138 self.sa.commit()
109 except:
139 except:
110 log.error(traceback.format_exc())
140 log.error(traceback.format_exc())
111 self.sa.rollback()
141 self.sa.rollback()
112 raise
142 raise
113
143
114 def update_my_account(self, user_id, form_data):
144 def update_my_account(self, user_id, form_data):
115 try:
145 try:
116 new_user = self.get(user_id, cache=False)
146 new_user = self.get(user_id, cache=False)
117 if new_user.username == 'default':
147 if new_user.username == 'default':
118 raise DefaultUserException(
148 raise DefaultUserException(
119 _("You can't Edit this user since it's"
149 _("You can't Edit this user since it's"
120 " crucial for entire application"))
150 " crucial for entire application"))
121 for k, v in form_data.items():
151 for k, v in form_data.items():
122 if k == 'new_password' and v != '':
152 if k == 'new_password' and v != '':
123 new_user.password = v
153 new_user.password = v
124 else:
154 else:
125 if k not in ['admin', 'active']:
155 if k not in ['admin', 'active']:
126 setattr(new_user, k, v)
156 setattr(new_user, k, v)
127
157
128 self.sa.add(new_user)
158 self.sa.add(new_user)
129 self.sa.commit()
159 self.sa.commit()
130 except:
160 except:
131 log.error(traceback.format_exc())
161 log.error(traceback.format_exc())
132 self.sa.rollback()
162 self.sa.rollback()
133 raise
163 raise
134
164
135 def delete(self, user_id):
165 def delete(self, user_id):
136 try:
166 try:
137 user = self.get(user_id, cache=False)
167 user = self.get(user_id, cache=False)
138 if user.username == 'default':
168 if user.username == 'default':
139 raise DefaultUserException(
169 raise DefaultUserException(
140 _("You can't remove this user since it's"
170 _("You can't remove this user since it's"
141 " crucial for entire application"))
171 " crucial for entire application"))
142 self.sa.delete(user)
172 self.sa.delete(user)
143 self.sa.commit()
173 self.sa.commit()
144 except:
174 except:
145 log.error(traceback.format_exc())
175 log.error(traceback.format_exc())
146 self.sa.rollback()
176 self.sa.rollback()
147 raise
177 raise
148
178
149 def reset_password(self, data):
179 def reset_password(self, data):
150 from rhodecode.lib.celerylib import tasks, run_task
180 from rhodecode.lib.celerylib import tasks, run_task
151 run_task(tasks.reset_user_password, data['email'])
181 run_task(tasks.reset_user_password, data['email'])
152
182
153
183
154 def fill_data(self, user):
184 def fill_data(self, user):
155 """
185 """
156 Fills user data with those from database and log out user if not
186 Fills user data with those from database and log out user if not
157 present in database
187 present in database
158 :param user:
188 :param user:
159 """
189 """
160
190
161 if not hasattr(user, 'user_id') or user.user_id is None:
191 if not hasattr(user, 'user_id') or user.user_id is None:
162 raise Exception('passed in user has to have the user_id attribute')
192 raise Exception('passed in user has to have the user_id attribute')
163
193
164
194
165 log.debug('filling auth user data')
195 log.debug('filling auth user data')
166 try:
196 try:
167 dbuser = self.get(user.user_id)
197 dbuser = self.get(user.user_id)
168 user.username = dbuser.username
198 user.username = dbuser.username
169 user.is_admin = dbuser.admin
199 user.is_admin = dbuser.admin
170 user.name = dbuser.name
200 user.name = dbuser.name
171 user.lastname = dbuser.lastname
201 user.lastname = dbuser.lastname
172 user.email = dbuser.email
202 user.email = dbuser.email
173 except:
203 except:
174 log.error(traceback.format_exc())
204 log.error(traceback.format_exc())
175 user.is_authenticated = False
205 user.is_authenticated = False
176
206
177 return user
207 return user
General Comments 0
You need to be logged in to leave comments. Login now