##// END OF EJS Templates
feat(forms): user profile form would detect nicely duplicates and show a form error.
super-admin -
r5353:7a7f1159 default
parent child Browse files
Show More
@@ -1,196 +1,202 b''
1 1 # Copyright (C) 2016-2023 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
5 5 # (only), as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU Affero General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 #
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 import re
20 20 import colander
21 21
22 22 from rhodecode import forms
23 23 from rhodecode.model.db import User, UserEmailMap
24 24 from rhodecode.model.validation_schema import types, validators
25 25 from rhodecode.translation import _
26 26 from rhodecode.lib.auth import check_password
27 27 from rhodecode.lib import helpers as h
28 28
29 29
30 30 @colander.deferred
31 31 def deferred_user_password_validator(node, kw):
32 32 username = kw.get('username')
33 33 user = User.get_by_username(username)
34 34
35 35 def _user_password_validator(node, value):
36 36 if not check_password(value, user.password):
37 37 msg = _('Password is incorrect')
38 38 raise colander.Invalid(node, msg)
39 39 return _user_password_validator
40 40
41 41
42 42
43 43 class ChangePasswordSchema(colander.Schema):
44 44
45 45 current_password = colander.SchemaNode(
46 46 colander.String(),
47 47 missing=colander.required,
48 48 widget=forms.widget.PasswordWidget(redisplay=True),
49 49 validator=deferred_user_password_validator)
50 50
51 51 new_password = colander.SchemaNode(
52 52 colander.String(),
53 53 missing=colander.required,
54 54 widget=forms.widget.CheckedPasswordWidget(redisplay=True),
55 55 validator=colander.Length(min=6))
56 56
57 57 def validator(self, form, values):
58 58 if values['current_password'] == values['new_password']:
59 59 exc = colander.Invalid(form)
60 60 exc['new_password'] = _('New password must be different '
61 61 'to old password')
62 62 raise exc
63 63
64 64
65 65 @colander.deferred
66 66 def deferred_username_validator(node, kw):
67 old_username = kw.get('username')
67 68
68 69 def name_validator(node, value):
69 70 msg = _(
70 71 'Username may only contain alphanumeric characters '
71 72 'underscores, periods or dashes and must begin with '
72 73 'alphanumeric character or underscore')
73 74
74 75 if not re.match(r'^[\w]{1}[\w\-\.]{0,254}$', value):
75 76 raise colander.Invalid(node, msg)
76 77
78 if value != old_username:
79 existing_user = User.get_by_username(value, case_insensitive=True)
80 if existing_user:
81 raise colander.Invalid(node, 'Username is already taken')
82
77 83 return name_validator
78 84
79 85
80 86 @colander.deferred
81 87 def deferred_email_validator(node, kw):
82 88 # NOTE(marcink): we might provide uniqueness validation later here...
83 89 return colander.Email()
84 90
85 91
86 92 class UserSchema(colander.Schema):
87 93 username = colander.SchemaNode(
88 94 colander.String(),
89 95 validator=deferred_username_validator)
90 96
91 97 email = colander.SchemaNode(
92 98 colander.String(),
93 99 validator=deferred_email_validator)
94 100
95 101 password = colander.SchemaNode(
96 102 colander.String(), missing='')
97 103
98 104 first_name = colander.SchemaNode(
99 105 colander.String(), missing='')
100 106
101 107 last_name = colander.SchemaNode(
102 108 colander.String(), missing='')
103 109
104 110 description = colander.SchemaNode(
105 111 colander.String(), missing='')
106 112
107 113 active = colander.SchemaNode(
108 114 types.StringBooleanType(),
109 115 missing=False)
110 116
111 117 admin = colander.SchemaNode(
112 118 types.StringBooleanType(),
113 119 missing=False)
114 120
115 121 extern_name = colander.SchemaNode(
116 122 colander.String(), missing='')
117 123
118 124 extern_type = colander.SchemaNode(
119 125 colander.String(), missing='')
120 126
121 127 def deserialize(self, cstruct):
122 128 """
123 129 Custom deserialize that allows to chain validation, and verify
124 130 permissions, and as last step uniqueness
125 131 """
126 132
127 133 appstruct = super().deserialize(cstruct)
128 134 return appstruct
129 135
130 136
131 137 @colander.deferred
132 138 def deferred_user_email_in_emails_validator(node, kw):
133 139 return colander.OneOf(kw.get('user_emails'))
134 140
135 141
136 142 @colander.deferred
137 143 def deferred_additional_email_validator(node, kw):
138 144 emails = kw.get('user_emails')
139 145
140 146 def name_validator(node, value):
141 147 if value in emails:
142 148 msg = _('This e-mail address is already taken')
143 149 raise colander.Invalid(node, msg)
144 150 user = User.get_by_email(value, case_insensitive=True)
145 151 if user:
146 152 msg = _('This e-mail address is already taken')
147 153 raise colander.Invalid(node, msg)
148 154 c = colander.Email()
149 155 return c(node, value)
150 156 return name_validator
151 157
152 158
153 159 @colander.deferred
154 160 def deferred_user_email_in_emails_widget(node, kw):
155 161 import deform.widget
156 162 emails = [(email, email) for email in kw.get('user_emails')]
157 163 return deform.widget.Select2Widget(values=emails)
158 164
159 165
160 166 class UserProfileSchema(colander.Schema):
161 167 username = colander.SchemaNode(
162 168 colander.String(),
163 169 validator=deferred_username_validator)
164 170
165 171 firstname = colander.SchemaNode(
166 172 colander.String(), missing='', title='First name')
167 173
168 174 lastname = colander.SchemaNode(
169 175 colander.String(), missing='', title='Last name')
170 176
171 177 description = colander.SchemaNode(
172 178 colander.String(), missing='', title='Personal Description',
173 179 widget=forms.widget.TextAreaWidget(),
174 180 validator=colander.Length(max=250)
175 181 )
176 182
177 183 email = colander.SchemaNode(
178 184 colander.String(), widget=deferred_user_email_in_emails_widget,
179 185 validator=deferred_user_email_in_emails_validator,
180 186 description=h.literal(
181 187 _('Additional emails can be specified at <a href="{}">extra emails</a> page.').format(
182 188 '/_admin/my_account/emails')),
183 189 )
184 190
185 191
186 192
187 193 class AddEmailSchema(colander.Schema):
188 194 current_password = colander.SchemaNode(
189 195 colander.String(),
190 196 missing=colander.required,
191 197 widget=forms.widget.PasswordWidget(redisplay=True),
192 198 validator=deferred_user_password_validator)
193 199
194 200 email = colander.SchemaNode(
195 201 colander.String(), title='New Email',
196 202 validator=deferred_additional_email_validator)
General Comments 0
You need to be logged in to leave comments. Login now