##// END OF EJS Templates
auth: disable password change when not using internal auth
Mads Kiilerich -
r5345:de9a3152 default
parent child Browse files
Show More
@@ -1,274 +1,278 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU General Public License
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 """
14 """
15 kallithea.controllers.admin.my_account
15 kallithea.controllers.admin.my_account
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17
17
18 my account controller for Kallithea admin
18 my account controller for Kallithea admin
19
19
20 This file was forked by the Kallithea project in July 2014.
20 This file was forked by the Kallithea project in July 2014.
21 Original author and date, and relevant copyright and licensing information is below:
21 Original author and date, and relevant copyright and licensing information is below:
22 :created_on: August 20, 2013
22 :created_on: August 20, 2013
23 :author: marcink
23 :author: marcink
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 :license: GPLv3, see LICENSE.md for more details.
25 :license: GPLv3, see LICENSE.md for more details.
26 """
26 """
27
27
28 import logging
28 import logging
29 import traceback
29 import traceback
30 import formencode
30 import formencode
31
31
32 from sqlalchemy import func
32 from sqlalchemy import func
33 from formencode import htmlfill
33 from formencode import htmlfill
34 from pylons import request, tmpl_context as c, url
34 from pylons import request, tmpl_context as c, url
35 from pylons.controllers.util import redirect
35 from pylons.controllers.util import redirect
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 from kallithea import EXTERN_TYPE_INTERNAL
38 from kallithea import EXTERN_TYPE_INTERNAL
39 from kallithea.lib import helpers as h
39 from kallithea.lib import helpers as h
40 from kallithea.lib import auth_modules
40 from kallithea.lib import auth_modules
41 from kallithea.lib.auth import LoginRequired, NotAnonymous, AuthUser
41 from kallithea.lib.auth import LoginRequired, NotAnonymous, AuthUser
42 from kallithea.lib.base import BaseController, render
42 from kallithea.lib.base import BaseController, render
43 from kallithea.lib.utils2 import generate_api_key, safe_int
43 from kallithea.lib.utils2 import generate_api_key, safe_int
44 from kallithea.lib.compat import json
44 from kallithea.lib.compat import json
45 from kallithea.model.db import Repository, \
45 from kallithea.model.db import Repository, \
46 UserEmailMap, UserApiKeys, User, UserFollowing
46 UserEmailMap, UserApiKeys, User, UserFollowing
47 from kallithea.model.forms import UserForm, PasswordChangeForm
47 from kallithea.model.forms import UserForm, PasswordChangeForm
48 from kallithea.model.user import UserModel
48 from kallithea.model.user import UserModel
49 from kallithea.model.repo import RepoModel
49 from kallithea.model.repo import RepoModel
50 from kallithea.model.api_key import ApiKeyModel
50 from kallithea.model.api_key import ApiKeyModel
51 from kallithea.model.meta import Session
51 from kallithea.model.meta import Session
52
52
53 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
54
54
55
55
56 class MyAccountController(BaseController):
56 class MyAccountController(BaseController):
57 """REST Controller styled on the Atom Publishing Protocol"""
57 """REST Controller styled on the Atom Publishing Protocol"""
58 # To properly map this controller, ensure your config/routing.py
58 # To properly map this controller, ensure your config/routing.py
59 # file has a resource setup:
59 # file has a resource setup:
60 # map.resource('setting', 'settings', controller='admin/settings',
60 # map.resource('setting', 'settings', controller='admin/settings',
61 # path_prefix='/admin', name_prefix='admin_')
61 # path_prefix='/admin', name_prefix='admin_')
62
62
63 @LoginRequired()
63 @LoginRequired()
64 @NotAnonymous()
64 @NotAnonymous()
65 def __before__(self):
65 def __before__(self):
66 super(MyAccountController, self).__before__()
66 super(MyAccountController, self).__before__()
67
67
68 def __load_data(self):
68 def __load_data(self):
69 c.user = User.get(self.authuser.user_id)
69 c.user = User.get(self.authuser.user_id)
70 if c.user.username == User.DEFAULT_USER:
70 if c.user.username == User.DEFAULT_USER:
71 h.flash(_("You can't edit this user since it's"
71 h.flash(_("You can't edit this user since it's"
72 " crucial for entire application"), category='warning')
72 " crucial for entire application"), category='warning')
73 return redirect(url('users'))
73 return redirect(url('users'))
74 c.EXTERN_TYPE_INTERNAL = EXTERN_TYPE_INTERNAL
74 c.EXTERN_TYPE_INTERNAL = EXTERN_TYPE_INTERNAL
75
75
76 def _load_my_repos_data(self, watched=False):
76 def _load_my_repos_data(self, watched=False):
77 if watched:
77 if watched:
78 admin = False
78 admin = False
79 repos_list = [x.follows_repository for x in
79 repos_list = [x.follows_repository for x in
80 Session().query(UserFollowing).filter(
80 Session().query(UserFollowing).filter(
81 UserFollowing.user_id ==
81 UserFollowing.user_id ==
82 self.authuser.user_id).all()]
82 self.authuser.user_id).all()]
83 else:
83 else:
84 admin = True
84 admin = True
85 repos_list = Session().query(Repository)\
85 repos_list = Session().query(Repository)\
86 .filter(Repository.user_id ==
86 .filter(Repository.user_id ==
87 self.authuser.user_id)\
87 self.authuser.user_id)\
88 .order_by(func.lower(Repository.repo_name)).all()
88 .order_by(func.lower(Repository.repo_name)).all()
89
89
90 repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list,
90 repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list,
91 admin=admin)
91 admin=admin)
92 #json used to render the grid
92 #json used to render the grid
93 return json.dumps(repos_data)
93 return json.dumps(repos_data)
94
94
95 def my_account(self):
95 def my_account(self):
96 """
96 """
97 GET /_admin/my_account Displays info about my account
97 GET /_admin/my_account Displays info about my account
98 """
98 """
99 # url('my_account')
99 # url('my_account')
100 c.active = 'profile'
100 c.active = 'profile'
101 self.__load_data()
101 self.__load_data()
102 c.perm_user = AuthUser(user_id=self.authuser.user_id)
102 c.perm_user = AuthUser(user_id=self.authuser.user_id)
103 c.ip_addr = self.ip_addr
103 c.ip_addr = self.ip_addr
104 managed_fields = auth_modules.get_managed_fields(c.user)
104 managed_fields = auth_modules.get_managed_fields(c.user)
105 def_user_perms = User.get_default_user().AuthUser.permissions['global']
105 def_user_perms = User.get_default_user().AuthUser.permissions['global']
106 if 'hg.register.none' in def_user_perms:
106 if 'hg.register.none' in def_user_perms:
107 managed_fields.extend(['username', 'firstname', 'lastname', 'email'])
107 managed_fields.extend(['username', 'firstname', 'lastname', 'email'])
108
108
109 c.readonly = lambda n: 'readonly' if n in managed_fields else None
109 c.readonly = lambda n: 'readonly' if n in managed_fields else None
110
110
111 defaults = c.user.get_dict()
111 defaults = c.user.get_dict()
112 update = False
112 update = False
113 if request.POST:
113 if request.POST:
114 _form = UserForm(edit=True,
114 _form = UserForm(edit=True,
115 old_data={'user_id': self.authuser.user_id,
115 old_data={'user_id': self.authuser.user_id,
116 'email': self.authuser.email})()
116 'email': self.authuser.email})()
117 form_result = {}
117 form_result = {}
118 try:
118 try:
119 post_data = dict(request.POST)
119 post_data = dict(request.POST)
120 post_data['new_password'] = ''
120 post_data['new_password'] = ''
121 post_data['password_confirmation'] = ''
121 post_data['password_confirmation'] = ''
122 form_result = _form.to_python(post_data)
122 form_result = _form.to_python(post_data)
123 # skip updating those attrs for my account
123 # skip updating those attrs for my account
124 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
124 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
125 'new_password', 'password_confirmation',
125 'new_password', 'password_confirmation',
126 ] + managed_fields
126 ] + managed_fields
127
127
128 UserModel().update(self.authuser.user_id, form_result,
128 UserModel().update(self.authuser.user_id, form_result,
129 skip_attrs=skip_attrs)
129 skip_attrs=skip_attrs)
130 h.flash(_('Your account was updated successfully'),
130 h.flash(_('Your account was updated successfully'),
131 category='success')
131 category='success')
132 Session().commit()
132 Session().commit()
133 update = True
133 update = True
134
134
135 except formencode.Invalid, errors:
135 except formencode.Invalid, errors:
136 return htmlfill.render(
136 return htmlfill.render(
137 render('admin/my_account/my_account.html'),
137 render('admin/my_account/my_account.html'),
138 defaults=errors.value,
138 defaults=errors.value,
139 errors=errors.error_dict or {},
139 errors=errors.error_dict or {},
140 prefix_error=False,
140 prefix_error=False,
141 encoding="UTF-8",
141 encoding="UTF-8",
142 force_defaults=False)
142 force_defaults=False)
143 except Exception:
143 except Exception:
144 log.error(traceback.format_exc())
144 log.error(traceback.format_exc())
145 h.flash(_('Error occurred during update of user %s') \
145 h.flash(_('Error occurred during update of user %s') \
146 % form_result.get('username'), category='error')
146 % form_result.get('username'), category='error')
147 if update:
147 if update:
148 return redirect('my_account')
148 return redirect('my_account')
149 return htmlfill.render(
149 return htmlfill.render(
150 render('admin/my_account/my_account.html'),
150 render('admin/my_account/my_account.html'),
151 defaults=defaults,
151 defaults=defaults,
152 encoding="UTF-8",
152 encoding="UTF-8",
153 force_defaults=False)
153 force_defaults=False)
154
154
155 def my_account_password(self):
155 def my_account_password(self):
156 c.active = 'password'
156 c.active = 'password'
157 self.__load_data()
157 self.__load_data()
158 if request.POST:
158
159 managed_fields = auth_modules.get_managed_fields(c.user)
160 c.can_change_password = 'password' not in managed_fields
161
162 if request.POST and c.can_change_password:
159 _form = PasswordChangeForm(self.authuser.username)()
163 _form = PasswordChangeForm(self.authuser.username)()
160 try:
164 try:
161 form_result = _form.to_python(request.POST)
165 form_result = _form.to_python(request.POST)
162 UserModel().update(self.authuser.user_id, form_result)
166 UserModel().update(self.authuser.user_id, form_result)
163 Session().commit()
167 Session().commit()
164 h.flash(_("Successfully updated password"), category='success')
168 h.flash(_("Successfully updated password"), category='success')
165 except formencode.Invalid as errors:
169 except formencode.Invalid as errors:
166 return htmlfill.render(
170 return htmlfill.render(
167 render('admin/my_account/my_account.html'),
171 render('admin/my_account/my_account.html'),
168 defaults=errors.value,
172 defaults=errors.value,
169 errors=errors.error_dict or {},
173 errors=errors.error_dict or {},
170 prefix_error=False,
174 prefix_error=False,
171 encoding="UTF-8",
175 encoding="UTF-8",
172 force_defaults=False)
176 force_defaults=False)
173 except Exception:
177 except Exception:
174 log.error(traceback.format_exc())
178 log.error(traceback.format_exc())
175 h.flash(_('Error occurred during update of user password'),
179 h.flash(_('Error occurred during update of user password'),
176 category='error')
180 category='error')
177 return render('admin/my_account/my_account.html')
181 return render('admin/my_account/my_account.html')
178
182
179 def my_account_repos(self):
183 def my_account_repos(self):
180 c.active = 'repos'
184 c.active = 'repos'
181 self.__load_data()
185 self.__load_data()
182
186
183 #json used to render the grid
187 #json used to render the grid
184 c.data = self._load_my_repos_data()
188 c.data = self._load_my_repos_data()
185 return render('admin/my_account/my_account.html')
189 return render('admin/my_account/my_account.html')
186
190
187 def my_account_watched(self):
191 def my_account_watched(self):
188 c.active = 'watched'
192 c.active = 'watched'
189 self.__load_data()
193 self.__load_data()
190
194
191 #json used to render the grid
195 #json used to render the grid
192 c.data = self._load_my_repos_data(watched=True)
196 c.data = self._load_my_repos_data(watched=True)
193 return render('admin/my_account/my_account.html')
197 return render('admin/my_account/my_account.html')
194
198
195 def my_account_perms(self):
199 def my_account_perms(self):
196 c.active = 'perms'
200 c.active = 'perms'
197 self.__load_data()
201 self.__load_data()
198 c.perm_user = AuthUser(user_id=self.authuser.user_id)
202 c.perm_user = AuthUser(user_id=self.authuser.user_id)
199 c.ip_addr = self.ip_addr
203 c.ip_addr = self.ip_addr
200
204
201 return render('admin/my_account/my_account.html')
205 return render('admin/my_account/my_account.html')
202
206
203 def my_account_emails(self):
207 def my_account_emails(self):
204 c.active = 'emails'
208 c.active = 'emails'
205 self.__load_data()
209 self.__load_data()
206
210
207 c.user_email_map = UserEmailMap.query()\
211 c.user_email_map = UserEmailMap.query()\
208 .filter(UserEmailMap.user == c.user).all()
212 .filter(UserEmailMap.user == c.user).all()
209 return render('admin/my_account/my_account.html')
213 return render('admin/my_account/my_account.html')
210
214
211 def my_account_emails_add(self):
215 def my_account_emails_add(self):
212 email = request.POST.get('new_email')
216 email = request.POST.get('new_email')
213
217
214 try:
218 try:
215 UserModel().add_extra_email(self.authuser.user_id, email)
219 UserModel().add_extra_email(self.authuser.user_id, email)
216 Session().commit()
220 Session().commit()
217 h.flash(_("Added email %s to user") % email, category='success')
221 h.flash(_("Added email %s to user") % email, category='success')
218 except formencode.Invalid, error:
222 except formencode.Invalid, error:
219 msg = error.error_dict['email']
223 msg = error.error_dict['email']
220 h.flash(msg, category='error')
224 h.flash(msg, category='error')
221 except Exception:
225 except Exception:
222 log.error(traceback.format_exc())
226 log.error(traceback.format_exc())
223 h.flash(_('An error occurred during email saving'),
227 h.flash(_('An error occurred during email saving'),
224 category='error')
228 category='error')
225 return redirect(url('my_account_emails'))
229 return redirect(url('my_account_emails'))
226
230
227 def my_account_emails_delete(self):
231 def my_account_emails_delete(self):
228 email_id = request.POST.get('del_email_id')
232 email_id = request.POST.get('del_email_id')
229 user_model = UserModel()
233 user_model = UserModel()
230 user_model.delete_extra_email(self.authuser.user_id, email_id)
234 user_model.delete_extra_email(self.authuser.user_id, email_id)
231 Session().commit()
235 Session().commit()
232 h.flash(_("Removed email from user"), category='success')
236 h.flash(_("Removed email from user"), category='success')
233 return redirect(url('my_account_emails'))
237 return redirect(url('my_account_emails'))
234
238
235 def my_account_api_keys(self):
239 def my_account_api_keys(self):
236 c.active = 'api_keys'
240 c.active = 'api_keys'
237 self.__load_data()
241 self.__load_data()
238 show_expired = True
242 show_expired = True
239 c.lifetime_values = [
243 c.lifetime_values = [
240 (str(-1), _('Forever')),
244 (str(-1), _('Forever')),
241 (str(5), _('5 minutes')),
245 (str(5), _('5 minutes')),
242 (str(60), _('1 hour')),
246 (str(60), _('1 hour')),
243 (str(60 * 24), _('1 day')),
247 (str(60 * 24), _('1 day')),
244 (str(60 * 24 * 30), _('1 month')),
248 (str(60 * 24 * 30), _('1 month')),
245 ]
249 ]
246 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
250 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
247 c.user_api_keys = ApiKeyModel().get_api_keys(self.authuser.user_id,
251 c.user_api_keys = ApiKeyModel().get_api_keys(self.authuser.user_id,
248 show_expired=show_expired)
252 show_expired=show_expired)
249 return render('admin/my_account/my_account.html')
253 return render('admin/my_account/my_account.html')
250
254
251 def my_account_api_keys_add(self):
255 def my_account_api_keys_add(self):
252 lifetime = safe_int(request.POST.get('lifetime'), -1)
256 lifetime = safe_int(request.POST.get('lifetime'), -1)
253 description = request.POST.get('description')
257 description = request.POST.get('description')
254 ApiKeyModel().create(self.authuser.user_id, description, lifetime)
258 ApiKeyModel().create(self.authuser.user_id, description, lifetime)
255 Session().commit()
259 Session().commit()
256 h.flash(_("API key successfully created"), category='success')
260 h.flash(_("API key successfully created"), category='success')
257 return redirect(url('my_account_api_keys'))
261 return redirect(url('my_account_api_keys'))
258
262
259 def my_account_api_keys_delete(self):
263 def my_account_api_keys_delete(self):
260 api_key = request.POST.get('del_api_key')
264 api_key = request.POST.get('del_api_key')
261 user_id = self.authuser.user_id
265 user_id = self.authuser.user_id
262 if request.POST.get('del_api_key_builtin'):
266 if request.POST.get('del_api_key_builtin'):
263 user = User.get(user_id)
267 user = User.get(user_id)
264 if user is not None:
268 if user is not None:
265 user.api_key = generate_api_key()
269 user.api_key = generate_api_key()
266 Session().add(user)
270 Session().add(user)
267 Session().commit()
271 Session().commit()
268 h.flash(_("API key successfully reset"), category='success')
272 h.flash(_("API key successfully reset"), category='success')
269 elif api_key:
273 elif api_key:
270 ApiKeyModel().delete(api_key, self.authuser.user_id)
274 ApiKeyModel().delete(api_key, self.authuser.user_id)
271 Session().commit()
275 Session().commit()
272 h.flash(_("API key successfully deleted"), category='success')
276 h.flash(_("API key successfully deleted"), category='success')
273
277
274 return redirect(url('my_account_api_keys'))
278 return redirect(url('my_account_api_keys'))
@@ -1,38 +1,47 b''
1 <div style="font-size: 20px; color: #666666; padding: 0px 0px 10px 0px">${_('Change Your Account Password')}</div>
1 <div style="font-size: 20px; color: #666666; padding: 0px 0px 10px 0px">${_('Change Your Account Password')}</div>
2
3 %if c.can_change_password:
4
2 ${h.form(url('my_account_password'), method='post')}
5 ${h.form(url('my_account_password'), method='post')}
3 <div class="form">
6 <div class="form">
4 <div class="fields">
7 <div class="fields">
5 <div class="field">
8 <div class="field">
6 <div class="label">
9 <div class="label">
7 <label for="current_password">${_('Current password')}:</label>
10 <label for="current_password">${_('Current password')}:</label>
8 </div>
11 </div>
9 <div class="input">
12 <div class="input">
10 ${h.password('current_password',class_='medium')}
13 ${h.password('current_password',class_='medium')}
11 </div>
14 </div>
12 </div>
15 </div>
13
16
14 <div class="field">
17 <div class="field">
15 <div class="label">
18 <div class="label">
16 <label for="new_password">${_('New password')}:</label>
19 <label for="new_password">${_('New password')}:</label>
17 </div>
20 </div>
18 <div class="input">
21 <div class="input">
19 ${h.password('new_password',class_='medium')}
22 ${h.password('new_password',class_='medium')}
20 </div>
23 </div>
21 </div>
24 </div>
22
25
23 <div class="field">
26 <div class="field">
24 <div class="label">
27 <div class="label">
25 <label for="new_password_confirmation">${_('Confirm new password')}:</label>
28 <label for="new_password_confirmation">${_('Confirm new password')}:</label>
26 </div>
29 </div>
27 <div class="input">
30 <div class="input">
28 ${h.password('new_password_confirmation',class_='medium')}
31 ${h.password('new_password_confirmation',class_='medium')}
29 </div>
32 </div>
30 </div>
33 </div>
31
34
32 <div class="buttons">
35 <div class="buttons">
33 ${h.submit('save',_('Save'),class_="btn")}
36 ${h.submit('save',_('Save'),class_="btn")}
34 ${h.reset('reset',_('Reset'),class_="btn")}
37 ${h.reset('reset',_('Reset'),class_="btn")}
35 </div>
38 </div>
36 </div>
39 </div>
37 </div>
40 </div>
38 ${h.end_form()}
41 ${h.end_form()}
42
43 %else:
44
45 ${_('This account is managed with %s and the password cannot be changed here') % c.user.extern_type}
46
47 %endif
General Comments 0
You need to be logged in to leave comments. Login now