##// END OF EJS Templates
Major rewrite of auth objects. Moved parts of filling user data into user model....
marcink -
r1117:6eb5bb24 beta
parent child Browse files
Show More
@@ -1,176 +1,176 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.users
3 rhodecode.controllers.admin.users
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Users crud controller for pylons
6 Users crud controller for pylons
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29 import traceback
29 import traceback
30 import formencode
30 import formencode
31
31
32 from formencode import htmlfill
32 from formencode import htmlfill
33 from pylons import request, session, tmpl_context as c, url, config
33 from pylons import request, session, tmpl_context as c, url, config
34 from pylons.controllers.util import abort, redirect
34 from pylons.controllers.util import abort, redirect
35 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
36
36
37 from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
37 from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
40 fill_perms
41 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
42
41
43 from rhodecode.model.db import User
42 from rhodecode.model.db import User
44 from rhodecode.model.forms import UserForm
43 from rhodecode.model.forms import UserForm
45 from rhodecode.model.user import UserModel
44 from rhodecode.model.user import UserModel
46
45
47 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
48
47
49 class UsersController(BaseController):
48 class UsersController(BaseController):
50 """REST Controller styled on the Atom Publishing Protocol"""
49 """REST Controller styled on the Atom Publishing Protocol"""
51 # To properly map this controller, ensure your config/routing.py
50 # To properly map this controller, ensure your config/routing.py
52 # file has a resource setup:
51 # file has a resource setup:
53 # map.resource('user', 'users')
52 # map.resource('user', 'users')
54
53
55 @LoginRequired()
54 @LoginRequired()
56 @HasPermissionAllDecorator('hg.admin')
55 @HasPermissionAllDecorator('hg.admin')
57 def __before__(self):
56 def __before__(self):
58 c.admin_user = session.get('admin_user')
57 c.admin_user = session.get('admin_user')
59 c.admin_username = session.get('admin_username')
58 c.admin_username = session.get('admin_username')
60 super(UsersController, self).__before__()
59 super(UsersController, self).__before__()
61 c.available_permissions = config['available_permissions']
60 c.available_permissions = config['available_permissions']
62
61
63 def index(self, format='html'):
62 def index(self, format='html'):
64 """GET /users: All items in the collection"""
63 """GET /users: All items in the collection"""
65 # url('users')
64 # url('users')
66
65
67 c.users_list = self.sa.query(User).all()
66 c.users_list = self.sa.query(User).all()
68 return render('admin/users/users.html')
67 return render('admin/users/users.html')
69
68
70 def create(self):
69 def create(self):
71 """POST /users: Create a new item"""
70 """POST /users: Create a new item"""
72 # url('users')
71 # url('users')
73
72
74 user_model = UserModel()
73 user_model = UserModel()
75 login_form = UserForm()()
74 login_form = UserForm()()
76 try:
75 try:
77 form_result = login_form.to_python(dict(request.POST))
76 form_result = login_form.to_python(dict(request.POST))
78 user_model.create(form_result)
77 user_model.create(form_result)
79 h.flash(_('created user %s') % form_result['username'],
78 h.flash(_('created user %s') % form_result['username'],
80 category='success')
79 category='success')
81 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
80 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
82 except formencode.Invalid, errors:
81 except formencode.Invalid, errors:
83 return htmlfill.render(
82 return htmlfill.render(
84 render('admin/users/user_add.html'),
83 render('admin/users/user_add.html'),
85 defaults=errors.value,
84 defaults=errors.value,
86 errors=errors.error_dict or {},
85 errors=errors.error_dict or {},
87 prefix_error=False,
86 prefix_error=False,
88 encoding="UTF-8")
87 encoding="UTF-8")
89 except Exception:
88 except Exception:
90 log.error(traceback.format_exc())
89 log.error(traceback.format_exc())
91 h.flash(_('error occurred during creation of user %s') \
90 h.flash(_('error occurred during creation of user %s') \
92 % request.POST.get('username'), category='error')
91 % request.POST.get('username'), category='error')
93 return redirect(url('users'))
92 return redirect(url('users'))
94
93
95 def new(self, format='html'):
94 def new(self, format='html'):
96 """GET /users/new: Form to create a new item"""
95 """GET /users/new: Form to create a new item"""
97 # url('new_user')
96 # url('new_user')
98 return render('admin/users/user_add.html')
97 return render('admin/users/user_add.html')
99
98
100 def update(self, id):
99 def update(self, id):
101 """PUT /users/id: Update an existing item"""
100 """PUT /users/id: Update an existing item"""
102 # Forms posted to this method should contain a hidden field:
101 # Forms posted to this method should contain a hidden field:
103 # <input type="hidden" name="_method" value="PUT" />
102 # <input type="hidden" name="_method" value="PUT" />
104 # Or using helpers:
103 # Or using helpers:
105 # h.form(url('user', id=ID),
104 # h.form(url('user', id=ID),
106 # method='put')
105 # method='put')
107 # url('user', id=ID)
106 # url('user', id=ID)
108 user_model = UserModel()
107 user_model = UserModel()
109 c.user = user_model.get(id)
108 c.user = user_model.get(id)
110
109
111 _form = UserForm(edit=True, old_data={'user_id':id,
110 _form = UserForm(edit=True, old_data={'user_id':id,
112 'email':c.user.email})()
111 'email':c.user.email})()
113 form_result = {}
112 form_result = {}
114 try:
113 try:
115 form_result = _form.to_python(dict(request.POST))
114 form_result = _form.to_python(dict(request.POST))
116 user_model.update(id, form_result)
115 user_model.update(id, form_result)
117 h.flash(_('User updated succesfully'), category='success')
116 h.flash(_('User updated succesfully'), category='success')
118
117
119 except formencode.Invalid, errors:
118 except formencode.Invalid, errors:
120 return htmlfill.render(
119 return htmlfill.render(
121 render('admin/users/user_edit.html'),
120 render('admin/users/user_edit.html'),
122 defaults=errors.value,
121 defaults=errors.value,
123 errors=errors.error_dict or {},
122 errors=errors.error_dict or {},
124 prefix_error=False,
123 prefix_error=False,
125 encoding="UTF-8")
124 encoding="UTF-8")
126 except Exception:
125 except Exception:
127 log.error(traceback.format_exc())
126 log.error(traceback.format_exc())
128 h.flash(_('error occurred during update of user %s') \
127 h.flash(_('error occurred during update of user %s') \
129 % form_result.get('username'), category='error')
128 % form_result.get('username'), category='error')
130
129
131 return redirect(url('users'))
130 return redirect(url('users'))
132
131
133 def delete(self, id):
132 def delete(self, id):
134 """DELETE /users/id: Delete an existing item"""
133 """DELETE /users/id: Delete an existing item"""
135 # Forms posted to this method should contain a hidden field:
134 # Forms posted to this method should contain a hidden field:
136 # <input type="hidden" name="_method" value="DELETE" />
135 # <input type="hidden" name="_method" value="DELETE" />
137 # Or using helpers:
136 # Or using helpers:
138 # h.form(url('user', id=ID),
137 # h.form(url('user', id=ID),
139 # method='delete')
138 # method='delete')
140 # url('user', id=ID)
139 # url('user', id=ID)
141 user_model = UserModel()
140 user_model = UserModel()
142 try:
141 try:
143 user_model.delete(id)
142 user_model.delete(id)
144 h.flash(_('successfully deleted user'), category='success')
143 h.flash(_('successfully deleted user'), category='success')
145 except (UserOwnsReposException, DefaultUserException), e:
144 except (UserOwnsReposException, DefaultUserException), e:
146 h.flash(str(e), category='warning')
145 h.flash(str(e), category='warning')
147 except Exception:
146 except Exception:
148 h.flash(_('An error occurred during deletion of user'),
147 h.flash(_('An error occurred during deletion of user'),
149 category='error')
148 category='error')
150 return redirect(url('users'))
149 return redirect(url('users'))
151
150
152 def show(self, id, format='html'):
151 def show(self, id, format='html'):
153 """GET /users/id: Show a specific item"""
152 """GET /users/id: Show a specific item"""
154 # url('user', id=ID)
153 # url('user', id=ID)
155
154
156
155
157 def edit(self, id, format='html'):
156 def edit(self, id, format='html'):
158 """GET /users/id/edit: Form to edit an existing item"""
157 """GET /users/id/edit: Form to edit an existing item"""
159 # url('edit_user', id=ID)
158 # url('edit_user', id=ID)
160 c.user = self.sa.query(User).get(id)
159 user_model = UserModel()
160 c.user = user_model.get(id)
161 if not c.user:
161 if not c.user:
162 return redirect(url('users'))
162 return redirect(url('users'))
163 if c.user.username == 'default':
163 if c.user.username == 'default':
164 h.flash(_("You can't edit this user"), category='warning')
164 h.flash(_("You can't edit this user"), category='warning')
165 return redirect(url('users'))
165 return redirect(url('users'))
166 c.user.permissions = {}
166 c.user.permissions = {}
167 c.granted_permissions = fill_perms(c.user).permissions['global']
167 c.granted_permissions = user_model.fill_perms(c.user).permissions['global']
168
168
169 defaults = c.user.get_dict()
169 defaults = c.user.get_dict()
170
170
171 return htmlfill.render(
171 return htmlfill.render(
172 render('admin/users/user_edit.html'),
172 render('admin/users/user_edit.html'),
173 defaults=defaults,
173 defaults=defaults,
174 encoding="UTF-8",
174 encoding="UTF-8",
175 force_defaults=False
175 force_defaults=False
176 )
176 )
@@ -1,185 +1,184 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.users_groups
3 rhodecode.controllers.admin.users_groups
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Users Groups crud controller for pylons
6 Users Groups crud controller for pylons
7
7
8 :created_on: Jan 25, 2011
8 :created_on: Jan 25, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29 import traceback
29 import traceback
30 import formencode
30 import formencode
31
31
32 from formencode import htmlfill
32 from formencode import htmlfill
33 from pylons import request, session, tmpl_context as c, url, config
33 from pylons import request, session, tmpl_context as c, url, config
34 from pylons.controllers.util import abort, redirect
34 from pylons.controllers.util import abort, redirect
35 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
36
36
37 from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
37 from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
40 fill_perms
41 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
42
41
43 from rhodecode.model.db import User, UsersGroup
42 from rhodecode.model.db import User, UsersGroup
44 from rhodecode.model.forms import UserForm, UsersGroupForm
43 from rhodecode.model.forms import UserForm, UsersGroupForm
45 from rhodecode.model.user import UserModel
44 from rhodecode.model.user import UserModel
46 from rhodecode.model.users_group import UsersGroupModel
45 from rhodecode.model.users_group import UsersGroupModel
47
46
48 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
49
48
50 class UsersGroupsController(BaseController):
49 class UsersGroupsController(BaseController):
51 """REST Controller styled on the Atom Publishing Protocol"""
50 """REST Controller styled on the Atom Publishing Protocol"""
52 # To properly map this controller, ensure your config/routing.py
51 # To properly map this controller, ensure your config/routing.py
53 # file has a resource setup:
52 # file has a resource setup:
54 # map.resource('users_group', 'users_groups')
53 # map.resource('users_group', 'users_groups')
55
54
56 @LoginRequired()
55 @LoginRequired()
57 @HasPermissionAllDecorator('hg.admin')
56 @HasPermissionAllDecorator('hg.admin')
58 def __before__(self):
57 def __before__(self):
59 c.admin_user = session.get('admin_user')
58 c.admin_user = session.get('admin_user')
60 c.admin_username = session.get('admin_username')
59 c.admin_username = session.get('admin_username')
61 super(UsersGroupsController, self).__before__()
60 super(UsersGroupsController, self).__before__()
62 c.available_permissions = config['available_permissions']
61 c.available_permissions = config['available_permissions']
63
62
64 def index(self, format='html'):
63 def index(self, format='html'):
65 """GET /users_groups: All items in the collection"""
64 """GET /users_groups: All items in the collection"""
66 # url('users_groups')
65 # url('users_groups')
67 c.users_groups_list = self.sa.query(UsersGroup).all()
66 c.users_groups_list = self.sa.query(UsersGroup).all()
68 return render('admin/users_groups/users_groups.html')
67 return render('admin/users_groups/users_groups.html')
69
68
70 def create(self):
69 def create(self):
71 """POST /users_groups: Create a new item"""
70 """POST /users_groups: Create a new item"""
72 # url('users_groups')
71 # url('users_groups')
73 users_group_model = UsersGroupModel()
72 users_group_model = UsersGroupModel()
74 users_group_form = UsersGroupForm()()
73 users_group_form = UsersGroupForm()()
75 try:
74 try:
76 form_result = users_group_form.to_python(dict(request.POST))
75 form_result = users_group_form.to_python(dict(request.POST))
77 users_group_model.create(form_result)
76 users_group_model.create(form_result)
78 h.flash(_('created users group %s') % form_result['users_group_name'],
77 h.flash(_('created users group %s') % form_result['users_group_name'],
79 category='success')
78 category='success')
80 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
79 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
81 except formencode.Invalid, errors:
80 except formencode.Invalid, errors:
82 return htmlfill.render(
81 return htmlfill.render(
83 render('admin/users_groups/users_group_add.html'),
82 render('admin/users_groups/users_group_add.html'),
84 defaults=errors.value,
83 defaults=errors.value,
85 errors=errors.error_dict or {},
84 errors=errors.error_dict or {},
86 prefix_error=False,
85 prefix_error=False,
87 encoding="UTF-8")
86 encoding="UTF-8")
88 except Exception:
87 except Exception:
89 log.error(traceback.format_exc())
88 log.error(traceback.format_exc())
90 h.flash(_('error occurred during creation of users group %s') \
89 h.flash(_('error occurred during creation of users group %s') \
91 % request.POST.get('users_group_name'), category='error')
90 % request.POST.get('users_group_name'), category='error')
92
91
93 return redirect(url('users_groups'))
92 return redirect(url('users_groups'))
94
93
95 def new(self, format='html'):
94 def new(self, format='html'):
96 """GET /users_groups/new: Form to create a new item"""
95 """GET /users_groups/new: Form to create a new item"""
97 # url('new_users_group')
96 # url('new_users_group')
98 return render('admin/users_groups/users_group_add.html')
97 return render('admin/users_groups/users_group_add.html')
99
98
100 def update(self, id):
99 def update(self, id):
101 """PUT /users_groups/id: Update an existing item"""
100 """PUT /users_groups/id: Update an existing item"""
102 # Forms posted to this method should contain a hidden field:
101 # Forms posted to this method should contain a hidden field:
103 # <input type="hidden" name="_method" value="PUT" />
102 # <input type="hidden" name="_method" value="PUT" />
104 # Or using helpers:
103 # Or using helpers:
105 # h.form(url('users_group', id=ID),
104 # h.form(url('users_group', id=ID),
106 # method='put')
105 # method='put')
107 # url('users_group', id=ID)
106 # url('users_group', id=ID)
108
107
109
108
110 users_group_model = UsersGroupModel()
109 users_group_model = UsersGroupModel()
111 c.users_group = users_group_model.get(id)
110 c.users_group = users_group_model.get(id)
112 c.group_members = [(x.user_id, x.user.username) for x in
111 c.group_members = [(x.user_id, x.user.username) for x in
113 c.users_group.members]
112 c.users_group.members]
114
113
115 c.available_members = [(x.user_id, x.username) for x in
114 c.available_members = [(x.user_id, x.username) for x in
116 self.sa.query(User).all()]
115 self.sa.query(User).all()]
117 users_group_form = UsersGroupForm(edit=True,
116 users_group_form = UsersGroupForm(edit=True,
118 old_data=c.users_group.get_dict(),
117 old_data=c.users_group.get_dict(),
119 available_members=[str(x[0]) for x
118 available_members=[str(x[0]) for x
120 in c.available_members])()
119 in c.available_members])()
121
120
122 try:
121 try:
123 form_result = users_group_form.to_python(request.POST)
122 form_result = users_group_form.to_python(request.POST)
124 users_group_model.update(id, form_result)
123 users_group_model.update(id, form_result)
125 h.flash(_('updated users group %s') % form_result['users_group_name'],
124 h.flash(_('updated users group %s') % form_result['users_group_name'],
126 category='success')
125 category='success')
127 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
126 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
128 except formencode.Invalid, errors:
127 except formencode.Invalid, errors:
129 return htmlfill.render(
128 return htmlfill.render(
130 render('admin/users_groups/users_group_edit.html'),
129 render('admin/users_groups/users_group_edit.html'),
131 defaults=errors.value,
130 defaults=errors.value,
132 errors=errors.error_dict or {},
131 errors=errors.error_dict or {},
133 prefix_error=False,
132 prefix_error=False,
134 encoding="UTF-8")
133 encoding="UTF-8")
135 except Exception:
134 except Exception:
136 log.error(traceback.format_exc())
135 log.error(traceback.format_exc())
137 h.flash(_('error occurred during update of users group %s') \
136 h.flash(_('error occurred during update of users group %s') \
138 % request.POST.get('users_group_name'), category='error')
137 % request.POST.get('users_group_name'), category='error')
139
138
140 return redirect(url('users_groups'))
139 return redirect(url('users_groups'))
141
140
142
141
143
142
144 def delete(self, id):
143 def delete(self, id):
145 """DELETE /users_groups/id: Delete an existing item"""
144 """DELETE /users_groups/id: Delete an existing item"""
146 # Forms posted to this method should contain a hidden field:
145 # Forms posted to this method should contain a hidden field:
147 # <input type="hidden" name="_method" value="DELETE" />
146 # <input type="hidden" name="_method" value="DELETE" />
148 # Or using helpers:
147 # Or using helpers:
149 # h.form(url('users_group', id=ID),
148 # h.form(url('users_group', id=ID),
150 # method='delete')
149 # method='delete')
151 # url('users_group', id=ID)
150 # url('users_group', id=ID)
152 users_group_model = UsersGroupModel()
151 users_group_model = UsersGroupModel()
153 try:
152 try:
154 users_group_model.delete(id)
153 users_group_model.delete(id)
155 h.flash(_('successfully deleted users group'), category='success')
154 h.flash(_('successfully deleted users group'), category='success')
156 except Exception:
155 except Exception:
157 h.flash(_('An error occurred during deletion of users group'),
156 h.flash(_('An error occurred during deletion of users group'),
158 category='error')
157 category='error')
159 return redirect(url('users_groups'))
158 return redirect(url('users_groups'))
160
159
161 def show(self, id, format='html'):
160 def show(self, id, format='html'):
162 """GET /users_groups/id: Show a specific item"""
161 """GET /users_groups/id: Show a specific item"""
163 # url('users_group', id=ID)
162 # url('users_group', id=ID)
164
163
165 def edit(self, id, format='html'):
164 def edit(self, id, format='html'):
166 """GET /users_groups/id/edit: Form to edit an existing item"""
165 """GET /users_groups/id/edit: Form to edit an existing item"""
167 # url('edit_users_group', id=ID)
166 # url('edit_users_group', id=ID)
168
167
169 c.users_group = self.sa.query(UsersGroup).get(id)
168 c.users_group = self.sa.query(UsersGroup).get(id)
170 if not c.users_group:
169 if not c.users_group:
171 return redirect(url('users_groups'))
170 return redirect(url('users_groups'))
172
171
173 c.users_group.permissions = {}
172 c.users_group.permissions = {}
174 c.group_members = [(x.user_id, x.user.username) for x in
173 c.group_members = [(x.user_id, x.user.username) for x in
175 c.users_group.members]
174 c.users_group.members]
176 c.available_members = [(x.user_id, x.username) for x in
175 c.available_members = [(x.user_id, x.username) for x in
177 self.sa.query(User).all()]
176 self.sa.query(User).all()]
178 defaults = c.users_group.get_dict()
177 defaults = c.users_group.get_dict()
179
178
180 return htmlfill.render(
179 return htmlfill.render(
181 render('admin/users_groups/users_group_edit.html'),
180 render('admin/users_groups/users_group_edit.html'),
182 defaults=defaults,
181 defaults=defaults,
183 encoding="UTF-8",
182 encoding="UTF-8",
184 force_defaults=False
183 force_defaults=False
185 )
184 )
@@ -1,244 +1,245 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.journal
3 rhodecode.controllers.journal
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Journal controller for pylons
6 Journal controller for pylons
7
7
8 :created_on: Nov 21, 2010
8 :created_on: Nov 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import logging
27 import logging
28
28
29 from sqlalchemy import or_
29 from sqlalchemy import or_
30 from sqlalchemy.orm import joinedload, make_transient
30 from sqlalchemy.orm import joinedload, make_transient
31 from webhelpers.paginate import Page
31 from webhelpers.paginate import Page
32 from itertools import groupby
32 from itertools import groupby
33
33
34 from paste.httpexceptions import HTTPInternalServerError
34 from paste.httpexceptions import HTTPInternalServerError
35 from pylons import request, tmpl_context as c, response, url
35 from pylons import request, tmpl_context as c, response, url
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
37 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
38
38
39 import rhodecode.lib.helpers as h
39 import rhodecode.lib.helpers as h
40 from rhodecode.lib.auth import LoginRequired, NotAnonymous
40 from rhodecode.lib.auth import LoginRequired, NotAnonymous
41 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.base import BaseController, render
42 from rhodecode.model.db import UserLog, UserFollowing
42 from rhodecode.model.db import UserLog, UserFollowing
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46 class JournalController(BaseController):
46 class JournalController(BaseController):
47
47
48
48
49 @LoginRequired()
49 @LoginRequired()
50 def __before__(self):
50 def __before__(self):
51 super(JournalController, self).__before__()
51 super(JournalController, self).__before__()
52 c.rhodecode_user = self.rhodecode_user
52 self.title = _('%s public journal %s feed') % (c.rhodecode_name, '%s')
53 self.title = _('%s public journal %s feed') % (c.rhodecode_name, '%s')
53 self.language = 'en-us'
54 self.language = 'en-us'
54 self.ttl = "5"
55 self.ttl = "5"
55 self.feed_nr = 20
56 self.feed_nr = 20
56
57
57 @NotAnonymous()
58 @NotAnonymous()
58 def index(self):
59 def index(self):
59 # Return a rendered template
60 # Return a rendered template
60 p = int(request.params.get('page', 1))
61 p = int(request.params.get('page', 1))
61
62
62 c.following = self.sa.query(UserFollowing)\
63 c.following = self.sa.query(UserFollowing)\
63 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
64 .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
64 .options(joinedload(UserFollowing.follows_repository))\
65 .options(joinedload(UserFollowing.follows_repository))\
65 .all()
66 .all()
66
67
67 journal = self._get_journal_data(c.following)
68 journal = self._get_journal_data(c.following)
68
69
69 c.journal_pager = Page(journal, page=p, items_per_page=20)
70 c.journal_pager = Page(journal, page=p, items_per_page=20)
70
71
71 c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)
72 c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)
72
73
73 c.journal_data = render('journal/journal_data.html')
74 c.journal_data = render('journal/journal_data.html')
74 if request.params.get('partial'):
75 if request.params.get('partial'):
75 return c.journal_data
76 return c.journal_data
76 return render('journal/journal.html')
77 return render('journal/journal.html')
77
78
78
79
79 def _get_daily_aggregate(self, journal):
80 def _get_daily_aggregate(self, journal):
80 groups = []
81 groups = []
81 for k, g in groupby(journal, lambda x:x.action_as_day):
82 for k, g in groupby(journal, lambda x:x.action_as_day):
82 user_group = []
83 user_group = []
83 for k2, g2 in groupby(list(g), lambda x:x.user.email):
84 for k2, g2 in groupby(list(g), lambda x:x.user.email):
84 l = list(g2)
85 l = list(g2)
85 user_group.append((l[0].user, l))
86 user_group.append((l[0].user, l))
86
87
87 groups.append((k, user_group,))
88 groups.append((k, user_group,))
88
89
89 return groups
90 return groups
90
91
91
92
92 def _get_journal_data(self, following_repos):
93 def _get_journal_data(self, following_repos):
93 repo_ids = [x.follows_repository.repo_id for x in following_repos
94 repo_ids = [x.follows_repository.repo_id for x in following_repos
94 if x.follows_repository is not None]
95 if x.follows_repository is not None]
95 user_ids = [x.follows_user.user_id for x in following_repos
96 user_ids = [x.follows_user.user_id for x in following_repos
96 if x.follows_user is not None]
97 if x.follows_user is not None]
97
98
98 filtering_criterion = None
99 filtering_criterion = None
99
100
100 if repo_ids and user_ids:
101 if repo_ids and user_ids:
101 filtering_criterion = or_(UserLog.repository_id.in_(repo_ids),
102 filtering_criterion = or_(UserLog.repository_id.in_(repo_ids),
102 UserLog.user_id.in_(user_ids))
103 UserLog.user_id.in_(user_ids))
103 if repo_ids and not user_ids:
104 if repo_ids and not user_ids:
104 filtering_criterion = UserLog.repository_id.in_(repo_ids)
105 filtering_criterion = UserLog.repository_id.in_(repo_ids)
105 if not repo_ids and user_ids:
106 if not repo_ids and user_ids:
106 filtering_criterion = UserLog.user_id.in_(user_ids)
107 filtering_criterion = UserLog.user_id.in_(user_ids)
107 if filtering_criterion is not None:
108 if filtering_criterion is not None:
108 journal = self.sa.query(UserLog)\
109 journal = self.sa.query(UserLog)\
109 .options(joinedload(UserLog.user))\
110 .options(joinedload(UserLog.user))\
110 .options(joinedload(UserLog.repository))\
111 .options(joinedload(UserLog.repository))\
111 .filter(filtering_criterion)\
112 .filter(filtering_criterion)\
112 .order_by(UserLog.action_date.desc())
113 .order_by(UserLog.action_date.desc())
113 else:
114 else:
114 journal = []
115 journal = []
115
116
116
117
117 return journal
118 return journal
118
119
119 @NotAnonymous()
120 @NotAnonymous()
120 def toggle_following(self):
121 def toggle_following(self):
121 cur_token = request.POST.get('auth_token')
122 cur_token = request.POST.get('auth_token')
122 token = h.get_token()
123 token = h.get_token()
123 if cur_token == token:
124 if cur_token == token:
124
125
125 user_id = request.POST.get('follows_user_id')
126 user_id = request.POST.get('follows_user_id')
126 if user_id:
127 if user_id:
127 try:
128 try:
128 self.scm_model.toggle_following_user(user_id,
129 self.scm_model.toggle_following_user(user_id,
129 c.rhodecode_user.user_id)
130 self.rhodecode_user.user_id)
130 return 'ok'
131 return 'ok'
131 except:
132 except:
132 raise HTTPInternalServerError()
133 raise HTTPInternalServerError()
133
134
134 repo_id = request.POST.get('follows_repo_id')
135 repo_id = request.POST.get('follows_repo_id')
135 if repo_id:
136 if repo_id:
136 try:
137 try:
137 self.scm_model.toggle_following_repo(repo_id,
138 self.scm_model.toggle_following_repo(repo_id,
138 c.rhodecode_user.user_id)
139 self.rhodecode_user.user_id)
139 return 'ok'
140 return 'ok'
140 except:
141 except:
141 raise HTTPInternalServerError()
142 raise HTTPInternalServerError()
142
143
143
144
144 log.debug('token mismatch %s vs %s', cur_token, token)
145 log.debug('token mismatch %s vs %s', cur_token, token)
145 raise HTTPInternalServerError()
146 raise HTTPInternalServerError()
146
147
147
148
148
149
149
150
150 def public_journal(self):
151 def public_journal(self):
151 # Return a rendered template
152 # Return a rendered template
152 p = int(request.params.get('page', 1))
153 p = int(request.params.get('page', 1))
153
154
154 c.following = self.sa.query(UserFollowing)\
155 c.following = self.sa.query(UserFollowing)\
155 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
156 .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
156 .options(joinedload(UserFollowing.follows_repository))\
157 .options(joinedload(UserFollowing.follows_repository))\
157 .all()
158 .all()
158
159
159 journal = self._get_journal_data(c.following)
160 journal = self._get_journal_data(c.following)
160
161
161 c.journal_pager = Page(journal, page=p, items_per_page=20)
162 c.journal_pager = Page(journal, page=p, items_per_page=20)
162
163
163 c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)
164 c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)
164
165
165 c.journal_data = render('journal/journal_data.html')
166 c.journal_data = render('journal/journal_data.html')
166 if request.params.get('partial'):
167 if request.params.get('partial'):
167 return c.journal_data
168 return c.journal_data
168 return render('journal/public_journal.html')
169 return render('journal/public_journal.html')
169
170
170
171
171
172
172 def public_journal_atom(self):
173 def public_journal_atom(self):
173 """
174 """
174 Produce an atom-1.0 feed via feedgenerator module
175 Produce an atom-1.0 feed via feedgenerator module
175 """
176 """
176 c.following = self.sa.query(UserFollowing)\
177 c.following = self.sa.query(UserFollowing)\
177 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
178 .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
178 .options(joinedload(UserFollowing.follows_repository))\
179 .options(joinedload(UserFollowing.follows_repository))\
179 .all()
180 .all()
180
181
181 journal = self._get_journal_data(c.following)
182 journal = self._get_journal_data(c.following)
182
183
183 feed = Atom1Feed(title=self.title % 'atom',
184 feed = Atom1Feed(title=self.title % 'atom',
184 link=url('public_journal_atom', qualified=True),
185 link=url('public_journal_atom', qualified=True),
185 description=_('Public journal'),
186 description=_('Public journal'),
186 language=self.language,
187 language=self.language,
187 ttl=self.ttl)
188 ttl=self.ttl)
188
189
189 for entry in journal[:self.feed_nr]:
190 for entry in journal[:self.feed_nr]:
190 #tmpl = h.action_parser(entry)[0]
191 #tmpl = h.action_parser(entry)[0]
191 action, action_extra = h.action_parser(entry, feed=True)
192 action, action_extra = h.action_parser(entry, feed=True)
192 title = "%s - %s %s" % (entry.user.short_contact, action,
193 title = "%s - %s %s" % (entry.user.short_contact, action,
193 entry.repository.repo_name)
194 entry.repository.repo_name)
194 desc = action_extra()
195 desc = action_extra()
195 feed.add_item(title=title,
196 feed.add_item(title=title,
196 pubdate=entry.action_date,
197 pubdate=entry.action_date,
197 link=url('', qualified=True),
198 link=url('', qualified=True),
198 author_email=entry.user.email,
199 author_email=entry.user.email,
199 author_name=entry.user.full_contact,
200 author_name=entry.user.full_contact,
200 description=desc)
201 description=desc)
201
202
202 response.content_type = feed.mime_type
203 response.content_type = feed.mime_type
203 return feed.writeString('utf-8')
204 return feed.writeString('utf-8')
204
205
205 def public_journal_rss(self):
206 def public_journal_rss(self):
206 """
207 """
207 Produce an rss2 feed via feedgenerator module
208 Produce an rss2 feed via feedgenerator module
208 """
209 """
209 c.following = self.sa.query(UserFollowing)\
210 c.following = self.sa.query(UserFollowing)\
210 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
211 .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
211 .options(joinedload(UserFollowing.follows_repository))\
212 .options(joinedload(UserFollowing.follows_repository))\
212 .all()
213 .all()
213
214
214 journal = self._get_journal_data(c.following)
215 journal = self._get_journal_data(c.following)
215
216
216 feed = Rss201rev2Feed(title=self.title % 'rss',
217 feed = Rss201rev2Feed(title=self.title % 'rss',
217 link=url('public_journal_rss', qualified=True),
218 link=url('public_journal_rss', qualified=True),
218 description=_('Public journal'),
219 description=_('Public journal'),
219 language=self.language,
220 language=self.language,
220 ttl=self.ttl)
221 ttl=self.ttl)
221
222
222 for entry in journal[:self.feed_nr]:
223 for entry in journal[:self.feed_nr]:
223 #tmpl = h.action_parser(entry)[0]
224 #tmpl = h.action_parser(entry)[0]
224 action, action_extra = h.action_parser(entry, feed=True)
225 action, action_extra = h.action_parser(entry, feed=True)
225 title = "%s - %s %s" % (entry.user.short_contact, action,
226 title = "%s - %s %s" % (entry.user.short_contact, action,
226 entry.repository.repo_name)
227 entry.repository.repo_name)
227 desc = action_extra()
228 desc = action_extra()
228 feed.add_item(title=title,
229 feed.add_item(title=title,
229 pubdate=entry.action_date,
230 pubdate=entry.action_date,
230 link=url('', qualified=True),
231 link=url('', qualified=True),
231 author_email=entry.user.email,
232 author_email=entry.user.email,
232 author_name=entry.user.full_contact,
233 author_name=entry.user.full_contact,
233 description=desc)
234 description=desc)
234
235
235 response.content_type = feed.mime_type
236 response.content_type = feed.mime_type
236 return feed.writeString('utf-8')
237 return feed.writeString('utf-8')
237
238
238
239
239
240
240
241
241
242
242
243
243
244
244
245
@@ -1,152 +1,150 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.login
3 rhodecode.controllers.login
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Login controller for rhodeocode
6 Login controller for rhodeocode
7
7
8 :created_on: Apr 22, 2010
8 :created_on: Apr 22, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29 import formencode
29 import formencode
30
30
31 from formencode import htmlfill
31 from formencode import htmlfill
32
32
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34 from pylons.controllers.util import abort, redirect
34 from pylons.controllers.util import abort, redirect
35 from pylons import request, response, session, tmpl_context as c, url
35 from pylons import request, response, session, tmpl_context as c, url
36
36
37 import rhodecode.lib.helpers as h
37 import rhodecode.lib.helpers as h
38 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
38 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
39 from rhodecode.lib.base import BaseController, render
39 from rhodecode.lib.base import BaseController, render
40 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
40 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
41 from rhodecode.model.user import UserModel
41 from rhodecode.model.user import UserModel
42
42
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46 class LoginController(BaseController):
46 class LoginController(BaseController):
47
47
48 def __before__(self):
48 def __before__(self):
49 super(LoginController, self).__before__()
49 super(LoginController, self).__before__()
50
50
51 def index(self):
51 def index(self):
52 #redirect if already logged in
52 #redirect if already logged in
53 c.came_from = request.GET.get('came_from', None)
53 c.came_from = request.GET.get('came_from', None)
54
54
55 if c.rhodecode_user.is_authenticated \
55 if c.rhodecode_user.is_authenticated \
56 and c.rhodecode_user.username != 'default':
56 and c.rhodecode_user.username != 'default':
57
57
58 return redirect(url('home'))
58 return redirect(url('home'))
59
59
60 if request.POST:
60 if request.POST:
61 #import Login Form validator class
61 #import Login Form validator class
62 login_form = LoginForm()
62 login_form = LoginForm()
63 try:
63 try:
64 c.form_result = login_form.to_python(dict(request.POST))
64 c.form_result = login_form.to_python(dict(request.POST))
65 #form checks for username/password, now we're authenticated
65 username = c.form_result['username']
66 username = c.form_result['username']
66 user = UserModel().get_by_username(username, case_insensitive=True)
67 user = UserModel().get_by_username(username,
67 auth_user = AuthUser()
68 case_insensitive=True)
68 auth_user.username = user.username
69 auth_user = AuthUser(user.user_id)
69 auth_user.is_authenticated = True
70 auth_user.set_authenticated()
70 auth_user.is_admin = user.admin
71 auth_user.user_id = user.user_id
72 auth_user.name = user.name
73 auth_user.lastname = user.lastname
74 session['rhodecode_user'] = auth_user
71 session['rhodecode_user'] = auth_user
75 session.save()
72 session.save()
76 log.info('user %s is now authenticated', username)
77
73
74 log.info('user %s is now authenticated and stored in session',
75 username)
78 user.update_lastlogin()
76 user.update_lastlogin()
79
77
80 if c.came_from:
78 if c.came_from:
81 return redirect(c.came_from)
79 return redirect(c.came_from)
82 else:
80 else:
83 return redirect(url('home'))
81 return redirect(url('home'))
84
82
85 except formencode.Invalid, errors:
83 except formencode.Invalid, errors:
86 return htmlfill.render(
84 return htmlfill.render(
87 render('/login.html'),
85 render('/login.html'),
88 defaults=errors.value,
86 defaults=errors.value,
89 errors=errors.error_dict or {},
87 errors=errors.error_dict or {},
90 prefix_error=False,
88 prefix_error=False,
91 encoding="UTF-8")
89 encoding="UTF-8")
92
90
93 return render('/login.html')
91 return render('/login.html')
94
92
95 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
93 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
96 'hg.register.manual_activate')
94 'hg.register.manual_activate')
97 def register(self):
95 def register(self):
98 user_model = UserModel()
96 user_model = UserModel()
99 c.auto_active = False
97 c.auto_active = False
100 for perm in user_model.get_by_username('default', cache=False).user_perms:
98 for perm in user_model.get_by_username('default', cache=False).user_perms:
101 if perm.permission.permission_name == 'hg.register.auto_activate':
99 if perm.permission.permission_name == 'hg.register.auto_activate':
102 c.auto_active = True
100 c.auto_active = True
103 break
101 break
104
102
105 if request.POST:
103 if request.POST:
106
104
107 register_form = RegisterForm()()
105 register_form = RegisterForm()()
108 try:
106 try:
109 form_result = register_form.to_python(dict(request.POST))
107 form_result = register_form.to_python(dict(request.POST))
110 form_result['active'] = c.auto_active
108 form_result['active'] = c.auto_active
111 user_model.create_registration(form_result)
109 user_model.create_registration(form_result)
112 h.flash(_('You have successfully registered into rhodecode'),
110 h.flash(_('You have successfully registered into rhodecode'),
113 category='success')
111 category='success')
114 return redirect(url('login_home'))
112 return redirect(url('login_home'))
115
113
116 except formencode.Invalid, errors:
114 except formencode.Invalid, errors:
117 return htmlfill.render(
115 return htmlfill.render(
118 render('/register.html'),
116 render('/register.html'),
119 defaults=errors.value,
117 defaults=errors.value,
120 errors=errors.error_dict or {},
118 errors=errors.error_dict or {},
121 prefix_error=False,
119 prefix_error=False,
122 encoding="UTF-8")
120 encoding="UTF-8")
123
121
124 return render('/register.html')
122 return render('/register.html')
125
123
126 def password_reset(self):
124 def password_reset(self):
127 user_model = UserModel()
125 user_model = UserModel()
128 if request.POST:
126 if request.POST:
129
127
130 password_reset_form = PasswordResetForm()()
128 password_reset_form = PasswordResetForm()()
131 try:
129 try:
132 form_result = password_reset_form.to_python(dict(request.POST))
130 form_result = password_reset_form.to_python(dict(request.POST))
133 user_model.reset_password(form_result)
131 user_model.reset_password(form_result)
134 h.flash(_('Your new password was sent'),
132 h.flash(_('Your new password was sent'),
135 category='success')
133 category='success')
136 return redirect(url('login_home'))
134 return redirect(url('login_home'))
137
135
138 except formencode.Invalid, errors:
136 except formencode.Invalid, errors:
139 return htmlfill.render(
137 return htmlfill.render(
140 render('/password_reset.html'),
138 render('/password_reset.html'),
141 defaults=errors.value,
139 defaults=errors.value,
142 errors=errors.error_dict or {},
140 errors=errors.error_dict or {},
143 prefix_error=False,
141 prefix_error=False,
144 encoding="UTF-8")
142 encoding="UTF-8")
145
143
146 return render('/password_reset.html')
144 return render('/password_reset.html')
147
145
148 def logout(self):
146 def logout(self):
149 session['rhodecode_user'] = AuthUser()
147 del session['rhodecode_user']
150 session.save()
148 session.save()
151 log.info('Logging out and setting user as Empty')
149 log.info('Logging out and setting user as Empty')
152 redirect(url('home'))
150 redirect(url('home'))
@@ -1,621 +1,550 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.auth
3 rhodecode.lib.auth
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 authentication and permission libraries
6 authentication and permission libraries
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 2010
9 :copyright: (c) 2010 by marcink.
9 :copyright: (c) 2010 by marcink.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
11 """
11 """
12 # This program is free software; you can redistribute it and/or
12 # This program is free software; you can redistribute it and/or
13 # modify it under the terms of the GNU General Public License
13 # modify it under the terms of the GNU General Public License
14 # as published by the Free Software Foundation; version 2
14 # as published by the Free Software Foundation; version 2
15 # of the License or (at your opinion) any later version of the license.
15 # of the License or (at your opinion) any later version of the license.
16 #
16 #
17 # This program is distributed in the hope that it will be useful,
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
20 # GNU General Public License for more details.
21 #
21 #
22 # You should have received a copy of the GNU General Public License
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # MA 02110-1301, USA.
25 # MA 02110-1301, USA.
26
26
27 import bcrypt
27 import bcrypt
28 import random
28 import random
29 import logging
29 import logging
30 import traceback
30 import traceback
31 import hashlib
31 import hashlib
32 from tempfile import _RandomNameSequence
32 from tempfile import _RandomNameSequence
33 from decorator import decorator
33 from decorator import decorator
34
34
35 from pylons import config, session, url, request
35 from pylons import config, session, url, request
36 from pylons.controllers.util import abort, redirect
36 from pylons.controllers.util import abort, redirect
37 from pylons.i18n.translation import _
37 from pylons.i18n.translation import _
38
38
39 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
39 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
40 from rhodecode.lib.utils import get_repo_slug
40 from rhodecode.lib.utils import get_repo_slug
41 from rhodecode.lib.auth_ldap import AuthLdap
41 from rhodecode.lib.auth_ldap import AuthLdap
42
42
43 from rhodecode.model import meta
43 from rhodecode.model import meta
44 from rhodecode.model.user import UserModel
44 from rhodecode.model.user import UserModel
45 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
45 from rhodecode.model.db import Permission
46 UserToPerm, UsersGroupToPerm, UsersGroupMember
47
46
48
47
49 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
50
49
51
52 PERM_WEIGHTS = {'repository.none':0,
53 'repository.read':1,
54 'repository.write':3,
55 'repository.admin':3}
56
57
58 class PasswordGenerator(object):
50 class PasswordGenerator(object):
59 """This is a simple class for generating password from
51 """This is a simple class for generating password from
60 different sets of characters
52 different sets of characters
61 usage:
53 usage:
62 passwd_gen = PasswordGenerator()
54 passwd_gen = PasswordGenerator()
63 #print 8-letter password containing only big and small letters of alphabet
55 #print 8-letter password containing only big and small letters of alphabet
64 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
56 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
65 """
57 """
66 ALPHABETS_NUM = r'''1234567890'''#[0]
58 ALPHABETS_NUM = r'''1234567890'''#[0]
67 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
59 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
68 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
60 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
69 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
61 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
70 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
62 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
71 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
63 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
72 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
64 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
73 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
65 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
74 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
66 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
75
67
76 def __init__(self, passwd=''):
68 def __init__(self, passwd=''):
77 self.passwd = passwd
69 self.passwd = passwd
78
70
79 def gen_password(self, len, type):
71 def gen_password(self, len, type):
80 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
72 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
81 return self.passwd
73 return self.passwd
82
74
83
75
84 def get_crypt_password(password):
76 def get_crypt_password(password):
85 """Cryptographic function used for password hashing based on pybcrypt
77 """Cryptographic function used for password hashing based on pybcrypt
86
78
87 :param password: password to hash
79 :param password: password to hash
88 """
80 """
89 return bcrypt.hashpw(password, bcrypt.gensalt(10))
81 return bcrypt.hashpw(password, bcrypt.gensalt(10))
90
82
91 def generate_api_key(username, salt=None):
83 def generate_api_key(username, salt=None):
92 if salt is None:
84 if salt is None:
93 salt = _RandomNameSequence().next()
85 salt = _RandomNameSequence().next()
94
86
95 return hashlib.sha1(username + salt).hexdigest()
87 return hashlib.sha1(username + salt).hexdigest()
96
88
97 def check_password(password, hashed):
89 def check_password(password, hashed):
98 return bcrypt.hashpw(password, hashed) == hashed
90 return bcrypt.hashpw(password, hashed) == hashed
99
91
100 def authfunc(environ, username, password):
92 def authfunc(environ, username, password):
101 """Dummy authentication function used in Mercurial/Git/ and access control,
93 """Dummy authentication function used in Mercurial/Git/ and access control,
102
94
103 :param environ: needed only for using in Basic auth
95 :param environ: needed only for using in Basic auth
104 """
96 """
105 return authenticate(username, password)
97 return authenticate(username, password)
106
98
107
99
108 def authenticate(username, password):
100 def authenticate(username, password):
109 """Authentication function used for access control,
101 """Authentication function used for access control,
110 firstly checks for db authentication then if ldap is enabled for ldap
102 firstly checks for db authentication then if ldap is enabled for ldap
111 authentication, also creates ldap user if not in database
103 authentication, also creates ldap user if not in database
112
104
113 :param username: username
105 :param username: username
114 :param password: password
106 :param password: password
115 """
107 """
116 user_model = UserModel()
108 user_model = UserModel()
117 user = user_model.get_by_username(username, cache=False)
109 user = user_model.get_by_username(username, cache=False)
118
110
119 log.debug('Authenticating user using RhodeCode account')
111 log.debug('Authenticating user using RhodeCode account')
120 if user is not None and not user.ldap_dn:
112 if user is not None and not user.ldap_dn:
121 if user.active:
113 if user.active:
122
114
123 if user.username == 'default' and user.active:
115 if user.username == 'default' and user.active:
124 log.info('user %s authenticated correctly as anonymous user',
116 log.info('user %s authenticated correctly as anonymous user',
125 username)
117 username)
126 return True
118 return True
127
119
128 elif user.username == username and check_password(password, user.password):
120 elif user.username == username and check_password(password, user.password):
129 log.info('user %s authenticated correctly', username)
121 log.info('user %s authenticated correctly', username)
130 return True
122 return True
131 else:
123 else:
132 log.warning('user %s is disabled', username)
124 log.warning('user %s is disabled', username)
133
125
134 else:
126 else:
135 log.debug('Regular authentication failed')
127 log.debug('Regular authentication failed')
136 user_obj = user_model.get_by_username(username, cache=False,
128 user_obj = user_model.get_by_username(username, cache=False,
137 case_insensitive=True)
129 case_insensitive=True)
138
130
139 if user_obj is not None and not user_obj.ldap_dn:
131 if user_obj is not None and not user_obj.ldap_dn:
140 log.debug('this user already exists as non ldap')
132 log.debug('this user already exists as non ldap')
141 return False
133 return False
142
134
143 from rhodecode.model.settings import SettingsModel
135 from rhodecode.model.settings import SettingsModel
144 ldap_settings = SettingsModel().get_ldap_settings()
136 ldap_settings = SettingsModel().get_ldap_settings()
145
137
146 #======================================================================
138 #======================================================================
147 # FALLBACK TO LDAP AUTH IF ENABLE
139 # FALLBACK TO LDAP AUTH IF ENABLE
148 #======================================================================
140 #======================================================================
149 if ldap_settings.get('ldap_active', False):
141 if ldap_settings.get('ldap_active', False):
150 log.debug("Authenticating user using ldap")
142 log.debug("Authenticating user using ldap")
151 kwargs = {
143 kwargs = {
152 'server':ldap_settings.get('ldap_host', ''),
144 'server':ldap_settings.get('ldap_host', ''),
153 'base_dn':ldap_settings.get('ldap_base_dn', ''),
145 'base_dn':ldap_settings.get('ldap_base_dn', ''),
154 'port':ldap_settings.get('ldap_port'),
146 'port':ldap_settings.get('ldap_port'),
155 'bind_dn':ldap_settings.get('ldap_dn_user'),
147 'bind_dn':ldap_settings.get('ldap_dn_user'),
156 'bind_pass':ldap_settings.get('ldap_dn_pass'),
148 'bind_pass':ldap_settings.get('ldap_dn_pass'),
157 'use_ldaps':ldap_settings.get('ldap_ldaps'),
149 'use_ldaps':ldap_settings.get('ldap_ldaps'),
158 'tls_reqcert':ldap_settings.get('ldap_tls_reqcert'),
150 'tls_reqcert':ldap_settings.get('ldap_tls_reqcert'),
159 'ldap_filter':ldap_settings.get('ldap_filter'),
151 'ldap_filter':ldap_settings.get('ldap_filter'),
160 'search_scope':ldap_settings.get('ldap_search_scope'),
152 'search_scope':ldap_settings.get('ldap_search_scope'),
161 'attr_login':ldap_settings.get('ldap_attr_login'),
153 'attr_login':ldap_settings.get('ldap_attr_login'),
162 'ldap_version':3,
154 'ldap_version':3,
163 }
155 }
164 log.debug('Checking for ldap authentication')
156 log.debug('Checking for ldap authentication')
165 try:
157 try:
166 aldap = AuthLdap(**kwargs)
158 aldap = AuthLdap(**kwargs)
167 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username, password)
159 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username, password)
168 log.debug('Got ldap DN response %s', user_dn)
160 log.debug('Got ldap DN response %s', user_dn)
169
161
170 user_attrs = {
162 user_attrs = {
171 'name' : ldap_attrs[ldap_settings.get('ldap_attr_firstname')][0],
163 'name' : ldap_attrs[ldap_settings.get('ldap_attr_firstname')][0],
172 'lastname' : ldap_attrs[ldap_settings.get('ldap_attr_lastname')][0],
164 'lastname' : ldap_attrs[ldap_settings.get('ldap_attr_lastname')][0],
173 'email' : ldap_attrs[ldap_settings.get('ldap_attr_email')][0],
165 'email' : ldap_attrs[ldap_settings.get('ldap_attr_email')][0],
174 }
166 }
175
167
176 if user_model.create_ldap(username, password, user_dn, user_attrs):
168 if user_model.create_ldap(username, password, user_dn, user_attrs):
177 log.info('created new ldap user %s', username)
169 log.info('created new ldap user %s', username)
178
170
179 return True
171 return True
180 except (LdapUsernameError, LdapPasswordError,):
172 except (LdapUsernameError, LdapPasswordError,):
181 pass
173 pass
182 except (Exception,):
174 except (Exception,):
183 log.error(traceback.format_exc())
175 log.error(traceback.format_exc())
184 pass
176 pass
185 return False
177 return False
186
178
187 class AuthUser(object):
179 class AuthUser(object):
188 """A simple object that handles a mercurial username for authentication
180 """
181 A simple object that handles all attributes of user in RhodeCode
182
183 It does lookup based on API key,given user, or user present in session
184 Then it fills all required information for such user. It also checks if
185 anonymous access is enabled and if so, it returns default user as logged
186 in
189 """
187 """
190
188
191 def __init__(self):
189 def __init__(self, user_id=None, api_key=None):
190
191 self.user_id = user_id
192 self.api_key = api_key
193
192 self.username = 'None'
194 self.username = 'None'
193 self.name = ''
195 self.name = ''
194 self.lastname = ''
196 self.lastname = ''
195 self.email = ''
197 self.email = ''
196 self.user_id = None
197 self.is_authenticated = False
198 self.is_authenticated = False
198 self.is_admin = False
199 self.admin = False
199 self.permissions = {}
200 self.permissions = {}
201 self.propagate_data()
202
203
204 def propagate_data(self):
205 user_model = UserModel()
206 if self.api_key:
207 #try go get user by api key
208 log.debug('Auth User lookup by API KEY %s', self.api_key)
209 user_model.fill_data(self, api_key=self.api_key)
210 else:
211 log.debug('Auth User lookup by USER ID %s', self.user_id)
212 self.anonymous_user = user_model.get_by_username('default', cache=True)
213
214 if self.user_id is not None and self.user_id != self.anonymous_user.user_id:
215 user_model.fill_data(self, user_id=self.user_id)
216 else:
217 if self.anonymous_user.active is True:
218 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
219 #then we set this user is logged in
220 self.is_authenticated = True
221 else:
222 self.is_authenticated = False
223
224 log.debug('Auth User is now %s', self)
225 user_model.fill_perms(self)
226
227 @property
228 def is_admin(self):
229 return self.admin
200
230
201 def __repr__(self):
231 def __repr__(self):
202 return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
232 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
233 self.is_authenticated)
234
235 def set_authenticated(self, authenticated=True):
236
237 if self.user_id != self.anonymous_user.user_id:
238 self.is_authenticated = authenticated
239
203
240
204 def set_available_permissions(config):
241 def set_available_permissions(config):
205 """This function will propagate pylons globals with all available defined
242 """This function will propagate pylons globals with all available defined
206 permission given in db. We don't want to check each time from db for new
243 permission given in db. We don't want to check each time from db for new
207 permissions since adding a new permission also requires application restart
244 permissions since adding a new permission also requires application restart
208 ie. to decorate new views with the newly created permission
245 ie. to decorate new views with the newly created permission
209
246
210 :param config: current pylons config instance
247 :param config: current pylons config instance
211
248
212 """
249 """
213 log.info('getting information about all available permissions')
250 log.info('getting information about all available permissions')
214 try:
251 try:
215 sa = meta.Session()
252 sa = meta.Session()
216 all_perms = sa.query(Permission).all()
253 all_perms = sa.query(Permission).all()
217 except:
254 except:
218 pass
255 pass
219 finally:
256 finally:
220 meta.Session.remove()
257 meta.Session.remove()
221
258
222 config['available_permissions'] = [x.permission_name for x in all_perms]
259 config['available_permissions'] = [x.permission_name for x in all_perms]
223
260
224 def fill_perms(user):
225 """Fills user permission attribute with permissions taken from database
226 works for permissions given for repositories, and for permissions that
227 as part of beeing group member
228
229 :param user: user instance to fill his perms
230 """
231
232 sa = meta.Session()
233 user.permissions['repositories'] = {}
234 user.permissions['global'] = set()
235
236 #===========================================================================
237 # fetch default permissions
238 #===========================================================================
239 default_user = UserModel().get_by_username('default', cache=True)
240
241 default_perms = sa.query(RepoToPerm, Repository, Permission)\
242 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
243 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
244 .filter(RepoToPerm.user == default_user).all()
245
246 if user.is_admin:
247 #=======================================================================
248 # #admin have all default rights set to admin
249 #=======================================================================
250 user.permissions['global'].add('hg.admin')
251
252 for perm in default_perms:
253 p = 'repository.admin'
254 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
255
256 else:
257 #=======================================================================
258 # set default permissions
259 #=======================================================================
260
261 #default global
262 default_global_perms = sa.query(UserToPerm)\
263 .filter(UserToPerm.user == sa.query(User)\
264 .filter(User.username == 'default').one())
265
266 for perm in default_global_perms:
267 user.permissions['global'].add(perm.permission.permission_name)
268
269 #default for repositories
270 for perm in default_perms:
271 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
272 #disable defaults for private repos,
273 p = 'repository.none'
274 elif perm.Repository.user_id == user.user_id:
275 #set admin if owner
276 p = 'repository.admin'
277 else:
278 p = perm.Permission.permission_name
279
280 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
281
282 #=======================================================================
283 # overwrite default with user permissions if any
284 #=======================================================================
285 user_perms = sa.query(RepoToPerm, Permission, Repository)\
286 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
287 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
288 .filter(RepoToPerm.user_id == user.user_id).all()
289
290 for perm in user_perms:
291 if perm.Repository.user_id == user.user_id:#set admin if owner
292 p = 'repository.admin'
293 else:
294 p = perm.Permission.permission_name
295 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
296
297
298 #=======================================================================
299 # check if user is part of groups for this repository and fill in
300 # (or replace with higher) permissions
301 #=======================================================================
302 user_perms_from_users_groups = sa.query(UsersGroupToPerm, Permission, Repository,)\
303 .join((Repository, UsersGroupToPerm.repository_id == Repository.repo_id))\
304 .join((Permission, UsersGroupToPerm.permission_id == Permission.permission_id))\
305 .join((UsersGroupMember, UsersGroupToPerm.users_group_id == UsersGroupMember.users_group_id))\
306 .filter(UsersGroupMember.user_id == user.user_id).all()
307
308 for perm in user_perms_from_users_groups:
309 p = perm.Permission.permission_name
310 cur_perm = user.permissions['repositories'][perm.UsersGroupToPerm.repository.repo_name]
311 #overwrite permission only if it's greater than permission given from other sources
312 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
313 user.permissions['repositories'][perm.UsersGroupToPerm.repository.repo_name] = p
314
315 meta.Session.remove()
316 return user
317
318 def get_user(session):
319 """Gets user from session, and wraps permissions into user
320
321 :param session:
322 """
323 user = session.get('rhodecode_user', AuthUser())
324 #if the user is not logged in we check for anonymous access
325 #if user is logged and it's a default user check if we still have anonymous
326 #access enabled
327 if user.user_id is None or user.username == 'default':
328 anonymous_user = UserModel().get_by_username('default', cache=True)
329 if anonymous_user.active is True:
330 #then we set this user is logged in
331 user.is_authenticated = True
332 user.user_id = anonymous_user.user_id
333 else:
334 user.is_authenticated = False
335
336 if user.is_authenticated:
337 user = UserModel().fill_data(user)
338
339 user = fill_perms(user)
340 session['rhodecode_user'] = user
341 session.save()
342 return user
343
344 #===============================================================================
261 #===============================================================================
345 # CHECK DECORATORS
262 # CHECK DECORATORS
346 #===============================================================================
263 #===============================================================================
347 class LoginRequired(object):
264 class LoginRequired(object):
348 """Must be logged in to execute this function else
265 """
349 redirect to login page"""
266 Must be logged in to execute this function else
267 redirect to login page
268
269 :param api_access: if enabled this checks only for valid auth token
270 and grants access based on valid token
271 """
272
273 def __init__(self, api_access=False):
274 self.api_access = api_access
350
275
351 def __call__(self, func):
276 def __call__(self, func):
352 return decorator(self.__wrapper, func)
277 return decorator(self.__wrapper, func)
353
278
354 def __wrapper(self, func, *fargs, **fkwargs):
279 def __wrapper(self, func, *fargs, **fkwargs):
355 user = session.get('rhodecode_user', AuthUser())
280 cls = fargs[0]
356 log.debug('Checking login required for user:%s', user.username)
281 user = cls.rhodecode_user
357 if user.is_authenticated:
282
283 api_access_ok = False
284 if self.api_access:
285 log.debug('Checking API KEY access for %s', cls)
286 if user.api_key == request.GET.get('api_key'):
287 api_access_ok = True
288 else:
289 log.debug("API KEY token not valid")
290
291 log.debug('Checking if %s is authenticated @ %s', user.username, cls)
292 if user.is_authenticated or api_access_ok:
358 log.debug('user %s is authenticated', user.username)
293 log.debug('user %s is authenticated', user.username)
359 return func(*fargs, **fkwargs)
294 return func(*fargs, **fkwargs)
360 else:
295 else:
361 log.warn('user %s not authenticated', user.username)
296 log.warn('user %s NOT authenticated', user.username)
362
297
363 p = ''
298 p = ''
364 if request.environ.get('SCRIPT_NAME') != '/':
299 if request.environ.get('SCRIPT_NAME') != '/':
365 p += request.environ.get('SCRIPT_NAME')
300 p += request.environ.get('SCRIPT_NAME')
366
301
367 p += request.environ.get('PATH_INFO')
302 p += request.environ.get('PATH_INFO')
368 if request.environ.get('QUERY_STRING'):
303 if request.environ.get('QUERY_STRING'):
369 p += '?' + request.environ.get('QUERY_STRING')
304 p += '?' + request.environ.get('QUERY_STRING')
370
305
371 log.debug('redirecting to login page with %s', p)
306 log.debug('redirecting to login page with %s', p)
372 return redirect(url('login_home', came_from=p))
307 return redirect(url('login_home', came_from=p))
373
308
374 class NotAnonymous(object):
309 class NotAnonymous(object):
375 """Must be logged in to execute this function else
310 """Must be logged in to execute this function else
376 redirect to login page"""
311 redirect to login page"""
377
312
378 def __call__(self, func):
313 def __call__(self, func):
379 return decorator(self.__wrapper, func)
314 return decorator(self.__wrapper, func)
380
315
381 def __wrapper(self, func, *fargs, **fkwargs):
316 def __wrapper(self, func, *fargs, **fkwargs):
382 user = session.get('rhodecode_user', AuthUser())
317 cls = fargs[0]
383 log.debug('Checking if user is not anonymous')
318 self.user = cls.rhodecode_user
384
319
385 anonymous = user.username == 'default'
320 log.debug('Checking if user is not anonymous @%s', cls)
321
322 anonymous = self.user.username == 'default'
386
323
387 if anonymous:
324 if anonymous:
388 p = ''
325 p = ''
389 if request.environ.get('SCRIPT_NAME') != '/':
326 if request.environ.get('SCRIPT_NAME') != '/':
390 p += request.environ.get('SCRIPT_NAME')
327 p += request.environ.get('SCRIPT_NAME')
391
328
392 p += request.environ.get('PATH_INFO')
329 p += request.environ.get('PATH_INFO')
393 if request.environ.get('QUERY_STRING'):
330 if request.environ.get('QUERY_STRING'):
394 p += '?' + request.environ.get('QUERY_STRING')
331 p += '?' + request.environ.get('QUERY_STRING')
395
332
396 import rhodecode.lib.helpers as h
333 import rhodecode.lib.helpers as h
397 h.flash(_('You need to be a registered user to perform this action'),
334 h.flash(_('You need to be a registered user to perform this action'),
398 category='warning')
335 category='warning')
399 return redirect(url('login_home', came_from=p))
336 return redirect(url('login_home', came_from=p))
400 else:
337 else:
401 return func(*fargs, **fkwargs)
338 return func(*fargs, **fkwargs)
402
339
403 class PermsDecorator(object):
340 class PermsDecorator(object):
404 """Base class for decorators"""
341 """Base class for controller decorators"""
405
342
406 def __init__(self, *required_perms):
343 def __init__(self, *required_perms):
407 available_perms = config['available_permissions']
344 available_perms = config['available_permissions']
408 for perm in required_perms:
345 for perm in required_perms:
409 if perm not in available_perms:
346 if perm not in available_perms:
410 raise Exception("'%s' permission is not defined" % perm)
347 raise Exception("'%s' permission is not defined" % perm)
411 self.required_perms = set(required_perms)
348 self.required_perms = set(required_perms)
412 self.user_perms = None
349 self.user_perms = None
413
350
414 def __call__(self, func):
351 def __call__(self, func):
415 return decorator(self.__wrapper, func)
352 return decorator(self.__wrapper, func)
416
353
417
354
418 def __wrapper(self, func, *fargs, **fkwargs):
355 def __wrapper(self, func, *fargs, **fkwargs):
419 # _wrapper.__name__ = func.__name__
356 cls = fargs[0]
420 # _wrapper.__dict__.update(func.__dict__)
357 self.user = cls.rhodecode_user
421 # _wrapper.__doc__ = func.__doc__
422 self.user = session.get('rhodecode_user', AuthUser())
423 self.user_perms = self.user.permissions
358 self.user_perms = self.user.permissions
424 log.debug('checking %s permissions %s for %s %s',
359 log.debug('checking %s permissions %s for %s %s',
425 self.__class__.__name__, self.required_perms, func.__name__,
360 self.__class__.__name__, self.required_perms, cls,
426 self.user)
361 self.user)
427
362
428 if self.check_permissions():
363 if self.check_permissions():
429 log.debug('Permission granted for %s %s', func.__name__, self.user)
364 log.debug('Permission granted for %s %s', cls, self.user)
430
431 return func(*fargs, **fkwargs)
365 return func(*fargs, **fkwargs)
432
366
433 else:
367 else:
434 log.warning('Permission denied for %s %s', func.__name__, self.user)
368 log.warning('Permission denied for %s %s', cls, self.user)
435 #redirect with forbidden ret code
369 #redirect with forbidden ret code
436 return abort(403)
370 return abort(403)
437
371
438
372
439
373
440 def check_permissions(self):
374 def check_permissions(self):
441 """Dummy function for overriding"""
375 """Dummy function for overriding"""
442 raise Exception('You have to write this function in child class')
376 raise Exception('You have to write this function in child class')
443
377
444 class HasPermissionAllDecorator(PermsDecorator):
378 class HasPermissionAllDecorator(PermsDecorator):
445 """Checks for access permission for all given predicates. All of them
379 """Checks for access permission for all given predicates. All of them
446 have to be meet in order to fulfill the request
380 have to be meet in order to fulfill the request
447 """
381 """
448
382
449 def check_permissions(self):
383 def check_permissions(self):
450 if self.required_perms.issubset(self.user_perms.get('global')):
384 if self.required_perms.issubset(self.user_perms.get('global')):
451 return True
385 return True
452 return False
386 return False
453
387
454
388
455 class HasPermissionAnyDecorator(PermsDecorator):
389 class HasPermissionAnyDecorator(PermsDecorator):
456 """Checks for access permission for any of given predicates. In order to
390 """Checks for access permission for any of given predicates. In order to
457 fulfill the request any of predicates must be meet
391 fulfill the request any of predicates must be meet
458 """
392 """
459
393
460 def check_permissions(self):
394 def check_permissions(self):
461 if self.required_perms.intersection(self.user_perms.get('global')):
395 if self.required_perms.intersection(self.user_perms.get('global')):
462 return True
396 return True
463 return False
397 return False
464
398
465 class HasRepoPermissionAllDecorator(PermsDecorator):
399 class HasRepoPermissionAllDecorator(PermsDecorator):
466 """Checks for access permission for all given predicates for specific
400 """Checks for access permission for all given predicates for specific
467 repository. All of them have to be meet in order to fulfill the request
401 repository. All of them have to be meet in order to fulfill the request
468 """
402 """
469
403
470 def check_permissions(self):
404 def check_permissions(self):
471 repo_name = get_repo_slug(request)
405 repo_name = get_repo_slug(request)
472 try:
406 try:
473 user_perms = set([self.user_perms['repositories'][repo_name]])
407 user_perms = set([self.user_perms['repositories'][repo_name]])
474 except KeyError:
408 except KeyError:
475 return False
409 return False
476 if self.required_perms.issubset(user_perms):
410 if self.required_perms.issubset(user_perms):
477 return True
411 return True
478 return False
412 return False
479
413
480
414
481 class HasRepoPermissionAnyDecorator(PermsDecorator):
415 class HasRepoPermissionAnyDecorator(PermsDecorator):
482 """Checks for access permission for any of given predicates for specific
416 """Checks for access permission for any of given predicates for specific
483 repository. In order to fulfill the request any of predicates must be meet
417 repository. In order to fulfill the request any of predicates must be meet
484 """
418 """
485
419
486 def check_permissions(self):
420 def check_permissions(self):
487 repo_name = get_repo_slug(request)
421 repo_name = get_repo_slug(request)
488
422
489 try:
423 try:
490 user_perms = set([self.user_perms['repositories'][repo_name]])
424 user_perms = set([self.user_perms['repositories'][repo_name]])
491 except KeyError:
425 except KeyError:
492 return False
426 return False
493 if self.required_perms.intersection(user_perms):
427 if self.required_perms.intersection(user_perms):
494 return True
428 return True
495 return False
429 return False
496 #===============================================================================
430 #===============================================================================
497 # CHECK FUNCTIONS
431 # CHECK FUNCTIONS
498 #===============================================================================
432 #===============================================================================
499
433
500 class PermsFunction(object):
434 class PermsFunction(object):
501 """Base function for other check functions"""
435 """Base function for other check functions"""
502
436
503 def __init__(self, *perms):
437 def __init__(self, *perms):
504 available_perms = config['available_permissions']
438 available_perms = config['available_permissions']
505
439
506 for perm in perms:
440 for perm in perms:
507 if perm not in available_perms:
441 if perm not in available_perms:
508 raise Exception("'%s' permission in not defined" % perm)
442 raise Exception("'%s' permission in not defined" % perm)
509 self.required_perms = set(perms)
443 self.required_perms = set(perms)
510 self.user_perms = None
444 self.user_perms = None
511 self.granted_for = ''
445 self.granted_for = ''
512 self.repo_name = None
446 self.repo_name = None
513
447
514 def __call__(self, check_Location=''):
448 def __call__(self, check_Location=''):
515 user = session.get('rhodecode_user', False)
449 user = session.get('rhodecode_user', False)
516 if not user:
450 if not user:
517 return False
451 return False
518 self.user_perms = user.permissions
452 self.user_perms = user.permissions
519 self.granted_for = user.username
453 self.granted_for = user
520 log.debug('checking %s %s %s', self.__class__.__name__,
454 log.debug('checking %s %s %s', self.__class__.__name__,
521 self.required_perms, user)
455 self.required_perms, user)
522
456
523 if self.check_permissions():
457 if self.check_permissions():
524 log.debug('Permission granted for %s @ %s %s', self.granted_for,
458 log.debug('Permission granted %s @ %s', self.granted_for,
525 check_Location, user)
459 check_Location or 'unspecified location')
526 return True
460 return True
527
461
528 else:
462 else:
529 log.warning('Permission denied for %s @ %s %s', self.granted_for,
463 log.warning('Permission denied for %s @ %s', self.granted_for,
530 check_Location, user)
464 check_Location or 'unspecified location')
531 return False
465 return False
532
466
533 def check_permissions(self):
467 def check_permissions(self):
534 """Dummy function for overriding"""
468 """Dummy function for overriding"""
535 raise Exception('You have to write this function in child class')
469 raise Exception('You have to write this function in child class')
536
470
537 class HasPermissionAll(PermsFunction):
471 class HasPermissionAll(PermsFunction):
538 def check_permissions(self):
472 def check_permissions(self):
539 if self.required_perms.issubset(self.user_perms.get('global')):
473 if self.required_perms.issubset(self.user_perms.get('global')):
540 return True
474 return True
541 return False
475 return False
542
476
543 class HasPermissionAny(PermsFunction):
477 class HasPermissionAny(PermsFunction):
544 def check_permissions(self):
478 def check_permissions(self):
545 if self.required_perms.intersection(self.user_perms.get('global')):
479 if self.required_perms.intersection(self.user_perms.get('global')):
546 return True
480 return True
547 return False
481 return False
548
482
549 class HasRepoPermissionAll(PermsFunction):
483 class HasRepoPermissionAll(PermsFunction):
550
484
551 def __call__(self, repo_name=None, check_Location=''):
485 def __call__(self, repo_name=None, check_Location=''):
552 self.repo_name = repo_name
486 self.repo_name = repo_name
553 return super(HasRepoPermissionAll, self).__call__(check_Location)
487 return super(HasRepoPermissionAll, self).__call__(check_Location)
554
488
555 def check_permissions(self):
489 def check_permissions(self):
556 if not self.repo_name:
490 if not self.repo_name:
557 self.repo_name = get_repo_slug(request)
491 self.repo_name = get_repo_slug(request)
558
492
559 try:
493 try:
560 self.user_perms = set([self.user_perms['repositories']\
494 self.user_perms = set([self.user_perms['repositories']\
561 [self.repo_name]])
495 [self.repo_name]])
562 except KeyError:
496 except KeyError:
563 return False
497 return False
564 self.granted_for = self.repo_name
498 self.granted_for = self.repo_name
565 if self.required_perms.issubset(self.user_perms):
499 if self.required_perms.issubset(self.user_perms):
566 return True
500 return True
567 return False
501 return False
568
502
569 class HasRepoPermissionAny(PermsFunction):
503 class HasRepoPermissionAny(PermsFunction):
570
504
571 def __call__(self, repo_name=None, check_Location=''):
505 def __call__(self, repo_name=None, check_Location=''):
572 self.repo_name = repo_name
506 self.repo_name = repo_name
573 return super(HasRepoPermissionAny, self).__call__(check_Location)
507 return super(HasRepoPermissionAny, self).__call__(check_Location)
574
508
575 def check_permissions(self):
509 def check_permissions(self):
576 if not self.repo_name:
510 if not self.repo_name:
577 self.repo_name = get_repo_slug(request)
511 self.repo_name = get_repo_slug(request)
578
512
579 try:
513 try:
580 self.user_perms = set([self.user_perms['repositories']\
514 self.user_perms = set([self.user_perms['repositories']\
581 [self.repo_name]])
515 [self.repo_name]])
582 except KeyError:
516 except KeyError:
583 return False
517 return False
584 self.granted_for = self.repo_name
518 self.granted_for = self.repo_name
585 if self.required_perms.intersection(self.user_perms):
519 if self.required_perms.intersection(self.user_perms):
586 return True
520 return True
587 return False
521 return False
588
522
589 #===============================================================================
523 #===============================================================================
590 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
524 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
591 #===============================================================================
525 #===============================================================================
592
526
593 class HasPermissionAnyMiddleware(object):
527 class HasPermissionAnyMiddleware(object):
594 def __init__(self, *perms):
528 def __init__(self, *perms):
595 self.required_perms = set(perms)
529 self.required_perms = set(perms)
596
530
597 def __call__(self, user, repo_name):
531 def __call__(self, user, repo_name):
598 usr = AuthUser()
532 usr = AuthUser(user.user_id)
599 usr.user_id = user.user_id
600 usr.username = user.username
601 usr.is_admin = user.admin
602
603 try:
533 try:
604 self.user_perms = set([fill_perms(usr)\
534 self.user_perms = set([usr.permissions['repositories'][repo_name]])
605 .permissions['repositories'][repo_name]])
606 except:
535 except:
607 self.user_perms = set()
536 self.user_perms = set()
608 self.granted_for = ''
537 self.granted_for = ''
609 self.username = user.username
538 self.username = user.username
610 self.repo_name = repo_name
539 self.repo_name = repo_name
611 return self.check_permissions()
540 return self.check_permissions()
612
541
613 def check_permissions(self):
542 def check_permissions(self):
614 log.debug('checking mercurial protocol '
543 log.debug('checking mercurial protocol '
615 'permissions %s for user:%s repository:%s', self.user_perms,
544 'permissions %s for user:%s repository:%s', self.user_perms,
616 self.username, self.repo_name)
545 self.username, self.repo_name)
617 if self.required_perms.intersection(self.user_perms):
546 if self.required_perms.intersection(self.user_perms):
618 log.debug('permission granted')
547 log.debug('permission granted')
619 return True
548 return True
620 log.debug('permission denied')
549 log.debug('permission denied')
621 return False
550 return False
@@ -1,63 +1,70 b''
1 """The base Controller API
1 """The base Controller API
2
2
3 Provides the BaseController class for subclassing.
3 Provides the BaseController class for subclassing.
4 """
4 """
5 from pylons import config, tmpl_context as c, request, session
5 from pylons import config, tmpl_context as c, request, session
6 from pylons.controllers import WSGIController
6 from pylons.controllers import WSGIController
7 from pylons.templating import render_mako as render
7 from pylons.templating import render_mako as render
8 from rhodecode import __version__
8 from rhodecode import __version__
9 from rhodecode.lib import auth
9 from rhodecode.lib.auth import AuthUser
10 from rhodecode.lib.utils import get_repo_slug
10 from rhodecode.lib.utils import get_repo_slug
11 from rhodecode.model import meta
11 from rhodecode.model import meta
12 from rhodecode.model.scm import ScmModel
12 from rhodecode.model.scm import ScmModel
13 from rhodecode import BACKENDS
13 from rhodecode import BACKENDS
14
14
15 class BaseController(WSGIController):
15 class BaseController(WSGIController):
16
16
17 def __before__(self):
17 def __before__(self):
18 c.rhodecode_version = __version__
18 c.rhodecode_version = __version__
19 c.rhodecode_name = config.get('rhodecode_title')
19 c.rhodecode_name = config.get('rhodecode_title')
20 c.ga_code = config.get('rhodecode_ga_code')
20 c.ga_code = config.get('rhodecode_ga_code')
21 c.repo_name = get_repo_slug(request)
21 c.repo_name = get_repo_slug(request)
22 c.backends = BACKENDS.keys()
22 c.backends = BACKENDS.keys()
23 self.cut_off_limit = int(config.get('cut_off_limit'))
23 self.cut_off_limit = int(config.get('cut_off_limit'))
24
24
25 self.sa = meta.Session()
25 self.sa = meta.Session()
26 self.scm_model = ScmModel(self.sa)
26 self.scm_model = ScmModel(self.sa)
27 c.cached_repo_list = self.scm_model.get_repos()
27 c.cached_repo_list = self.scm_model.get_repos()
28 #c.unread_journal = scm_model.get_unread_journal()
28 #c.unread_journal = scm_model.get_unread_journal()
29
29
30 def __call__(self, environ, start_response):
30 def __call__(self, environ, start_response):
31 """Invoke the Controller"""
31 """Invoke the Controller"""
32 # WSGIController.__call__ dispatches to the Controller method
32 # WSGIController.__call__ dispatches to the Controller method
33 # the request is routed to. This routing information is
33 # the request is routed to. This routing information is
34 # available in environ['pylons.routes_dict']
34 # available in environ['pylons.routes_dict']
35 try:
35 try:
36 #putting this here makes sure that we update permissions every time
36 #putting this here makes sure that we update permissions every time
37 self.rhodecode_user = c.rhodecode_user = auth.get_user(session)
37 api_key = request.GET.get('api_key')
38 user_id = getattr(session.get('rhodecode_user'), 'user_id', None)
39 self.rhodecode_user = c.rhodecode_user = AuthUser(user_id, api_key)
40 self.rhodecode_user.set_authenticated(
41 getattr(session.get('rhodecode_user'),
42 'is_authenticated', False))
43 session['rhodecode_user'] = self.rhodecode_user
44 session.save()
38 return WSGIController.__call__(self, environ, start_response)
45 return WSGIController.__call__(self, environ, start_response)
39 finally:
46 finally:
40 meta.Session.remove()
47 meta.Session.remove()
41
48
42
49
43 class BaseRepoController(BaseController):
50 class BaseRepoController(BaseController):
44 """
51 """
45 Base class for controllers responsible for loading all needed data
52 Base class for controllers responsible for loading all needed data
46 for those controllers, loaded items are
53 for those controllers, loaded items are
47
54
48 c.rhodecode_repo: instance of scm repository (taken from cache)
55 c.rhodecode_repo: instance of scm repository (taken from cache)
49
56
50 """
57 """
51
58
52 def __before__(self):
59 def __before__(self):
53 super(BaseRepoController, self).__before__()
60 super(BaseRepoController, self).__before__()
54 if c.repo_name:
61 if c.repo_name:
55
62
56 c.rhodecode_repo, dbrepo = self.scm_model.get(c.repo_name,
63 c.rhodecode_repo, dbrepo = self.scm_model.get(c.repo_name,
57 retval='repo')
64 retval='repo')
58 if c.rhodecode_repo:
65 if c.rhodecode_repo:
59 c.repository_followers = self.scm_model.get_followers(c.repo_name)
66 c.repository_followers = self.scm_model.get_followers(c.repo_name)
60 c.repository_forks = self.scm_model.get_forks(c.repo_name)
67 c.repository_forks = self.scm_model.get_forks(c.repo_name)
61 else:
68 else:
62 c.repository_followers = 0
69 c.repository_followers = 0
63 c.repository_forks = 0
70 c.repository_forks = 0
@@ -1,230 +1,343 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.user
3 rhodecode.model.user
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 users model for RhodeCode
6 users model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29 import traceback
29 import traceback
30
30
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32
32
33 from rhodecode.model import BaseModel
33 from rhodecode.model import BaseModel
34 from rhodecode.model.caching_query import FromCache
34 from rhodecode.model.caching_query import FromCache
35 from rhodecode.model.db import User
35 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
36
36 UserToPerm, UsersGroupToPerm, UsersGroupMember
37 from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
37 from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
38
38
39 from sqlalchemy.exc import DatabaseError
39 from sqlalchemy.exc import DatabaseError
40 from rhodecode.lib import generate_api_key
40 from rhodecode.lib import generate_api_key
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44 PERM_WEIGHTS = {'repository.none':0,
45 'repository.read':1,
46 'repository.write':3,
47 'repository.admin':3}
48
44 class UserModel(BaseModel):
49 class UserModel(BaseModel):
45
50
46 def get(self, user_id, cache=False):
51 def get(self, user_id, cache=False):
47 user = self.sa.query(User)
52 user = self.sa.query(User)
48 if cache:
53 if cache:
49 user = user.options(FromCache("sql_cache_short",
54 user = user.options(FromCache("sql_cache_short",
50 "get_user_%s" % user_id))
55 "get_user_%s" % user_id))
51 return user.get(user_id)
56 return user.get(user_id)
52
57
53
58
54 def get_by_username(self, username, cache=False, case_insensitive=False):
59 def get_by_username(self, username, cache=False, case_insensitive=False):
55
60
56 if case_insensitive:
61 if case_insensitive:
57 user = self.sa.query(User).filter(User.username.ilike(username))
62 user = self.sa.query(User).filter(User.username.ilike(username))
58 else:
63 else:
59 user = self.sa.query(User)\
64 user = self.sa.query(User)\
60 .filter(User.username == username)
65 .filter(User.username == username)
61 if cache:
66 if cache:
62 user = user.options(FromCache("sql_cache_short",
67 user = user.options(FromCache("sql_cache_short",
63 "get_user_%s" % username))
68 "get_user_%s" % username))
64 return user.scalar()
69 return user.scalar()
65
70
71
72 def get_by_api_key(self, api_key, cache=False):
73
74 user = self.sa.query(User)\
75 .filter(User.api_key == api_key)
76 if cache:
77 user = user.options(FromCache("sql_cache_short",
78 "get_user_%s" % api_key))
79 return user.scalar()
80
66 def create(self, form_data):
81 def create(self, form_data):
67 try:
82 try:
68 new_user = User()
83 new_user = User()
69 for k, v in form_data.items():
84 for k, v in form_data.items():
70 setattr(new_user, k, v)
85 setattr(new_user, k, v)
71
86
72 new_user.api_key = generate_api_key(form_data['username'])
87 new_user.api_key = generate_api_key(form_data['username'])
73 self.sa.add(new_user)
88 self.sa.add(new_user)
74 self.sa.commit()
89 self.sa.commit()
75 except:
90 except:
76 log.error(traceback.format_exc())
91 log.error(traceback.format_exc())
77 self.sa.rollback()
92 self.sa.rollback()
78 raise
93 raise
79
94
80 def create_ldap(self, username, password, user_dn, attrs):
95 def create_ldap(self, username, password, user_dn, attrs):
81 """
96 """
82 Checks if user is in database, if not creates this user marked
97 Checks if user is in database, if not creates this user marked
83 as ldap user
98 as ldap user
84 :param username:
99 :param username:
85 :param password:
100 :param password:
86 :param user_dn:
101 :param user_dn:
87 :param attrs:
102 :param attrs:
88 """
103 """
89 from rhodecode.lib.auth import get_crypt_password
104 from rhodecode.lib.auth import get_crypt_password
90 log.debug('Checking for such ldap account in RhodeCode database')
105 log.debug('Checking for such ldap account in RhodeCode database')
91 if self.get_by_username(username, case_insensitive=True) is None:
106 if self.get_by_username(username, case_insensitive=True) is None:
92 try:
107 try:
93 new_user = User()
108 new_user = User()
94 new_user.username = username.lower() # add ldap account always lowercase
109 new_user.username = username.lower() # add ldap account always lowercase
95 new_user.password = get_crypt_password(password)
110 new_user.password = get_crypt_password(password)
96 new_user.api_key = generate_api_key(username)
111 new_user.api_key = generate_api_key(username)
97 new_user.email = attrs['email']
112 new_user.email = attrs['email']
98 new_user.active = True
113 new_user.active = True
99 new_user.ldap_dn = user_dn
114 new_user.ldap_dn = user_dn
100 new_user.name = attrs['name']
115 new_user.name = attrs['name']
101 new_user.lastname = attrs['lastname']
116 new_user.lastname = attrs['lastname']
102
117
103
118
104 self.sa.add(new_user)
119 self.sa.add(new_user)
105 self.sa.commit()
120 self.sa.commit()
106 return True
121 return True
107 except (DatabaseError,):
122 except (DatabaseError,):
108 log.error(traceback.format_exc())
123 log.error(traceback.format_exc())
109 self.sa.rollback()
124 self.sa.rollback()
110 raise
125 raise
111 log.debug('this %s user exists skipping creation of ldap account',
126 log.debug('this %s user exists skipping creation of ldap account',
112 username)
127 username)
113 return False
128 return False
114
129
115 def create_registration(self, form_data):
130 def create_registration(self, form_data):
116 from rhodecode.lib.celerylib import tasks, run_task
131 from rhodecode.lib.celerylib import tasks, run_task
117 try:
132 try:
118 new_user = User()
133 new_user = User()
119 for k, v in form_data.items():
134 for k, v in form_data.items():
120 if k != 'admin':
135 if k != 'admin':
121 setattr(new_user, k, v)
136 setattr(new_user, k, v)
122
137
123 self.sa.add(new_user)
138 self.sa.add(new_user)
124 self.sa.commit()
139 self.sa.commit()
125 body = ('New user registration\n'
140 body = ('New user registration\n'
126 'username: %s\n'
141 'username: %s\n'
127 'email: %s\n')
142 'email: %s\n')
128 body = body % (form_data['username'], form_data['email'])
143 body = body % (form_data['username'], form_data['email'])
129
144
130 run_task(tasks.send_email, None,
145 run_task(tasks.send_email, None,
131 _('[RhodeCode] New User registration'),
146 _('[RhodeCode] New User registration'),
132 body)
147 body)
133 except:
148 except:
134 log.error(traceback.format_exc())
149 log.error(traceback.format_exc())
135 self.sa.rollback()
150 self.sa.rollback()
136 raise
151 raise
137
152
138 def update(self, user_id, form_data):
153 def update(self, user_id, form_data):
139 try:
154 try:
140 user = self.get(user_id, cache=False)
155 user = self.get(user_id, cache=False)
141 if user.username == 'default':
156 if user.username == 'default':
142 raise DefaultUserException(
157 raise DefaultUserException(
143 _("You can't Edit this user since it's"
158 _("You can't Edit this user since it's"
144 " crucial for entire application"))
159 " crucial for entire application"))
145
160
146 for k, v in form_data.items():
161 for k, v in form_data.items():
147 if k == 'new_password' and v != '':
162 if k == 'new_password' and v != '':
148 user.password = v
163 user.password = v
149 user.api_key = generate_api_key(user.username)
164 user.api_key = generate_api_key(user.username)
150 else:
165 else:
151 setattr(user, k, v)
166 setattr(user, k, v)
152
167
153 self.sa.add(user)
168 self.sa.add(user)
154 self.sa.commit()
169 self.sa.commit()
155 except:
170 except:
156 log.error(traceback.format_exc())
171 log.error(traceback.format_exc())
157 self.sa.rollback()
172 self.sa.rollback()
158 raise
173 raise
159
174
160 def update_my_account(self, user_id, form_data):
175 def update_my_account(self, user_id, form_data):
161 try:
176 try:
162 user = self.get(user_id, cache=False)
177 user = self.get(user_id, cache=False)
163 if user.username == 'default':
178 if user.username == 'default':
164 raise DefaultUserException(
179 raise DefaultUserException(
165 _("You can't Edit this user since it's"
180 _("You can't Edit this user since it's"
166 " crucial for entire application"))
181 " crucial for entire application"))
167 for k, v in form_data.items():
182 for k, v in form_data.items():
168 if k == 'new_password' and v != '':
183 if k == 'new_password' and v != '':
169 user.password = v
184 user.password = v
170 user.api_key = generate_api_key(user.username)
185 user.api_key = generate_api_key(user.username)
171 else:
186 else:
172 if k not in ['admin', 'active']:
187 if k not in ['admin', 'active']:
173 setattr(user, k, v)
188 setattr(user, k, v)
174
189
175 self.sa.add(user)
190 self.sa.add(user)
176 self.sa.commit()
191 self.sa.commit()
177 except:
192 except:
178 log.error(traceback.format_exc())
193 log.error(traceback.format_exc())
179 self.sa.rollback()
194 self.sa.rollback()
180 raise
195 raise
181
196
182 def delete(self, user_id):
197 def delete(self, user_id):
183 try:
198 try:
184 user = self.get(user_id, cache=False)
199 user = self.get(user_id, cache=False)
185 if user.username == 'default':
200 if user.username == 'default':
186 raise DefaultUserException(
201 raise DefaultUserException(
187 _("You can't remove this user since it's"
202 _("You can't remove this user since it's"
188 " crucial for entire application"))
203 " crucial for entire application"))
189 if user.repositories:
204 if user.repositories:
190 raise UserOwnsReposException(_('This user still owns %s '
205 raise UserOwnsReposException(_('This user still owns %s '
191 'repositories and cannot be '
206 'repositories and cannot be '
192 'removed. Switch owners or '
207 'removed. Switch owners or '
193 'remove those repositories') \
208 'remove those repositories') \
194 % user.repositories)
209 % user.repositories)
195 self.sa.delete(user)
210 self.sa.delete(user)
196 self.sa.commit()
211 self.sa.commit()
197 except:
212 except:
198 log.error(traceback.format_exc())
213 log.error(traceback.format_exc())
199 self.sa.rollback()
214 self.sa.rollback()
200 raise
215 raise
201
216
202 def reset_password(self, data):
217 def reset_password(self, data):
203 from rhodecode.lib.celerylib import tasks, run_task
218 from rhodecode.lib.celerylib import tasks, run_task
204 run_task(tasks.reset_user_password, data['email'])
219 run_task(tasks.reset_user_password, data['email'])
205
220
206
221
207 def fill_data(self, user):
222 def fill_data(self, auth_user, user_id=None, api_key=None):
208 """
223 """
209 Fills user data with those from database and log out user if not
224 Fetches auth_user by user_id,or api_key if present.
225 Fills auth_user attributes with those taken from database.
226 Additionally set's is_authenitated if lookup fails
210 present in database
227 present in database
211 :param user:
228
229 :param auth_user: instance of user to set attributes
230 :param user_id: user id to fetch by
231 :param api_key: api key to fetch by
212 """
232 """
233 if not user_id and not not api_key:
234 raise Exception('You need to pass user_id or api_key')
213
235
214 if not hasattr(user, 'user_id') or user.user_id is None:
236 try:
215 raise Exception('passed in user has to have the user_id attribute')
237 if api_key:
238 dbuser = self.get_by_api_key(api_key)
239 else:
240 dbuser = self.get(user_id)
241
242 log.debug('filling %s data', dbuser)
243 for k, v in dbuser.get_dict().items():
244 setattr(auth_user, k, v)
245
246 except:
247 log.error(traceback.format_exc())
248 auth_user.is_authenticated = False
249
250 return auth_user
216
251
217
252
218 log.debug('filling auth user data')
253 def fill_perms(self, user):
219 try:
254 """Fills user permission attribute with permissions taken from database
220 dbuser = self.get(user.user_id)
255 works for permissions given for repositories, and for permissions that
221 user.username = dbuser.username
256 as part of beeing group member
222 user.is_admin = dbuser.admin
257
223 user.name = dbuser.name
258 :param user: user instance to fill his perms
224 user.lastname = dbuser.lastname
259 """
225 user.email = dbuser.email
260
226 except:
261 user.permissions['repositories'] = {}
227 log.error(traceback.format_exc())
262 user.permissions['global'] = set()
228 user.is_authenticated = False
263
264 #===========================================================================
265 # fetch default permissions
266 #===========================================================================
267 default_user = self.get_by_username('default', cache=True)
268
269 default_perms = self.sa.query(RepoToPerm, Repository, Permission)\
270 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
271 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
272 .filter(RepoToPerm.user == default_user).all()
273
274 if user.is_admin:
275 #=======================================================================
276 # #admin have all default rights set to admin
277 #=======================================================================
278 user.permissions['global'].add('hg.admin')
279
280 for perm in default_perms:
281 p = 'repository.admin'
282 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
283
284 else:
285 #=======================================================================
286 # set default permissions
287 #=======================================================================
288
289 #default global
290 default_global_perms = self.sa.query(UserToPerm)\
291 .filter(UserToPerm.user == self.sa.query(User)\
292 .filter(User.username == 'default').one())
293
294 for perm in default_global_perms:
295 user.permissions['global'].add(perm.permission.permission_name)
296
297 #default for repositories
298 for perm in default_perms:
299 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
300 #diself.sable defaults for private repos,
301 p = 'repository.none'
302 elif perm.Repository.user_id == user.user_id:
303 #set admin if owner
304 p = 'repository.admin'
305 else:
306 p = perm.Permission.permission_name
307
308 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
309
310 #=======================================================================
311 # overwrite default with user permissions if any
312 #=======================================================================
313 user_perms = self.sa.query(RepoToPerm, Permission, Repository)\
314 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
315 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
316 .filter(RepoToPerm.user_id == user.user_id).all()
317
318 for perm in user_perms:
319 if perm.Repository.user_id == user.user_id:#set admin if owner
320 p = 'repository.admin'
321 else:
322 p = perm.Permission.permission_name
323 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
324
325
326 #=======================================================================
327 # check if user is part of groups for this repository and fill in
328 # (or replace with higher) permissions
329 #=======================================================================
330 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm, Permission, Repository,)\
331 .join((Repository, UsersGroupToPerm.repository_id == Repository.repo_id))\
332 .join((Permission, UsersGroupToPerm.permission_id == Permission.permission_id))\
333 .join((UsersGroupMember, UsersGroupToPerm.users_group_id == UsersGroupMember.users_group_id))\
334 .filter(UsersGroupMember.user_id == user.user_id).all()
335
336 for perm in user_perms_from_users_groups:
337 p = perm.Permission.permission_name
338 cur_perm = user.permissions['repositories'][perm.UsersGroupToPerm.repository.repo_name]
339 #overwrite permission only if it's greater than permission given from other sources
340 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
341 user.permissions['repositories'][perm.UsersGroupToPerm.repository.repo_name] = p
229
342
230 return user
343 return user
General Comments 0
You need to be logged in to leave comments. Login now