Show More
@@ -59,6 +59,21 b' class TestCreateUser(object):' | |||||
59 | expected = "email `%s` already exist" % (TEST_USER_REGULAR_EMAIL,) |
|
59 | expected = "email `%s` already exist" % (TEST_USER_REGULAR_EMAIL,) | |
60 | assert_error(id_, expected, given=response.body) |
|
60 | assert_error(id_, expected, given=response.body) | |
61 |
|
61 | |||
|
62 | def test_api_create_user_with_wrong_username(self): | |||
|
63 | bad_username = '<> HELLO WORLD <>' | |||
|
64 | id_, params = build_data( | |||
|
65 | self.apikey, 'create_user', | |||
|
66 | username=bad_username, | |||
|
67 | email='new@email.com', | |||
|
68 | password='trololo') | |||
|
69 | response = api_call(self.app, params) | |||
|
70 | ||||
|
71 | expected = {'username': | |||
|
72 | "Username may only contain alphanumeric characters " | |||
|
73 | "underscores, periods or dashes and must begin with " | |||
|
74 | "alphanumeric character or underscore"} | |||
|
75 | assert_error(id_, expected, given=response.body) | |||
|
76 | ||||
62 | def test_api_create_user(self): |
|
77 | def test_api_create_user(self): | |
63 | username = 'test_new_api_user' |
|
78 | username = 'test_new_api_user' | |
64 | email = username + "@foo.com" |
|
79 | email = username + "@foo.com" | |
@@ -175,7 +190,6 b' class TestCreateUser(object):' | |||||
175 | fixture.destroy_repo_group(username) |
|
190 | fixture.destroy_repo_group(username) | |
176 | fixture.destroy_user(usr.user_id) |
|
191 | fixture.destroy_user(usr.user_id) | |
177 |
|
192 | |||
178 |
|
||||
179 | @mock.patch.object(UserModel, 'create_or_update', crash) |
|
193 | @mock.patch.object(UserModel, 'create_or_update', crash) | |
180 | def test_api_create_user_when_exception_happened(self): |
|
194 | def test_api_create_user_when_exception_happened(self): | |
181 |
|
195 |
@@ -20,7 +20,8 b'' | |||||
20 |
|
20 | |||
21 | import logging |
|
21 | import logging | |
22 |
|
22 | |||
23 | from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden |
|
23 | from rhodecode.api import ( | |
|
24 | jsonrpc_method, JSONRPCError, JSONRPCForbidden, JSONRPCValidationError) | |||
24 | from rhodecode.api.utils import ( |
|
25 | from rhodecode.api.utils import ( | |
25 | Optional, OAttr, has_superadmin_permission, get_user_or_error, store_update) |
|
26 | Optional, OAttr, has_superadmin_permission, get_user_or_error, store_update) | |
26 | from rhodecode.lib import audit_logger |
|
27 | from rhodecode.lib import audit_logger | |
@@ -29,6 +30,8 b' from rhodecode.lib.exceptions import Def' | |||||
29 | from rhodecode.lib.utils2 import safe_int, str2bool |
|
30 | from rhodecode.lib.utils2 import safe_int, str2bool | |
30 | from rhodecode.model.db import Session, User, Repository |
|
31 | from rhodecode.model.db import Session, User, Repository | |
31 | from rhodecode.model.user import UserModel |
|
32 | from rhodecode.model.user import UserModel | |
|
33 | from rhodecode.model import validation_schema | |||
|
34 | from rhodecode.model.validation_schema.schemas import user_schema | |||
32 |
|
35 | |||
33 | log = logging.getLogger(__name__) |
|
36 | log = logging.getLogger(__name__) | |
34 |
|
37 | |||
@@ -238,17 +241,45 b' def create_user(request, apiuser, userna' | |||||
238 | if isinstance(create_repo_group, basestring): |
|
241 | if isinstance(create_repo_group, basestring): | |
239 | create_repo_group = str2bool(create_repo_group) |
|
242 | create_repo_group = str2bool(create_repo_group) | |
240 |
|
243 | |||
|
244 | username = Optional.extract(username) | |||
|
245 | password = Optional.extract(password) | |||
|
246 | email = Optional.extract(email) | |||
|
247 | first_name = Optional.extract(firstname) | |||
|
248 | last_name = Optional.extract(lastname) | |||
|
249 | active = Optional.extract(active) | |||
|
250 | admin = Optional.extract(admin) | |||
|
251 | extern_type = Optional.extract(extern_type) | |||
|
252 | extern_name = Optional.extract(extern_name) | |||
|
253 | ||||
|
254 | schema = user_schema.UserSchema().bind( | |||
|
255 | # user caller | |||
|
256 | user=apiuser) | |||
|
257 | try: | |||
|
258 | schema_data = schema.deserialize(dict( | |||
|
259 | username=username, | |||
|
260 | email=email, | |||
|
261 | password=password, | |||
|
262 | first_name=first_name, | |||
|
263 | last_name=last_name, | |||
|
264 | active=active, | |||
|
265 | admin=admin, | |||
|
266 | extern_type=extern_type, | |||
|
267 | extern_name=extern_name, | |||
|
268 | )) | |||
|
269 | except validation_schema.Invalid as err: | |||
|
270 | raise JSONRPCValidationError(colander_exc=err) | |||
|
271 | ||||
241 | try: |
|
272 | try: | |
242 | user = UserModel().create_or_update( |
|
273 | user = UserModel().create_or_update( | |
243 |
username= |
|
274 | username=schema_data['username'], | |
244 |
password= |
|
275 | password=schema_data['password'], | |
245 |
email= |
|
276 | email=schema_data['email'], | |
246 |
firstname= |
|
277 | firstname=schema_data['first_name'], | |
247 |
lastname= |
|
278 | lastname=schema_data['last_name'], | |
248 |
active= |
|
279 | active=schema_data['active'], | |
249 |
admin= |
|
280 | admin=schema_data['admin'], | |
250 |
extern_type= |
|
281 | extern_type=schema_data['extern_type'], | |
251 |
extern_name= |
|
282 | extern_name=schema_data['extern_name'], | |
252 | force_password_change=Optional.extract(force_password_change), |
|
283 | force_password_change=Optional.extract(force_password_change), | |
253 | create_repo_group=create_repo_group |
|
284 | create_repo_group=create_repo_group | |
254 | ) |
|
285 | ) |
@@ -20,8 +20,8 b'' | |||||
20 |
|
20 | |||
21 | import logging |
|
21 | import logging | |
22 |
|
22 | |||
23 | from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden, \ |
|
23 | from rhodecode.api import ( | |
24 | JSONRPCValidationError |
|
24 | jsonrpc_method, JSONRPCError, JSONRPCForbidden, JSONRPCValidationError) | |
25 | from rhodecode.api.utils import ( |
|
25 | from rhodecode.api.utils import ( | |
26 | Optional, OAttr, store_update, has_superadmin_permission, get_origin, |
|
26 | Optional, OAttr, store_update, has_superadmin_permission, get_origin, | |
27 | get_user_or_error, get_user_group_or_error, get_perm_or_error) |
|
27 | get_user_or_error, get_user_group_or_error, get_perm_or_error) |
@@ -18,10 +18,12 b'' | |||||
18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
20 |
|
20 | |||
|
21 | import re | |||
21 | import colander |
|
22 | import colander | |
22 |
|
23 | |||
23 | from rhodecode import forms |
|
24 | from rhodecode import forms | |
24 | from rhodecode.model.db import User |
|
25 | from rhodecode.model.db import User | |
|
26 | from rhodecode.model.validation_schema import types, validators | |||
25 | from rhodecode.translation import _ |
|
27 | from rhodecode.translation import _ | |
26 | from rhodecode.lib.auth import check_password |
|
28 | from rhodecode.lib.auth import check_password | |
27 |
|
29 | |||
@@ -52,10 +54,72 b' class ChangePasswordSchema(colander.Sche' | |||||
52 | widget=forms.widget.CheckedPasswordWidget(redisplay=True), |
|
54 | widget=forms.widget.CheckedPasswordWidget(redisplay=True), | |
53 | validator=colander.Length(min=6)) |
|
55 | validator=colander.Length(min=6)) | |
54 |
|
56 | |||
55 |
|
||||
56 | def validator(self, form, values): |
|
57 | def validator(self, form, values): | |
57 | if values['current_password'] == values['new_password']: |
|
58 | if values['current_password'] == values['new_password']: | |
58 | exc = colander.Invalid(form) |
|
59 | exc = colander.Invalid(form) | |
59 | exc['new_password'] = _('New password must be different ' |
|
60 | exc['new_password'] = _('New password must be different ' | |
60 | 'to old password') |
|
61 | 'to old password') | |
61 | raise exc |
|
62 | raise exc | |
|
63 | ||||
|
64 | ||||
|
65 | @colander.deferred | |||
|
66 | def deferred_username_validator(node, kw): | |||
|
67 | ||||
|
68 | def name_validator(node, value): | |||
|
69 | msg = _( | |||
|
70 | u'Username may only contain alphanumeric characters ' | |||
|
71 | u'underscores, periods or dashes and must begin with ' | |||
|
72 | u'alphanumeric character or underscore') | |||
|
73 | ||||
|
74 | if not re.match(r'^[\w]{1}[\w\-\.]{0,254}$', value): | |||
|
75 | raise colander.Invalid(node, msg) | |||
|
76 | ||||
|
77 | return name_validator | |||
|
78 | ||||
|
79 | ||||
|
80 | @colander.deferred | |||
|
81 | def deferred_email_validator(node, kw): | |||
|
82 | # NOTE(marcink): we might provide uniqueness validation later here... | |||
|
83 | return colander.Email() | |||
|
84 | ||||
|
85 | ||||
|
86 | class UserSchema(colander.Schema): | |||
|
87 | username = colander.SchemaNode( | |||
|
88 | colander.String(), | |||
|
89 | validator=deferred_username_validator) | |||
|
90 | ||||
|
91 | email = colander.SchemaNode( | |||
|
92 | colander.String(), | |||
|
93 | validator=deferred_email_validator) | |||
|
94 | ||||
|
95 | password = colander.SchemaNode( | |||
|
96 | colander.String(), missing='') | |||
|
97 | ||||
|
98 | first_name = colander.SchemaNode( | |||
|
99 | colander.String(), missing='') | |||
|
100 | ||||
|
101 | last_name = colander.SchemaNode( | |||
|
102 | colander.String(), missing='') | |||
|
103 | ||||
|
104 | active = colander.SchemaNode( | |||
|
105 | types.StringBooleanType(), | |||
|
106 | missing=False) | |||
|
107 | ||||
|
108 | admin = colander.SchemaNode( | |||
|
109 | types.StringBooleanType(), | |||
|
110 | missing=False) | |||
|
111 | ||||
|
112 | extern_name = colander.SchemaNode( | |||
|
113 | colander.String(), missing='') | |||
|
114 | ||||
|
115 | extern_type = colander.SchemaNode( | |||
|
116 | colander.String(), missing='') | |||
|
117 | ||||
|
118 | def deserialize(self, cstruct): | |||
|
119 | """ | |||
|
120 | Custom deserialize that allows to chain validation, and verify | |||
|
121 | permissions, and as last step uniqueness | |||
|
122 | """ | |||
|
123 | ||||
|
124 | appstruct = super(UserSchema, self).deserialize(cstruct) | |||
|
125 | return appstruct |
General Comments 0
You need to be logged in to leave comments.
Login now