##// END OF EJS Templates
settings: fix #3944 add password reset permission
lisaq -
r1034:d1b70f85 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,42 b''
1 import logging
2 import datetime
3
4 from sqlalchemy import *
5 from sqlalchemy.exc import DatabaseError
6 from sqlalchemy.orm import relation, backref, class_mapper, joinedload
7 from sqlalchemy.orm.session import Session
8 from sqlalchemy.ext.declarative import declarative_base
9
10 from rhodecode.lib.dbmigrate.migrate import *
11 from rhodecode.lib.dbmigrate.migrate.changeset import *
12 from rhodecode.lib.utils2 import str2bool
13
14 from rhodecode.model.meta import Base
15 from rhodecode.model import meta
16 from rhodecode.lib.dbmigrate.versions import _reset_base, notify
17
18 log = logging.getLogger(__name__)
19
20
21 def upgrade(migrate_engine):
22 """
23 Upgrade operations go here.
24 Don't create your own engine; bind migrate_engine to your metadata
25 """
26 _reset_base(migrate_engine)
27 from rhodecode.lib.dbmigrate.schema import db_4_5_0_0
28
29 fixups(db_4_5_0_0, meta.Session)
30
31 def downgrade(migrate_engine):
32 meta = MetaData()
33 meta.bind = migrate_engine
34
35 def fixups(models, _SESSION):
36 # ** create default permissions ** #
37 from rhodecode.model.permission import PermissionModel
38 PermissionModel(_SESSION()).create_permissions()
39
40 res = PermissionModel(_SESSION()).create_default_user_permissions(
41 models.User.DEFAULT_USER)
42 _SESSION().commit()
@@ -1,63 +1,63 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 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 """
22 22
23 23 RhodeCode, a web based repository management software
24 24 versioning implementation: http://www.python.org/dev/peps/pep-0386/
25 25 """
26 26
27 27 import os
28 28 import sys
29 29 import platform
30 30
31 31 VERSION = tuple(open(os.path.join(
32 32 os.path.dirname(__file__), 'VERSION')).read().split('.'))
33 33
34 34 BACKENDS = {
35 35 'hg': 'Mercurial repository',
36 36 'git': 'Git repository',
37 37 'svn': 'Subversion repository',
38 38 }
39 39
40 40 CELERY_ENABLED = False
41 41 CELERY_EAGER = False
42 42
43 43 # link to config for pylons
44 44 CONFIG = {}
45 45
46 46 # Populated with the settings dictionary from application init in
47 47 # rhodecode.conf.environment.load_pyramid_environment
48 48 PYRAMID_SETTINGS = {}
49 49
50 50 # Linked module for extensions
51 51 EXTENSIONS = {}
52 52
53 53 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
54 __dbversion__ = 60 # defines current db version for migrations
54 __dbversion__ = 61 # defines current db version for migrations
55 55 __platform__ = platform.system()
56 56 __license__ = 'AGPLv3, and Commercial License'
57 57 __author__ = 'RhodeCode GmbH'
58 58 __url__ = 'http://rhodecode.com'
59 59
60 60 is_windows = __platform__ in ['Windows']
61 61 is_unix = not is_windows
62 62 is_test = False
63 63 disable_error_handler = False
@@ -1,465 +1,466 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2016 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 23 from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden
24 24 from rhodecode.api.utils import (
25 25 Optional, OAttr, has_superadmin_permission, get_user_or_error, store_update)
26 26 from rhodecode.lib.auth import AuthUser, PasswordGenerator
27 27 from rhodecode.lib.exceptions import DefaultUserException
28 28 from rhodecode.lib.utils2 import safe_int
29 29 from rhodecode.model.db import Session, User, Repository
30 30 from rhodecode.model.user import UserModel
31 31
32 32
33 33 log = logging.getLogger(__name__)
34 34
35 35
36 36 @jsonrpc_method()
37 37 def get_user(request, apiuser, userid=Optional(OAttr('apiuser'))):
38 38 """
39 39 Returns the information associated with a username or userid.
40 40
41 41 * If the ``userid`` is not set, this command returns the information
42 42 for the ``userid`` calling the method.
43 43
44 44 .. note::
45 45
46 46 Normal users may only run this command against their ``userid``. For
47 47 full privileges you must run this command using an |authtoken| with
48 48 admin rights.
49 49
50 50 :param apiuser: This is filled automatically from the |authtoken|.
51 51 :type apiuser: AuthUser
52 52 :param userid: Sets the userid for which data will be returned.
53 53 :type userid: Optional(str or int)
54 54
55 55 Example output:
56 56
57 57 .. code-block:: bash
58 58
59 59 {
60 60 "error": null,
61 61 "id": <id>,
62 62 "result": {
63 63 "active": true,
64 64 "admin": false,
65 65 "api_key": "api-key",
66 66 "api_keys": [ list of keys ],
67 67 "email": "user@example.com",
68 68 "emails": [
69 69 "user@example.com"
70 70 ],
71 71 "extern_name": "rhodecode",
72 72 "extern_type": "rhodecode",
73 73 "firstname": "username",
74 74 "ip_addresses": [],
75 75 "language": null,
76 76 "last_login": "Timestamp",
77 77 "lastname": "surnae",
78 78 "permissions": {
79 79 "global": [
80 80 "hg.inherit_default_perms.true",
81 81 "usergroup.read",
82 82 "hg.repogroup.create.false",
83 83 "hg.create.none",
84 "hg.password_reset.enabled",
84 85 "hg.extern_activate.manual",
85 86 "hg.create.write_on_repogroup.false",
86 87 "hg.usergroup.create.false",
87 88 "group.none",
88 89 "repository.none",
89 90 "hg.register.none",
90 91 "hg.fork.repository"
91 92 ],
92 93 "repositories": { "username/example": "repository.write"},
93 94 "repositories_groups": { "user-group/repo": "group.none" },
94 95 "user_groups": { "user_group_name": "usergroup.read" }
95 96 },
96 97 "user_id": 32,
97 98 "username": "username"
98 99 }
99 100 }
100 101 """
101 102
102 103 if not has_superadmin_permission(apiuser):
103 104 # make sure normal user does not pass someone else userid,
104 105 # he is not allowed to do that
105 106 if not isinstance(userid, Optional) and userid != apiuser.user_id:
106 107 raise JSONRPCError('userid is not the same as your user')
107 108
108 109 userid = Optional.extract(userid, evaluate_locals=locals())
109 110 userid = getattr(userid, 'user_id', userid)
110 111
111 112 user = get_user_or_error(userid)
112 113 data = user.get_api_data(include_secrets=True)
113 114 data['permissions'] = AuthUser(user_id=user.user_id).permissions
114 115 return data
115 116
116 117
117 118 @jsonrpc_method()
118 119 def get_users(request, apiuser):
119 120 """
120 121 Lists all users in the |RCE| user database.
121 122
122 123 This command can only be run using an |authtoken| with admin rights to
123 124 the specified repository.
124 125
125 126 This command takes the following options:
126 127
127 128 :param apiuser: This is filled automatically from the |authtoken|.
128 129 :type apiuser: AuthUser
129 130
130 131 Example output:
131 132
132 133 .. code-block:: bash
133 134
134 135 id : <id_given_in_input>
135 136 result: [<user_object>, ...]
136 137 error: null
137 138 """
138 139
139 140 if not has_superadmin_permission(apiuser):
140 141 raise JSONRPCForbidden()
141 142
142 143 result = []
143 144 users_list = User.query().order_by(User.username) \
144 145 .filter(User.username != User.DEFAULT_USER) \
145 146 .all()
146 147 for user in users_list:
147 148 result.append(user.get_api_data(include_secrets=True))
148 149 return result
149 150
150 151
151 152 @jsonrpc_method()
152 153 def create_user(request, apiuser, username, email, password=Optional(''),
153 154 firstname=Optional(''), lastname=Optional(''),
154 155 active=Optional(True), admin=Optional(False),
155 156 extern_name=Optional('rhodecode'),
156 157 extern_type=Optional('rhodecode'),
157 158 force_password_change=Optional(False)):
158 159 """
159 160 Creates a new user and returns the new user object.
160 161
161 162 This command can only be run using an |authtoken| with admin rights to
162 163 the specified repository.
163 164
164 165 This command takes the following options:
165 166
166 167 :param apiuser: This is filled automatically from the |authtoken|.
167 168 :type apiuser: AuthUser
168 169 :param username: Set the new username.
169 170 :type username: str or int
170 171 :param email: Set the user email address.
171 172 :type email: str
172 173 :param password: Set the new user password.
173 174 :type password: Optional(str)
174 175 :param firstname: Set the new user firstname.
175 176 :type firstname: Optional(str)
176 177 :param lastname: Set the new user surname.
177 178 :type lastname: Optional(str)
178 179 :param active: Set the user as active.
179 180 :type active: Optional(``True`` | ``False``)
180 181 :param admin: Give the new user admin rights.
181 182 :type admin: Optional(``True`` | ``False``)
182 183 :param extern_name: Set the authentication plugin name.
183 184 Using LDAP this is filled with LDAP UID.
184 185 :type extern_name: Optional(str)
185 186 :param extern_type: Set the new user authentication plugin.
186 187 :type extern_type: Optional(str)
187 188 :param force_password_change: Force the new user to change password
188 189 on next login.
189 190 :type force_password_change: Optional(``True`` | ``False``)
190 191
191 192 Example output:
192 193
193 194 .. code-block:: bash
194 195
195 196 id : <id_given_in_input>
196 197 result: {
197 198 "msg" : "created new user `<username>`",
198 199 "user": <user_obj>
199 200 }
200 201 error: null
201 202
202 203 Example error output:
203 204
204 205 .. code-block:: bash
205 206
206 207 id : <id_given_in_input>
207 208 result : null
208 209 error : {
209 210 "user `<username>` already exist"
210 211 or
211 212 "email `<email>` already exist"
212 213 or
213 214 "failed to create user `<username>`"
214 215 }
215 216
216 217 """
217 218 if not has_superadmin_permission(apiuser):
218 219 raise JSONRPCForbidden()
219 220
220 221 if UserModel().get_by_username(username):
221 222 raise JSONRPCError("user `%s` already exist" % (username,))
222 223
223 224 if UserModel().get_by_email(email, case_insensitive=True):
224 225 raise JSONRPCError("email `%s` already exist" % (email,))
225 226
226 227 # generate random password if we actually given the
227 228 # extern_name and it's not rhodecode
228 229 if (not isinstance(extern_name, Optional) and
229 230 Optional.extract(extern_name) != 'rhodecode'):
230 231 # generate temporary password if user is external
231 232 password = PasswordGenerator().gen_password(length=16)
232 233
233 234 try:
234 235 user = UserModel().create_or_update(
235 236 username=Optional.extract(username),
236 237 password=Optional.extract(password),
237 238 email=Optional.extract(email),
238 239 firstname=Optional.extract(firstname),
239 240 lastname=Optional.extract(lastname),
240 241 active=Optional.extract(active),
241 242 admin=Optional.extract(admin),
242 243 extern_type=Optional.extract(extern_type),
243 244 extern_name=Optional.extract(extern_name),
244 245 force_password_change=Optional.extract(force_password_change),
245 246 )
246 247 Session().commit()
247 248 return {
248 249 'msg': 'created new user `%s`' % username,
249 250 'user': user.get_api_data(include_secrets=True)
250 251 }
251 252 except Exception:
252 253 log.exception('Error occurred during creation of user')
253 254 raise JSONRPCError('failed to create user `%s`' % (username,))
254 255
255 256
256 257 @jsonrpc_method()
257 258 def update_user(request, apiuser, userid, username=Optional(None),
258 259 email=Optional(None), password=Optional(None),
259 260 firstname=Optional(None), lastname=Optional(None),
260 261 active=Optional(None), admin=Optional(None),
261 262 extern_type=Optional(None), extern_name=Optional(None), ):
262 263 """
263 264 Updates the details for the specified user, if that user exists.
264 265
265 266 This command can only be run using an |authtoken| with admin rights to
266 267 the specified repository.
267 268
268 269 This command takes the following options:
269 270
270 271 :param apiuser: This is filled automatically from |authtoken|.
271 272 :type apiuser: AuthUser
272 273 :param userid: Set the ``userid`` to update.
273 274 :type userid: str or int
274 275 :param username: Set the new username.
275 276 :type username: str or int
276 277 :param email: Set the new email.
277 278 :type email: str
278 279 :param password: Set the new password.
279 280 :type password: Optional(str)
280 281 :param firstname: Set the new first name.
281 282 :type firstname: Optional(str)
282 283 :param lastname: Set the new surname.
283 284 :type lastname: Optional(str)
284 285 :param active: Set the new user as active.
285 286 :type active: Optional(``True`` | ``False``)
286 287 :param admin: Give the user admin rights.
287 288 :type admin: Optional(``True`` | ``False``)
288 289 :param extern_name: Set the authentication plugin user name.
289 290 Using LDAP this is filled with LDAP UID.
290 291 :type extern_name: Optional(str)
291 292 :param extern_type: Set the authentication plugin type.
292 293 :type extern_type: Optional(str)
293 294
294 295
295 296 Example output:
296 297
297 298 .. code-block:: bash
298 299
299 300 id : <id_given_in_input>
300 301 result: {
301 302 "msg" : "updated user ID:<userid> <username>",
302 303 "user": <user_object>,
303 304 }
304 305 error: null
305 306
306 307 Example error output:
307 308
308 309 .. code-block:: bash
309 310
310 311 id : <id_given_in_input>
311 312 result : null
312 313 error : {
313 314 "failed to update user `<username>`"
314 315 }
315 316
316 317 """
317 318 if not has_superadmin_permission(apiuser):
318 319 raise JSONRPCForbidden()
319 320
320 321 user = get_user_or_error(userid)
321 322
322 323 # only non optional arguments will be stored in updates
323 324 updates = {}
324 325
325 326 try:
326 327
327 328 store_update(updates, username, 'username')
328 329 store_update(updates, password, 'password')
329 330 store_update(updates, email, 'email')
330 331 store_update(updates, firstname, 'name')
331 332 store_update(updates, lastname, 'lastname')
332 333 store_update(updates, active, 'active')
333 334 store_update(updates, admin, 'admin')
334 335 store_update(updates, extern_name, 'extern_name')
335 336 store_update(updates, extern_type, 'extern_type')
336 337
337 338 user = UserModel().update_user(user, **updates)
338 339 Session().commit()
339 340 return {
340 341 'msg': 'updated user ID:%s %s' % (user.user_id, user.username),
341 342 'user': user.get_api_data(include_secrets=True)
342 343 }
343 344 except DefaultUserException:
344 345 log.exception("Default user edit exception")
345 346 raise JSONRPCError('editing default user is forbidden')
346 347 except Exception:
347 348 log.exception("Error occurred during update of user")
348 349 raise JSONRPCError('failed to update user `%s`' % (userid,))
349 350
350 351
351 352 @jsonrpc_method()
352 353 def delete_user(request, apiuser, userid):
353 354 """
354 355 Deletes the specified user from the |RCE| user database.
355 356
356 357 This command can only be run using an |authtoken| with admin rights to
357 358 the specified repository.
358 359
359 360 .. important::
360 361
361 362 Ensure all open pull requests and open code review
362 363 requests to this user are close.
363 364
364 365 Also ensure all repositories, or repository groups owned by this
365 366 user are reassigned before deletion.
366 367
367 368 This command takes the following options:
368 369
369 370 :param apiuser: This is filled automatically from the |authtoken|.
370 371 :type apiuser: AuthUser
371 372 :param userid: Set the user to delete.
372 373 :type userid: str or int
373 374
374 375 Example output:
375 376
376 377 .. code-block:: bash
377 378
378 379 id : <id_given_in_input>
379 380 result: {
380 381 "msg" : "deleted user ID:<userid> <username>",
381 382 "user": null
382 383 }
383 384 error: null
384 385
385 386 Example error output:
386 387
387 388 .. code-block:: bash
388 389
389 390 id : <id_given_in_input>
390 391 result : null
391 392 error : {
392 393 "failed to delete user ID:<userid> <username>"
393 394 }
394 395
395 396 """
396 397 if not has_superadmin_permission(apiuser):
397 398 raise JSONRPCForbidden()
398 399
399 400 user = get_user_or_error(userid)
400 401
401 402 try:
402 403 UserModel().delete(userid)
403 404 Session().commit()
404 405 return {
405 406 'msg': 'deleted user ID:%s %s' % (user.user_id, user.username),
406 407 'user': None
407 408 }
408 409 except Exception:
409 410 log.exception("Error occurred during deleting of user")
410 411 raise JSONRPCError(
411 412 'failed to delete user ID:%s %s' % (user.user_id, user.username))
412 413
413 414
414 415 @jsonrpc_method()
415 416 def get_user_locks(request, apiuser, userid=Optional(OAttr('apiuser'))):
416 417 """
417 418 Displays all repositories locked by the specified user.
418 419
419 420 * If this command is run by a non-admin user, it returns
420 421 a list of |repos| locked by that user.
421 422
422 423 This command takes the following options:
423 424
424 425 :param apiuser: This is filled automatically from the |authtoken|.
425 426 :type apiuser: AuthUser
426 427 :param userid: Sets the userid whose list of locked |repos| will be
427 428 displayed.
428 429 :type userid: Optional(str or int)
429 430
430 431 Example output:
431 432
432 433 .. code-block:: bash
433 434
434 435 id : <id_given_in_input>
435 436 result : {
436 437 [repo_object, repo_object,...]
437 438 }
438 439 error : null
439 440 """
440 441
441 442 include_secrets = False
442 443 if not has_superadmin_permission(apiuser):
443 444 # make sure normal user does not pass someone else userid,
444 445 # he is not allowed to do that
445 446 if not isinstance(userid, Optional) and userid != apiuser.user_id:
446 447 raise JSONRPCError('userid is not the same as your user')
447 448 else:
448 449 include_secrets = True
449 450
450 451 userid = Optional.extract(userid, evaluate_locals=locals())
451 452 userid = getattr(userid, 'user_id', userid)
452 453 user = get_user_or_error(userid)
453 454
454 455 ret = []
455 456
456 457 # show all locks
457 458 for r in Repository.getAll():
458 459 _user_id, _time, _reason = r.locked
459 460 if _user_id and _time:
460 461 _api_data = r.get_api_data(include_secrets=include_secrets)
461 462 # if we use user filter just show the locks for this user
462 463 if safe_int(_user_id) == user.user_id:
463 464 ret.append(_api_data)
464 465
465 466 return ret
@@ -1,248 +1,249 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 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
22 22 """
23 23 permissions controller for RhodeCode Enterprise
24 24 """
25 25
26 26
27 27 import logging
28 28
29 29 import formencode
30 30 from formencode import htmlfill
31 31 from pylons import request, tmpl_context as c, url
32 32 from pylons.controllers.util import redirect
33 33 from pylons.i18n.translation import _
34 34
35 35 from rhodecode.lib import helpers as h
36 36 from rhodecode.lib import auth
37 37 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
38 38 from rhodecode.lib.base import BaseController, render
39 39 from rhodecode.model.db import User, UserIpMap
40 40 from rhodecode.model.forms import (
41 41 ApplicationPermissionsForm, ObjectPermissionsForm, UserPermissionsForm)
42 42 from rhodecode.model.meta import Session
43 43 from rhodecode.model.permission import PermissionModel
44 44 from rhodecode.model.settings import SettingsModel
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48
49 49 class PermissionsController(BaseController):
50 50 """REST Controller styled on the Atom Publishing Protocol"""
51 51 # To properly map this controller, ensure your config/routing.py
52 52 # file has a resource setup:
53 53 # map.resource('permission', 'permissions')
54 54
55 55 @LoginRequired()
56 56 def __before__(self):
57 57 super(PermissionsController, self).__before__()
58 58
59 59 def __load_data(self):
60 60 PermissionModel().set_global_permission_choices(c, translator=_)
61 61
62 62 @HasPermissionAllDecorator('hg.admin')
63 63 def permission_application(self):
64 64 c.active = 'application'
65 65 self.__load_data()
66 66
67 67 c.user = User.get_default_user()
68 68
69 69 # TODO: johbo: The default user might be based on outdated state which
70 70 # has been loaded from the cache. A call to refresh() ensures that the
71 71 # latest state from the database is used.
72 72 Session().refresh(c.user)
73 73
74 74 app_settings = SettingsModel().get_all_settings()
75 75 defaults = {
76 76 'anonymous': c.user.active,
77 77 'default_register_message': app_settings.get(
78 78 'rhodecode_register_message')
79 79 }
80 80 defaults.update(c.user.get_default_perms())
81 81
82 82 return htmlfill.render(
83 83 render('admin/permissions/permissions.html'),
84 84 defaults=defaults,
85 85 encoding="UTF-8",
86 86 force_defaults=False)
87 87
88 88 @HasPermissionAllDecorator('hg.admin')
89 89 @auth.CSRFRequired()
90 90 def permission_application_update(self):
91 91 c.active = 'application'
92 92 self.__load_data()
93 93 _form = ApplicationPermissionsForm(
94 94 [x[0] for x in c.register_choices],
95 [x[0] for x in c.password_reset_choices],
95 96 [x[0] for x in c.extern_activate_choices])()
96 97
97 98 try:
98 99 form_result = _form.to_python(dict(request.POST))
99 100 form_result.update({'perm_user_name': User.DEFAULT_USER})
100 101 PermissionModel().update_application_permissions(form_result)
101 102
102 103 settings = [
103 104 ('register_message', 'default_register_message'),
104 105 ]
105 106 for setting, form_key in settings:
106 107 sett = SettingsModel().create_or_update_setting(
107 108 setting, form_result[form_key])
108 109 Session().add(sett)
109 110
110 111 Session().commit()
111 112 h.flash(_('Application permissions updated successfully'),
112 113 category='success')
113 114
114 115 except formencode.Invalid as errors:
115 116 defaults = errors.value
116 117
117 118 return htmlfill.render(
118 119 render('admin/permissions/permissions.html'),
119 120 defaults=defaults,
120 121 errors=errors.error_dict or {},
121 122 prefix_error=False,
122 123 encoding="UTF-8",
123 124 force_defaults=False)
124 125 except Exception:
125 126 log.exception("Exception during update of permissions")
126 127 h.flash(_('Error occurred during update of permissions'),
127 128 category='error')
128 129
129 130 return redirect(url('admin_permissions_application'))
130 131
131 132 @HasPermissionAllDecorator('hg.admin')
132 133 def permission_objects(self):
133 134 c.active = 'objects'
134 135 self.__load_data()
135 136 c.user = User.get_default_user()
136 137 defaults = {}
137 138 defaults.update(c.user.get_default_perms())
138 139 return htmlfill.render(
139 140 render('admin/permissions/permissions.html'),
140 141 defaults=defaults,
141 142 encoding="UTF-8",
142 143 force_defaults=False)
143 144
144 145 @HasPermissionAllDecorator('hg.admin')
145 146 @auth.CSRFRequired()
146 147 def permission_objects_update(self):
147 148 c.active = 'objects'
148 149 self.__load_data()
149 150 _form = ObjectPermissionsForm(
150 151 [x[0] for x in c.repo_perms_choices],
151 152 [x[0] for x in c.group_perms_choices],
152 153 [x[0] for x in c.user_group_perms_choices])()
153 154
154 155 try:
155 156 form_result = _form.to_python(dict(request.POST))
156 157 form_result.update({'perm_user_name': User.DEFAULT_USER})
157 158 PermissionModel().update_object_permissions(form_result)
158 159
159 160 Session().commit()
160 161 h.flash(_('Object permissions updated successfully'),
161 162 category='success')
162 163
163 164 except formencode.Invalid as errors:
164 165 defaults = errors.value
165 166
166 167 return htmlfill.render(
167 168 render('admin/permissions/permissions.html'),
168 169 defaults=defaults,
169 170 errors=errors.error_dict or {},
170 171 prefix_error=False,
171 172 encoding="UTF-8",
172 173 force_defaults=False)
173 174 except Exception:
174 175 log.exception("Exception during update of permissions")
175 176 h.flash(_('Error occurred during update of permissions'),
176 177 category='error')
177 178
178 179 return redirect(url('admin_permissions_object'))
179 180
180 181 @HasPermissionAllDecorator('hg.admin')
181 182 def permission_global(self):
182 183 c.active = 'global'
183 184 self.__load_data()
184 185
185 186 c.user = User.get_default_user()
186 187 defaults = {}
187 188 defaults.update(c.user.get_default_perms())
188 189
189 190 return htmlfill.render(
190 191 render('admin/permissions/permissions.html'),
191 192 defaults=defaults,
192 193 encoding="UTF-8",
193 194 force_defaults=False)
194 195
195 196 @HasPermissionAllDecorator('hg.admin')
196 197 @auth.CSRFRequired()
197 198 def permission_global_update(self):
198 199 c.active = 'global'
199 200 self.__load_data()
200 201 _form = UserPermissionsForm(
201 202 [x[0] for x in c.repo_create_choices],
202 203 [x[0] for x in c.repo_create_on_write_choices],
203 204 [x[0] for x in c.repo_group_create_choices],
204 205 [x[0] for x in c.user_group_create_choices],
205 206 [x[0] for x in c.fork_choices],
206 207 [x[0] for x in c.inherit_default_permission_choices])()
207 208
208 209 try:
209 210 form_result = _form.to_python(dict(request.POST))
210 211 form_result.update({'perm_user_name': User.DEFAULT_USER})
211 212 PermissionModel().update_user_permissions(form_result)
212 213
213 214 Session().commit()
214 215 h.flash(_('Global permissions updated successfully'),
215 216 category='success')
216 217
217 218 except formencode.Invalid as errors:
218 219 defaults = errors.value
219 220
220 221 return htmlfill.render(
221 222 render('admin/permissions/permissions.html'),
222 223 defaults=defaults,
223 224 errors=errors.error_dict or {},
224 225 prefix_error=False,
225 226 encoding="UTF-8",
226 227 force_defaults=False)
227 228 except Exception:
228 229 log.exception("Exception during update of permissions")
229 230 h.flash(_('Error occurred during update of permissions'),
230 231 category='error')
231 232
232 233 return redirect(url('admin_permissions_global'))
233 234
234 235 @HasPermissionAllDecorator('hg.admin')
235 236 def permission_ips(self):
236 237 c.active = 'ips'
237 238 c.user = User.get_default_user()
238 239 c.user_ip_map = (
239 240 UserIpMap.query().filter(UserIpMap.user == c.user).all())
240 241
241 242 return render('admin/permissions/permissions.html')
242 243
243 244 @HasPermissionAllDecorator('hg.admin')
244 245 def permission_perms(self):
245 246 c.active = 'perms'
246 247 c.user = User.get_default_user()
247 248 c.perm_user = c.user.AuthUser
248 249 return render('admin/permissions/permissions.html')
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,551 +1,553 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 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 """
22 22 this is forms validation classes
23 23 http://formencode.org/module-formencode.validators.html
24 24 for list off all availible validators
25 25
26 26 we can create our own validators
27 27
28 28 The table below outlines the options which can be used in a schema in addition to the validators themselves
29 29 pre_validators [] These validators will be applied before the schema
30 30 chained_validators [] These validators will be applied after the schema
31 31 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
32 32 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
33 33 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
34 34 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
35 35
36 36
37 37 <name> = formencode.validators.<name of validator>
38 38 <name> must equal form name
39 39 list=[1,2,3,4,5]
40 40 for SELECT use formencode.All(OneOf(list), Int())
41 41
42 42 """
43 43
44 44 import deform
45 45 import logging
46 46 import formencode
47 47
48 48 from pkg_resources import resource_filename
49 49 from formencode import All, Pipe
50 50
51 51 from pylons.i18n.translation import _
52 52
53 53 from rhodecode import BACKENDS
54 54 from rhodecode.lib import helpers
55 55 from rhodecode.model import validators as v
56 56
57 57 log = logging.getLogger(__name__)
58 58
59 59
60 60 deform_templates = resource_filename('deform', 'templates')
61 61 rhodecode_templates = resource_filename('rhodecode', 'templates/forms')
62 62 search_path = (rhodecode_templates, deform_templates)
63 63
64 64
65 65 class RhodecodeFormZPTRendererFactory(deform.ZPTRendererFactory):
66 66 """ Subclass of ZPTRendererFactory to add rhodecode context variables """
67 67 def __call__(self, template_name, **kw):
68 68 kw['h'] = helpers
69 69 return self.load(template_name)(**kw)
70 70
71 71
72 72 form_renderer = RhodecodeFormZPTRendererFactory(search_path)
73 73 deform.Form.set_default_renderer(form_renderer)
74 74
75 75
76 76 def LoginForm():
77 77 class _LoginForm(formencode.Schema):
78 78 allow_extra_fields = True
79 79 filter_extra_fields = True
80 80 username = v.UnicodeString(
81 81 strip=True,
82 82 min=1,
83 83 not_empty=True,
84 84 messages={
85 85 'empty': _(u'Please enter a login'),
86 86 'tooShort': _(u'Enter a value %(min)i characters long or more')
87 87 }
88 88 )
89 89
90 90 password = v.UnicodeString(
91 91 strip=False,
92 92 min=3,
93 93 not_empty=True,
94 94 messages={
95 95 'empty': _(u'Please enter a password'),
96 96 'tooShort': _(u'Enter %(min)i characters or more')}
97 97 )
98 98
99 99 remember = v.StringBoolean(if_missing=False)
100 100
101 101 chained_validators = [v.ValidAuth()]
102 102 return _LoginForm
103 103
104 104
105 105 def UserForm(edit=False, available_languages=[], old_data={}):
106 106 class _UserForm(formencode.Schema):
107 107 allow_extra_fields = True
108 108 filter_extra_fields = True
109 109 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
110 110 v.ValidUsername(edit, old_data))
111 111 if edit:
112 112 new_password = All(
113 113 v.ValidPassword(),
114 114 v.UnicodeString(strip=False, min=6, not_empty=False)
115 115 )
116 116 password_confirmation = All(
117 117 v.ValidPassword(),
118 118 v.UnicodeString(strip=False, min=6, not_empty=False),
119 119 )
120 120 admin = v.StringBoolean(if_missing=False)
121 121 else:
122 122 password = All(
123 123 v.ValidPassword(),
124 124 v.UnicodeString(strip=False, min=6, not_empty=True)
125 125 )
126 126 password_confirmation = All(
127 127 v.ValidPassword(),
128 128 v.UnicodeString(strip=False, min=6, not_empty=False)
129 129 )
130 130
131 131 password_change = v.StringBoolean(if_missing=False)
132 132 create_repo_group = v.StringBoolean(if_missing=False)
133 133
134 134 active = v.StringBoolean(if_missing=False)
135 135 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
136 136 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
137 137 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
138 138 extern_name = v.UnicodeString(strip=True)
139 139 extern_type = v.UnicodeString(strip=True)
140 140 language = v.OneOf(available_languages, hideList=False,
141 141 testValueList=True, if_missing=None)
142 142 chained_validators = [v.ValidPasswordsMatch()]
143 143 return _UserForm
144 144
145 145
146 146 def UserGroupForm(edit=False, old_data=None, available_members=None,
147 147 allow_disabled=False):
148 148 old_data = old_data or {}
149 149 available_members = available_members or []
150 150
151 151 class _UserGroupForm(formencode.Schema):
152 152 allow_extra_fields = True
153 153 filter_extra_fields = True
154 154
155 155 users_group_name = All(
156 156 v.UnicodeString(strip=True, min=1, not_empty=True),
157 157 v.ValidUserGroup(edit, old_data)
158 158 )
159 159 user_group_description = v.UnicodeString(strip=True, min=1,
160 160 not_empty=False)
161 161
162 162 users_group_active = v.StringBoolean(if_missing=False)
163 163
164 164 if edit:
165 165 users_group_members = v.OneOf(
166 166 available_members, hideList=False, testValueList=True,
167 167 if_missing=None, not_empty=False
168 168 )
169 169 # this is user group owner
170 170 user = All(
171 171 v.UnicodeString(not_empty=True),
172 172 v.ValidRepoUser(allow_disabled))
173 173 return _UserGroupForm
174 174
175 175
176 176 def RepoGroupForm(edit=False, old_data=None, available_groups=None,
177 177 can_create_in_root=False, allow_disabled=False):
178 178 old_data = old_data or {}
179 179 available_groups = available_groups or []
180 180
181 181 class _RepoGroupForm(formencode.Schema):
182 182 allow_extra_fields = True
183 183 filter_extra_fields = False
184 184
185 185 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
186 186 v.SlugifyName(),)
187 187 group_description = v.UnicodeString(strip=True, min=1,
188 188 not_empty=False)
189 189 group_copy_permissions = v.StringBoolean(if_missing=False)
190 190
191 191 group_parent_id = v.OneOf(available_groups, hideList=False,
192 192 testValueList=True, not_empty=True)
193 193 enable_locking = v.StringBoolean(if_missing=False)
194 194 chained_validators = [
195 195 v.ValidRepoGroup(edit, old_data, can_create_in_root)]
196 196
197 197 if edit:
198 198 # this is repo group owner
199 199 user = All(
200 200 v.UnicodeString(not_empty=True),
201 201 v.ValidRepoUser(allow_disabled))
202 202
203 203 return _RepoGroupForm
204 204
205 205
206 206 def RegisterForm(edit=False, old_data={}):
207 207 class _RegisterForm(formencode.Schema):
208 208 allow_extra_fields = True
209 209 filter_extra_fields = True
210 210 username = All(
211 211 v.ValidUsername(edit, old_data),
212 212 v.UnicodeString(strip=True, min=1, not_empty=True)
213 213 )
214 214 password = All(
215 215 v.ValidPassword(),
216 216 v.UnicodeString(strip=False, min=6, not_empty=True)
217 217 )
218 218 password_confirmation = All(
219 219 v.ValidPassword(),
220 220 v.UnicodeString(strip=False, min=6, not_empty=True)
221 221 )
222 222 active = v.StringBoolean(if_missing=False)
223 223 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
224 224 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
225 225 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
226 226
227 227 chained_validators = [v.ValidPasswordsMatch()]
228 228
229 229 return _RegisterForm
230 230
231 231
232 232 def PasswordResetForm():
233 233 class _PasswordResetForm(formencode.Schema):
234 234 allow_extra_fields = True
235 235 filter_extra_fields = True
236 236 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
237 237 return _PasswordResetForm
238 238
239 239
240 240 def RepoForm(edit=False, old_data=None, repo_groups=None, landing_revs=None,
241 241 allow_disabled=False):
242 242 old_data = old_data or {}
243 243 repo_groups = repo_groups or []
244 244 landing_revs = landing_revs or []
245 245 supported_backends = BACKENDS.keys()
246 246
247 247 class _RepoForm(formencode.Schema):
248 248 allow_extra_fields = True
249 249 filter_extra_fields = False
250 250 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
251 251 v.SlugifyName())
252 252 repo_group = All(v.CanWriteGroup(old_data),
253 253 v.OneOf(repo_groups, hideList=True))
254 254 repo_type = v.OneOf(supported_backends, required=False,
255 255 if_missing=old_data.get('repo_type'))
256 256 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
257 257 repo_private = v.StringBoolean(if_missing=False)
258 258 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
259 259 repo_copy_permissions = v.StringBoolean(if_missing=False)
260 260 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
261 261
262 262 repo_enable_statistics = v.StringBoolean(if_missing=False)
263 263 repo_enable_downloads = v.StringBoolean(if_missing=False)
264 264 repo_enable_locking = v.StringBoolean(if_missing=False)
265 265
266 266 if edit:
267 267 # this is repo owner
268 268 user = All(
269 269 v.UnicodeString(not_empty=True),
270 270 v.ValidRepoUser(allow_disabled))
271 271 clone_uri_change = v.UnicodeString(
272 272 not_empty=False, if_missing=v.Missing)
273 273
274 274 chained_validators = [v.ValidCloneUri(),
275 275 v.ValidRepoName(edit, old_data)]
276 276 return _RepoForm
277 277
278 278
279 279 def RepoPermsForm():
280 280 class _RepoPermsForm(formencode.Schema):
281 281 allow_extra_fields = True
282 282 filter_extra_fields = False
283 283 chained_validators = [v.ValidPerms(type_='repo')]
284 284 return _RepoPermsForm
285 285
286 286
287 287 def RepoGroupPermsForm(valid_recursive_choices):
288 288 class _RepoGroupPermsForm(formencode.Schema):
289 289 allow_extra_fields = True
290 290 filter_extra_fields = False
291 291 recursive = v.OneOf(valid_recursive_choices)
292 292 chained_validators = [v.ValidPerms(type_='repo_group')]
293 293 return _RepoGroupPermsForm
294 294
295 295
296 296 def UserGroupPermsForm():
297 297 class _UserPermsForm(formencode.Schema):
298 298 allow_extra_fields = True
299 299 filter_extra_fields = False
300 300 chained_validators = [v.ValidPerms(type_='user_group')]
301 301 return _UserPermsForm
302 302
303 303
304 304 def RepoFieldForm():
305 305 class _RepoFieldForm(formencode.Schema):
306 306 filter_extra_fields = True
307 307 allow_extra_fields = True
308 308
309 309 new_field_key = All(v.FieldKey(),
310 310 v.UnicodeString(strip=True, min=3, not_empty=True))
311 311 new_field_value = v.UnicodeString(not_empty=False, if_missing=u'')
312 312 new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'],
313 313 if_missing='str')
314 314 new_field_label = v.UnicodeString(not_empty=False)
315 315 new_field_desc = v.UnicodeString(not_empty=False)
316 316
317 317 return _RepoFieldForm
318 318
319 319
320 320 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
321 321 repo_groups=[], landing_revs=[]):
322 322 class _RepoForkForm(formencode.Schema):
323 323 allow_extra_fields = True
324 324 filter_extra_fields = False
325 325 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
326 326 v.SlugifyName())
327 327 repo_group = All(v.CanWriteGroup(),
328 328 v.OneOf(repo_groups, hideList=True))
329 329 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
330 330 description = v.UnicodeString(strip=True, min=1, not_empty=True)
331 331 private = v.StringBoolean(if_missing=False)
332 332 copy_permissions = v.StringBoolean(if_missing=False)
333 333 fork_parent_id = v.UnicodeString()
334 334 chained_validators = [v.ValidForkName(edit, old_data)]
335 335 landing_rev = v.OneOf(landing_revs, hideList=True)
336 336
337 337 return _RepoForkForm
338 338
339 339
340 340 def ApplicationSettingsForm():
341 341 class _ApplicationSettingsForm(formencode.Schema):
342 342 allow_extra_fields = True
343 343 filter_extra_fields = False
344 344 rhodecode_title = v.UnicodeString(strip=True, max=40, not_empty=False)
345 345 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
346 346 rhodecode_pre_code = v.UnicodeString(strip=True, min=1, not_empty=False)
347 347 rhodecode_post_code = v.UnicodeString(strip=True, min=1, not_empty=False)
348 348 rhodecode_captcha_public_key = v.UnicodeString(strip=True, min=1, not_empty=False)
349 349 rhodecode_captcha_private_key = v.UnicodeString(strip=True, min=1, not_empty=False)
350 350
351 351 return _ApplicationSettingsForm
352 352
353 353
354 354 def ApplicationVisualisationForm():
355 355 class _ApplicationVisualisationForm(formencode.Schema):
356 356 allow_extra_fields = True
357 357 filter_extra_fields = False
358 358 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
359 359 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
360 360 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
361 361
362 362 rhodecode_repository_fields = v.StringBoolean(if_missing=False)
363 363 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
364 364 rhodecode_dashboard_items = v.Int(min=5, not_empty=True)
365 365 rhodecode_admin_grid_items = v.Int(min=5, not_empty=True)
366 366 rhodecode_show_version = v.StringBoolean(if_missing=False)
367 367 rhodecode_use_gravatar = v.StringBoolean(if_missing=False)
368 368 rhodecode_markup_renderer = v.OneOf(['markdown', 'rst'])
369 369 rhodecode_gravatar_url = v.UnicodeString(min=3)
370 370 rhodecode_clone_uri_tmpl = v.UnicodeString(min=3)
371 371 rhodecode_support_url = v.UnicodeString()
372 372 rhodecode_show_revision_number = v.StringBoolean(if_missing=False)
373 373 rhodecode_show_sha_length = v.Int(min=4, not_empty=True)
374 374
375 375 return _ApplicationVisualisationForm
376 376
377 377
378 378 class _BaseVcsSettingsForm(formencode.Schema):
379 379 allow_extra_fields = True
380 380 filter_extra_fields = False
381 381 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
382 382 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
383 383 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
384 384
385 385 extensions_largefiles = v.StringBoolean(if_missing=False)
386 386 phases_publish = v.StringBoolean(if_missing=False)
387 387
388 388 rhodecode_pr_merge_enabled = v.StringBoolean(if_missing=False)
389 389 rhodecode_use_outdated_comments = v.StringBoolean(if_missing=False)
390 390 rhodecode_hg_use_rebase_for_merging = v.StringBoolean(if_missing=False)
391 391
392 392 vcs_svn_proxy_http_requests_enabled = v.StringBoolean(if_missing=False)
393 393 vcs_svn_proxy_http_server_url = v.UnicodeString(strip=True, if_missing=None)
394 394
395 395
396 396 def ApplicationUiSettingsForm():
397 397 class _ApplicationUiSettingsForm(_BaseVcsSettingsForm):
398 398 web_push_ssl = v.StringBoolean(if_missing=False)
399 399 paths_root_path = All(
400 400 v.ValidPath(),
401 401 v.UnicodeString(strip=True, min=1, not_empty=True)
402 402 )
403 403 extensions_hgsubversion = v.StringBoolean(if_missing=False)
404 404 extensions_hggit = v.StringBoolean(if_missing=False)
405 405 new_svn_branch = v.ValidSvnPattern(section='vcs_svn_branch')
406 406 new_svn_tag = v.ValidSvnPattern(section='vcs_svn_tag')
407 407
408 408 return _ApplicationUiSettingsForm
409 409
410 410
411 411 def RepoVcsSettingsForm(repo_name):
412 412 class _RepoVcsSettingsForm(_BaseVcsSettingsForm):
413 413 inherit_global_settings = v.StringBoolean(if_missing=False)
414 414 new_svn_branch = v.ValidSvnPattern(
415 415 section='vcs_svn_branch', repo_name=repo_name)
416 416 new_svn_tag = v.ValidSvnPattern(
417 417 section='vcs_svn_tag', repo_name=repo_name)
418 418
419 419 return _RepoVcsSettingsForm
420 420
421 421
422 422 def LabsSettingsForm():
423 423 class _LabSettingsForm(formencode.Schema):
424 424 allow_extra_fields = True
425 425 filter_extra_fields = False
426 426
427 427 return _LabSettingsForm
428 428
429 429
430 def ApplicationPermissionsForm(register_choices, extern_activate_choices):
430 def ApplicationPermissionsForm(
431 register_choices, password_reset_choices, extern_activate_choices):
431 432 class _DefaultPermissionsForm(formencode.Schema):
432 433 allow_extra_fields = True
433 434 filter_extra_fields = True
434 435
435 436 anonymous = v.StringBoolean(if_missing=False)
436 437 default_register = v.OneOf(register_choices)
437 438 default_register_message = v.UnicodeString()
439 default_password_reset = v.OneOf(password_reset_choices)
438 440 default_extern_activate = v.OneOf(extern_activate_choices)
439 441
440 442 return _DefaultPermissionsForm
441 443
442 444
443 445 def ObjectPermissionsForm(repo_perms_choices, group_perms_choices,
444 446 user_group_perms_choices):
445 447 class _ObjectPermissionsForm(formencode.Schema):
446 448 allow_extra_fields = True
447 449 filter_extra_fields = True
448 450 overwrite_default_repo = v.StringBoolean(if_missing=False)
449 451 overwrite_default_group = v.StringBoolean(if_missing=False)
450 452 overwrite_default_user_group = v.StringBoolean(if_missing=False)
451 453 default_repo_perm = v.OneOf(repo_perms_choices)
452 454 default_group_perm = v.OneOf(group_perms_choices)
453 455 default_user_group_perm = v.OneOf(user_group_perms_choices)
454 456
455 457 return _ObjectPermissionsForm
456 458
457 459
458 460 def UserPermissionsForm(create_choices, create_on_write_choices,
459 461 repo_group_create_choices, user_group_create_choices,
460 462 fork_choices, inherit_default_permissions_choices):
461 463 class _DefaultPermissionsForm(formencode.Schema):
462 464 allow_extra_fields = True
463 465 filter_extra_fields = True
464 466
465 467 anonymous = v.StringBoolean(if_missing=False)
466 468
467 469 default_repo_create = v.OneOf(create_choices)
468 470 default_repo_create_on_write = v.OneOf(create_on_write_choices)
469 471 default_user_group_create = v.OneOf(user_group_create_choices)
470 472 default_repo_group_create = v.OneOf(repo_group_create_choices)
471 473 default_fork_create = v.OneOf(fork_choices)
472 474 default_inherit_default_permissions = v.OneOf(inherit_default_permissions_choices)
473 475
474 476 return _DefaultPermissionsForm
475 477
476 478
477 479 def UserIndividualPermissionsForm():
478 480 class _DefaultPermissionsForm(formencode.Schema):
479 481 allow_extra_fields = True
480 482 filter_extra_fields = True
481 483
482 484 inherit_default_permissions = v.StringBoolean(if_missing=False)
483 485
484 486 return _DefaultPermissionsForm
485 487
486 488
487 489 def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
488 490 class _DefaultsForm(formencode.Schema):
489 491 allow_extra_fields = True
490 492 filter_extra_fields = True
491 493 default_repo_type = v.OneOf(supported_backends)
492 494 default_repo_private = v.StringBoolean(if_missing=False)
493 495 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
494 496 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
495 497 default_repo_enable_locking = v.StringBoolean(if_missing=False)
496 498
497 499 return _DefaultsForm
498 500
499 501
500 502 def AuthSettingsForm():
501 503 class _AuthSettingsForm(formencode.Schema):
502 504 allow_extra_fields = True
503 505 filter_extra_fields = True
504 506 auth_plugins = All(v.ValidAuthPlugins(),
505 507 v.UniqueListFromString()(not_empty=True))
506 508
507 509 return _AuthSettingsForm
508 510
509 511
510 512 def UserExtraEmailForm():
511 513 class _UserExtraEmailForm(formencode.Schema):
512 514 email = All(v.UniqSystemEmail(), v.Email(not_empty=True))
513 515 return _UserExtraEmailForm
514 516
515 517
516 518 def UserExtraIpForm():
517 519 class _UserExtraIpForm(formencode.Schema):
518 520 ip = v.ValidIp()(not_empty=True)
519 521 return _UserExtraIpForm
520 522
521 523
522 524
523 525 def PullRequestForm(repo_id):
524 526 class ReviewerForm(formencode.Schema):
525 527 user_id = v.Int(not_empty=True)
526 528 reasons = All()
527 529
528 530 class _PullRequestForm(formencode.Schema):
529 531 allow_extra_fields = True
530 532 filter_extra_fields = True
531 533
532 534 user = v.UnicodeString(strip=True, required=True)
533 535 source_repo = v.UnicodeString(strip=True, required=True)
534 536 source_ref = v.UnicodeString(strip=True, required=True)
535 537 target_repo = v.UnicodeString(strip=True, required=True)
536 538 target_ref = v.UnicodeString(strip=True, required=True)
537 539 revisions = All(#v.NotReviewedRevisions(repo_id)(),
538 540 v.UniqueList()(not_empty=True))
539 541 review_members = formencode.ForEach(ReviewerForm())
540 542 pullrequest_title = v.UnicodeString(strip=True, required=True)
541 543 pullrequest_desc = v.UnicodeString(strip=True, required=False)
542 544
543 545 return _PullRequestForm
544 546
545 547
546 548 def IssueTrackerPatternsForm():
547 549 class _IssueTrackerPatternsForm(formencode.Schema):
548 550 allow_extra_fields = True
549 551 filter_extra_fields = False
550 552 chained_validators = [v.ValidPattern()]
551 553 return _IssueTrackerPatternsForm
@@ -1,470 +1,482 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 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 """
22 22 permissions model for RhodeCode
23 23 """
24 24
25 25
26 26 import logging
27 27 import traceback
28 28
29 29 from sqlalchemy.exc import DatabaseError
30 30
31 31 from rhodecode.model import BaseModel
32 32 from rhodecode.model.db import (
33 33 User, Permission, UserToPerm, UserRepoToPerm, UserRepoGroupToPerm,
34 34 UserUserGroupToPerm, UserGroup, UserGroupToPerm)
35 35 from rhodecode.lib.utils2 import str2bool, safe_int
36 36
37 37 log = logging.getLogger(__name__)
38 38
39 39
40 40 class PermissionModel(BaseModel):
41 41 """
42 42 Permissions model for RhodeCode
43 43 """
44 44
45 45 cls = Permission
46 46 global_perms = {
47 47 'default_repo_create': None,
48 48 # special case for create repos on write access to group
49 49 'default_repo_create_on_write': None,
50 50 'default_repo_group_create': None,
51 51 'default_user_group_create': None,
52 52 'default_fork_create': None,
53 53 'default_inherit_default_permissions': None,
54
55 54 'default_register': None,
55 'default_password_reset': None,
56 56 'default_extern_activate': None,
57 57
58 58 # object permissions below
59 59 'default_repo_perm': None,
60 60 'default_group_perm': None,
61 61 'default_user_group_perm': None,
62 62 }
63 63
64 64 def set_global_permission_choices(self, c_obj, translator):
65 65 c_obj.repo_perms_choices = [
66 66 ('repository.none', translator('None'),),
67 67 ('repository.read', translator('Read'),),
68 68 ('repository.write', translator('Write'),),
69 69 ('repository.admin', translator('Admin'),)]
70 70
71 71 c_obj.group_perms_choices = [
72 72 ('group.none', translator('None'),),
73 73 ('group.read', translator('Read'),),
74 74 ('group.write', translator('Write'),),
75 75 ('group.admin', translator('Admin'),)]
76 76
77 77 c_obj.user_group_perms_choices = [
78 78 ('usergroup.none', translator('None'),),
79 79 ('usergroup.read', translator('Read'),),
80 80 ('usergroup.write', translator('Write'),),
81 81 ('usergroup.admin', translator('Admin'),)]
82 82
83 83 c_obj.register_choices = [
84 84 ('hg.register.none', translator('Disabled')),
85 85 ('hg.register.manual_activate', translator('Allowed with manual account activation')),
86 86 ('hg.register.auto_activate', translator('Allowed with automatic account activation')),]
87 87
88 c_obj.password_reset_choices = [
89 ('hg.password_reset.enabled', translator('Allow password recovery')),
90 ('hg.password_reset.hidden', translator('Hide password recovery link')),
91 ('hg.password_reset.disabled', translator('Disable password recovery')),]
92
88 93 c_obj.extern_activate_choices = [
89 94 ('hg.extern_activate.manual', translator('Manual activation of external account')),
90 95 ('hg.extern_activate.auto', translator('Automatic activation of external account')),]
91 96
92 97 c_obj.repo_create_choices = [
93 98 ('hg.create.none', translator('Disabled')),
94 99 ('hg.create.repository', translator('Enabled'))]
95 100
96 101 c_obj.repo_create_on_write_choices = [
97 102 ('hg.create.write_on_repogroup.false', translator('Disabled')),
98 103 ('hg.create.write_on_repogroup.true', translator('Enabled'))]
99 104
100 105 c_obj.user_group_create_choices = [
101 106 ('hg.usergroup.create.false', translator('Disabled')),
102 107 ('hg.usergroup.create.true', translator('Enabled'))]
103 108
104 109 c_obj.repo_group_create_choices = [
105 110 ('hg.repogroup.create.false', translator('Disabled')),
106 111 ('hg.repogroup.create.true', translator('Enabled'))]
107 112
108 113 c_obj.fork_choices = [
109 114 ('hg.fork.none', translator('Disabled')),
110 115 ('hg.fork.repository', translator('Enabled'))]
111 116
112 117 c_obj.inherit_default_permission_choices = [
113 118 ('hg.inherit_default_perms.false', translator('Disabled')),
114 119 ('hg.inherit_default_perms.true', translator('Enabled'))]
115 120
116 121 def get_default_perms(self, object_perms, suffix):
117 122 defaults = {}
118 123 for perm in object_perms:
119 124 # perms
120 125 if perm.permission.permission_name.startswith('repository.'):
121 126 defaults['default_repo_perm' + suffix] = perm.permission.permission_name
122 127
123 128 if perm.permission.permission_name.startswith('group.'):
124 129 defaults['default_group_perm' + suffix] = perm.permission.permission_name
125 130
126 131 if perm.permission.permission_name.startswith('usergroup.'):
127 132 defaults['default_user_group_perm' + suffix] = perm.permission.permission_name
128 133
129 134 # creation of objects
130 135 if perm.permission.permission_name.startswith('hg.create.write_on_repogroup'):
131 136 defaults['default_repo_create_on_write' + suffix] = perm.permission.permission_name
132 137
133 138 elif perm.permission.permission_name.startswith('hg.create.'):
134 139 defaults['default_repo_create' + suffix] = perm.permission.permission_name
135 140
136 141 if perm.permission.permission_name.startswith('hg.fork.'):
137 142 defaults['default_fork_create' + suffix] = perm.permission.permission_name
138 143
139 144 if perm.permission.permission_name.startswith('hg.inherit_default_perms.'):
140 145 defaults['default_inherit_default_permissions' + suffix] = perm.permission.permission_name
141 146
142 147 if perm.permission.permission_name.startswith('hg.repogroup.'):
143 148 defaults['default_repo_group_create' + suffix] = perm.permission.permission_name
144 149
145 150 if perm.permission.permission_name.startswith('hg.usergroup.'):
146 151 defaults['default_user_group_create' + suffix] = perm.permission.permission_name
147 152
148 153 # registration and external account activation
149 154 if perm.permission.permission_name.startswith('hg.register.'):
150 155 defaults['default_register' + suffix] = perm.permission.permission_name
151 156
157 if perm.permission.permission_name.startswith('hg.password_reset.'):
158 defaults['default_password_reset' + suffix] = perm.permission.permission_name
159
152 160 if perm.permission.permission_name.startswith('hg.extern_activate.'):
153 161 defaults['default_extern_activate' + suffix] = perm.permission.permission_name
154 162
155 163 return defaults
156 164
157 165 def _make_new_user_perm(self, user, perm_name):
158 166 log.debug('Creating new user permission:%s', perm_name)
159 167 new = UserToPerm()
160 168 new.user = user
161 169 new.permission = Permission.get_by_key(perm_name)
162 170 return new
163 171
164 172 def _make_new_user_group_perm(self, user_group, perm_name):
165 173 log.debug('Creating new user group permission:%s', perm_name)
166 174 new = UserGroupToPerm()
167 175 new.users_group = user_group
168 176 new.permission = Permission.get_by_key(perm_name)
169 177 return new
170 178
171 179 def _keep_perm(self, perm_name, keep_fields):
172 180 def get_pat(field_name):
173 181 return {
174 182 # global perms
175 183 'default_repo_create': 'hg.create.',
176 184 # special case for create repos on write access to group
177 185 'default_repo_create_on_write': 'hg.create.write_on_repogroup.',
178 186 'default_repo_group_create': 'hg.repogroup.create.',
179 187 'default_user_group_create': 'hg.usergroup.create.',
180 188 'default_fork_create': 'hg.fork.',
181 189 'default_inherit_default_permissions': 'hg.inherit_default_perms.',
182 190
183 191 # application perms
184 192 'default_register': 'hg.register.',
193 'default_password_reset': 'hg.password_reset.',
185 194 'default_extern_activate': 'hg.extern_activate.',
186 195
187 196 # object permissions below
188 197 'default_repo_perm': 'repository.',
189 198 'default_group_perm': 'group.',
190 199 'default_user_group_perm': 'usergroup.',
191 200 }[field_name]
192 201 for field in keep_fields:
193 202 pat = get_pat(field)
194 203 if perm_name.startswith(pat):
195 204 return True
196 205 return False
197 206
198 207 def _clear_object_perm(self, object_perms, preserve=None):
199 208 preserve = preserve or []
200 209 _deleted = []
201 210 for perm in object_perms:
202 211 perm_name = perm.permission.permission_name
203 212 if not self._keep_perm(perm_name, keep_fields=preserve):
204 213 _deleted.append(perm_name)
205 214 self.sa.delete(perm)
206 215 return _deleted
207 216
208 217 def _clear_user_perms(self, user_id, preserve=None):
209 218 perms = self.sa.query(UserToPerm)\
210 219 .filter(UserToPerm.user_id == user_id)\
211 220 .all()
212 221 return self._clear_object_perm(perms, preserve=preserve)
213 222
214 223 def _clear_user_group_perms(self, user_group_id, preserve=None):
215 224 perms = self.sa.query(UserGroupToPerm)\
216 225 .filter(UserGroupToPerm.users_group_id == user_group_id)\
217 226 .all()
218 227 return self._clear_object_perm(perms, preserve=preserve)
219 228
220 229 def _set_new_object_perms(self, obj_type, object, form_result, preserve=None):
221 230 # clear current entries, to make this function idempotent
222 231 # it will fix even if we define more permissions or permissions
223 232 # are somehow missing
224 233 preserve = preserve or []
225 234 _global_perms = self.global_perms.copy()
226 235 if obj_type not in ['user', 'user_group']:
227 236 raise ValueError("obj_type must be on of 'user' or 'user_group'")
228 237 if len(_global_perms) != len(Permission.DEFAULT_USER_PERMISSIONS):
229 238 raise Exception('Inconsistent permissions definition')
230 239
231 240 if obj_type == 'user':
232 241 self._clear_user_perms(object.user_id, preserve)
233 242 if obj_type == 'user_group':
234 243 self._clear_user_group_perms(object.users_group_id, preserve)
235 244
236 245 # now kill the keys that we want to preserve from the form.
237 246 for key in preserve:
238 247 del _global_perms[key]
239 248
240 249 for k in _global_perms.copy():
241 250 _global_perms[k] = form_result[k]
242 251
243 252 # at that stage we validate all are passed inside form_result
244 253 for _perm_key, perm_value in _global_perms.items():
245 254 if perm_value is None:
246 255 raise ValueError('Missing permission for %s' % (_perm_key,))
247 256
248 257 if obj_type == 'user':
249 258 p = self._make_new_user_perm(object, perm_value)
250 259 self.sa.add(p)
251 260 if obj_type == 'user_group':
252 261 p = self._make_new_user_group_perm(object, perm_value)
253 262 self.sa.add(p)
254 263
255 264 def _set_new_user_perms(self, user, form_result, preserve=None):
256 265 return self._set_new_object_perms(
257 266 'user', user, form_result, preserve)
258 267
259 268 def _set_new_user_group_perms(self, user_group, form_result, preserve=None):
260 269 return self._set_new_object_perms(
261 270 'user_group', user_group, form_result, preserve)
262 271
263 272 def set_new_user_perms(self, user, form_result):
264 273 # calculate what to preserve from what is given in form_result
265 274 preserve = set(self.global_perms.keys()).difference(set(form_result.keys()))
266 275 return self._set_new_user_perms(user, form_result, preserve)
267 276
268 277 def set_new_user_group_perms(self, user_group, form_result):
269 278 # calculate what to preserve from what is given in form_result
270 279 preserve = set(self.global_perms.keys()).difference(set(form_result.keys()))
271 280 return self._set_new_user_group_perms(user_group, form_result, preserve)
272 281
273 282 def create_permissions(self):
274 283 """
275 284 Create permissions for whole system
276 285 """
277 286 for p in Permission.PERMS:
278 287 if not Permission.get_by_key(p[0]):
279 288 new_perm = Permission()
280 289 new_perm.permission_name = p[0]
281 290 new_perm.permission_longname = p[0] # translation err with p[1]
282 291 self.sa.add(new_perm)
283 292
284 293 def _create_default_object_permission(self, obj_type, obj, obj_perms,
285 294 force=False):
286 295 if obj_type not in ['user', 'user_group']:
287 296 raise ValueError("obj_type must be on of 'user' or 'user_group'")
288 297
289 298 def _get_group(perm_name):
290 299 return '.'.join(perm_name.split('.')[:1])
291 300
292 301 defined_perms_groups = map(
293 302 _get_group, (x.permission.permission_name for x in obj_perms))
294 303 log.debug('GOT ALREADY DEFINED:%s', obj_perms)
295 304
296 305 if force:
297 306 self._clear_object_perm(obj_perms)
298 307 self.sa.commit()
299 308 defined_perms_groups = []
300 309 # for every default permission that needs to be created, we check if
301 310 # it's group is already defined, if it's not we create default perm
302 311 for perm_name in Permission.DEFAULT_USER_PERMISSIONS:
303 312 gr = _get_group(perm_name)
304 313 if gr not in defined_perms_groups:
305 314 log.debug('GR:%s not found, creating permission %s',
306 315 gr, perm_name)
307 316 if obj_type == 'user':
308 317 new_perm = self._make_new_user_perm(obj, perm_name)
309 318 self.sa.add(new_perm)
310 319 if obj_type == 'user_group':
311 320 new_perm = self._make_new_user_group_perm(obj, perm_name)
312 321 self.sa.add(new_perm)
313 322
314 323 def create_default_user_permissions(self, user, force=False):
315 324 """
316 325 Creates only missing default permissions for user, if force is set it
317 326 resets the default permissions for that user
318 327
319 328 :param user:
320 329 :param force:
321 330 """
322 331 user = self._get_user(user)
323 332 obj_perms = UserToPerm.query().filter(UserToPerm.user == user).all()
324 333 return self._create_default_object_permission(
325 334 'user', user, obj_perms, force)
326 335
327 336 def create_default_user_group_permissions(self, user_group, force=False):
328 337 """
329 338 Creates only missing default permissions for user group, if force is set it
330 339 resets the default permissions for that user group
331 340
332 341 :param user_group:
333 342 :param force:
334 343 """
335 344 user_group = self._get_user_group(user_group)
336 345 obj_perms = UserToPerm.query().filter(UserGroupToPerm.users_group == user_group).all()
337 346 return self._create_default_object_permission(
338 347 'user_group', user_group, obj_perms, force)
339 348
340 349 def update_application_permissions(self, form_result):
341 350 if 'perm_user_id' in form_result:
342 351 perm_user = User.get(safe_int(form_result['perm_user_id']))
343 352 else:
344 353 # used mostly to do lookup for default user
345 354 perm_user = User.get_by_username(form_result['perm_user_name'])
346 355
347 356 try:
348 357 # stage 1 set anonymous access
349 358 if perm_user.username == User.DEFAULT_USER:
350 359 perm_user.active = str2bool(form_result['anonymous'])
351 360 self.sa.add(perm_user)
352 361
353 362 # stage 2 reset defaults and set them from form data
354 363 self._set_new_user_perms(perm_user, form_result, preserve=[
355 364 'default_repo_perm',
356 365 'default_group_perm',
357 366 'default_user_group_perm',
358 367
359 368 'default_repo_group_create',
360 369 'default_user_group_create',
361 370 'default_repo_create_on_write',
362 371 'default_repo_create',
363 372 'default_fork_create',
364 373 'default_inherit_default_permissions',])
365 374
366 375 self.sa.commit()
367 376 except (DatabaseError,):
368 377 log.error(traceback.format_exc())
369 378 self.sa.rollback()
370 379 raise
371 380
372 381 def update_user_permissions(self, form_result):
373 382 if 'perm_user_id' in form_result:
374 383 perm_user = User.get(safe_int(form_result['perm_user_id']))
375 384 else:
376 385 # used mostly to do lookup for default user
377 386 perm_user = User.get_by_username(form_result['perm_user_name'])
378 387 try:
379 388 # stage 2 reset defaults and set them from form data
380 389 self._set_new_user_perms(perm_user, form_result, preserve=[
381 390 'default_repo_perm',
382 391 'default_group_perm',
383 392 'default_user_group_perm',
384 393
385 394 'default_register',
395 'default_password_reset',
386 396 'default_extern_activate'])
387 397 self.sa.commit()
388 398 except (DatabaseError,):
389 399 log.error(traceback.format_exc())
390 400 self.sa.rollback()
391 401 raise
392 402
393 403 def update_user_group_permissions(self, form_result):
394 404 if 'perm_user_group_id' in form_result:
395 405 perm_user_group = UserGroup.get(safe_int(form_result['perm_user_group_id']))
396 406 else:
397 407 # used mostly to do lookup for default user
398 408 perm_user_group = UserGroup.get_by_group_name(form_result['perm_user_group_name'])
399 409 try:
400 410 # stage 2 reset defaults and set them from form data
401 411 self._set_new_user_group_perms(perm_user_group, form_result, preserve=[
402 412 'default_repo_perm',
403 413 'default_group_perm',
404 414 'default_user_group_perm',
405 415
406 416 'default_register',
417 'default_password_reset',
407 418 'default_extern_activate'])
408 419 self.sa.commit()
409 420 except (DatabaseError,):
410 421 log.error(traceback.format_exc())
411 422 self.sa.rollback()
412 423 raise
413 424
414 425 def update_object_permissions(self, form_result):
415 426 if 'perm_user_id' in form_result:
416 427 perm_user = User.get(safe_int(form_result['perm_user_id']))
417 428 else:
418 429 # used mostly to do lookup for default user
419 430 perm_user = User.get_by_username(form_result['perm_user_name'])
420 431 try:
421 432
422 433 # stage 2 reset defaults and set them from form data
423 434 self._set_new_user_perms(perm_user, form_result, preserve=[
424 435 'default_repo_group_create',
425 436 'default_user_group_create',
426 437 'default_repo_create_on_write',
427 438 'default_repo_create',
428 439 'default_fork_create',
429 440 'default_inherit_default_permissions',
430 441
431 442 'default_register',
443 'default_password_reset',
432 444 'default_extern_activate'])
433 445
434 446 # overwrite default repo permissions
435 447 if form_result['overwrite_default_repo']:
436 448 _def_name = form_result['default_repo_perm'].split('repository.')[-1]
437 449 _def = Permission.get_by_key('repository.' + _def_name)
438 450 for r2p in self.sa.query(UserRepoToPerm)\
439 451 .filter(UserRepoToPerm.user == perm_user)\
440 452 .all():
441 453 # don't reset PRIVATE repositories
442 454 if not r2p.repository.private:
443 455 r2p.permission = _def
444 456 self.sa.add(r2p)
445 457
446 458 # overwrite default repo group permissions
447 459 if form_result['overwrite_default_group']:
448 460 _def_name = form_result['default_group_perm'].split('group.')[-1]
449 461 _def = Permission.get_by_key('group.' + _def_name)
450 462 for g2p in self.sa.query(UserRepoGroupToPerm)\
451 463 .filter(UserRepoGroupToPerm.user == perm_user)\
452 464 .all():
453 465 g2p.permission = _def
454 466 self.sa.add(g2p)
455 467
456 468 # overwrite default user group permissions
457 469 if form_result['overwrite_default_user_group']:
458 470 _def_name = form_result['default_user_group_perm'].split('usergroup.')[-1]
459 471 # user groups
460 472 _def = Permission.get_by_key('usergroup.' + _def_name)
461 473 for g2p in self.sa.query(UserUserGroupToPerm)\
462 474 .filter(UserUserGroupToPerm.user == perm_user)\
463 475 .all():
464 476 g2p.permission = _def
465 477 self.sa.add(g2p)
466 478 self.sa.commit()
467 479 except (DatabaseError,):
468 480 log.exception('Failed to set default object permissions')
469 481 self.sa.rollback()
470 482 raise
@@ -1,297 +1,301 b''
1 1 //LOGIN
2 2
3 3
4 4 .loginbox {
5 5 max-width: 65%;
6 6 margin: @pagepadding auto;
7 7 font-family: @text-light;
8 8 border: @border-thickness solid @grey5;
9 9 box-sizing: border-box;
10 10
11 11 @media (max-width:1200px) {
12 12 max-width: 85%;
13 13 }
14 14
15 15 @media (max-width:768px) {
16 16 max-width: 100%;
17 17 width: 100%;
18 18 margin: 0;
19 19 }
20 20
21 21 .title {
22 22 float: none;
23 23 }
24 24
25 25 .header {
26 26 width: 100%;
27 27 padding: 0 35px;
28 28 box-sizing: border-box;
29 29
30 30 .title {
31 31 padding: 0;
32 32 }
33 33 }
34 34
35 35 .loginwrapper {
36 36 float: left;
37 37 height: 100%;
38 38 width: 100%;
39 39 padding: 35px 55px 35px 0;
40 40 background-color: white;
41 41 box-sizing: border-box;
42 42
43 43 @media (max-width:414px) {
44 44 padding: 35px;
45 45 }
46 46 }
47 47
48 48 .left-column {
49 49 float: left;
50 50 position: relative;
51 51 width: 50%;
52 52 height: 100%;
53 53
54 54 @media (max-width:414px) {
55 55 display:none;
56 56 }
57 57 }
58 58
59 59 .right-column {
60 60 float: right;
61 61 position: relative;
62 62 width: 50%;
63 63
64 64 @media (max-width:414px) {
65 65 width: 100%;
66 66 }
67 67 }
68 68
69 69 .sign-in-image {
70 70 display: block;
71 71 width: 65%;
72 72 margin: 5% auto;
73 73 }
74 74
75 75 .sign-in-title {
76 76 h1 {
77 77 margin: 0;
78 78 }
79 79
80 80 h4 {
81 81 margin: @padding*2 0;
82 82 }
83 83 }
84 84
85 85 .form {
86 86 label {
87 87 display: block;
88 88 }
89 89
90 90 input {
91 91 width: 100%;
92 92 margin: 0 10% @padding 0;
93 93 .box-sizing(border-box) ;
94 94
95 95 &[type="checkbox"] {
96 96 clear: both;
97 97 width: auto;
98 98 margin: 0 1em @padding 0;
99 99 }
100 100 }
101 101
102 102 .checkbox {
103 103 display: inline;
104 104 width: auto;
105 105 }
106 106
107 107 .sign-in {
108 108 clear: both;
109 109 width: 100%;
110 110 margin: @padding 0;
111 111 }
112 112 }
113 113 .register_message,
114 114 .activation_msg {
115 115 padding: 0 0 @padding;
116 116 }
117 117
118 118 .buttons,
119 119 .links {
120 120 padding: 0;
121 121 }
122 122
123 123 .buttons {
124 124 input {
125 125 margin-right: 0;
126 126 .box-sizing(border-box);
127 127 }
128 128
129 129 #sign_up, #send {
130 130 width: 100%;
131 131 }
132 132 }
133 133
134 134 .fields {
135 135 .field.field-compact {
136 136 margin-bottom: 0px;
137 137 }
138 138
139 139 .buttons {
140 140 margin: 0;
141 141 }
142 142
143 143 .field {
144 144 margin-bottom: 15px;
145 145
146 146 input {
147 147 width: 100%;
148 148 .box-sizing(border-box);
149 149 }
150 150
151 151 .input {
152 152 margin-left: 0;
153 153 }
154 154
155 155 .label {
156 156 padding-top: 0;
157 157 }
158 158 }
159 159 }
160 160
161 161 .checkbox {
162 162 margin: 0 0 @textmargin 0;
163 163
164 164 input[type="checkbox"] {
165 165 width: auto;
166 166 }
167 167
168 168 label {
169 169 padding: 0;
170 170 min-height: 0;
171 171 }
172 172 }
173 173
174 174 .activation_msg {
175 175 padding: @padding 0 0;
176 176 color: @grey4;
177 177 }
178 178
179 179 .links {
180 180 float: right;
181 181 margin: 0;
182 182 padding: 0;
183 183 line-height: 1;
184 184
185 185 p {
186 186 float: right;
187 187 margin: 0;
188 188 line-height: 1.5em;
189 189 }
190 190 }
191
192 p.help-block {
193 margin-left: 0;
194 }
191 195 }
192 196
193 197 .user-menu.submenu {
194 198 right: 0;
195 199 left: auto;
196 200 }
197 201 #quick_login {
198 202 left: auto;
199 203 right: 0;
200 204 padding: @menupadding;
201 205 z-index: 999;
202 206 overflow: hidden;
203 207 background-color: @grey6;
204 208 color: @grey2;
205 209
206 210 h4 {
207 211 margin-bottom: 12px;
208 212 }
209 213
210 214 .form {
211 215 width: auto;
212 216 }
213 217
214 218 label, .field {
215 219 margin-bottom: 0;
216 220 }
217 221
218 222 .label {
219 223 padding-top: 0;
220 224 }
221 225
222 226 input {
223 227 min-width: 215px;
224 228 margin: 8px 0 @padding;
225 229 }
226 230
227 231 input[type="submit"] {
228 232 &:extend(.btn-primary);
229 233 width:100%;
230 234 min-width: 0;
231 235 }
232 236
233 237 .forgot_password,
234 238 .buttons .register {
235 239 a {
236 240 color: @rcblue;
237 241
238 242 &:hover {
239 243 color: @rcdarkblue;
240 244 }
241 245 }
242 246 }
243 247
244 248 .buttons {
245 249 margin: 0;
246 250 }
247 251
248 252 .buttons a {
249 253 padding: 8px 0;
250 254 line-height: 1.4em;
251 255 color: @grey4;
252 256
253 257 &:hover {
254 258 color: @grey2;
255 259 }
256 260 }
257 261
258 262 #sign_in {
259 263 margin-bottom: 0
260 264 }
261 265
262 266 .big_gravatar {
263 267 float: left;
264 268 display: block;
265 269 margin-top: .5em;
266 270 }
267 271
268 272 .full_name,
269 273 .email {
270 274 margin: 0 0 0 65px;
271 275 }
272 276
273 277 .email {
274 278 font-family: @text-light;
275 279 }
276 280
277 281 ol.links {
278 282 clear:both;
279 283 margin: 0;
280 284 padding: @padding 0 0 0;
281 285
282 286 li {
283 287 border-top: @border-thickness solid @grey5;
284 288
285 289 input {
286 290 margin: @padding 0 0 0;
287 291 }
288 292 }
289 293 }
290 294 }
291 295 .submenu #quick_login li:hover {
292 296 background-color: transparent;
293 297 }
294 298
295 299 #quick_login_link:hover + #quick_login {
296 300 display: block;
297 301 }
@@ -1,71 +1,81 b''
1 1 <div class="panel panel-default">
2 2 <div class="panel-heading">
3 3 <h3 class="panel-title">${_('System Wide Application Permissions')}</h3>
4 4 </div>
5 5 <div class="panel-body">
6 6 ${h.secure_form(url('admin_permissions_application'), method='post')}
7 7 <div class="form">
8 8 <!-- fields -->
9 9 <div class="fields">
10 10 <div class="field">
11 11 <div class="label label-checkbox">
12 12 <label for="anonymous">${_('Anonymous Access')}:</label>
13 13 </div>
14 14 <div class="checkboxes">
15 15 <div class="checkbox">
16 16 ${h.checkbox('anonymous',True)} Allow Anonymous Access
17 17 </div>
18 18 <span class="help-block">${h.literal(_('Allow access to RhodeCode Enterprise without requiring users to login. Anonymous users get the %s permission settings.' % (h.link_to('"default user"',h.url('admin_permissions_object')))))}</span>
19 19 </div>
20 20 </div>
21 21
22 22 <div class="field">
23 23 <div class="label label-select">
24 24 <label for="default_register">${_('Registration')}:</label>
25 25 </div>
26 26 <div class="select">
27 27 ${h.select('default_register','',c.register_choices)}
28 28 </div>
29 29 </div>
30 30
31 31 <div class="field">
32 <div class="label label-select">
33 <label for="default_password_reset">${_('Password Reset')}:</label>
34 </div>
35 <div class="select">
36 ${h.select('default_password_reset','',c.password_reset_choices)}
37 </div>
38 </div>
39
40 <div class="field">
32 41 <div class="label label-textarea">
33 42 <label for="default_register_message">${_('Registration Page Message')}:</label>
34 43 </div>
35 44 <div class="textarea text-area editor" >
36 45 ${h.textarea('default_register_message', class_="medium", )}
37 46 <span class="help-block">${_('Custom message to be displayed on the registration page. HTML syntax is supported.')}</span>
38 47 </div>
39 48 </div>
40 49
41 50 <div class="field">
42 51 <div class="label">
43 52 <label for="default_extern_activate">${_('External Authentication Account Activation')}:</label>
44 53 </div>
45 54 <div class="select">
46 55 ${h.select('default_extern_activate','',c.extern_activate_choices)}
47 56 </div>
48 57 </div>
49 58 <div class="buttons">
50 59 ${h.submit('save',_('Save'),class_="btn")}
51 60 ${h.reset('reset',_('Reset'),class_="btn")}
52 61 </div>
53 62 </div>
54 63 </div>
55 64 ${h.end_form()}
56 65 </div>
57 66 </div>
58 67
59 68 <script>
60 69 $(document).ready(function(){
61 70 var select2Options = {
62 71 containerCssClass: 'drop-menu',
63 72 dropdownCssClass: 'drop-menu-dropdown',
64 73 dropdownAutoWidth: true,
65 74 minimumResultsForSearch: -1
66 75 };
67 76
68 77 $("#default_register").select2(select2Options);
78 $("#default_password_reset").select2(select2Options);
69 79 $("#default_extern_activate").select2(select2Options);
70 80 });
71 81 </script>
@@ -1,651 +1,653 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="root.html"/>
3 3
4 4 <div class="outerwrapper">
5 5 <!-- HEADER -->
6 6 <div class="header">
7 7 <div id="header-inner" class="wrapper">
8 8 <div id="logo">
9 9 <div class="logo-wrapper">
10 10 <a href="${h.url('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
11 11 </div>
12 12 %if c.rhodecode_name:
13 13 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
14 14 %endif
15 15 </div>
16 16 <!-- MENU BAR NAV -->
17 17 ${self.menu_bar_nav()}
18 18 <!-- END MENU BAR NAV -->
19 19 </div>
20 20 </div>
21 21 ${self.menu_bar_subnav()}
22 22 <!-- END HEADER -->
23 23
24 24 <!-- CONTENT -->
25 25 <div id="content" class="wrapper">
26 26 <div class="main">
27 27 ${next.main()}
28 28 </div>
29 29 </div>
30 30 <!-- END CONTENT -->
31 31
32 32 </div>
33 33 <!-- FOOTER -->
34 34 <div id="footer">
35 35 <div id="footer-inner" class="title wrapper">
36 36 <div>
37 37 <p class="footer-link-right">
38 38 % if c.visual.show_version:
39 39 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
40 40 % endif
41 41 &copy; 2010-${h.datetime.today().year}, <a href="${h.url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
42 42 % if c.visual.rhodecode_support_url:
43 43 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
44 44 % endif
45 45 </p>
46 46 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
47 47 <p class="server-instance" style="display:${sid}">
48 48 ## display hidden instance ID if specially defined
49 49 % if c.rhodecode_instanceid:
50 50 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
51 51 % endif
52 52 </p>
53 53 </div>
54 54 </div>
55 55 </div>
56 56
57 57 <!-- END FOOTER -->
58 58
59 59 ### MAKO DEFS ###
60 60
61 61 <%def name="menu_bar_subnav()">
62 62 </%def>
63 63
64 64 <%def name="breadcrumbs(class_='breadcrumbs')">
65 65 <div class="${class_}">
66 66 ${self.breadcrumbs_links()}
67 67 </div>
68 68 </%def>
69 69
70 70 <%def name="admin_menu()">
71 71 <ul class="admin_menu submenu">
72 72 <li><a href="${h.url('admin_home')}">${_('Admin journal')}</a></li>
73 73 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
74 74 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
75 75 <li><a href="${h.url('users')}">${_('Users')}</a></li>
76 76 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
77 77 <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li>
78 78 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
79 79 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
80 80 <li><a href="${h.url('admin_defaults_repositories')}">${_('Defaults')}</a></li>
81 81 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
82 82 </ul>
83 83 </%def>
84 84
85 85
86 86 <%def name="dt_info_panel(elements)">
87 87 <dl class="dl-horizontal">
88 88 %for dt, dd, title, show_items in elements:
89 89 <dt>${dt}:</dt>
90 90 <dd title="${title}">
91 91 %if callable(dd):
92 92 ## allow lazy evaluation of elements
93 93 ${dd()}
94 94 %else:
95 95 ${dd}
96 96 %endif
97 97 %if show_items:
98 98 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
99 99 %endif
100 100 </dd>
101 101
102 102 %if show_items:
103 103 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
104 104 %for item in show_items:
105 105 <dt></dt>
106 106 <dd>${item}</dd>
107 107 %endfor
108 108 </div>
109 109 %endif
110 110
111 111 %endfor
112 112 </dl>
113 113 </%def>
114 114
115 115
116 116 <%def name="gravatar(email, size=16)">
117 117 <%
118 118 if (size > 16):
119 119 gravatar_class = 'gravatar gravatar-large'
120 120 else:
121 121 gravatar_class = 'gravatar'
122 122 %>
123 123 <%doc>
124 124 TODO: johbo: For now we serve double size images to make it smooth
125 125 for retina. This is how it worked until now. Should be replaced
126 126 with a better solution at some point.
127 127 </%doc>
128 128 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
129 129 </%def>
130 130
131 131
132 132 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
133 133 <% email = h.email_or_none(contact) %>
134 134 <div class="rc-user tooltip" title="${h.author_string(email)}">
135 135 ${self.gravatar(email, size)}
136 136 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
137 137 </div>
138 138 </%def>
139 139
140 140
141 141 ## admin menu used for people that have some admin resources
142 142 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
143 143 <ul class="submenu">
144 144 %if repositories:
145 145 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
146 146 %endif
147 147 %if repository_groups:
148 148 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
149 149 %endif
150 150 %if user_groups:
151 151 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
152 152 %endif
153 153 </ul>
154 154 </%def>
155 155
156 156 <%def name="repo_page_title(repo_instance)">
157 157 <div class="title-content">
158 158 <div class="title-main">
159 159 ## SVN/HG/GIT icons
160 160 %if h.is_hg(repo_instance):
161 161 <i class="icon-hg"></i>
162 162 %endif
163 163 %if h.is_git(repo_instance):
164 164 <i class="icon-git"></i>
165 165 %endif
166 166 %if h.is_svn(repo_instance):
167 167 <i class="icon-svn"></i>
168 168 %endif
169 169
170 170 ## public/private
171 171 %if repo_instance.private:
172 172 <i class="icon-repo-private"></i>
173 173 %else:
174 174 <i class="icon-repo-public"></i>
175 175 %endif
176 176
177 177 ## repo name with group name
178 178 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
179 179
180 180 </div>
181 181
182 182 ## FORKED
183 183 %if repo_instance.fork:
184 184 <p>
185 185 <i class="icon-code-fork"></i> ${_('Fork of')}
186 186 <a href="${h.url('summary_home',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
187 187 </p>
188 188 %endif
189 189
190 190 ## IMPORTED FROM REMOTE
191 191 %if repo_instance.clone_uri:
192 192 <p>
193 193 <i class="icon-code-fork"></i> ${_('Clone from')}
194 194 <a href="${h.url(h.safe_str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
195 195 </p>
196 196 %endif
197 197
198 198 ## LOCKING STATUS
199 199 %if repo_instance.locked[0]:
200 200 <p class="locking_locked">
201 201 <i class="icon-repo-lock"></i>
202 202 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
203 203 </p>
204 204 %elif repo_instance.enable_locking:
205 205 <p class="locking_unlocked">
206 206 <i class="icon-repo-unlock"></i>
207 207 ${_('Repository not locked. Pull repository to lock it.')}
208 208 </p>
209 209 %endif
210 210
211 211 </div>
212 212 </%def>
213 213
214 214 <%def name="repo_menu(active=None)">
215 215 <%
216 216 def is_active(selected):
217 217 if selected == active:
218 218 return "active"
219 219 %>
220 220
221 221 <!--- CONTEXT BAR -->
222 222 <div id="context-bar">
223 223 <div class="wrapper">
224 224 <ul id="context-pages" class="horizontal-list navigation">
225 225 <li class="${is_active('summary')}"><a class="menulink" href="${h.url('summary_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
226 226 <li class="${is_active('changelog')}"><a class="menulink" href="${h.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
227 227 <li class="${is_active('files')}"><a class="menulink" href="${h.url('files_home', repo_name=c.repo_name, revision=c.rhodecode_db_repo.landing_rev[1])}"><div class="menulabel">${_('Files')}</div></a></li>
228 228 <li class="${is_active('compare')}">
229 229 <a class="menulink" href="${h.url('compare_home',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a>
230 230 </li>
231 231 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
232 232 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
233 233 <li class="${is_active('showpullrequest')}">
234 234 <a class="menulink" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}">
235 235 %if c.repository_pull_requests:
236 236 <span class="pr_notifications">${c.repository_pull_requests}</span>
237 237 %endif
238 238 <div class="menulabel">${_('Pull Requests')}</div>
239 239 </a>
240 240 </li>
241 241 %endif
242 242 <li class="${is_active('options')}">
243 243 <a class="menulink" href="#" class="dropdown"><div class="menulabel">${_('Options')} <div class="show_more"></div></div></a>
244 244 <ul class="submenu">
245 245 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
246 246 <li><a href="${h.url('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
247 247 %endif
248 248 %if c.rhodecode_db_repo.fork:
249 249 <li><a href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,source_ref_type=c.rhodecode_db_repo.landing_rev[0],source_ref=c.rhodecode_db_repo.landing_rev[1], target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], merge=1)}">
250 250 ${_('Compare fork')}</a></li>
251 251 %endif
252 252
253 253 <li><a href="${h.url('search_repo_home',repo_name=c.repo_name)}">${_('Search')}</a></li>
254 254
255 255 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
256 256 %if c.rhodecode_db_repo.locked[0]:
257 257 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
258 258 %else:
259 259 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
260 260 %endif
261 261 %endif
262 262 %if c.rhodecode_user.username != h.DEFAULT_USER:
263 263 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
264 264 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
265 265 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
266 266 %endif
267 267 %endif
268 268 </ul>
269 269 </li>
270 270 </ul>
271 271 </div>
272 272 <div class="clear"></div>
273 273 </div>
274 274 <!--- END CONTEXT BAR -->
275 275
276 276 </%def>
277 277
278 278 <%def name="usermenu()">
279 279 ## USER MENU
280 280 <li id="quick_login_li">
281 281 <a id="quick_login_link" class="menulink childs">
282 282 ${gravatar(c.rhodecode_user.email, 20)}
283 283 <span class="user">
284 284 %if c.rhodecode_user.username != h.DEFAULT_USER:
285 285 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
286 286 %else:
287 287 <span>${_('Sign in')}</span>
288 288 %endif
289 289 </span>
290 290 </a>
291 291
292 292 <div class="user-menu submenu">
293 293 <div id="quick_login">
294 294 %if c.rhodecode_user.username == h.DEFAULT_USER:
295 295 <h4>${_('Sign in to your account')}</h4>
296 296 ${h.form(h.route_path('login', _query={'came_from': h.url.current()}), needs_csrf_token=False)}
297 297 <div class="form form-vertical">
298 298 <div class="fields">
299 299 <div class="field">
300 300 <div class="label">
301 301 <label for="username">${_('Username')}:</label>
302 302 </div>
303 303 <div class="input">
304 304 ${h.text('username',class_='focus',tabindex=1)}
305 305 </div>
306 306
307 307 </div>
308 308 <div class="field">
309 309 <div class="label">
310 310 <label for="password">${_('Password')}:</label>
311 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'))}</span>
311 %if h.HasPermissionAny('hg.password_reset.enabled')():
312 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'))}</span>
313 %endif
312 314 </div>
313 315 <div class="input">
314 316 ${h.password('password',class_='focus',tabindex=2)}
315 317 </div>
316 318 </div>
317 319 <div class="buttons">
318 320 <div class="register">
319 321 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
320 322 ${h.link_to(_("Don't have an account ?"),h.route_path('register'))}
321 323 %endif
322 324 </div>
323 325 <div class="submit">
324 326 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
325 327 </div>
326 328 </div>
327 329 </div>
328 330 </div>
329 331 ${h.end_form()}
330 332 %else:
331 333 <div class="">
332 334 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
333 335 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
334 336 <div class="email">${c.rhodecode_user.email}</div>
335 337 </div>
336 338 <div class="">
337 339 <ol class="links">
338 340 <li>${h.link_to(_(u'My account'),h.url('my_account'))}</li>
339 341 <li class="logout">
340 342 ${h.secure_form(h.route_path('logout'))}
341 343 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
342 344 ${h.end_form()}
343 345 </li>
344 346 </ol>
345 347 </div>
346 348 %endif
347 349 </div>
348 350 </div>
349 351 %if c.rhodecode_user.username != h.DEFAULT_USER:
350 352 <div class="pill_container">
351 353 % if c.unread_notifications == 0:
352 354 <a class="menu_link_notifications empty" href="${h.url('notifications')}">${c.unread_notifications}</a>
353 355 % else:
354 356 <a class="menu_link_notifications" href="${h.url('notifications')}">${c.unread_notifications}</a>
355 357 % endif
356 358 </div>
357 359 % endif
358 360 </li>
359 361 </%def>
360 362
361 363 <%def name="menu_items(active=None)">
362 364 <%
363 365 def is_active(selected):
364 366 if selected == active:
365 367 return "active"
366 368 return ""
367 369 %>
368 370 <ul id="quick" class="main_nav navigation horizontal-list">
369 371 <!-- repo switcher -->
370 372 <li class="${is_active('repositories')} repo_switcher_li has_select2">
371 373 <input id="repo_switcher" name="repo_switcher" type="hidden">
372 374 </li>
373 375
374 376 ## ROOT MENU
375 377 %if c.rhodecode_user.username != h.DEFAULT_USER:
376 378 <li class="${is_active('journal')}">
377 379 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
378 380 <div class="menulabel">${_('Journal')}</div>
379 381 </a>
380 382 </li>
381 383 %else:
382 384 <li class="${is_active('journal')}">
383 385 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
384 386 <div class="menulabel">${_('Public journal')}</div>
385 387 </a>
386 388 </li>
387 389 %endif
388 390 <li class="${is_active('gists')}">
389 391 <a class="menulink childs" title="${_('Show Gists')}" href="${h.url('gists')}">
390 392 <div class="menulabel">${_('Gists')}</div>
391 393 </a>
392 394 </li>
393 395 <li class="${is_active('search')}">
394 396 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.url('search')}">
395 397 <div class="menulabel">${_('Search')}</div>
396 398 </a>
397 399 </li>
398 400 % if h.HasPermissionAll('hg.admin')('access admin main page'):
399 401 <li class="${is_active('admin')}">
400 402 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
401 403 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
402 404 </a>
403 405 ${admin_menu()}
404 406 </li>
405 407 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
406 408 <li class="${is_active('admin')}">
407 409 <a class="menulink childs" title="${_('Delegated Admin settings')}">
408 410 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
409 411 </a>
410 412 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
411 413 c.rhodecode_user.repository_groups_admin,
412 414 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
413 415 </li>
414 416 % endif
415 417 % if c.debug_style:
416 418 <li class="${is_active('debug_style')}">
417 419 <a class="menulink" title="${_('Style')}" href="${h.url('debug_style_home')}">
418 420 <div class="menulabel">${_('Style')}</div>
419 421 </a>
420 422 </li>
421 423 % endif
422 424 ## render extra user menu
423 425 ${usermenu()}
424 426 </ul>
425 427
426 428 <script type="text/javascript">
427 429 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
428 430
429 431 /*format the look of items in the list*/
430 432 var format = function(state, escapeMarkup){
431 433 if (!state.id){
432 434 return state.text; // optgroup
433 435 }
434 436 var obj_dict = state.obj;
435 437 var tmpl = '';
436 438
437 439 if(obj_dict && state.type == 'repo'){
438 440 if(obj_dict['repo_type'] === 'hg'){
439 441 tmpl += '<i class="icon-hg"></i> ';
440 442 }
441 443 else if(obj_dict['repo_type'] === 'git'){
442 444 tmpl += '<i class="icon-git"></i> ';
443 445 }
444 446 else if(obj_dict['repo_type'] === 'svn'){
445 447 tmpl += '<i class="icon-svn"></i> ';
446 448 }
447 449 if(obj_dict['private']){
448 450 tmpl += '<i class="icon-lock" ></i> ';
449 451 }
450 452 else if(visual_show_public_icon){
451 453 tmpl += '<i class="icon-unlock-alt"></i> ';
452 454 }
453 455 }
454 456 if(obj_dict && state.type == 'commit') {
455 457 tmpl += '<i class="icon-tag"></i>';
456 458 }
457 459 if(obj_dict && state.type == 'group'){
458 460 tmpl += '<i class="icon-folder-close"></i> ';
459 461 }
460 462 tmpl += escapeMarkup(state.text);
461 463 return tmpl;
462 464 };
463 465
464 466 var formatResult = function(result, container, query, escapeMarkup) {
465 467 return format(result, escapeMarkup);
466 468 };
467 469
468 470 var formatSelection = function(data, container, escapeMarkup) {
469 471 return format(data, escapeMarkup);
470 472 };
471 473
472 474 $("#repo_switcher").select2({
473 475 cachedDataSource: {},
474 476 minimumInputLength: 2,
475 477 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
476 478 dropdownAutoWidth: true,
477 479 formatResult: formatResult,
478 480 formatSelection: formatSelection,
479 481 containerCssClass: "repo-switcher",
480 482 dropdownCssClass: "repo-switcher-dropdown",
481 483 escapeMarkup: function(m){
482 484 // don't escape our custom placeholder
483 485 if(m.substr(0,23) == '<div class="menulabel">'){
484 486 return m;
485 487 }
486 488
487 489 return Select2.util.escapeMarkup(m);
488 490 },
489 491 query: $.debounce(250, function(query){
490 492 self = this;
491 493 var cacheKey = query.term;
492 494 var cachedData = self.cachedDataSource[cacheKey];
493 495
494 496 if (cachedData) {
495 497 query.callback({results: cachedData.results});
496 498 } else {
497 499 $.ajax({
498 500 url: "${h.url('goto_switcher_data')}",
499 501 data: {'query': query.term},
500 502 dataType: 'json',
501 503 type: 'GET',
502 504 success: function(data) {
503 505 self.cachedDataSource[cacheKey] = data;
504 506 query.callback({results: data.results});
505 507 },
506 508 error: function(data, textStatus, errorThrown) {
507 509 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
508 510 }
509 511 })
510 512 }
511 513 })
512 514 });
513 515
514 516 $("#repo_switcher").on('select2-selecting', function(e){
515 517 e.preventDefault();
516 518 window.location = e.choice.url;
517 519 });
518 520
519 521 ## Global mouse bindings ##
520 522
521 523 // general help "?"
522 524 Mousetrap.bind(['?'], function(e) {
523 525 $('#help_kb').modal({})
524 526 });
525 527
526 528 // / open the quick filter
527 529 Mousetrap.bind(['/'], function(e) {
528 530 $("#repo_switcher").select2("open");
529 531
530 532 // return false to prevent default browser behavior
531 533 // and stop event from bubbling
532 534 return false;
533 535 });
534 536
535 537 // general nav g + action
536 538 Mousetrap.bind(['g h'], function(e) {
537 539 window.location = pyroutes.url('home');
538 540 });
539 541 Mousetrap.bind(['g g'], function(e) {
540 542 window.location = pyroutes.url('gists', {'private':1});
541 543 });
542 544 Mousetrap.bind(['g G'], function(e) {
543 545 window.location = pyroutes.url('gists', {'public':1});
544 546 });
545 547 Mousetrap.bind(['n g'], function(e) {
546 548 window.location = pyroutes.url('new_gist');
547 549 });
548 550 Mousetrap.bind(['n r'], function(e) {
549 551 window.location = pyroutes.url('new_repo');
550 552 });
551 553
552 554 % if hasattr(c, 'repo_name') and hasattr(c, 'rhodecode_db_repo'):
553 555 // nav in repo context
554 556 Mousetrap.bind(['g s'], function(e) {
555 557 window.location = pyroutes.url('summary_home', {'repo_name': REPO_NAME});
556 558 });
557 559 Mousetrap.bind(['g c'], function(e) {
558 560 window.location = pyroutes.url('changelog_home', {'repo_name': REPO_NAME});
559 561 });
560 562 Mousetrap.bind(['g F'], function(e) {
561 563 window.location = pyroutes.url('files_home', {'repo_name': REPO_NAME, 'revision': '${c.rhodecode_db_repo.landing_rev[1]}', 'f_path': '', 'search': '1'});
562 564 });
563 565 Mousetrap.bind(['g f'], function(e) {
564 566 window.location = pyroutes.url('files_home', {'repo_name': REPO_NAME, 'revision': '${c.rhodecode_db_repo.landing_rev[1]}', 'f_path': ''});
565 567 });
566 568 Mousetrap.bind(['g p'], function(e) {
567 569 window.location = pyroutes.url('pullrequest_show_all', {'repo_name': REPO_NAME});
568 570 });
569 571 Mousetrap.bind(['g o'], function(e) {
570 572 window.location = pyroutes.url('edit_repo', {'repo_name': REPO_NAME});
571 573 });
572 574 Mousetrap.bind(['g O'], function(e) {
573 575 window.location = pyroutes.url('edit_repo_perms', {'repo_name': REPO_NAME});
574 576 });
575 577 % endif
576 578
577 579 </script>
578 580 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
579 581 </%def>
580 582
581 583 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
582 584 <div class="modal-dialog">
583 585 <div class="modal-content">
584 586 <div class="modal-header">
585 587 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
586 588 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
587 589 </div>
588 590 <div class="modal-body">
589 591 <div class="block-left">
590 592 <table class="keyboard-mappings">
591 593 <tbody>
592 594 <tr>
593 595 <th></th>
594 596 <th>${_('Site-wide shortcuts')}</th>
595 597 </tr>
596 598 <%
597 599 elems = [
598 600 ('/', 'Open quick search box'),
599 601 ('g h', 'Goto home page'),
600 602 ('g g', 'Goto my private gists page'),
601 603 ('g G', 'Goto my public gists page'),
602 604 ('n r', 'New repository page'),
603 605 ('n g', 'New gist page'),
604 606 ]
605 607 %>
606 608 %for key, desc in elems:
607 609 <tr>
608 610 <td class="keys">
609 611 <span class="key tag">${key}</span>
610 612 </td>
611 613 <td>${desc}</td>
612 614 </tr>
613 615 %endfor
614 616 </tbody>
615 617 </table>
616 618 </div>
617 619 <div class="block-left">
618 620 <table class="keyboard-mappings">
619 621 <tbody>
620 622 <tr>
621 623 <th></th>
622 624 <th>${_('Repositories')}</th>
623 625 </tr>
624 626 <%
625 627 elems = [
626 628 ('g s', 'Goto summary page'),
627 629 ('g c', 'Goto changelog page'),
628 630 ('g f', 'Goto files page'),
629 631 ('g F', 'Goto files page with file search activated'),
630 632 ('g p', 'Goto pull requests page'),
631 633 ('g o', 'Goto repository settings'),
632 634 ('g O', 'Goto repository permissions settings'),
633 635 ]
634 636 %>
635 637 %for key, desc in elems:
636 638 <tr>
637 639 <td class="keys">
638 640 <span class="key tag">${key}</span>
639 641 </td>
640 642 <td>${desc}</td>
641 643 </tr>
642 644 %endfor
643 645 </tbody>
644 646 </table>
645 647 </div>
646 648 </div>
647 649 <div class="modal-footer">
648 650 </div>
649 651 </div><!-- /.modal-content -->
650 652 </div><!-- /.modal-dialog -->
651 653 </div><!-- /.modal -->
@@ -1,76 +1,84 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="base/root.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Sign In')}
6 6 %if c.rhodecode_name:
7 7 &middot; ${h.branding(c.rhodecode_name)}
8 8 %endif
9 9 </%def>
10 10
11 11 <style>body{background-color:#eeeeee;}</style>
12 12 <div class="loginbox">
13 13 <div class="header">
14 14 <div id="header-inner" class="title">
15 15 <div id="logo">
16 16 <div class="logo-wrapper">
17 17 <a href="${h.url('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
18 18 </div>
19 19 %if c.rhodecode_name:
20 20 <div class="branding"> ${h.branding(c.rhodecode_name)}</div>
21 21 %endif
22 22 </div>
23 23 </div>
24 24 </div>
25 25
26 26 <div class="loginwrapper">
27 27 <div class="left-column">
28 28 <img class="sign-in-image" src="${h.asset('images/sign-in.png')}" alt="RhodeCode"/>
29 29 </div>
30 30 <%block name="above_login_button" />
31 31 <div id="login" class="right-column">
32 32 <!-- login -->
33 33 <div class="sign-in-title">
34 34 <h1>${_('Sign In')}</h1>
35 35 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
36 36 <h4>${h.link_to(_("Go to the registration page to create a new account."), request.route_path('register'))}</h4>
37 37 %endif
38 38 </div>
39 39 <div class="inner form">
40 40 ${h.form(request.route_path('login', _query={'came_from': came_from}), needs_csrf_token=False)}
41 41
42 42 <label for="username">${_('Username')}:</label>
43 43 ${h.text('username', class_='focus', value=defaults.get('username'))}
44 44 %if 'username' in errors:
45 45 <span class="error-message">${errors.get('username')}</span>
46 46 <br />
47 47 %endif
48 48
49 49 <label for="password">${_('Password')}:</label>
50 50 ${h.password('password', class_='focus')}
51 51 %if 'password' in errors:
52 52 <span class="error-message">${errors.get('password')}</span>
53 53 <br />
54 54 %endif
55 55
56 56 ${h.checkbox('remember', value=True, checked=defaults.get('remember'))}
57 57 <label class="checkbox" for="remember">${_('Remember me')}</label>
58 58
59 <p class="links">
60 ${h.link_to(_('Forgot your password?'), h.route_path('reset_password'))}
61 </p>
59
60 %if h.HasPermissionAny('hg.password_reset.enable')():
61 <p class="links">
62 ${h.link_to(_('Forgot your password?'), h.route_path('reset_password'))}
63 </p>
64 %elif h.HasPermissionAny('hg.password_reset.hidden')():
65 <p class="help-block">
66 ${_('Contact an administrator if you have forgotten your password.')}
67 </p>
68 %endif
69
62 70
63 71 ${h.submit('sign_in', _('Sign In'), class_="btn sign-in")}
64 72
65 73 ${h.end_form()}
66 74 <script type="text/javascript">
67 75 $(document).ready(function(){
68 76 $('#username').focus();
69 77 })
70 78 </script>
71 79 </div>
72 80 <!-- end login -->
73 81 <%block name="below_login_button" />
74 82 </div>
75 83 </div>
76 84 </div>
@@ -1,77 +1,83 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="base/root.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Create an Account')}
6 6 %if c.rhodecode_name:
7 7 &middot; ${h.branding(c.rhodecode_name)}
8 8 %endif
9 9 </%def>
10 10 <style>body{background-color:#eeeeee;}</style>
11 11
12 12 <div class="loginbox">
13 13 <div class="header">
14 14 <div id="header-inner" class="title">
15 15 <div id="logo">
16 16 <div class="logo-wrapper">
17 17 <a href="${h.url('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
18 18 </div>
19 19 %if c.rhodecode_name:
20 20 <div class="branding"> ${h.branding(c.rhodecode_name)}</div>
21 21 %endif
22 22 </div>
23 23 </div>
24 24 </div>
25 25
26 26 <div class="loginwrapper">
27 27 <div class="left-column">
28 28 <img class="sign-in-image" src="${h.asset('images/sign-in.png')}" alt="RhodeCode"/>
29 29 </div>
30 30
31 <div id="register" class="right-column">
32 <!-- login -->
33 <div class="sign-in-title">
34 <h1>${_('Reset your Password')}</h1>
35 <h4>${h.link_to(_("Go to the login page to sign in."), request.route_path('login'))}</h4>
31 %if h.HasPermissionAny('hg.password_reset.disabled')():
32 <div class="right-column">
33 <p>${_('Password reset has been disabled.')}</p>
36 34 </div>
37 <div class="inner form">
38 ${h.form(request.route_path('reset_password'), needs_csrf_token=False)}
39 <label for="email">${_('Email Address')}:</label>
40 ${h.text('email', defaults.get('email'))}
41 %if 'email' in errors:
42 <span class="error-message">${errors.get('email')}</span>
43 <br />
44 %endif
45
46 %if captcha_active:
47 <div class="login-captcha"
48 <label for="email">${_('Captcha')}:</label>
49 ${h.hidden('recaptcha_field')}
50 <div id="recaptcha"></div>
51 %if 'recaptcha_field' in errors:
52 <span class="error-message">${errors.get('recaptcha_field')}</span>
35 %else:
36 <div id="register" class="right-column">
37 <!-- login -->
38 <div class="sign-in-title">
39 <h1>${_('Reset your Password')}</h1>
40 <h4>${h.link_to(_("Go to the login page to sign in."), request.route_path('login'))}</h4>
41 </div>
42 <div class="inner form">
43 ${h.form(request.route_path('reset_password'), needs_csrf_token=False)}
44 <label for="email">${_('Email Address')}:</label>
45 ${h.text('email', defaults.get('email'))}
46 %if 'email' in errors:
47 <span class="error-message">${errors.get('email')}</span>
53 48 <br />
54 49 %endif
55 </div>
56 %endif
57
58 ${h.submit('send', _('Send password reset email'), class_="btn sign-in")}
59 <div class="activation_msg">${_('Password reset link will be sent to matching email address')}</div>
60
61 ${h.end_form()}
50
51 %if captcha_active:
52 <div class="login-captcha"
53 <label for="email">${_('Captcha')}:</label>
54 ${h.hidden('recaptcha_field')}
55 <div id="recaptcha"></div>
56 %if 'recaptcha_field' in errors:
57 <span class="error-message">${errors.get('recaptcha_field')}</span>
58 <br />
59 %endif
60 </div>
61 %endif
62
63 ${h.submit('send', _('Send password reset email'), class_="btn sign-in")}
64 <div class="activation_msg">${_('Password reset link will be sent to matching email address')}</div>
65
66 ${h.end_form()}
67 </div>
62 68 </div>
63 </div>
69 %endif
64 70 </div>
65 71 </div>
66 72
67 73 %if captcha_active:
68 74 <script type="text/javascript" src="https://www.google.com/recaptcha/api/js/recaptcha_ajax.js"></script>
69 75 %endif
70 76 <script type="text/javascript">
71 77 $(document).ready(function(){
72 78 $('#email').focus();
73 79 %if captcha_active:
74 80 Recaptcha.create("${captcha_public_key}", "recaptcha", {theme: "white"});
75 81 %endif
76 82 });
77 83 </script>
General Comments 0
You need to be logged in to leave comments. Login now