##// END OF EJS Templates
user-api: use simple schema validator to be consistent how we validate between API and web views.
marcink -
r1832:d176880c default
parent child Browse files
Show More
@@ -1,192 +1,206 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import mock
22 22 import pytest
23 23
24 24 from rhodecode.lib.auth import check_password
25 25 from rhodecode.model.user import UserModel
26 26 from rhodecode.tests import (
27 27 TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_EMAIL)
28 28 from rhodecode.api.tests.utils import (
29 29 build_data, api_call, assert_ok, assert_error, jsonify, crash)
30 30 from rhodecode.tests.fixture import Fixture
31 31 from rhodecode.model.db import RepoGroup
32 32
33 33
34 34 # TODO: mikhail: remove fixture from here
35 35 fixture = Fixture()
36 36
37 37
38 38 @pytest.mark.usefixtures("testuser_api", "app")
39 39 class TestCreateUser(object):
40 40 def test_api_create_existing_user(self):
41 41 id_, params = build_data(
42 42 self.apikey, 'create_user',
43 43 username=TEST_USER_ADMIN_LOGIN,
44 44 email='test@foo.com',
45 45 password='trololo')
46 46 response = api_call(self.app, params)
47 47
48 48 expected = "user `%s` already exist" % (TEST_USER_ADMIN_LOGIN,)
49 49 assert_error(id_, expected, given=response.body)
50 50
51 51 def test_api_create_user_with_existing_email(self):
52 52 id_, params = build_data(
53 53 self.apikey, 'create_user',
54 54 username=TEST_USER_ADMIN_LOGIN + 'new',
55 55 email=TEST_USER_REGULAR_EMAIL,
56 56 password='trololo')
57 57 response = api_call(self.app, params)
58 58
59 59 expected = "email `%s` already exist" % (TEST_USER_REGULAR_EMAIL,)
60 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 77 def test_api_create_user(self):
63 78 username = 'test_new_api_user'
64 79 email = username + "@foo.com"
65 80
66 81 id_, params = build_data(
67 82 self.apikey, 'create_user',
68 83 username=username,
69 84 email=email,
70 85 password='example')
71 86 response = api_call(self.app, params)
72 87
73 88 usr = UserModel().get_by_username(username)
74 89 ret = {
75 90 'msg': 'created new user `%s`' % (username,),
76 91 'user': jsonify(usr.get_api_data(include_secrets=True)),
77 92 }
78 93 try:
79 94 expected = ret
80 95 assert check_password('example', usr.password)
81 96 assert_ok(id_, expected, given=response.body)
82 97 finally:
83 98 fixture.destroy_user(usr.user_id)
84 99
85 100 def test_api_create_user_without_password(self):
86 101 username = 'test_new_api_user_passwordless'
87 102 email = username + "@foo.com"
88 103
89 104 id_, params = build_data(
90 105 self.apikey, 'create_user',
91 106 username=username,
92 107 email=email)
93 108 response = api_call(self.app, params)
94 109
95 110 usr = UserModel().get_by_username(username)
96 111 ret = {
97 112 'msg': 'created new user `%s`' % (username,),
98 113 'user': jsonify(usr.get_api_data(include_secrets=True)),
99 114 }
100 115 try:
101 116 expected = ret
102 117 assert_ok(id_, expected, given=response.body)
103 118 finally:
104 119 fixture.destroy_user(usr.user_id)
105 120
106 121 def test_api_create_user_with_extern_name(self):
107 122 username = 'test_new_api_user_passwordless'
108 123 email = username + "@foo.com"
109 124
110 125 id_, params = build_data(
111 126 self.apikey, 'create_user',
112 127 username=username,
113 128 email=email, extern_name='rhodecode')
114 129 response = api_call(self.app, params)
115 130
116 131 usr = UserModel().get_by_username(username)
117 132 ret = {
118 133 'msg': 'created new user `%s`' % (username,),
119 134 'user': jsonify(usr.get_api_data(include_secrets=True)),
120 135 }
121 136 try:
122 137 expected = ret
123 138 assert_ok(id_, expected, given=response.body)
124 139 finally:
125 140 fixture.destroy_user(usr.user_id)
126 141
127 142 def test_api_create_user_with_password_change(self):
128 143 username = 'test_new_api_user_password_change'
129 144 email = username + "@foo.com"
130 145
131 146 id_, params = build_data(
132 147 self.apikey, 'create_user',
133 148 username=username,
134 149 email=email, extern_name='rhodecode',
135 150 force_password_change=True)
136 151 response = api_call(self.app, params)
137 152
138 153 usr = UserModel().get_by_username(username)
139 154 ret = {
140 155 'msg': 'created new user `%s`' % (username,),
141 156 'user': jsonify(usr.get_api_data(include_secrets=True)),
142 157 }
143 158 try:
144 159 expected = ret
145 160 assert_ok(id_, expected, given=response.body)
146 161 finally:
147 162 fixture.destroy_user(usr.user_id)
148 163
149 164 def test_api_create_user_with_personal_repo_group(self):
150 165 username = 'test_new_api_user_personal_group'
151 166 email = username + "@foo.com"
152 167
153 168 id_, params = build_data(
154 169 self.apikey, 'create_user',
155 170 username=username,
156 171 email=email, extern_name='rhodecode',
157 172 create_personal_repo_group=True)
158 173 response = api_call(self.app, params)
159 174
160 175 usr = UserModel().get_by_username(username)
161 176 ret = {
162 177 'msg': 'created new user `%s`' % (username,),
163 178 'user': jsonify(usr.get_api_data(include_secrets=True)),
164 179 }
165 180
166 181 personal_group = RepoGroup.get_by_group_name(username)
167 182 assert personal_group
168 183 assert personal_group.personal == True
169 184 assert personal_group.user.username == username
170 185
171 186 try:
172 187 expected = ret
173 188 assert_ok(id_, expected, given=response.body)
174 189 finally:
175 190 fixture.destroy_repo_group(username)
176 191 fixture.destroy_user(usr.user_id)
177 192
178
179 193 @mock.patch.object(UserModel, 'create_or_update', crash)
180 194 def test_api_create_user_when_exception_happened(self):
181 195
182 196 username = 'test_new_api_user'
183 197 email = username + "@foo.com"
184 198
185 199 id_, params = build_data(
186 200 self.apikey, 'create_user',
187 201 username=username,
188 202 email=email,
189 203 password='trololo')
190 204 response = api_call(self.app, params)
191 205 expected = 'failed to create user `%s`' % (username,)
192 206 assert_error(id_, expected, given=response.body)
@@ -1,529 +1,560 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 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 25 from rhodecode.api.utils import (
25 26 Optional, OAttr, has_superadmin_permission, get_user_or_error, store_update)
26 27 from rhodecode.lib import audit_logger
27 28 from rhodecode.lib.auth import AuthUser, PasswordGenerator
28 29 from rhodecode.lib.exceptions import DefaultUserException
29 30 from rhodecode.lib.utils2 import safe_int, str2bool
30 31 from rhodecode.model.db import Session, User, Repository
31 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 36 log = logging.getLogger(__name__)
34 37
35 38
36 39 @jsonrpc_method()
37 40 def get_user(request, apiuser, userid=Optional(OAttr('apiuser'))):
38 41 """
39 42 Returns the information associated with a username or userid.
40 43
41 44 * If the ``userid`` is not set, this command returns the information
42 45 for the ``userid`` calling the method.
43 46
44 47 .. note::
45 48
46 49 Normal users may only run this command against their ``userid``. For
47 50 full privileges you must run this command using an |authtoken| with
48 51 admin rights.
49 52
50 53 :param apiuser: This is filled automatically from the |authtoken|.
51 54 :type apiuser: AuthUser
52 55 :param userid: Sets the userid for which data will be returned.
53 56 :type userid: Optional(str or int)
54 57
55 58 Example output:
56 59
57 60 .. code-block:: bash
58 61
59 62 {
60 63 "error": null,
61 64 "id": <id>,
62 65 "result": {
63 66 "active": true,
64 67 "admin": false,
65 68 "api_keys": [ list of keys ],
66 69 "auth_tokens": [ list of tokens with details ],
67 70 "email": "user@example.com",
68 71 "emails": [
69 72 "user@example.com"
70 73 ],
71 74 "extern_name": "rhodecode",
72 75 "extern_type": "rhodecode",
73 76 "firstname": "username",
74 77 "ip_addresses": [],
75 78 "language": null,
76 79 "last_login": "Timestamp",
77 80 "last_activity": "Timestamp",
78 81 "lastname": "surnae",
79 82 "permissions": {
80 83 "global": [
81 84 "hg.inherit_default_perms.true",
82 85 "usergroup.read",
83 86 "hg.repogroup.create.false",
84 87 "hg.create.none",
85 88 "hg.password_reset.enabled",
86 89 "hg.extern_activate.manual",
87 90 "hg.create.write_on_repogroup.false",
88 91 "hg.usergroup.create.false",
89 92 "group.none",
90 93 "repository.none",
91 94 "hg.register.none",
92 95 "hg.fork.repository"
93 96 ],
94 97 "repositories": { "username/example": "repository.write"},
95 98 "repositories_groups": { "user-group/repo": "group.none" },
96 99 "user_groups": { "user_group_name": "usergroup.read" }
97 100 },
98 101 "user_id": 32,
99 102 "username": "username"
100 103 }
101 104 }
102 105 """
103 106
104 107 if not has_superadmin_permission(apiuser):
105 108 # make sure normal user does not pass someone else userid,
106 109 # he is not allowed to do that
107 110 if not isinstance(userid, Optional) and userid != apiuser.user_id:
108 111 raise JSONRPCError('userid is not the same as your user')
109 112
110 113 userid = Optional.extract(userid, evaluate_locals=locals())
111 114 userid = getattr(userid, 'user_id', userid)
112 115
113 116 user = get_user_or_error(userid)
114 117 data = user.get_api_data(include_secrets=True)
115 118 data['permissions'] = AuthUser(user_id=user.user_id).permissions
116 119 return data
117 120
118 121
119 122 @jsonrpc_method()
120 123 def get_users(request, apiuser):
121 124 """
122 125 Lists all users in the |RCE| user database.
123 126
124 127 This command can only be run using an |authtoken| with admin rights to
125 128 the specified repository.
126 129
127 130 This command takes the following options:
128 131
129 132 :param apiuser: This is filled automatically from the |authtoken|.
130 133 :type apiuser: AuthUser
131 134
132 135 Example output:
133 136
134 137 .. code-block:: bash
135 138
136 139 id : <id_given_in_input>
137 140 result: [<user_object>, ...]
138 141 error: null
139 142 """
140 143
141 144 if not has_superadmin_permission(apiuser):
142 145 raise JSONRPCForbidden()
143 146
144 147 result = []
145 148 users_list = User.query().order_by(User.username) \
146 149 .filter(User.username != User.DEFAULT_USER) \
147 150 .all()
148 151 for user in users_list:
149 152 result.append(user.get_api_data(include_secrets=True))
150 153 return result
151 154
152 155
153 156 @jsonrpc_method()
154 157 def create_user(request, apiuser, username, email, password=Optional(''),
155 158 firstname=Optional(''), lastname=Optional(''),
156 159 active=Optional(True), admin=Optional(False),
157 160 extern_name=Optional('rhodecode'),
158 161 extern_type=Optional('rhodecode'),
159 162 force_password_change=Optional(False),
160 163 create_personal_repo_group=Optional(None)):
161 164 """
162 165 Creates a new user and returns the new user object.
163 166
164 167 This command can only be run using an |authtoken| with admin rights to
165 168 the specified repository.
166 169
167 170 This command takes the following options:
168 171
169 172 :param apiuser: This is filled automatically from the |authtoken|.
170 173 :type apiuser: AuthUser
171 174 :param username: Set the new username.
172 175 :type username: str or int
173 176 :param email: Set the user email address.
174 177 :type email: str
175 178 :param password: Set the new user password.
176 179 :type password: Optional(str)
177 180 :param firstname: Set the new user firstname.
178 181 :type firstname: Optional(str)
179 182 :param lastname: Set the new user surname.
180 183 :type lastname: Optional(str)
181 184 :param active: Set the user as active.
182 185 :type active: Optional(``True`` | ``False``)
183 186 :param admin: Give the new user admin rights.
184 187 :type admin: Optional(``True`` | ``False``)
185 188 :param extern_name: Set the authentication plugin name.
186 189 Using LDAP this is filled with LDAP UID.
187 190 :type extern_name: Optional(str)
188 191 :param extern_type: Set the new user authentication plugin.
189 192 :type extern_type: Optional(str)
190 193 :param force_password_change: Force the new user to change password
191 194 on next login.
192 195 :type force_password_change: Optional(``True`` | ``False``)
193 196 :param create_personal_repo_group: Create personal repo group for this user
194 197 :type create_personal_repo_group: Optional(``True`` | ``False``)
195 198
196 199 Example output:
197 200
198 201 .. code-block:: bash
199 202
200 203 id : <id_given_in_input>
201 204 result: {
202 205 "msg" : "created new user `<username>`",
203 206 "user": <user_obj>
204 207 }
205 208 error: null
206 209
207 210 Example error output:
208 211
209 212 .. code-block:: bash
210 213
211 214 id : <id_given_in_input>
212 215 result : null
213 216 error : {
214 217 "user `<username>` already exist"
215 218 or
216 219 "email `<email>` already exist"
217 220 or
218 221 "failed to create user `<username>`"
219 222 }
220 223
221 224 """
222 225 if not has_superadmin_permission(apiuser):
223 226 raise JSONRPCForbidden()
224 227
225 228 if UserModel().get_by_username(username):
226 229 raise JSONRPCError("user `%s` already exist" % (username,))
227 230
228 231 if UserModel().get_by_email(email, case_insensitive=True):
229 232 raise JSONRPCError("email `%s` already exist" % (email,))
230 233
231 234 # generate random password if we actually given the
232 235 # extern_name and it's not rhodecode
233 236 if (not isinstance(extern_name, Optional) and
234 237 Optional.extract(extern_name) != 'rhodecode'):
235 238 # generate temporary password if user is external
236 239 password = PasswordGenerator().gen_password(length=16)
237 240 create_repo_group = Optional.extract(create_personal_repo_group)
238 241 if isinstance(create_repo_group, basestring):
239 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 272 try:
242 273 user = UserModel().create_or_update(
243 username=Optional.extract(username),
244 password=Optional.extract(password),
245 email=Optional.extract(email),
246 firstname=Optional.extract(firstname),
247 lastname=Optional.extract(lastname),
248 active=Optional.extract(active),
249 admin=Optional.extract(admin),
250 extern_type=Optional.extract(extern_type),
251 extern_name=Optional.extract(extern_name),
274 username=schema_data['username'],
275 password=schema_data['password'],
276 email=schema_data['email'],
277 firstname=schema_data['first_name'],
278 lastname=schema_data['last_name'],
279 active=schema_data['active'],
280 admin=schema_data['admin'],
281 extern_type=schema_data['extern_type'],
282 extern_name=schema_data['extern_name'],
252 283 force_password_change=Optional.extract(force_password_change),
253 284 create_repo_group=create_repo_group
254 285 )
255 286 Session().flush()
256 287 creation_data = user.get_api_data()
257 288 audit_logger.store_api(
258 289 'user.create', action_data={'data': creation_data},
259 290 user=apiuser)
260 291
261 292 Session().commit()
262 293 return {
263 294 'msg': 'created new user `%s`' % username,
264 295 'user': user.get_api_data(include_secrets=True)
265 296 }
266 297 except Exception:
267 298 log.exception('Error occurred during creation of user')
268 299 raise JSONRPCError('failed to create user `%s`' % (username,))
269 300
270 301
271 302 @jsonrpc_method()
272 303 def update_user(request, apiuser, userid, username=Optional(None),
273 304 email=Optional(None), password=Optional(None),
274 305 firstname=Optional(None), lastname=Optional(None),
275 306 active=Optional(None), admin=Optional(None),
276 307 extern_type=Optional(None), extern_name=Optional(None), ):
277 308 """
278 309 Updates the details for the specified user, if that user exists.
279 310
280 311 This command can only be run using an |authtoken| with admin rights to
281 312 the specified repository.
282 313
283 314 This command takes the following options:
284 315
285 316 :param apiuser: This is filled automatically from |authtoken|.
286 317 :type apiuser: AuthUser
287 318 :param userid: Set the ``userid`` to update.
288 319 :type userid: str or int
289 320 :param username: Set the new username.
290 321 :type username: str or int
291 322 :param email: Set the new email.
292 323 :type email: str
293 324 :param password: Set the new password.
294 325 :type password: Optional(str)
295 326 :param firstname: Set the new first name.
296 327 :type firstname: Optional(str)
297 328 :param lastname: Set the new surname.
298 329 :type lastname: Optional(str)
299 330 :param active: Set the new user as active.
300 331 :type active: Optional(``True`` | ``False``)
301 332 :param admin: Give the user admin rights.
302 333 :type admin: Optional(``True`` | ``False``)
303 334 :param extern_name: Set the authentication plugin user name.
304 335 Using LDAP this is filled with LDAP UID.
305 336 :type extern_name: Optional(str)
306 337 :param extern_type: Set the authentication plugin type.
307 338 :type extern_type: Optional(str)
308 339
309 340
310 341 Example output:
311 342
312 343 .. code-block:: bash
313 344
314 345 id : <id_given_in_input>
315 346 result: {
316 347 "msg" : "updated user ID:<userid> <username>",
317 348 "user": <user_object>,
318 349 }
319 350 error: null
320 351
321 352 Example error output:
322 353
323 354 .. code-block:: bash
324 355
325 356 id : <id_given_in_input>
326 357 result : null
327 358 error : {
328 359 "failed to update user `<username>`"
329 360 }
330 361
331 362 """
332 363 if not has_superadmin_permission(apiuser):
333 364 raise JSONRPCForbidden()
334 365
335 366 user = get_user_or_error(userid)
336 367 old_data = user.get_api_data()
337 368 # only non optional arguments will be stored in updates
338 369 updates = {}
339 370
340 371 try:
341 372
342 373 store_update(updates, username, 'username')
343 374 store_update(updates, password, 'password')
344 375 store_update(updates, email, 'email')
345 376 store_update(updates, firstname, 'name')
346 377 store_update(updates, lastname, 'lastname')
347 378 store_update(updates, active, 'active')
348 379 store_update(updates, admin, 'admin')
349 380 store_update(updates, extern_name, 'extern_name')
350 381 store_update(updates, extern_type, 'extern_type')
351 382
352 383 user = UserModel().update_user(user, **updates)
353 384 audit_logger.store_api(
354 385 'user.edit', action_data={'old_data': old_data},
355 386 user=apiuser)
356 387 Session().commit()
357 388 return {
358 389 'msg': 'updated user ID:%s %s' % (user.user_id, user.username),
359 390 'user': user.get_api_data(include_secrets=True)
360 391 }
361 392 except DefaultUserException:
362 393 log.exception("Default user edit exception")
363 394 raise JSONRPCError('editing default user is forbidden')
364 395 except Exception:
365 396 log.exception("Error occurred during update of user")
366 397 raise JSONRPCError('failed to update user `%s`' % (userid,))
367 398
368 399
369 400 @jsonrpc_method()
370 401 def delete_user(request, apiuser, userid):
371 402 """
372 403 Deletes the specified user from the |RCE| user database.
373 404
374 405 This command can only be run using an |authtoken| with admin rights to
375 406 the specified repository.
376 407
377 408 .. important::
378 409
379 410 Ensure all open pull requests and open code review
380 411 requests to this user are close.
381 412
382 413 Also ensure all repositories, or repository groups owned by this
383 414 user are reassigned before deletion.
384 415
385 416 This command takes the following options:
386 417
387 418 :param apiuser: This is filled automatically from the |authtoken|.
388 419 :type apiuser: AuthUser
389 420 :param userid: Set the user to delete.
390 421 :type userid: str or int
391 422
392 423 Example output:
393 424
394 425 .. code-block:: bash
395 426
396 427 id : <id_given_in_input>
397 428 result: {
398 429 "msg" : "deleted user ID:<userid> <username>",
399 430 "user": null
400 431 }
401 432 error: null
402 433
403 434 Example error output:
404 435
405 436 .. code-block:: bash
406 437
407 438 id : <id_given_in_input>
408 439 result : null
409 440 error : {
410 441 "failed to delete user ID:<userid> <username>"
411 442 }
412 443
413 444 """
414 445 if not has_superadmin_permission(apiuser):
415 446 raise JSONRPCForbidden()
416 447
417 448 user = get_user_or_error(userid)
418 449 old_data = user.get_api_data()
419 450 try:
420 451 UserModel().delete(userid)
421 452 audit_logger.store_api(
422 453 'user.delete', action_data={'old_data': old_data},
423 454 user=apiuser)
424 455
425 456 Session().commit()
426 457 return {
427 458 'msg': 'deleted user ID:%s %s' % (user.user_id, user.username),
428 459 'user': None
429 460 }
430 461 except Exception:
431 462 log.exception("Error occurred during deleting of user")
432 463 raise JSONRPCError(
433 464 'failed to delete user ID:%s %s' % (user.user_id, user.username))
434 465
435 466
436 467 @jsonrpc_method()
437 468 def get_user_locks(request, apiuser, userid=Optional(OAttr('apiuser'))):
438 469 """
439 470 Displays all repositories locked by the specified user.
440 471
441 472 * If this command is run by a non-admin user, it returns
442 473 a list of |repos| locked by that user.
443 474
444 475 This command takes the following options:
445 476
446 477 :param apiuser: This is filled automatically from the |authtoken|.
447 478 :type apiuser: AuthUser
448 479 :param userid: Sets the userid whose list of locked |repos| will be
449 480 displayed.
450 481 :type userid: Optional(str or int)
451 482
452 483 Example output:
453 484
454 485 .. code-block:: bash
455 486
456 487 id : <id_given_in_input>
457 488 result : {
458 489 [repo_object, repo_object,...]
459 490 }
460 491 error : null
461 492 """
462 493
463 494 include_secrets = False
464 495 if not has_superadmin_permission(apiuser):
465 496 # make sure normal user does not pass someone else userid,
466 497 # he is not allowed to do that
467 498 if not isinstance(userid, Optional) and userid != apiuser.user_id:
468 499 raise JSONRPCError('userid is not the same as your user')
469 500 else:
470 501 include_secrets = True
471 502
472 503 userid = Optional.extract(userid, evaluate_locals=locals())
473 504 userid = getattr(userid, 'user_id', userid)
474 505 user = get_user_or_error(userid)
475 506
476 507 ret = []
477 508
478 509 # show all locks
479 510 for r in Repository.getAll():
480 511 _user_id, _time, _reason = r.locked
481 512 if _user_id and _time:
482 513 _api_data = r.get_api_data(include_secrets=include_secrets)
483 514 # if we use user filter just show the locks for this user
484 515 if safe_int(_user_id) == user.user_id:
485 516 ret.append(_api_data)
486 517
487 518 return ret
488 519
489 520
490 521 @jsonrpc_method()
491 522 def get_user_audit_logs(request, apiuser, userid=Optional(OAttr('apiuser'))):
492 523 """
493 524 Fetches all action logs made by the specified user.
494 525
495 526 This command takes the following options:
496 527
497 528 :param apiuser: This is filled automatically from the |authtoken|.
498 529 :type apiuser: AuthUser
499 530 :param userid: Sets the userid whose list of locked |repos| will be
500 531 displayed.
501 532 :type userid: Optional(str or int)
502 533
503 534 Example output:
504 535
505 536 .. code-block:: bash
506 537
507 538 id : <id_given_in_input>
508 539 result : {
509 540 [action, action,...]
510 541 }
511 542 error : null
512 543 """
513 544
514 545 if not has_superadmin_permission(apiuser):
515 546 # make sure normal user does not pass someone else userid,
516 547 # he is not allowed to do that
517 548 if not isinstance(userid, Optional) and userid != apiuser.user_id:
518 549 raise JSONRPCError('userid is not the same as your user')
519 550
520 551 userid = Optional.extract(userid, evaluate_locals=locals())
521 552 userid = getattr(userid, 'user_id', userid)
522 553 user = get_user_or_error(userid)
523 554
524 555 ret = []
525 556
526 557 # show all user actions
527 558 for entry in UserModel().get_user_log(user, filter_term=None):
528 559 ret.append(entry)
529 560 return ret
@@ -1,818 +1,818 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23 from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden, \
24 JSONRPCValidationError
23 from rhodecode.api import (
24 jsonrpc_method, JSONRPCError, JSONRPCForbidden, JSONRPCValidationError)
25 25 from rhodecode.api.utils import (
26 26 Optional, OAttr, store_update, has_superadmin_permission, get_origin,
27 27 get_user_or_error, get_user_group_or_error, get_perm_or_error)
28 28 from rhodecode.lib import audit_logger
29 29 from rhodecode.lib.auth import HasUserGroupPermissionAnyApi, HasPermissionAnyApi
30 30 from rhodecode.lib.exceptions import UserGroupAssignedException
31 31 from rhodecode.model.db import Session
32 32 from rhodecode.model.scm import UserGroupList
33 33 from rhodecode.model.user_group import UserGroupModel
34 34 from rhodecode.model import validation_schema
35 35 from rhodecode.model.validation_schema.schemas import user_group_schema
36 36
37 37 log = logging.getLogger(__name__)
38 38
39 39
40 40 @jsonrpc_method()
41 41 def get_user_group(request, apiuser, usergroupid):
42 42 """
43 43 Returns the data of an existing user group.
44 44
45 45 This command can only be run using an |authtoken| with admin rights to
46 46 the specified repository.
47 47
48 48 :param apiuser: This is filled automatically from the |authtoken|.
49 49 :type apiuser: AuthUser
50 50 :param usergroupid: Set the user group from which to return data.
51 51 :type usergroupid: str or int
52 52
53 53 Example error output:
54 54
55 55 .. code-block:: bash
56 56
57 57 {
58 58 "error": null,
59 59 "id": <id>,
60 60 "result": {
61 61 "active": true,
62 62 "group_description": "group description",
63 63 "group_name": "group name",
64 64 "members": [
65 65 {
66 66 "name": "owner-name",
67 67 "origin": "owner",
68 68 "permission": "usergroup.admin",
69 69 "type": "user"
70 70 },
71 71 {
72 72 {
73 73 "name": "user name",
74 74 "origin": "permission",
75 75 "permission": "usergroup.admin",
76 76 "type": "user"
77 77 },
78 78 {
79 79 "name": "user group name",
80 80 "origin": "permission",
81 81 "permission": "usergroup.write",
82 82 "type": "user_group"
83 83 }
84 84 ],
85 85 "owner": "owner name",
86 86 "users": [],
87 87 "users_group_id": 2
88 88 }
89 89 }
90 90
91 91 """
92 92
93 93 user_group = get_user_group_or_error(usergroupid)
94 94 if not has_superadmin_permission(apiuser):
95 95 # check if we have at least read permission for this user group !
96 96 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
97 97 if not HasUserGroupPermissionAnyApi(*_perms)(
98 98 user=apiuser, user_group_name=user_group.users_group_name):
99 99 raise JSONRPCError('user group `%s` does not exist' % (
100 100 usergroupid,))
101 101
102 102 permissions = []
103 103 for _user in user_group.permissions():
104 104 user_data = {
105 105 'name': _user.username,
106 106 'permission': _user.permission,
107 107 'origin': get_origin(_user),
108 108 'type': "user",
109 109 }
110 110 permissions.append(user_data)
111 111
112 112 for _user_group in user_group.permission_user_groups():
113 113 user_group_data = {
114 114 'name': _user_group.users_group_name,
115 115 'permission': _user_group.permission,
116 116 'origin': get_origin(_user_group),
117 117 'type': "user_group",
118 118 }
119 119 permissions.append(user_group_data)
120 120
121 121 data = user_group.get_api_data()
122 122 data['members'] = permissions
123 123
124 124 return data
125 125
126 126
127 127 @jsonrpc_method()
128 128 def get_user_groups(request, apiuser):
129 129 """
130 130 Lists all the existing user groups within RhodeCode.
131 131
132 132 This command can only be run using an |authtoken| with admin rights to
133 133 the specified repository.
134 134
135 135 This command takes the following options:
136 136
137 137 :param apiuser: This is filled automatically from the |authtoken|.
138 138 :type apiuser: AuthUser
139 139
140 140 Example error output:
141 141
142 142 .. code-block:: bash
143 143
144 144 id : <id_given_in_input>
145 145 result : [<user_group_obj>,...]
146 146 error : null
147 147 """
148 148
149 149 include_secrets = has_superadmin_permission(apiuser)
150 150
151 151 result = []
152 152 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
153 153 extras = {'user': apiuser}
154 154 for user_group in UserGroupList(UserGroupModel().get_all(),
155 155 perm_set=_perms, extra_kwargs=extras):
156 156 result.append(
157 157 user_group.get_api_data(include_secrets=include_secrets))
158 158 return result
159 159
160 160
161 161 @jsonrpc_method()
162 162 def create_user_group(
163 163 request, apiuser, group_name, description=Optional(''),
164 164 owner=Optional(OAttr('apiuser')), active=Optional(True)):
165 165 """
166 166 Creates a new user group.
167 167
168 168 This command can only be run using an |authtoken| with admin rights to
169 169 the specified repository.
170 170
171 171 This command takes the following options:
172 172
173 173 :param apiuser: This is filled automatically from the |authtoken|.
174 174 :type apiuser: AuthUser
175 175 :param group_name: Set the name of the new user group.
176 176 :type group_name: str
177 177 :param description: Give a description of the new user group.
178 178 :type description: str
179 179 :param owner: Set the owner of the new user group.
180 180 If not set, the owner is the |authtoken| user.
181 181 :type owner: Optional(str or int)
182 182 :param active: Set this group as active.
183 183 :type active: Optional(``True`` | ``False``)
184 184
185 185 Example output:
186 186
187 187 .. code-block:: bash
188 188
189 189 id : <id_given_in_input>
190 190 result: {
191 191 "msg": "created new user group `<groupname>`",
192 192 "user_group": <user_group_object>
193 193 }
194 194 error: null
195 195
196 196 Example error output:
197 197
198 198 .. code-block:: bash
199 199
200 200 id : <id_given_in_input>
201 201 result : null
202 202 error : {
203 203 "user group `<group name>` already exist"
204 204 or
205 205 "failed to create group `<group name>`"
206 206 }
207 207
208 208 """
209 209
210 210 if not has_superadmin_permission(apiuser):
211 211 if not HasPermissionAnyApi('hg.usergroup.create.true')(user=apiuser):
212 212 raise JSONRPCForbidden()
213 213
214 214 if UserGroupModel().get_by_name(group_name):
215 215 raise JSONRPCError("user group `%s` already exist" % (group_name,))
216 216
217 217 if isinstance(owner, Optional):
218 218 owner = apiuser.user_id
219 219
220 220 owner = get_user_or_error(owner)
221 221 active = Optional.extract(active)
222 222 description = Optional.extract(description)
223 223
224 224 schema = user_group_schema.UserGroupSchema().bind(
225 225 # user caller
226 226 user=apiuser)
227 227 try:
228 228 schema_data = schema.deserialize(dict(
229 229 user_group_name=group_name,
230 230 user_group_description=description,
231 231 user_group_owner=owner.username,
232 232 user_group_active=active,
233 233 ))
234 234 except validation_schema.Invalid as err:
235 235 raise JSONRPCValidationError(colander_exc=err)
236 236
237 237 try:
238 238 user_group = UserGroupModel().create(
239 239 name=schema_data['user_group_name'],
240 240 description=schema_data['user_group_description'],
241 241 owner=owner,
242 242 active=schema_data['user_group_active'])
243 243 Session().flush()
244 244 creation_data = user_group.get_api_data()
245 245 audit_logger.store_api(
246 246 'user_group.create', action_data={'data': creation_data},
247 247 user=apiuser)
248 248 Session().commit()
249 249 return {
250 250 'msg': 'created new user group `%s`' % group_name,
251 251 'user_group': creation_data
252 252 }
253 253 except Exception:
254 254 log.exception("Error occurred during creation of user group")
255 255 raise JSONRPCError('failed to create group `%s`' % (group_name,))
256 256
257 257
258 258 @jsonrpc_method()
259 259 def update_user_group(request, apiuser, usergroupid, group_name=Optional(''),
260 260 description=Optional(''), owner=Optional(None),
261 261 active=Optional(True)):
262 262 """
263 263 Updates the specified `user group` with the details provided.
264 264
265 265 This command can only be run using an |authtoken| with admin rights to
266 266 the specified repository.
267 267
268 268 :param apiuser: This is filled automatically from the |authtoken|.
269 269 :type apiuser: AuthUser
270 270 :param usergroupid: Set the id of the `user group` to update.
271 271 :type usergroupid: str or int
272 272 :param group_name: Set the new name the `user group`
273 273 :type group_name: str
274 274 :param description: Give a description for the `user group`
275 275 :type description: str
276 276 :param owner: Set the owner of the `user group`.
277 277 :type owner: Optional(str or int)
278 278 :param active: Set the group as active.
279 279 :type active: Optional(``True`` | ``False``)
280 280
281 281 Example output:
282 282
283 283 .. code-block:: bash
284 284
285 285 id : <id_given_in_input>
286 286 result : {
287 287 "msg": 'updated user group ID:<user group id> <user group name>',
288 288 "user_group": <user_group_object>
289 289 }
290 290 error : null
291 291
292 292 Example error output:
293 293
294 294 .. code-block:: bash
295 295
296 296 id : <id_given_in_input>
297 297 result : null
298 298 error : {
299 299 "failed to update user group `<user group name>`"
300 300 }
301 301
302 302 """
303 303
304 304 user_group = get_user_group_or_error(usergroupid)
305 305 include_secrets = False
306 306 if not has_superadmin_permission(apiuser):
307 307 # check if we have admin permission for this user group !
308 308 _perms = ('usergroup.admin',)
309 309 if not HasUserGroupPermissionAnyApi(*_perms)(
310 310 user=apiuser, user_group_name=user_group.users_group_name):
311 311 raise JSONRPCError(
312 312 'user group `%s` does not exist' % (usergroupid,))
313 313 else:
314 314 include_secrets = True
315 315
316 316 if not isinstance(owner, Optional):
317 317 owner = get_user_or_error(owner)
318 318
319 319 old_data = user_group.get_api_data()
320 320 updates = {}
321 321 store_update(updates, group_name, 'users_group_name')
322 322 store_update(updates, description, 'user_group_description')
323 323 store_update(updates, owner, 'user')
324 324 store_update(updates, active, 'users_group_active')
325 325 try:
326 326 UserGroupModel().update(user_group, updates)
327 327 audit_logger.store_api(
328 328 'user_group.edit', action_data={'old_data': old_data},
329 329 user=apiuser)
330 330 Session().commit()
331 331 return {
332 332 'msg': 'updated user group ID:%s %s' % (
333 333 user_group.users_group_id, user_group.users_group_name),
334 334 'user_group': user_group.get_api_data(
335 335 include_secrets=include_secrets)
336 336 }
337 337 except Exception:
338 338 log.exception("Error occurred during update of user group")
339 339 raise JSONRPCError(
340 340 'failed to update user group `%s`' % (usergroupid,))
341 341
342 342
343 343 @jsonrpc_method()
344 344 def delete_user_group(request, apiuser, usergroupid):
345 345 """
346 346 Deletes the specified `user group`.
347 347
348 348 This command can only be run using an |authtoken| with admin rights to
349 349 the specified repository.
350 350
351 351 This command takes the following options:
352 352
353 353 :param apiuser: filled automatically from apikey
354 354 :type apiuser: AuthUser
355 355 :param usergroupid:
356 356 :type usergroupid: int
357 357
358 358 Example output:
359 359
360 360 .. code-block:: bash
361 361
362 362 id : <id_given_in_input>
363 363 result : {
364 364 "msg": "deleted user group ID:<user_group_id> <user_group_name>"
365 365 }
366 366 error : null
367 367
368 368 Example error output:
369 369
370 370 .. code-block:: bash
371 371
372 372 id : <id_given_in_input>
373 373 result : null
374 374 error : {
375 375 "failed to delete user group ID:<user_group_id> <user_group_name>"
376 376 or
377 377 "RepoGroup assigned to <repo_groups_list>"
378 378 }
379 379
380 380 """
381 381
382 382 user_group = get_user_group_or_error(usergroupid)
383 383 if not has_superadmin_permission(apiuser):
384 384 # check if we have admin permission for this user group !
385 385 _perms = ('usergroup.admin',)
386 386 if not HasUserGroupPermissionAnyApi(*_perms)(
387 387 user=apiuser, user_group_name=user_group.users_group_name):
388 388 raise JSONRPCError(
389 389 'user group `%s` does not exist' % (usergroupid,))
390 390
391 391 old_data = user_group.get_api_data()
392 392 try:
393 393 UserGroupModel().delete(user_group)
394 394 audit_logger.store_api(
395 395 'user_group.delete', action_data={'old_data': old_data},
396 396 user=apiuser)
397 397 Session().commit()
398 398 return {
399 399 'msg': 'deleted user group ID:%s %s' % (
400 400 user_group.users_group_id, user_group.users_group_name),
401 401 'user_group': None
402 402 }
403 403 except UserGroupAssignedException as e:
404 404 log.exception("UserGroupAssigned error")
405 405 raise JSONRPCError(str(e))
406 406 except Exception:
407 407 log.exception("Error occurred during deletion of user group")
408 408 raise JSONRPCError(
409 409 'failed to delete user group ID:%s %s' %(
410 410 user_group.users_group_id, user_group.users_group_name))
411 411
412 412
413 413 @jsonrpc_method()
414 414 def add_user_to_user_group(request, apiuser, usergroupid, userid):
415 415 """
416 416 Adds a user to a `user group`. If the user already exists in the group
417 417 this command will return false.
418 418
419 419 This command can only be run using an |authtoken| with admin rights to
420 420 the specified user group.
421 421
422 422 This command takes the following options:
423 423
424 424 :param apiuser: This is filled automatically from the |authtoken|.
425 425 :type apiuser: AuthUser
426 426 :param usergroupid: Set the name of the `user group` to which a
427 427 user will be added.
428 428 :type usergroupid: int
429 429 :param userid: Set the `user_id` of the user to add to the group.
430 430 :type userid: int
431 431
432 432 Example output:
433 433
434 434 .. code-block:: bash
435 435
436 436 id : <id_given_in_input>
437 437 result : {
438 438 "success": True|False # depends on if member is in group
439 439 "msg": "added member `<username>` to user group `<groupname>` |
440 440 User is already in that group"
441 441
442 442 }
443 443 error : null
444 444
445 445 Example error output:
446 446
447 447 .. code-block:: bash
448 448
449 449 id : <id_given_in_input>
450 450 result : null
451 451 error : {
452 452 "failed to add member to user group `<user_group_name>`"
453 453 }
454 454
455 455 """
456 456
457 457 user = get_user_or_error(userid)
458 458 user_group = get_user_group_or_error(usergroupid)
459 459 if not has_superadmin_permission(apiuser):
460 460 # check if we have admin permission for this user group !
461 461 _perms = ('usergroup.admin',)
462 462 if not HasUserGroupPermissionAnyApi(*_perms)(
463 463 user=apiuser, user_group_name=user_group.users_group_name):
464 464 raise JSONRPCError('user group `%s` does not exist' % (
465 465 usergroupid,))
466 466
467 467 try:
468 468 ugm = UserGroupModel().add_user_to_group(user_group, user)
469 469 success = True if ugm is not True else False
470 470 msg = 'added member `%s` to user group `%s`' % (
471 471 user.username, user_group.users_group_name
472 472 )
473 473 msg = msg if success else 'User is already in that group'
474 474 if success:
475 475 user_data = user.get_api_data()
476 476 audit_logger.store_api(
477 477 'user_group.edit.member.add', action_data={'user': user_data},
478 478 user=apiuser)
479 479
480 480 Session().commit()
481 481
482 482 return {
483 483 'success': success,
484 484 'msg': msg
485 485 }
486 486 except Exception:
487 487 log.exception("Error occurred during adding a member to user group")
488 488 raise JSONRPCError(
489 489 'failed to add member to user group `%s`' % (
490 490 user_group.users_group_name,
491 491 )
492 492 )
493 493
494 494
495 495 @jsonrpc_method()
496 496 def remove_user_from_user_group(request, apiuser, usergroupid, userid):
497 497 """
498 498 Removes a user from a user group.
499 499
500 500 * If the specified user is not in the group, this command will return
501 501 `false`.
502 502
503 503 This command can only be run using an |authtoken| with admin rights to
504 504 the specified user group.
505 505
506 506 :param apiuser: This is filled automatically from the |authtoken|.
507 507 :type apiuser: AuthUser
508 508 :param usergroupid: Sets the user group name.
509 509 :type usergroupid: str or int
510 510 :param userid: The user you wish to remove from |RCE|.
511 511 :type userid: str or int
512 512
513 513 Example output:
514 514
515 515 .. code-block:: bash
516 516
517 517 id : <id_given_in_input>
518 518 result: {
519 519 "success": True|False, # depends on if member is in group
520 520 "msg": "removed member <username> from user group <groupname> |
521 521 User wasn't in group"
522 522 }
523 523 error: null
524 524
525 525 """
526 526
527 527 user = get_user_or_error(userid)
528 528 user_group = get_user_group_or_error(usergroupid)
529 529 if not has_superadmin_permission(apiuser):
530 530 # check if we have admin permission for this user group !
531 531 _perms = ('usergroup.admin',)
532 532 if not HasUserGroupPermissionAnyApi(*_perms)(
533 533 user=apiuser, user_group_name=user_group.users_group_name):
534 534 raise JSONRPCError(
535 535 'user group `%s` does not exist' % (usergroupid,))
536 536
537 537 try:
538 538 success = UserGroupModel().remove_user_from_group(user_group, user)
539 539 msg = 'removed member `%s` from user group `%s`' % (
540 540 user.username, user_group.users_group_name
541 541 )
542 542 msg = msg if success else "User wasn't in group"
543 543 if success:
544 544 user_data = user.get_api_data()
545 545 audit_logger.store_api(
546 546 'user_group.edit.member.delete', action_data={'user': user_data},
547 547 user=apiuser)
548 548
549 549 Session().commit()
550 550 return {'success': success, 'msg': msg}
551 551 except Exception:
552 552 log.exception("Error occurred during removing an member from user group")
553 553 raise JSONRPCError(
554 554 'failed to remove member from user group `%s`' % (
555 555 user_group.users_group_name,
556 556 )
557 557 )
558 558
559 559
560 560 @jsonrpc_method()
561 561 def grant_user_permission_to_user_group(
562 562 request, apiuser, usergroupid, userid, perm):
563 563 """
564 564 Set permissions for a user in a user group.
565 565
566 566 :param apiuser: This is filled automatically from the |authtoken|.
567 567 :type apiuser: AuthUser
568 568 :param usergroupid: Set the user group to edit permissions on.
569 569 :type usergroupid: str or int
570 570 :param userid: Set the user from whom you wish to set permissions.
571 571 :type userid: str
572 572 :param perm: (usergroup.(none|read|write|admin))
573 573 :type perm: str
574 574
575 575 Example output:
576 576
577 577 .. code-block:: bash
578 578
579 579 id : <id_given_in_input>
580 580 result : {
581 581 "msg": "Granted perm: `<perm_name>` for user: `<username>` in user group: `<user_group_name>`",
582 582 "success": true
583 583 }
584 584 error : null
585 585 """
586 586
587 587 user_group = get_user_group_or_error(usergroupid)
588 588
589 589 if not has_superadmin_permission(apiuser):
590 590 # check if we have admin permission for this user group !
591 591 _perms = ('usergroup.admin',)
592 592 if not HasUserGroupPermissionAnyApi(*_perms)(
593 593 user=apiuser, user_group_name=user_group.users_group_name):
594 594 raise JSONRPCError(
595 595 'user group `%s` does not exist' % (usergroupid,))
596 596
597 597 user = get_user_or_error(userid)
598 598 perm = get_perm_or_error(perm, prefix='usergroup.')
599 599
600 600 try:
601 601 UserGroupModel().grant_user_permission(
602 602 user_group=user_group, user=user, perm=perm)
603 603 Session().commit()
604 604 return {
605 605 'msg':
606 606 'Granted perm: `%s` for user: `%s` in user group: `%s`' % (
607 607 perm.permission_name, user.username,
608 608 user_group.users_group_name
609 609 ),
610 610 'success': True
611 611 }
612 612 except Exception:
613 613 log.exception("Error occurred during editing permissions "
614 614 "for user in user group")
615 615 raise JSONRPCError(
616 616 'failed to edit permission for user: '
617 617 '`%s` in user group: `%s`' % (
618 618 userid, user_group.users_group_name))
619 619
620 620
621 621 @jsonrpc_method()
622 622 def revoke_user_permission_from_user_group(
623 623 request, apiuser, usergroupid, userid):
624 624 """
625 625 Revoke a users permissions in a user group.
626 626
627 627 :param apiuser: This is filled automatically from the |authtoken|.
628 628 :type apiuser: AuthUser
629 629 :param usergroupid: Set the user group from which to revoke the user
630 630 permissions.
631 631 :type: usergroupid: str or int
632 632 :param userid: Set the userid of the user whose permissions will be
633 633 revoked.
634 634 :type userid: str
635 635
636 636 Example output:
637 637
638 638 .. code-block:: bash
639 639
640 640 id : <id_given_in_input>
641 641 result : {
642 642 "msg": "Revoked perm for user: `<username>` in user group: `<user_group_name>`",
643 643 "success": true
644 644 }
645 645 error : null
646 646 """
647 647
648 648 user_group = get_user_group_or_error(usergroupid)
649 649
650 650 if not has_superadmin_permission(apiuser):
651 651 # check if we have admin permission for this user group !
652 652 _perms = ('usergroup.admin',)
653 653 if not HasUserGroupPermissionAnyApi(*_perms)(
654 654 user=apiuser, user_group_name=user_group.users_group_name):
655 655 raise JSONRPCError(
656 656 'user group `%s` does not exist' % (usergroupid,))
657 657
658 658 user = get_user_or_error(userid)
659 659
660 660 try:
661 661 UserGroupModel().revoke_user_permission(
662 662 user_group=user_group, user=user)
663 663 Session().commit()
664 664 return {
665 665 'msg': 'Revoked perm for user: `%s` in user group: `%s`' % (
666 666 user.username, user_group.users_group_name
667 667 ),
668 668 'success': True
669 669 }
670 670 except Exception:
671 671 log.exception("Error occurred during editing permissions "
672 672 "for user in user group")
673 673 raise JSONRPCError(
674 674 'failed to edit permission for user: `%s` in user group: `%s`'
675 675 % (userid, user_group.users_group_name))
676 676
677 677
678 678 @jsonrpc_method()
679 679 def grant_user_group_permission_to_user_group(
680 680 request, apiuser, usergroupid, sourceusergroupid, perm):
681 681 """
682 682 Give one user group permissions to another user group.
683 683
684 684 :param apiuser: This is filled automatically from the |authtoken|.
685 685 :type apiuser: AuthUser
686 686 :param usergroupid: Set the user group on which to edit permissions.
687 687 :type usergroupid: str or int
688 688 :param sourceusergroupid: Set the source user group to which
689 689 access/permissions will be granted.
690 690 :type sourceusergroupid: str or int
691 691 :param perm: (usergroup.(none|read|write|admin))
692 692 :type perm: str
693 693
694 694 Example output:
695 695
696 696 .. code-block:: bash
697 697
698 698 id : <id_given_in_input>
699 699 result : {
700 700 "msg": "Granted perm: `<perm_name>` for user group: `<source_user_group_name>` in user group: `<user_group_name>`",
701 701 "success": true
702 702 }
703 703 error : null
704 704 """
705 705
706 706 user_group = get_user_group_or_error(sourceusergroupid)
707 707 target_user_group = get_user_group_or_error(usergroupid)
708 708 perm = get_perm_or_error(perm, prefix='usergroup.')
709 709
710 710 if not has_superadmin_permission(apiuser):
711 711 # check if we have admin permission for this user group !
712 712 _perms = ('usergroup.admin',)
713 713 if not HasUserGroupPermissionAnyApi(*_perms)(
714 714 user=apiuser,
715 715 user_group_name=target_user_group.users_group_name):
716 716 raise JSONRPCError(
717 717 'to user group `%s` does not exist' % (usergroupid,))
718 718
719 719 # check if we have at least read permission for source user group !
720 720 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
721 721 if not HasUserGroupPermissionAnyApi(*_perms)(
722 722 user=apiuser, user_group_name=user_group.users_group_name):
723 723 raise JSONRPCError(
724 724 'user group `%s` does not exist' % (sourceusergroupid,))
725 725
726 726 try:
727 727 UserGroupModel().grant_user_group_permission(
728 728 target_user_group=target_user_group,
729 729 user_group=user_group, perm=perm)
730 730 Session().commit()
731 731
732 732 return {
733 733 'msg': 'Granted perm: `%s` for user group: `%s` '
734 734 'in user group: `%s`' % (
735 735 perm.permission_name, user_group.users_group_name,
736 736 target_user_group.users_group_name
737 737 ),
738 738 'success': True
739 739 }
740 740 except Exception:
741 741 log.exception("Error occurred during editing permissions "
742 742 "for user group in user group")
743 743 raise JSONRPCError(
744 744 'failed to edit permission for user group: `%s` in '
745 745 'user group: `%s`' % (
746 746 sourceusergroupid, target_user_group.users_group_name
747 747 )
748 748 )
749 749
750 750
751 751 @jsonrpc_method()
752 752 def revoke_user_group_permission_from_user_group(
753 753 request, apiuser, usergroupid, sourceusergroupid):
754 754 """
755 755 Revoke the permissions that one user group has to another.
756 756
757 757 :param apiuser: This is filled automatically from the |authtoken|.
758 758 :type apiuser: AuthUser
759 759 :param usergroupid: Set the user group on which to edit permissions.
760 760 :type usergroupid: str or int
761 761 :param sourceusergroupid: Set the user group from which permissions
762 762 are revoked.
763 763 :type sourceusergroupid: str or int
764 764
765 765 Example output:
766 766
767 767 .. code-block:: bash
768 768
769 769 id : <id_given_in_input>
770 770 result : {
771 771 "msg": "Revoked perm for user group: `<user_group_name>` in user group: `<target_user_group_name>`",
772 772 "success": true
773 773 }
774 774 error : null
775 775 """
776 776
777 777 user_group = get_user_group_or_error(sourceusergroupid)
778 778 target_user_group = get_user_group_or_error(usergroupid)
779 779
780 780 if not has_superadmin_permission(apiuser):
781 781 # check if we have admin permission for this user group !
782 782 _perms = ('usergroup.admin',)
783 783 if not HasUserGroupPermissionAnyApi(*_perms)(
784 784 user=apiuser,
785 785 user_group_name=target_user_group.users_group_name):
786 786 raise JSONRPCError(
787 787 'to user group `%s` does not exist' % (usergroupid,))
788 788
789 789 # check if we have at least read permission
790 790 # for the source user group !
791 791 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
792 792 if not HasUserGroupPermissionAnyApi(*_perms)(
793 793 user=apiuser, user_group_name=user_group.users_group_name):
794 794 raise JSONRPCError(
795 795 'user group `%s` does not exist' % (sourceusergroupid,))
796 796
797 797 try:
798 798 UserGroupModel().revoke_user_group_permission(
799 799 target_user_group=target_user_group, user_group=user_group)
800 800 Session().commit()
801 801
802 802 return {
803 803 'msg': 'Revoked perm for user group: '
804 804 '`%s` in user group: `%s`' % (
805 805 user_group.users_group_name,
806 806 target_user_group.users_group_name
807 807 ),
808 808 'success': True
809 809 }
810 810 except Exception:
811 811 log.exception("Error occurred during editing permissions "
812 812 "for user group in user group")
813 813 raise JSONRPCError(
814 814 'failed to edit permission for user group: '
815 815 '`%s` in user group: `%s`' % (
816 816 sourceusergroupid, target_user_group.users_group_name
817 817 )
818 818 )
@@ -1,61 +1,125 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 import re
21 22 import colander
22 23
23 24 from rhodecode import forms
24 25 from rhodecode.model.db import User
26 from rhodecode.model.validation_schema import types, validators
25 27 from rhodecode.translation import _
26 28 from rhodecode.lib.auth import check_password
27 29
28 30
29 31 @colander.deferred
30 32 def deferred_user_password_validator(node, kw):
31 33 username = kw.get('username')
32 34 user = User.get_by_username(username)
33 35
34 36 def _user_password_validator(node, value):
35 37 if not check_password(value, user.password):
36 38 msg = _('Password is incorrect')
37 39 raise colander.Invalid(node, msg)
38 40 return _user_password_validator
39 41
40 42
41 43 class ChangePasswordSchema(colander.Schema):
42 44
43 45 current_password = colander.SchemaNode(
44 46 colander.String(),
45 47 missing=colander.required,
46 48 widget=forms.widget.PasswordWidget(redisplay=True),
47 49 validator=deferred_user_password_validator)
48 50
49 51 new_password = colander.SchemaNode(
50 52 colander.String(),
51 53 missing=colander.required,
52 54 widget=forms.widget.CheckedPasswordWidget(redisplay=True),
53 55 validator=colander.Length(min=6))
54 56
55
56 57 def validator(self, form, values):
57 58 if values['current_password'] == values['new_password']:
58 59 exc = colander.Invalid(form)
59 60 exc['new_password'] = _('New password must be different '
60 61 'to old password')
61 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