##// END OF EJS Templates
Added separate default permission for external_auth account...
marcink -
r3786:222e6769 beta
parent child Browse files
Show More
@@ -1,207 +1,217 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.permissions
3 rhodecode.controllers.admin.permissions
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 permissions controller for Rhodecode
6 permissions controller for Rhodecode
7
7
8 :created_on: Apr 27, 2010
8 :created_on: Apr 27, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29 from formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from pylons import request, session, tmpl_context as c, url
31 from pylons import request, session, tmpl_context as c, url
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34
34
35 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
36 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator,\
36 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator,\
37 AuthUser
37 AuthUser
38 from rhodecode.lib.base import BaseController, render
38 from rhodecode.lib.base import BaseController, render
39 from rhodecode.model.forms import DefaultPermissionsForm
39 from rhodecode.model.forms import DefaultPermissionsForm
40 from rhodecode.model.permission import PermissionModel
40 from rhodecode.model.permission import PermissionModel
41 from rhodecode.model.db import User, UserIpMap, Permission
41 from rhodecode.model.db import User, UserIpMap, Permission
42 from rhodecode.model.meta import Session
42 from rhodecode.model.meta import Session
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46
46
47 class PermissionsController(BaseController):
47 class PermissionsController(BaseController):
48 """REST Controller styled on the Atom Publishing Protocol"""
48 """REST Controller styled on the Atom Publishing Protocol"""
49 # To properly map this controller, ensure your config/routing.py
49 # To properly map this controller, ensure your config/routing.py
50 # file has a resource setup:
50 # file has a resource setup:
51 # map.resource('permission', 'permissions')
51 # map.resource('permission', 'permissions')
52
52
53 @LoginRequired()
53 @LoginRequired()
54 @HasPermissionAllDecorator('hg.admin')
54 @HasPermissionAllDecorator('hg.admin')
55 def __before__(self):
55 def __before__(self):
56 super(PermissionsController, self).__before__()
56 super(PermissionsController, self).__before__()
57
57
58 c.repo_perms_choices = [('repository.none', _('None'),),
58 c.repo_perms_choices = [('repository.none', _('None'),),
59 ('repository.read', _('Read'),),
59 ('repository.read', _('Read'),),
60 ('repository.write', _('Write'),),
60 ('repository.write', _('Write'),),
61 ('repository.admin', _('Admin'),)]
61 ('repository.admin', _('Admin'),)]
62 c.group_perms_choices = [('group.none', _('None'),),
62 c.group_perms_choices = [('group.none', _('None'),),
63 ('group.read', _('Read'),),
63 ('group.read', _('Read'),),
64 ('group.write', _('Write'),),
64 ('group.write', _('Write'),),
65 ('group.admin', _('Admin'),)]
65 ('group.admin', _('Admin'),)]
66 c.user_group_perms_choices = [('usergroup.none', _('None'),),
66 c.user_group_perms_choices = [('usergroup.none', _('None'),),
67 ('usergroup.read', _('Read'),),
67 ('usergroup.read', _('Read'),),
68 ('usergroup.write', _('Write'),),
68 ('usergroup.write', _('Write'),),
69 ('usergroup.admin', _('Admin'),)]
69 ('usergroup.admin', _('Admin'),)]
70 c.register_choices = [
70 c.register_choices = [
71 ('hg.register.none',
71 ('hg.register.none',
72 _('Disabled')),
72 _('Disabled')),
73 ('hg.register.manual_activate',
73 ('hg.register.manual_activate',
74 _('Allowed with manual account activation')),
74 _('Allowed with manual account activation')),
75 ('hg.register.auto_activate',
75 ('hg.register.auto_activate',
76 _('Allowed with automatic account activation')), ]
76 _('Allowed with automatic account activation')), ]
77
77
78 c.extern_activate_choices = [
79 ('hg.extern_activate.manual', _('Manual activation of external account')),
80 ('hg.extern_activate.auto', _('Automatic activation of external account')),
81 ]
82
78 c.repo_create_choices = [('hg.create.none', _('Disabled')),
83 c.repo_create_choices = [('hg.create.none', _('Disabled')),
79 ('hg.create.repository', _('Enabled'))]
84 ('hg.create.repository', _('Enabled'))]
80
85
81 c.user_group_create_choices = [('hg.usergroup.create.false', _('Disabled')),
86 c.user_group_create_choices = [('hg.usergroup.create.false', _('Disabled')),
82 ('hg.usergroup.create.true', _('Enabled'))]
87 ('hg.usergroup.create.true', _('Enabled'))]
83
88
84 c.repo_group_create_choices = [('hg.repogroup.create.false', _('Disabled')),
89 c.repo_group_create_choices = [('hg.repogroup.create.false', _('Disabled')),
85 ('hg.repogroup.create.true', _('Enabled'))]
90 ('hg.repogroup.create.true', _('Enabled'))]
86
91
87 c.fork_choices = [('hg.fork.none', _('Disabled')),
92 c.fork_choices = [('hg.fork.none', _('Disabled')),
88 ('hg.fork.repository', _('Enabled'))]
93 ('hg.fork.repository', _('Enabled'))]
89
94
90 def index(self, format='html'):
95 def index(self, format='html'):
91 """GET /permissions: All items in the collection"""
96 """GET /permissions: All items in the collection"""
92 # url('permissions')
97 # url('permissions')
93
98
94 def create(self):
99 def create(self):
95 """POST /permissions: Create a new item"""
100 """POST /permissions: Create a new item"""
96 # url('permissions')
101 # url('permissions')
97
102
98 def new(self, format='html'):
103 def new(self, format='html'):
99 """GET /permissions/new: Form to create a new item"""
104 """GET /permissions/new: Form to create a new item"""
100 # url('new_permission')
105 # url('new_permission')
101
106
102 def update(self, id):
107 def update(self, id):
103 """PUT /permissions/id: Update an existing item"""
108 """PUT /permissions/id: Update an existing item"""
104 # Forms posted to this method should contain a hidden field:
109 # Forms posted to this method should contain a hidden field:
105 # <input type="hidden" name="_method" value="PUT" />
110 # <input type="hidden" name="_method" value="PUT" />
106 # Or using helpers:
111 # Or using helpers:
107 # h.form(url('permission', id=ID),
112 # h.form(url('permission', id=ID),
108 # method='put')
113 # method='put')
109 # url('permission', id=ID)
114 # url('permission', id=ID)
110 if id == 'default':
115 if id == 'default':
111 c.user = default_user = User.get_default_user()
116 c.user = default_user = User.get_default_user()
112 c.perm_user = AuthUser(user_id=default_user.user_id)
117 c.perm_user = AuthUser(user_id=default_user.user_id)
113 c.user_ip_map = UserIpMap.query()\
118 c.user_ip_map = UserIpMap.query()\
114 .filter(UserIpMap.user == default_user).all()
119 .filter(UserIpMap.user == default_user).all()
115
120
116 _form = DefaultPermissionsForm(
121 _form = DefaultPermissionsForm(
117 [x[0] for x in c.repo_perms_choices],
122 [x[0] for x in c.repo_perms_choices],
118 [x[0] for x in c.group_perms_choices],
123 [x[0] for x in c.group_perms_choices],
119 [x[0] for x in c.user_group_perms_choices],
124 [x[0] for x in c.user_group_perms_choices],
120 [x[0] for x in c.repo_create_choices],
125 [x[0] for x in c.repo_create_choices],
121 [x[0] for x in c.repo_group_create_choices],
126 [x[0] for x in c.repo_group_create_choices],
122 [x[0] for x in c.user_group_create_choices],
127 [x[0] for x in c.user_group_create_choices],
123 [x[0] for x in c.fork_choices],
128 [x[0] for x in c.fork_choices],
124 [x[0] for x in c.register_choices])()
129 [x[0] for x in c.register_choices],
130 [x[0] for x in c.extern_activate_choices],
131 )()
125
132
126 try:
133 try:
127 form_result = _form.to_python(dict(request.POST))
134 form_result = _form.to_python(dict(request.POST))
128 form_result.update({'perm_user_name': id})
135 form_result.update({'perm_user_name': id})
129 PermissionModel().update(form_result)
136 PermissionModel().update(form_result)
130 Session().commit()
137 Session().commit()
131 h.flash(_('Default permissions updated successfully'),
138 h.flash(_('Default permissions updated successfully'),
132 category='success')
139 category='success')
133
140
134 except formencode.Invalid, errors:
141 except formencode.Invalid, errors:
135 defaults = errors.value
142 defaults = errors.value
136
143
137 return htmlfill.render(
144 return htmlfill.render(
138 render('admin/permissions/permissions.html'),
145 render('admin/permissions/permissions.html'),
139 defaults=defaults,
146 defaults=defaults,
140 errors=errors.error_dict or {},
147 errors=errors.error_dict or {},
141 prefix_error=False,
148 prefix_error=False,
142 encoding="UTF-8")
149 encoding="UTF-8")
143 except Exception:
150 except Exception:
144 log.error(traceback.format_exc())
151 log.error(traceback.format_exc())
145 h.flash(_('Error occurred during update of permissions'),
152 h.flash(_('Error occurred during update of permissions'),
146 category='error')
153 category='error')
147
154
148 return redirect(url('edit_permission', id=id))
155 return redirect(url('edit_permission', id=id))
149
156
150 def delete(self, id):
157 def delete(self, id):
151 """DELETE /permissions/id: Delete an existing item"""
158 """DELETE /permissions/id: Delete an existing item"""
152 # Forms posted to this method should contain a hidden field:
159 # Forms posted to this method should contain a hidden field:
153 # <input type="hidden" name="_method" value="DELETE" />
160 # <input type="hidden" name="_method" value="DELETE" />
154 # Or using helpers:
161 # Or using helpers:
155 # h.form(url('permission', id=ID),
162 # h.form(url('permission', id=ID),
156 # method='delete')
163 # method='delete')
157 # url('permission', id=ID)
164 # url('permission', id=ID)
158
165
159 def show(self, id, format='html'):
166 def show(self, id, format='html'):
160 """GET /permissions/id: Show a specific item"""
167 """GET /permissions/id: Show a specific item"""
161 # url('permission', id=ID)
168 # url('permission', id=ID)
162 Permission.get_or_404(-1)
169 Permission.get_or_404(-1)
163
170
164 def edit(self, id, format='html'):
171 def edit(self, id, format='html'):
165 """GET /permissions/id/edit: Form to edit an existing item"""
172 """GET /permissions/id/edit: Form to edit an existing item"""
166 #url('edit_permission', id=ID)
173 #url('edit_permission', id=ID)
167
174
168 #this form can only edit default user permissions
175 #this form can only edit default user permissions
169 if id == 'default':
176 if id == 'default':
170 c.user = User.get_default_user()
177 c.user = User.get_default_user()
171 defaults = {'anonymous': c.user.active}
178 defaults = {'anonymous': c.user.active}
172 c.perm_user = c.user.AuthUser
179 c.perm_user = c.user.AuthUser
173 c.user_ip_map = UserIpMap.query()\
180 c.user_ip_map = UserIpMap.query()\
174 .filter(UserIpMap.user == c.user).all()
181 .filter(UserIpMap.user == c.user).all()
175 for p in c.user.user_perms:
182 for p in c.user.user_perms:
176 if p.permission.permission_name.startswith('repository.'):
183 if p.permission.permission_name.startswith('repository.'):
177 defaults['default_repo_perm'] = p.permission.permission_name
184 defaults['default_repo_perm'] = p.permission.permission_name
178
185
179 if p.permission.permission_name.startswith('group.'):
186 if p.permission.permission_name.startswith('group.'):
180 defaults['default_group_perm'] = p.permission.permission_name
187 defaults['default_group_perm'] = p.permission.permission_name
181
188
182 if p.permission.permission_name.startswith('usergroup.'):
189 if p.permission.permission_name.startswith('usergroup.'):
183 defaults['default_user_group_perm'] = p.permission.permission_name
190 defaults['default_user_group_perm'] = p.permission.permission_name
184
191
185 if p.permission.permission_name.startswith('hg.create.'):
192 if p.permission.permission_name.startswith('hg.create.'):
186 defaults['default_repo_create'] = p.permission.permission_name
193 defaults['default_repo_create'] = p.permission.permission_name
187
194
188 if p.permission.permission_name.startswith('hg.repogroup.'):
195 if p.permission.permission_name.startswith('hg.repogroup.'):
189 defaults['default_repo_group_create'] = p.permission.permission_name
196 defaults['default_repo_group_create'] = p.permission.permission_name
190
197
191 if p.permission.permission_name.startswith('hg.usergroup.'):
198 if p.permission.permission_name.startswith('hg.usergroup.'):
192 defaults['default_user_group_create'] = p.permission.permission_name
199 defaults['default_user_group_create'] = p.permission.permission_name
193
200
194 if p.permission.permission_name.startswith('hg.register.'):
201 if p.permission.permission_name.startswith('hg.register.'):
195 defaults['default_register'] = p.permission.permission_name
202 defaults['default_register'] = p.permission.permission_name
196
203
204 if p.permission.permission_name.startswith('hg.extern_activate.'):
205 defaults['default_extern_activate'] = p.permission.permission_name
206
197 if p.permission.permission_name.startswith('hg.fork.'):
207 if p.permission.permission_name.startswith('hg.fork.'):
198 defaults['default_fork'] = p.permission.permission_name
208 defaults['default_fork'] = p.permission.permission_name
199
209
200 return htmlfill.render(
210 return htmlfill.render(
201 render('admin/permissions/permissions.html'),
211 render('admin/permissions/permissions.html'),
202 defaults=defaults,
212 defaults=defaults,
203 encoding="UTF-8",
213 encoding="UTF-8",
204 force_defaults=False
214 force_defaults=False
205 )
215 )
206 else:
216 else:
207 return redirect(url('admin_home'))
217 return redirect(url('admin_home'))
@@ -1,1113 +1,1113 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.auth
3 rhodecode.lib.auth
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 authentication and permission libraries
6 authentication and permission libraries
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import random
26 import random
27 import logging
27 import logging
28 import traceback
28 import traceback
29 import hashlib
29 import hashlib
30
30
31 from tempfile import _RandomNameSequence
31 from tempfile import _RandomNameSequence
32 from decorator import decorator
32 from decorator import decorator
33
33
34 from pylons import config, url, request
34 from pylons import config, url, request
35 from pylons.controllers.util import abort, redirect
35 from pylons.controllers.util import abort, redirect
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37 from sqlalchemy.orm.exc import ObjectDeletedError
37 from sqlalchemy.orm.exc import ObjectDeletedError
38
38
39 from rhodecode import __platform__, is_windows, is_unix
39 from rhodecode import __platform__, is_windows, is_unix
40 from rhodecode.model.meta import Session
40 from rhodecode.model.meta import Session
41
41
42 from rhodecode.lib.utils2 import str2bool, safe_unicode, aslist
42 from rhodecode.lib.utils2 import str2bool, safe_unicode, aslist
43 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError,\
43 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError,\
44 LdapImportError
44 LdapImportError
45 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug,\
45 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug,\
46 get_user_group_slug
46 get_user_group_slug
47 from rhodecode.lib.auth_ldap import AuthLdap
47 from rhodecode.lib.auth_ldap import AuthLdap
48
48
49 from rhodecode.model import meta
49 from rhodecode.model import meta
50 from rhodecode.model.user import UserModel
50 from rhodecode.model.user import UserModel
51 from rhodecode.model.db import Permission, RhodeCodeSetting, User, UserIpMap
51 from rhodecode.model.db import Permission, RhodeCodeSetting, User, UserIpMap
52 from rhodecode.lib.caching_query import FromCache
52 from rhodecode.lib.caching_query import FromCache
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56
56
57 class PasswordGenerator(object):
57 class PasswordGenerator(object):
58 """
58 """
59 This is a simple class for generating password from different sets of
59 This is a simple class for generating password from different sets of
60 characters
60 characters
61 usage::
61 usage::
62
62
63 passwd_gen = PasswordGenerator()
63 passwd_gen = PasswordGenerator()
64 #print 8-letter password containing only big and small letters
64 #print 8-letter password containing only big and small letters
65 of alphabet
65 of alphabet
66 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
66 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
67 """
67 """
68 ALPHABETS_NUM = r'''1234567890'''
68 ALPHABETS_NUM = r'''1234567890'''
69 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
69 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
70 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
70 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
71 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
71 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
72 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
72 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
73 + ALPHABETS_NUM + ALPHABETS_SPECIAL
73 + ALPHABETS_NUM + ALPHABETS_SPECIAL
74 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
74 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
75 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
75 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
76 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
76 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
77 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
77 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
78
78
79 def __init__(self, passwd=''):
79 def __init__(self, passwd=''):
80 self.passwd = passwd
80 self.passwd = passwd
81
81
82 def gen_password(self, length, type_=None):
82 def gen_password(self, length, type_=None):
83 if type_ is None:
83 if type_ is None:
84 type_ = self.ALPHABETS_FULL
84 type_ = self.ALPHABETS_FULL
85 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
85 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
86 return self.passwd
86 return self.passwd
87
87
88
88
89 class RhodeCodeCrypto(object):
89 class RhodeCodeCrypto(object):
90
90
91 @classmethod
91 @classmethod
92 def hash_string(cls, str_):
92 def hash_string(cls, str_):
93 """
93 """
94 Cryptographic function used for password hashing based on pybcrypt
94 Cryptographic function used for password hashing based on pybcrypt
95 or pycrypto in windows
95 or pycrypto in windows
96
96
97 :param password: password to hash
97 :param password: password to hash
98 """
98 """
99 if is_windows:
99 if is_windows:
100 from hashlib import sha256
100 from hashlib import sha256
101 return sha256(str_).hexdigest()
101 return sha256(str_).hexdigest()
102 elif is_unix:
102 elif is_unix:
103 import bcrypt
103 import bcrypt
104 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
104 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
105 else:
105 else:
106 raise Exception('Unknown or unsupported platform %s' \
106 raise Exception('Unknown or unsupported platform %s' \
107 % __platform__)
107 % __platform__)
108
108
109 @classmethod
109 @classmethod
110 def hash_check(cls, password, hashed):
110 def hash_check(cls, password, hashed):
111 """
111 """
112 Checks matching password with it's hashed value, runs different
112 Checks matching password with it's hashed value, runs different
113 implementation based on platform it runs on
113 implementation based on platform it runs on
114
114
115 :param password: password
115 :param password: password
116 :param hashed: password in hashed form
116 :param hashed: password in hashed form
117 """
117 """
118
118
119 if is_windows:
119 if is_windows:
120 from hashlib import sha256
120 from hashlib import sha256
121 return sha256(password).hexdigest() == hashed
121 return sha256(password).hexdigest() == hashed
122 elif is_unix:
122 elif is_unix:
123 import bcrypt
123 import bcrypt
124 return bcrypt.hashpw(password, hashed) == hashed
124 return bcrypt.hashpw(password, hashed) == hashed
125 else:
125 else:
126 raise Exception('Unknown or unsupported platform %s' \
126 raise Exception('Unknown or unsupported platform %s' \
127 % __platform__)
127 % __platform__)
128
128
129
129
130 def get_crypt_password(password):
130 def get_crypt_password(password):
131 return RhodeCodeCrypto.hash_string(password)
131 return RhodeCodeCrypto.hash_string(password)
132
132
133
133
134 def check_password(password, hashed):
134 def check_password(password, hashed):
135 return RhodeCodeCrypto.hash_check(password, hashed)
135 return RhodeCodeCrypto.hash_check(password, hashed)
136
136
137
137
138 def generate_api_key(str_, salt=None):
138 def generate_api_key(str_, salt=None):
139 """
139 """
140 Generates API KEY from given string
140 Generates API KEY from given string
141
141
142 :param str_:
142 :param str_:
143 :param salt:
143 :param salt:
144 """
144 """
145
145
146 if salt is None:
146 if salt is None:
147 salt = _RandomNameSequence().next()
147 salt = _RandomNameSequence().next()
148
148
149 return hashlib.sha1(str_ + salt).hexdigest()
149 return hashlib.sha1(str_ + salt).hexdigest()
150
150
151
151
152 def authfunc(environ, username, password):
152 def authfunc(environ, username, password):
153 """
153 """
154 Dummy authentication wrapper function used in Mercurial and Git for
154 Dummy authentication wrapper function used in Mercurial and Git for
155 access control.
155 access control.
156
156
157 :param environ: needed only for using in Basic auth
157 :param environ: needed only for using in Basic auth
158 """
158 """
159 return authenticate(username, password)
159 return authenticate(username, password)
160
160
161
161
162 def authenticate(username, password):
162 def authenticate(username, password):
163 """
163 """
164 Authentication function used for access control,
164 Authentication function used for access control,
165 firstly checks for db authentication then if ldap is enabled for ldap
165 firstly checks for db authentication then if ldap is enabled for ldap
166 authentication, also creates ldap user if not in database
166 authentication, also creates ldap user if not in database
167
167
168 :param username: username
168 :param username: username
169 :param password: password
169 :param password: password
170 """
170 """
171
171
172 user_model = UserModel()
172 user_model = UserModel()
173 user = User.get_by_username(username)
173 user = User.get_by_username(username)
174
174
175 log.debug('Authenticating user using RhodeCode account')
175 log.debug('Authenticating user using RhodeCode account')
176 if user is not None and not user.ldap_dn:
176 if user is not None and not user.ldap_dn:
177 if user.active:
177 if user.active:
178 if user.username == 'default' and user.active:
178 if user.username == 'default' and user.active:
179 log.info('user %s authenticated correctly as anonymous user' %
179 log.info('user %s authenticated correctly as anonymous user' %
180 username)
180 username)
181 return True
181 return True
182
182
183 elif user.username == username and check_password(password,
183 elif user.username == username and check_password(password,
184 user.password):
184 user.password):
185 log.info('user %s authenticated correctly' % username)
185 log.info('user %s authenticated correctly' % username)
186 return True
186 return True
187 else:
187 else:
188 log.warning('user %s tried auth but is disabled' % username)
188 log.warning('user %s tried auth but is disabled' % username)
189
189
190 else:
190 else:
191 log.debug('Regular authentication failed')
191 log.debug('Regular authentication failed')
192 user_obj = User.get_by_username(username, case_insensitive=True)
192 user_obj = User.get_by_username(username, case_insensitive=True)
193
193
194 if user_obj is not None and not user_obj.ldap_dn:
194 if user_obj is not None and not user_obj.ldap_dn:
195 log.debug('this user already exists as non ldap')
195 log.debug('this user already exists as non ldap')
196 return False
196 return False
197
197
198 ldap_settings = RhodeCodeSetting.get_ldap_settings()
198 ldap_settings = RhodeCodeSetting.get_ldap_settings()
199 #======================================================================
199 #======================================================================
200 # FALLBACK TO LDAP AUTH IF ENABLE
200 # FALLBACK TO LDAP AUTH IF ENABLE
201 #======================================================================
201 #======================================================================
202 if str2bool(ldap_settings.get('ldap_active')):
202 if str2bool(ldap_settings.get('ldap_active')):
203 log.debug("Authenticating user using ldap")
203 log.debug("Authenticating user using ldap")
204 kwargs = {
204 kwargs = {
205 'server': ldap_settings.get('ldap_host', ''),
205 'server': ldap_settings.get('ldap_host', ''),
206 'base_dn': ldap_settings.get('ldap_base_dn', ''),
206 'base_dn': ldap_settings.get('ldap_base_dn', ''),
207 'port': ldap_settings.get('ldap_port'),
207 'port': ldap_settings.get('ldap_port'),
208 'bind_dn': ldap_settings.get('ldap_dn_user'),
208 'bind_dn': ldap_settings.get('ldap_dn_user'),
209 'bind_pass': ldap_settings.get('ldap_dn_pass'),
209 'bind_pass': ldap_settings.get('ldap_dn_pass'),
210 'tls_kind': ldap_settings.get('ldap_tls_kind'),
210 'tls_kind': ldap_settings.get('ldap_tls_kind'),
211 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
211 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
212 'ldap_filter': ldap_settings.get('ldap_filter'),
212 'ldap_filter': ldap_settings.get('ldap_filter'),
213 'search_scope': ldap_settings.get('ldap_search_scope'),
213 'search_scope': ldap_settings.get('ldap_search_scope'),
214 'attr_login': ldap_settings.get('ldap_attr_login'),
214 'attr_login': ldap_settings.get('ldap_attr_login'),
215 'ldap_version': 3,
215 'ldap_version': 3,
216 }
216 }
217 log.debug('Checking for ldap authentication')
217 log.debug('Checking for ldap authentication')
218 try:
218 try:
219 aldap = AuthLdap(**kwargs)
219 aldap = AuthLdap(**kwargs)
220 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
220 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
221 password)
221 password)
222 log.debug('Got ldap DN response %s' % user_dn)
222 log.debug('Got ldap DN response %s' % user_dn)
223
223
224 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
224 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
225 .get(k), [''])[0]
225 .get(k), [''])[0]
226
226
227 user_attrs = {
227 user_attrs = {
228 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
228 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
229 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
229 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
230 'email': get_ldap_attr('ldap_attr_email'),
230 'email': get_ldap_attr('ldap_attr_email'),
231 'active': 'hg.register.auto_activate' in User\
231 'active': 'hg.extern_activate.auto' in User.get_default_user()\
232 .get_default_user().AuthUser.permissions['global']
232 .AuthUser.permissions['global']
233 }
233 }
234
234
235 # don't store LDAP password since we don't need it. Override
235 # don't store LDAP password since we don't need it. Override
236 # with some random generated password
236 # with some random generated password
237 _password = PasswordGenerator().gen_password(length=8)
237 _password = PasswordGenerator().gen_password(length=8)
238 # create this user on the fly if it doesn't exist in rhodecode
238 # create this user on the fly if it doesn't exist in rhodecode
239 # database
239 # database
240 if user_model.create_ldap(username, _password, user_dn,
240 if user_model.create_ldap(username, _password, user_dn,
241 user_attrs):
241 user_attrs):
242 log.info('created new ldap user %s' % username)
242 log.info('created new ldap user %s' % username)
243
243
244 Session().commit()
244 Session().commit()
245 return True
245 return True
246 except (LdapUsernameError, LdapPasswordError, LdapImportError):
246 except (LdapUsernameError, LdapPasswordError, LdapImportError):
247 pass
247 pass
248 except (Exception,):
248 except (Exception,):
249 log.error(traceback.format_exc())
249 log.error(traceback.format_exc())
250 pass
250 pass
251 return False
251 return False
252
252
253
253
254 def login_container_auth(username):
254 def login_container_auth(username):
255 user = User.get_by_username(username)
255 user = User.get_by_username(username)
256 if user is None:
256 if user is None:
257 user_attrs = {
257 user_attrs = {
258 'name': username,
258 'name': username,
259 'lastname': None,
259 'lastname': None,
260 'email': None,
260 'email': None,
261 'active': 'hg.register.auto_activate' in User\
261 'active': 'hg.extern_activate.auto' in User.get_default_user()\
262 .get_default_user().AuthUser.permissions['global']
262 .AuthUser.permissions['global']
263 }
263 }
264 user = UserModel().create_for_container_auth(username, user_attrs)
264 user = UserModel().create_for_container_auth(username, user_attrs)
265 if not user:
265 if not user:
266 return None
266 return None
267 log.info('User %s was created by container authentication' % username)
267 log.info('User %s was created by container authentication' % username)
268
268
269 if not user.active:
269 if not user.active:
270 return None
270 return None
271
271
272 user.update_lastlogin()
272 user.update_lastlogin()
273 Session().commit()
273 Session().commit()
274
274
275 log.debug('User %s is now logged in by container authentication',
275 log.debug('User %s is now logged in by container authentication',
276 user.username)
276 user.username)
277 return user
277 return user
278
278
279
279
280 def get_container_username(environ, config, clean_username=False):
280 def get_container_username(environ, config, clean_username=False):
281 """
281 """
282 Get's the container_auth username (or email). It tries to get username
282 Get's the container_auth username (or email). It tries to get username
283 from REMOTE_USER if container_auth_enabled is enabled, if that fails
283 from REMOTE_USER if container_auth_enabled is enabled, if that fails
284 it tries to get username from HTTP_X_FORWARDED_USER if proxypass_auth_enabled
284 it tries to get username from HTTP_X_FORWARDED_USER if proxypass_auth_enabled
285 is enabled. clean_username extracts the username from this data if it's
285 is enabled. clean_username extracts the username from this data if it's
286 having @ in it.
286 having @ in it.
287
287
288 :param environ:
288 :param environ:
289 :param config:
289 :param config:
290 :param clean_username:
290 :param clean_username:
291 """
291 """
292 username = None
292 username = None
293
293
294 if str2bool(config.get('container_auth_enabled', False)):
294 if str2bool(config.get('container_auth_enabled', False)):
295 from paste.httpheaders import REMOTE_USER
295 from paste.httpheaders import REMOTE_USER
296 username = REMOTE_USER(environ)
296 username = REMOTE_USER(environ)
297 log.debug('extracted REMOTE_USER:%s' % (username))
297 log.debug('extracted REMOTE_USER:%s' % (username))
298
298
299 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
299 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
300 username = environ.get('HTTP_X_FORWARDED_USER')
300 username = environ.get('HTTP_X_FORWARDED_USER')
301 log.debug('extracted HTTP_X_FORWARDED_USER:%s' % (username))
301 log.debug('extracted HTTP_X_FORWARDED_USER:%s' % (username))
302
302
303 if username and clean_username:
303 if username and clean_username:
304 # Removing realm and domain from username
304 # Removing realm and domain from username
305 username = username.partition('@')[0]
305 username = username.partition('@')[0]
306 username = username.rpartition('\\')[2]
306 username = username.rpartition('\\')[2]
307 log.debug('Received username %s from container' % username)
307 log.debug('Received username %s from container' % username)
308
308
309 return username
309 return username
310
310
311
311
312 class CookieStoreWrapper(object):
312 class CookieStoreWrapper(object):
313
313
314 def __init__(self, cookie_store):
314 def __init__(self, cookie_store):
315 self.cookie_store = cookie_store
315 self.cookie_store = cookie_store
316
316
317 def __repr__(self):
317 def __repr__(self):
318 return 'CookieStore<%s>' % (self.cookie_store)
318 return 'CookieStore<%s>' % (self.cookie_store)
319
319
320 def get(self, key, other=None):
320 def get(self, key, other=None):
321 if isinstance(self.cookie_store, dict):
321 if isinstance(self.cookie_store, dict):
322 return self.cookie_store.get(key, other)
322 return self.cookie_store.get(key, other)
323 elif isinstance(self.cookie_store, AuthUser):
323 elif isinstance(self.cookie_store, AuthUser):
324 return self.cookie_store.__dict__.get(key, other)
324 return self.cookie_store.__dict__.get(key, other)
325
325
326
326
327 class AuthUser(object):
327 class AuthUser(object):
328 """
328 """
329 A simple object that handles all attributes of user in RhodeCode
329 A simple object that handles all attributes of user in RhodeCode
330
330
331 It does lookup based on API key,given user, or user present in session
331 It does lookup based on API key,given user, or user present in session
332 Then it fills all required information for such user. It also checks if
332 Then it fills all required information for such user. It also checks if
333 anonymous access is enabled and if so, it returns default user as logged
333 anonymous access is enabled and if so, it returns default user as logged
334 in
334 in
335 """
335 """
336
336
337 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
337 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
338
338
339 self.user_id = user_id
339 self.user_id = user_id
340 self.api_key = None
340 self.api_key = None
341 self.username = username
341 self.username = username
342 self.ip_addr = ip_addr
342 self.ip_addr = ip_addr
343
343
344 self.name = ''
344 self.name = ''
345 self.lastname = ''
345 self.lastname = ''
346 self.email = ''
346 self.email = ''
347 self.is_authenticated = False
347 self.is_authenticated = False
348 self.admin = False
348 self.admin = False
349 self.inherit_default_permissions = False
349 self.inherit_default_permissions = False
350 self.permissions = {}
350 self.permissions = {}
351 self._api_key = api_key
351 self._api_key = api_key
352 self.propagate_data()
352 self.propagate_data()
353 self._instance = None
353 self._instance = None
354
354
355 def propagate_data(self):
355 def propagate_data(self):
356 user_model = UserModel()
356 user_model = UserModel()
357 self.anonymous_user = User.get_by_username('default', cache=True)
357 self.anonymous_user = User.get_by_username('default', cache=True)
358 is_user_loaded = False
358 is_user_loaded = False
359
359
360 # try go get user by api key
360 # try go get user by api key
361 if self._api_key and self._api_key != self.anonymous_user.api_key:
361 if self._api_key and self._api_key != self.anonymous_user.api_key:
362 log.debug('Auth User lookup by API KEY %s' % self._api_key)
362 log.debug('Auth User lookup by API KEY %s' % self._api_key)
363 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
363 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
364 # lookup by userid
364 # lookup by userid
365 elif (self.user_id is not None and
365 elif (self.user_id is not None and
366 self.user_id != self.anonymous_user.user_id):
366 self.user_id != self.anonymous_user.user_id):
367 log.debug('Auth User lookup by USER ID %s' % self.user_id)
367 log.debug('Auth User lookup by USER ID %s' % self.user_id)
368 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
368 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
369 # lookup by username
369 # lookup by username
370 elif self.username and \
370 elif self.username and \
371 str2bool(config.get('container_auth_enabled', False)):
371 str2bool(config.get('container_auth_enabled', False)):
372
372
373 log.debug('Auth User lookup by USER NAME %s' % self.username)
373 log.debug('Auth User lookup by USER NAME %s' % self.username)
374 dbuser = login_container_auth(self.username)
374 dbuser = login_container_auth(self.username)
375 if dbuser is not None:
375 if dbuser is not None:
376 log.debug('filling all attributes to object')
376 log.debug('filling all attributes to object')
377 for k, v in dbuser.get_dict().items():
377 for k, v in dbuser.get_dict().items():
378 setattr(self, k, v)
378 setattr(self, k, v)
379 self.set_authenticated()
379 self.set_authenticated()
380 is_user_loaded = True
380 is_user_loaded = True
381 else:
381 else:
382 log.debug('No data in %s that could been used to log in' % self)
382 log.debug('No data in %s that could been used to log in' % self)
383
383
384 if not is_user_loaded:
384 if not is_user_loaded:
385 # if we cannot authenticate user try anonymous
385 # if we cannot authenticate user try anonymous
386 if self.anonymous_user.active:
386 if self.anonymous_user.active:
387 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
387 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
388 # then we set this user is logged in
388 # then we set this user is logged in
389 self.is_authenticated = True
389 self.is_authenticated = True
390 else:
390 else:
391 self.user_id = None
391 self.user_id = None
392 self.username = None
392 self.username = None
393 self.is_authenticated = False
393 self.is_authenticated = False
394
394
395 if not self.username:
395 if not self.username:
396 self.username = 'None'
396 self.username = 'None'
397
397
398 log.debug('Auth User is now %s' % self)
398 log.debug('Auth User is now %s' % self)
399 user_model.fill_perms(self)
399 user_model.fill_perms(self)
400
400
401 @property
401 @property
402 def is_admin(self):
402 def is_admin(self):
403 return self.admin
403 return self.admin
404
404
405 @property
405 @property
406 def repos_admin(self):
406 def repos_admin(self):
407 """
407 """
408 Returns list of repositories you're an admin of
408 Returns list of repositories you're an admin of
409 """
409 """
410 return [x[0] for x in self.permissions['repositories'].iteritems()
410 return [x[0] for x in self.permissions['repositories'].iteritems()
411 if x[1] == 'repository.admin']
411 if x[1] == 'repository.admin']
412
412
413 @property
413 @property
414 def repository_groups_admin(self):
414 def repository_groups_admin(self):
415 """
415 """
416 Returns list of repository groups you're an admin of
416 Returns list of repository groups you're an admin of
417 """
417 """
418 return [x[0] for x in self.permissions['repositories_groups'].iteritems()
418 return [x[0] for x in self.permissions['repositories_groups'].iteritems()
419 if x[1] == 'group.admin']
419 if x[1] == 'group.admin']
420
420
421 @property
421 @property
422 def user_groups_admin(self):
422 def user_groups_admin(self):
423 """
423 """
424 Returns list of user groups you're an admin of
424 Returns list of user groups you're an admin of
425 """
425 """
426 return [x[0] for x in self.permissions['user_groups'].iteritems()
426 return [x[0] for x in self.permissions['user_groups'].iteritems()
427 if x[1] == 'usergroup.admin']
427 if x[1] == 'usergroup.admin']
428
428
429 @property
429 @property
430 def ip_allowed(self):
430 def ip_allowed(self):
431 """
431 """
432 Checks if ip_addr used in constructor is allowed from defined list of
432 Checks if ip_addr used in constructor is allowed from defined list of
433 allowed ip_addresses for user
433 allowed ip_addresses for user
434
434
435 :returns: boolean, True if ip is in allowed ip range
435 :returns: boolean, True if ip is in allowed ip range
436 """
436 """
437 #check IP
437 #check IP
438 allowed_ips = AuthUser.get_allowed_ips(self.user_id, cache=True)
438 allowed_ips = AuthUser.get_allowed_ips(self.user_id, cache=True)
439 if check_ip_access(source_ip=self.ip_addr, allowed_ips=allowed_ips):
439 if check_ip_access(source_ip=self.ip_addr, allowed_ips=allowed_ips):
440 log.debug('IP:%s is in range of %s' % (self.ip_addr, allowed_ips))
440 log.debug('IP:%s is in range of %s' % (self.ip_addr, allowed_ips))
441 return True
441 return True
442 else:
442 else:
443 log.info('Access for IP:%s forbidden, '
443 log.info('Access for IP:%s forbidden, '
444 'not in %s' % (self.ip_addr, allowed_ips))
444 'not in %s' % (self.ip_addr, allowed_ips))
445 return False
445 return False
446
446
447 def __repr__(self):
447 def __repr__(self):
448 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
448 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
449 self.is_authenticated)
449 self.is_authenticated)
450
450
451 def set_authenticated(self, authenticated=True):
451 def set_authenticated(self, authenticated=True):
452 if self.user_id != self.anonymous_user.user_id:
452 if self.user_id != self.anonymous_user.user_id:
453 self.is_authenticated = authenticated
453 self.is_authenticated = authenticated
454
454
455 def get_cookie_store(self):
455 def get_cookie_store(self):
456 return {'username': self.username,
456 return {'username': self.username,
457 'user_id': self.user_id,
457 'user_id': self.user_id,
458 'is_authenticated': self.is_authenticated}
458 'is_authenticated': self.is_authenticated}
459
459
460 @classmethod
460 @classmethod
461 def from_cookie_store(cls, cookie_store):
461 def from_cookie_store(cls, cookie_store):
462 """
462 """
463 Creates AuthUser from a cookie store
463 Creates AuthUser from a cookie store
464
464
465 :param cls:
465 :param cls:
466 :param cookie_store:
466 :param cookie_store:
467 """
467 """
468 user_id = cookie_store.get('user_id')
468 user_id = cookie_store.get('user_id')
469 username = cookie_store.get('username')
469 username = cookie_store.get('username')
470 api_key = cookie_store.get('api_key')
470 api_key = cookie_store.get('api_key')
471 return AuthUser(user_id, api_key, username)
471 return AuthUser(user_id, api_key, username)
472
472
473 @classmethod
473 @classmethod
474 def get_allowed_ips(cls, user_id, cache=False):
474 def get_allowed_ips(cls, user_id, cache=False):
475 _set = set()
475 _set = set()
476 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
476 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
477 if cache:
477 if cache:
478 user_ips = user_ips.options(FromCache("sql_cache_short",
478 user_ips = user_ips.options(FromCache("sql_cache_short",
479 "get_user_ips_%s" % user_id))
479 "get_user_ips_%s" % user_id))
480 for ip in user_ips:
480 for ip in user_ips:
481 try:
481 try:
482 _set.add(ip.ip_addr)
482 _set.add(ip.ip_addr)
483 except ObjectDeletedError:
483 except ObjectDeletedError:
484 # since we use heavy caching sometimes it happens that we get
484 # since we use heavy caching sometimes it happens that we get
485 # deleted objects here, we just skip them
485 # deleted objects here, we just skip them
486 pass
486 pass
487 return _set or set(['0.0.0.0/0', '::/0'])
487 return _set or set(['0.0.0.0/0', '::/0'])
488
488
489
489
490 def set_available_permissions(config):
490 def set_available_permissions(config):
491 """
491 """
492 This function will propagate pylons globals with all available defined
492 This function will propagate pylons globals with all available defined
493 permission given in db. We don't want to check each time from db for new
493 permission given in db. We don't want to check each time from db for new
494 permissions since adding a new permission also requires application restart
494 permissions since adding a new permission also requires application restart
495 ie. to decorate new views with the newly created permission
495 ie. to decorate new views with the newly created permission
496
496
497 :param config: current pylons config instance
497 :param config: current pylons config instance
498
498
499 """
499 """
500 log.info('getting information about all available permissions')
500 log.info('getting information about all available permissions')
501 try:
501 try:
502 sa = meta.Session
502 sa = meta.Session
503 all_perms = sa.query(Permission).all()
503 all_perms = sa.query(Permission).all()
504 except Exception:
504 except Exception:
505 pass
505 pass
506 finally:
506 finally:
507 meta.Session.remove()
507 meta.Session.remove()
508
508
509 config['available_permissions'] = [x.permission_name for x in all_perms]
509 config['available_permissions'] = [x.permission_name for x in all_perms]
510
510
511
511
512 #==============================================================================
512 #==============================================================================
513 # CHECK DECORATORS
513 # CHECK DECORATORS
514 #==============================================================================
514 #==============================================================================
515 class LoginRequired(object):
515 class LoginRequired(object):
516 """
516 """
517 Must be logged in to execute this function else
517 Must be logged in to execute this function else
518 redirect to login page
518 redirect to login page
519
519
520 :param api_access: if enabled this checks only for valid auth token
520 :param api_access: if enabled this checks only for valid auth token
521 and grants access based on valid token
521 and grants access based on valid token
522 """
522 """
523
523
524 def __init__(self, api_access=False):
524 def __init__(self, api_access=False):
525 self.api_access = api_access
525 self.api_access = api_access
526
526
527 def __call__(self, func):
527 def __call__(self, func):
528 return decorator(self.__wrapper, func)
528 return decorator(self.__wrapper, func)
529
529
530 def __wrapper(self, func, *fargs, **fkwargs):
530 def __wrapper(self, func, *fargs, **fkwargs):
531 cls = fargs[0]
531 cls = fargs[0]
532 user = cls.rhodecode_user
532 user = cls.rhodecode_user
533 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
533 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
534 # defined whitelist of controllers which API access will be enabled
534 # defined whitelist of controllers which API access will be enabled
535 whitelist = aslist(config.get('api_access_controllers_whitelist'),
535 whitelist = aslist(config.get('api_access_controllers_whitelist'),
536 sep=',')
536 sep=',')
537 api_access_whitelist = loc in whitelist
537 api_access_whitelist = loc in whitelist
538 log.debug('loc:%s is in API whitelist:%s:%s' % (loc, whitelist,
538 log.debug('loc:%s is in API whitelist:%s:%s' % (loc, whitelist,
539 api_access_whitelist))
539 api_access_whitelist))
540 #check IP
540 #check IP
541 ip_access_ok = True
541 ip_access_ok = True
542 if not user.ip_allowed:
542 if not user.ip_allowed:
543 from rhodecode.lib import helpers as h
543 from rhodecode.lib import helpers as h
544 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))),
544 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))),
545 category='warning')
545 category='warning')
546 ip_access_ok = False
546 ip_access_ok = False
547
547
548 api_access_ok = False
548 api_access_ok = False
549 if self.api_access or api_access_whitelist:
549 if self.api_access or api_access_whitelist:
550 log.debug('Checking API KEY access for %s' % cls)
550 log.debug('Checking API KEY access for %s' % cls)
551 if user.api_key == request.GET.get('api_key'):
551 if user.api_key == request.GET.get('api_key'):
552 api_access_ok = True
552 api_access_ok = True
553 else:
553 else:
554 log.debug("API KEY token not valid")
554 log.debug("API KEY token not valid")
555
555
556 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
556 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
557 if (user.is_authenticated or api_access_ok) and ip_access_ok:
557 if (user.is_authenticated or api_access_ok) and ip_access_ok:
558 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
558 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
559 log.info('user %s is authenticated and granted access to %s '
559 log.info('user %s is authenticated and granted access to %s '
560 'using %s' % (user.username, loc, reason)
560 'using %s' % (user.username, loc, reason)
561 )
561 )
562 return func(*fargs, **fkwargs)
562 return func(*fargs, **fkwargs)
563 else:
563 else:
564 log.warn('user %s NOT authenticated on func: %s' % (
564 log.warn('user %s NOT authenticated on func: %s' % (
565 user, loc)
565 user, loc)
566 )
566 )
567 p = url.current()
567 p = url.current()
568
568
569 log.debug('redirecting to login page with %s' % p)
569 log.debug('redirecting to login page with %s' % p)
570 return redirect(url('login_home', came_from=p))
570 return redirect(url('login_home', came_from=p))
571
571
572
572
573 class NotAnonymous(object):
573 class NotAnonymous(object):
574 """
574 """
575 Must be logged in to execute this function else
575 Must be logged in to execute this function else
576 redirect to login page"""
576 redirect to login page"""
577
577
578 def __call__(self, func):
578 def __call__(self, func):
579 return decorator(self.__wrapper, func)
579 return decorator(self.__wrapper, func)
580
580
581 def __wrapper(self, func, *fargs, **fkwargs):
581 def __wrapper(self, func, *fargs, **fkwargs):
582 cls = fargs[0]
582 cls = fargs[0]
583 self.user = cls.rhodecode_user
583 self.user = cls.rhodecode_user
584
584
585 log.debug('Checking if user is not anonymous @%s' % cls)
585 log.debug('Checking if user is not anonymous @%s' % cls)
586
586
587 anonymous = self.user.username == 'default'
587 anonymous = self.user.username == 'default'
588
588
589 if anonymous:
589 if anonymous:
590 p = url.current()
590 p = url.current()
591
591
592 import rhodecode.lib.helpers as h
592 import rhodecode.lib.helpers as h
593 h.flash(_('You need to be a registered user to '
593 h.flash(_('You need to be a registered user to '
594 'perform this action'),
594 'perform this action'),
595 category='warning')
595 category='warning')
596 return redirect(url('login_home', came_from=p))
596 return redirect(url('login_home', came_from=p))
597 else:
597 else:
598 return func(*fargs, **fkwargs)
598 return func(*fargs, **fkwargs)
599
599
600
600
601 class PermsDecorator(object):
601 class PermsDecorator(object):
602 """Base class for controller decorators"""
602 """Base class for controller decorators"""
603
603
604 def __init__(self, *required_perms):
604 def __init__(self, *required_perms):
605 available_perms = config['available_permissions']
605 available_perms = config['available_permissions']
606 for perm in required_perms:
606 for perm in required_perms:
607 if perm not in available_perms:
607 if perm not in available_perms:
608 raise Exception("'%s' permission is not defined" % perm)
608 raise Exception("'%s' permission is not defined" % perm)
609 self.required_perms = set(required_perms)
609 self.required_perms = set(required_perms)
610 self.user_perms = None
610 self.user_perms = None
611
611
612 def __call__(self, func):
612 def __call__(self, func):
613 return decorator(self.__wrapper, func)
613 return decorator(self.__wrapper, func)
614
614
615 def __wrapper(self, func, *fargs, **fkwargs):
615 def __wrapper(self, func, *fargs, **fkwargs):
616 cls = fargs[0]
616 cls = fargs[0]
617 self.user = cls.rhodecode_user
617 self.user = cls.rhodecode_user
618 self.user_perms = self.user.permissions
618 self.user_perms = self.user.permissions
619 log.debug('checking %s permissions %s for %s %s',
619 log.debug('checking %s permissions %s for %s %s',
620 self.__class__.__name__, self.required_perms, cls, self.user)
620 self.__class__.__name__, self.required_perms, cls, self.user)
621
621
622 if self.check_permissions():
622 if self.check_permissions():
623 log.debug('Permission granted for %s %s' % (cls, self.user))
623 log.debug('Permission granted for %s %s' % (cls, self.user))
624 return func(*fargs, **fkwargs)
624 return func(*fargs, **fkwargs)
625
625
626 else:
626 else:
627 log.debug('Permission denied for %s %s' % (cls, self.user))
627 log.debug('Permission denied for %s %s' % (cls, self.user))
628 anonymous = self.user.username == 'default'
628 anonymous = self.user.username == 'default'
629
629
630 if anonymous:
630 if anonymous:
631 p = url.current()
631 p = url.current()
632
632
633 import rhodecode.lib.helpers as h
633 import rhodecode.lib.helpers as h
634 h.flash(_('You need to be a signed in to '
634 h.flash(_('You need to be a signed in to '
635 'view this page'),
635 'view this page'),
636 category='warning')
636 category='warning')
637 return redirect(url('login_home', came_from=p))
637 return redirect(url('login_home', came_from=p))
638
638
639 else:
639 else:
640 # redirect with forbidden ret code
640 # redirect with forbidden ret code
641 return abort(403)
641 return abort(403)
642
642
643 def check_permissions(self):
643 def check_permissions(self):
644 """Dummy function for overriding"""
644 """Dummy function for overriding"""
645 raise Exception('You have to write this function in child class')
645 raise Exception('You have to write this function in child class')
646
646
647
647
648 class HasPermissionAllDecorator(PermsDecorator):
648 class HasPermissionAllDecorator(PermsDecorator):
649 """
649 """
650 Checks for access permission for all given predicates. All of them
650 Checks for access permission for all given predicates. All of them
651 have to be meet in order to fulfill the request
651 have to be meet in order to fulfill the request
652 """
652 """
653
653
654 def check_permissions(self):
654 def check_permissions(self):
655 if self.required_perms.issubset(self.user_perms.get('global')):
655 if self.required_perms.issubset(self.user_perms.get('global')):
656 return True
656 return True
657 return False
657 return False
658
658
659
659
660 class HasPermissionAnyDecorator(PermsDecorator):
660 class HasPermissionAnyDecorator(PermsDecorator):
661 """
661 """
662 Checks for access permission for any of given predicates. In order to
662 Checks for access permission for any of given predicates. In order to
663 fulfill the request any of predicates must be meet
663 fulfill the request any of predicates must be meet
664 """
664 """
665
665
666 def check_permissions(self):
666 def check_permissions(self):
667 if self.required_perms.intersection(self.user_perms.get('global')):
667 if self.required_perms.intersection(self.user_perms.get('global')):
668 return True
668 return True
669 return False
669 return False
670
670
671
671
672 class HasRepoPermissionAllDecorator(PermsDecorator):
672 class HasRepoPermissionAllDecorator(PermsDecorator):
673 """
673 """
674 Checks for access permission for all given predicates for specific
674 Checks for access permission for all given predicates for specific
675 repository. All of them have to be meet in order to fulfill the request
675 repository. All of them have to be meet in order to fulfill the request
676 """
676 """
677
677
678 def check_permissions(self):
678 def check_permissions(self):
679 repo_name = get_repo_slug(request)
679 repo_name = get_repo_slug(request)
680 try:
680 try:
681 user_perms = set([self.user_perms['repositories'][repo_name]])
681 user_perms = set([self.user_perms['repositories'][repo_name]])
682 except KeyError:
682 except KeyError:
683 return False
683 return False
684 if self.required_perms.issubset(user_perms):
684 if self.required_perms.issubset(user_perms):
685 return True
685 return True
686 return False
686 return False
687
687
688
688
689 class HasRepoPermissionAnyDecorator(PermsDecorator):
689 class HasRepoPermissionAnyDecorator(PermsDecorator):
690 """
690 """
691 Checks for access permission for any of given predicates for specific
691 Checks for access permission for any of given predicates for specific
692 repository. In order to fulfill the request any of predicates must be meet
692 repository. In order to fulfill the request any of predicates must be meet
693 """
693 """
694
694
695 def check_permissions(self):
695 def check_permissions(self):
696 repo_name = get_repo_slug(request)
696 repo_name = get_repo_slug(request)
697 try:
697 try:
698 user_perms = set([self.user_perms['repositories'][repo_name]])
698 user_perms = set([self.user_perms['repositories'][repo_name]])
699 except KeyError:
699 except KeyError:
700 return False
700 return False
701
701
702 if self.required_perms.intersection(user_perms):
702 if self.required_perms.intersection(user_perms):
703 return True
703 return True
704 return False
704 return False
705
705
706
706
707 class HasReposGroupPermissionAllDecorator(PermsDecorator):
707 class HasReposGroupPermissionAllDecorator(PermsDecorator):
708 """
708 """
709 Checks for access permission for all given predicates for specific
709 Checks for access permission for all given predicates for specific
710 repository group. All of them have to be meet in order to fulfill the request
710 repository group. All of them have to be meet in order to fulfill the request
711 """
711 """
712
712
713 def check_permissions(self):
713 def check_permissions(self):
714 group_name = get_repos_group_slug(request)
714 group_name = get_repos_group_slug(request)
715 try:
715 try:
716 user_perms = set([self.user_perms['repositories_groups'][group_name]])
716 user_perms = set([self.user_perms['repositories_groups'][group_name]])
717 except KeyError:
717 except KeyError:
718 return False
718 return False
719
719
720 if self.required_perms.issubset(user_perms):
720 if self.required_perms.issubset(user_perms):
721 return True
721 return True
722 return False
722 return False
723
723
724
724
725 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
725 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
726 """
726 """
727 Checks for access permission for any of given predicates for specific
727 Checks for access permission for any of given predicates for specific
728 repository group. In order to fulfill the request any of predicates must be meet
728 repository group. In order to fulfill the request any of predicates must be meet
729 """
729 """
730
730
731 def check_permissions(self):
731 def check_permissions(self):
732 group_name = get_repos_group_slug(request)
732 group_name = get_repos_group_slug(request)
733 try:
733 try:
734 user_perms = set([self.user_perms['repositories_groups'][group_name]])
734 user_perms = set([self.user_perms['repositories_groups'][group_name]])
735 except KeyError:
735 except KeyError:
736 return False
736 return False
737
737
738 if self.required_perms.intersection(user_perms):
738 if self.required_perms.intersection(user_perms):
739 return True
739 return True
740 return False
740 return False
741
741
742
742
743 class HasUserGroupPermissionAllDecorator(PermsDecorator):
743 class HasUserGroupPermissionAllDecorator(PermsDecorator):
744 """
744 """
745 Checks for access permission for all given predicates for specific
745 Checks for access permission for all given predicates for specific
746 user group. All of them have to be meet in order to fulfill the request
746 user group. All of them have to be meet in order to fulfill the request
747 """
747 """
748
748
749 def check_permissions(self):
749 def check_permissions(self):
750 group_name = get_user_group_slug(request)
750 group_name = get_user_group_slug(request)
751 try:
751 try:
752 user_perms = set([self.user_perms['user_groups'][group_name]])
752 user_perms = set([self.user_perms['user_groups'][group_name]])
753 except KeyError:
753 except KeyError:
754 return False
754 return False
755
755
756 if self.required_perms.issubset(user_perms):
756 if self.required_perms.issubset(user_perms):
757 return True
757 return True
758 return False
758 return False
759
759
760
760
761 class HasUserGroupPermissionAnyDecorator(PermsDecorator):
761 class HasUserGroupPermissionAnyDecorator(PermsDecorator):
762 """
762 """
763 Checks for access permission for any of given predicates for specific
763 Checks for access permission for any of given predicates for specific
764 user group. In order to fulfill the request any of predicates must be meet
764 user group. In order to fulfill the request any of predicates must be meet
765 """
765 """
766
766
767 def check_permissions(self):
767 def check_permissions(self):
768 group_name = get_user_group_slug(request)
768 group_name = get_user_group_slug(request)
769 try:
769 try:
770 user_perms = set([self.user_perms['user_groups'][group_name]])
770 user_perms = set([self.user_perms['user_groups'][group_name]])
771 except KeyError:
771 except KeyError:
772 return False
772 return False
773
773
774 if self.required_perms.intersection(user_perms):
774 if self.required_perms.intersection(user_perms):
775 return True
775 return True
776 return False
776 return False
777
777
778
778
779 #==============================================================================
779 #==============================================================================
780 # CHECK FUNCTIONS
780 # CHECK FUNCTIONS
781 #==============================================================================
781 #==============================================================================
782 class PermsFunction(object):
782 class PermsFunction(object):
783 """Base function for other check functions"""
783 """Base function for other check functions"""
784
784
785 def __init__(self, *perms):
785 def __init__(self, *perms):
786 available_perms = config['available_permissions']
786 available_perms = config['available_permissions']
787
787
788 for perm in perms:
788 for perm in perms:
789 if perm not in available_perms:
789 if perm not in available_perms:
790 raise Exception("'%s' permission is not defined" % perm)
790 raise Exception("'%s' permission is not defined" % perm)
791 self.required_perms = set(perms)
791 self.required_perms = set(perms)
792 self.user_perms = None
792 self.user_perms = None
793 self.repo_name = None
793 self.repo_name = None
794 self.group_name = None
794 self.group_name = None
795
795
796 def __call__(self, check_location=''):
796 def __call__(self, check_location=''):
797 #TODO: put user as attribute here
797 #TODO: put user as attribute here
798 user = request.user
798 user = request.user
799 cls_name = self.__class__.__name__
799 cls_name = self.__class__.__name__
800 check_scope = {
800 check_scope = {
801 'HasPermissionAll': '',
801 'HasPermissionAll': '',
802 'HasPermissionAny': '',
802 'HasPermissionAny': '',
803 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
803 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
804 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
804 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
805 'HasReposGroupPermissionAll': 'group:%s' % self.group_name,
805 'HasReposGroupPermissionAll': 'group:%s' % self.group_name,
806 'HasReposGroupPermissionAny': 'group:%s' % self.group_name,
806 'HasReposGroupPermissionAny': 'group:%s' % self.group_name,
807 }.get(cls_name, '?')
807 }.get(cls_name, '?')
808 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
808 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
809 self.required_perms, user, check_scope,
809 self.required_perms, user, check_scope,
810 check_location or 'unspecified location')
810 check_location or 'unspecified location')
811 if not user:
811 if not user:
812 log.debug('Empty request user')
812 log.debug('Empty request user')
813 return False
813 return False
814 self.user_perms = user.permissions
814 self.user_perms = user.permissions
815 if self.check_permissions():
815 if self.check_permissions():
816 log.debug('Permission to %s granted for user: %s @ %s', self.repo_name, user,
816 log.debug('Permission to %s granted for user: %s @ %s', self.repo_name, user,
817 check_location or 'unspecified location')
817 check_location or 'unspecified location')
818 return True
818 return True
819
819
820 else:
820 else:
821 log.debug('Permission to %s denied for user: %s @ %s', self.repo_name, user,
821 log.debug('Permission to %s denied for user: %s @ %s', self.repo_name, user,
822 check_location or 'unspecified location')
822 check_location or 'unspecified location')
823 return False
823 return False
824
824
825 def check_permissions(self):
825 def check_permissions(self):
826 """Dummy function for overriding"""
826 """Dummy function for overriding"""
827 raise Exception('You have to write this function in child class')
827 raise Exception('You have to write this function in child class')
828
828
829
829
830 class HasPermissionAll(PermsFunction):
830 class HasPermissionAll(PermsFunction):
831 def check_permissions(self):
831 def check_permissions(self):
832 if self.required_perms.issubset(self.user_perms.get('global')):
832 if self.required_perms.issubset(self.user_perms.get('global')):
833 return True
833 return True
834 return False
834 return False
835
835
836
836
837 class HasPermissionAny(PermsFunction):
837 class HasPermissionAny(PermsFunction):
838 def check_permissions(self):
838 def check_permissions(self):
839 if self.required_perms.intersection(self.user_perms.get('global')):
839 if self.required_perms.intersection(self.user_perms.get('global')):
840 return True
840 return True
841 return False
841 return False
842
842
843
843
844 class HasRepoPermissionAll(PermsFunction):
844 class HasRepoPermissionAll(PermsFunction):
845 def __call__(self, repo_name=None, check_location=''):
845 def __call__(self, repo_name=None, check_location=''):
846 self.repo_name = repo_name
846 self.repo_name = repo_name
847 return super(HasRepoPermissionAll, self).__call__(check_location)
847 return super(HasRepoPermissionAll, self).__call__(check_location)
848
848
849 def check_permissions(self):
849 def check_permissions(self):
850 if not self.repo_name:
850 if not self.repo_name:
851 self.repo_name = get_repo_slug(request)
851 self.repo_name = get_repo_slug(request)
852
852
853 try:
853 try:
854 self._user_perms = set(
854 self._user_perms = set(
855 [self.user_perms['repositories'][self.repo_name]]
855 [self.user_perms['repositories'][self.repo_name]]
856 )
856 )
857 except KeyError:
857 except KeyError:
858 return False
858 return False
859 if self.required_perms.issubset(self._user_perms):
859 if self.required_perms.issubset(self._user_perms):
860 return True
860 return True
861 return False
861 return False
862
862
863
863
864 class HasRepoPermissionAny(PermsFunction):
864 class HasRepoPermissionAny(PermsFunction):
865 def __call__(self, repo_name=None, check_location=''):
865 def __call__(self, repo_name=None, check_location=''):
866 self.repo_name = repo_name
866 self.repo_name = repo_name
867 return super(HasRepoPermissionAny, self).__call__(check_location)
867 return super(HasRepoPermissionAny, self).__call__(check_location)
868
868
869 def check_permissions(self):
869 def check_permissions(self):
870 if not self.repo_name:
870 if not self.repo_name:
871 self.repo_name = get_repo_slug(request)
871 self.repo_name = get_repo_slug(request)
872
872
873 try:
873 try:
874 self._user_perms = set(
874 self._user_perms = set(
875 [self.user_perms['repositories'][self.repo_name]]
875 [self.user_perms['repositories'][self.repo_name]]
876 )
876 )
877 except KeyError:
877 except KeyError:
878 return False
878 return False
879 if self.required_perms.intersection(self._user_perms):
879 if self.required_perms.intersection(self._user_perms):
880 return True
880 return True
881 return False
881 return False
882
882
883
883
884 class HasReposGroupPermissionAny(PermsFunction):
884 class HasReposGroupPermissionAny(PermsFunction):
885 def __call__(self, group_name=None, check_location=''):
885 def __call__(self, group_name=None, check_location=''):
886 self.group_name = group_name
886 self.group_name = group_name
887 return super(HasReposGroupPermissionAny, self).__call__(check_location)
887 return super(HasReposGroupPermissionAny, self).__call__(check_location)
888
888
889 def check_permissions(self):
889 def check_permissions(self):
890 try:
890 try:
891 self._user_perms = set(
891 self._user_perms = set(
892 [self.user_perms['repositories_groups'][self.group_name]]
892 [self.user_perms['repositories_groups'][self.group_name]]
893 )
893 )
894 except KeyError:
894 except KeyError:
895 return False
895 return False
896 if self.required_perms.intersection(self._user_perms):
896 if self.required_perms.intersection(self._user_perms):
897 return True
897 return True
898 return False
898 return False
899
899
900
900
901 class HasReposGroupPermissionAll(PermsFunction):
901 class HasReposGroupPermissionAll(PermsFunction):
902 def __call__(self, group_name=None, check_location=''):
902 def __call__(self, group_name=None, check_location=''):
903 self.group_name = group_name
903 self.group_name = group_name
904 return super(HasReposGroupPermissionAll, self).__call__(check_location)
904 return super(HasReposGroupPermissionAll, self).__call__(check_location)
905
905
906 def check_permissions(self):
906 def check_permissions(self):
907 try:
907 try:
908 self._user_perms = set(
908 self._user_perms = set(
909 [self.user_perms['repositories_groups'][self.group_name]]
909 [self.user_perms['repositories_groups'][self.group_name]]
910 )
910 )
911 except KeyError:
911 except KeyError:
912 return False
912 return False
913 if self.required_perms.issubset(self._user_perms):
913 if self.required_perms.issubset(self._user_perms):
914 return True
914 return True
915 return False
915 return False
916
916
917
917
918 class HasUserGroupPermissionAny(PermsFunction):
918 class HasUserGroupPermissionAny(PermsFunction):
919 def __call__(self, user_group_name=None, check_location=''):
919 def __call__(self, user_group_name=None, check_location=''):
920 self.user_group_name = user_group_name
920 self.user_group_name = user_group_name
921 return super(HasUserGroupPermissionAny, self).__call__(check_location)
921 return super(HasUserGroupPermissionAny, self).__call__(check_location)
922
922
923 def check_permissions(self):
923 def check_permissions(self):
924 try:
924 try:
925 self._user_perms = set(
925 self._user_perms = set(
926 [self.user_perms['user_groups'][self.user_group_name]]
926 [self.user_perms['user_groups'][self.user_group_name]]
927 )
927 )
928 except KeyError:
928 except KeyError:
929 return False
929 return False
930 if self.required_perms.intersection(self._user_perms):
930 if self.required_perms.intersection(self._user_perms):
931 return True
931 return True
932 return False
932 return False
933
933
934
934
935 class HasUserGroupPermissionAll(PermsFunction):
935 class HasUserGroupPermissionAll(PermsFunction):
936 def __call__(self, user_group_name=None, check_location=''):
936 def __call__(self, user_group_name=None, check_location=''):
937 self.user_group_name = user_group_name
937 self.user_group_name = user_group_name
938 return super(HasUserGroupPermissionAll, self).__call__(check_location)
938 return super(HasUserGroupPermissionAll, self).__call__(check_location)
939
939
940 def check_permissions(self):
940 def check_permissions(self):
941 try:
941 try:
942 self._user_perms = set(
942 self._user_perms = set(
943 [self.user_perms['user_groups'][self.user_group_name]]
943 [self.user_perms['user_groups'][self.user_group_name]]
944 )
944 )
945 except KeyError:
945 except KeyError:
946 return False
946 return False
947 if self.required_perms.issubset(self._user_perms):
947 if self.required_perms.issubset(self._user_perms):
948 return True
948 return True
949 return False
949 return False
950
950
951 #==============================================================================
951 #==============================================================================
952 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
952 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
953 #==============================================================================
953 #==============================================================================
954 class HasPermissionAnyMiddleware(object):
954 class HasPermissionAnyMiddleware(object):
955 def __init__(self, *perms):
955 def __init__(self, *perms):
956 self.required_perms = set(perms)
956 self.required_perms = set(perms)
957
957
958 def __call__(self, user, repo_name):
958 def __call__(self, user, repo_name):
959 # repo_name MUST be unicode, since we handle keys in permission
959 # repo_name MUST be unicode, since we handle keys in permission
960 # dict by unicode
960 # dict by unicode
961 repo_name = safe_unicode(repo_name)
961 repo_name = safe_unicode(repo_name)
962 usr = AuthUser(user.user_id)
962 usr = AuthUser(user.user_id)
963 try:
963 try:
964 self.user_perms = set([usr.permissions['repositories'][repo_name]])
964 self.user_perms = set([usr.permissions['repositories'][repo_name]])
965 except Exception:
965 except Exception:
966 log.error('Exception while accessing permissions %s' %
966 log.error('Exception while accessing permissions %s' %
967 traceback.format_exc())
967 traceback.format_exc())
968 self.user_perms = set()
968 self.user_perms = set()
969 self.username = user.username
969 self.username = user.username
970 self.repo_name = repo_name
970 self.repo_name = repo_name
971 return self.check_permissions()
971 return self.check_permissions()
972
972
973 def check_permissions(self):
973 def check_permissions(self):
974 log.debug('checking VCS protocol '
974 log.debug('checking VCS protocol '
975 'permissions %s for user:%s repository:%s', self.user_perms,
975 'permissions %s for user:%s repository:%s', self.user_perms,
976 self.username, self.repo_name)
976 self.username, self.repo_name)
977 if self.required_perms.intersection(self.user_perms):
977 if self.required_perms.intersection(self.user_perms):
978 log.debug('permission granted for user:%s on repo:%s' % (
978 log.debug('permission granted for user:%s on repo:%s' % (
979 self.username, self.repo_name
979 self.username, self.repo_name
980 )
980 )
981 )
981 )
982 return True
982 return True
983 log.debug('permission denied for user:%s on repo:%s' % (
983 log.debug('permission denied for user:%s on repo:%s' % (
984 self.username, self.repo_name
984 self.username, self.repo_name
985 )
985 )
986 )
986 )
987 return False
987 return False
988
988
989
989
990 #==============================================================================
990 #==============================================================================
991 # SPECIAL VERSION TO HANDLE API AUTH
991 # SPECIAL VERSION TO HANDLE API AUTH
992 #==============================================================================
992 #==============================================================================
993 class _BaseApiPerm(object):
993 class _BaseApiPerm(object):
994 def __init__(self, *perms):
994 def __init__(self, *perms):
995 self.required_perms = set(perms)
995 self.required_perms = set(perms)
996
996
997 def __call__(self, check_location='unspecified', user=None, repo_name=None):
997 def __call__(self, check_location='unspecified', user=None, repo_name=None):
998 cls_name = self.__class__.__name__
998 cls_name = self.__class__.__name__
999 check_scope = 'user:%s, repo:%s' % (user, repo_name)
999 check_scope = 'user:%s, repo:%s' % (user, repo_name)
1000 log.debug('checking cls:%s %s %s @ %s', cls_name,
1000 log.debug('checking cls:%s %s %s @ %s', cls_name,
1001 self.required_perms, check_scope, check_location)
1001 self.required_perms, check_scope, check_location)
1002 if not user:
1002 if not user:
1003 log.debug('Empty User passed into arguments')
1003 log.debug('Empty User passed into arguments')
1004 return False
1004 return False
1005
1005
1006 ## process user
1006 ## process user
1007 if not isinstance(user, AuthUser):
1007 if not isinstance(user, AuthUser):
1008 user = AuthUser(user.user_id)
1008 user = AuthUser(user.user_id)
1009
1009
1010 if self.check_permissions(user.permissions, repo_name):
1010 if self.check_permissions(user.permissions, repo_name):
1011 log.debug('Permission to %s granted for user: %s @ %s', repo_name,
1011 log.debug('Permission to %s granted for user: %s @ %s', repo_name,
1012 user, check_location)
1012 user, check_location)
1013 return True
1013 return True
1014
1014
1015 else:
1015 else:
1016 log.debug('Permission to %s denied for user: %s @ %s', repo_name,
1016 log.debug('Permission to %s denied for user: %s @ %s', repo_name,
1017 user, check_location)
1017 user, check_location)
1018 return False
1018 return False
1019
1019
1020 def check_permissions(self, perm_defs, repo_name):
1020 def check_permissions(self, perm_defs, repo_name):
1021 """
1021 """
1022 implement in child class should return True if permissions are ok,
1022 implement in child class should return True if permissions are ok,
1023 False otherwise
1023 False otherwise
1024
1024
1025 :param perm_defs: dict with permission definitions
1025 :param perm_defs: dict with permission definitions
1026 :param repo_name: repo name
1026 :param repo_name: repo name
1027 """
1027 """
1028 raise NotImplementedError()
1028 raise NotImplementedError()
1029
1029
1030
1030
1031 class HasPermissionAllApi(_BaseApiPerm):
1031 class HasPermissionAllApi(_BaseApiPerm):
1032 def __call__(self, user, check_location=''):
1032 def __call__(self, user, check_location=''):
1033 return super(HasPermissionAllApi, self)\
1033 return super(HasPermissionAllApi, self)\
1034 .__call__(check_location=check_location, user=user)
1034 .__call__(check_location=check_location, user=user)
1035
1035
1036 def check_permissions(self, perm_defs, repo):
1036 def check_permissions(self, perm_defs, repo):
1037 if self.required_perms.issubset(perm_defs.get('global')):
1037 if self.required_perms.issubset(perm_defs.get('global')):
1038 return True
1038 return True
1039 return False
1039 return False
1040
1040
1041
1041
1042 class HasPermissionAnyApi(_BaseApiPerm):
1042 class HasPermissionAnyApi(_BaseApiPerm):
1043 def __call__(self, user, check_location=''):
1043 def __call__(self, user, check_location=''):
1044 return super(HasPermissionAnyApi, self)\
1044 return super(HasPermissionAnyApi, self)\
1045 .__call__(check_location=check_location, user=user)
1045 .__call__(check_location=check_location, user=user)
1046
1046
1047 def check_permissions(self, perm_defs, repo):
1047 def check_permissions(self, perm_defs, repo):
1048 if self.required_perms.intersection(perm_defs.get('global')):
1048 if self.required_perms.intersection(perm_defs.get('global')):
1049 return True
1049 return True
1050 return False
1050 return False
1051
1051
1052
1052
1053 class HasRepoPermissionAllApi(_BaseApiPerm):
1053 class HasRepoPermissionAllApi(_BaseApiPerm):
1054 def __call__(self, user, repo_name, check_location=''):
1054 def __call__(self, user, repo_name, check_location=''):
1055 return super(HasRepoPermissionAllApi, self)\
1055 return super(HasRepoPermissionAllApi, self)\
1056 .__call__(check_location=check_location, user=user,
1056 .__call__(check_location=check_location, user=user,
1057 repo_name=repo_name)
1057 repo_name=repo_name)
1058
1058
1059 def check_permissions(self, perm_defs, repo_name):
1059 def check_permissions(self, perm_defs, repo_name):
1060
1060
1061 try:
1061 try:
1062 self._user_perms = set(
1062 self._user_perms = set(
1063 [perm_defs['repositories'][repo_name]]
1063 [perm_defs['repositories'][repo_name]]
1064 )
1064 )
1065 except KeyError:
1065 except KeyError:
1066 log.warning(traceback.format_exc())
1066 log.warning(traceback.format_exc())
1067 return False
1067 return False
1068 if self.required_perms.issubset(self._user_perms):
1068 if self.required_perms.issubset(self._user_perms):
1069 return True
1069 return True
1070 return False
1070 return False
1071
1071
1072
1072
1073 class HasRepoPermissionAnyApi(_BaseApiPerm):
1073 class HasRepoPermissionAnyApi(_BaseApiPerm):
1074 def __call__(self, user, repo_name, check_location=''):
1074 def __call__(self, user, repo_name, check_location=''):
1075 return super(HasRepoPermissionAnyApi, self)\
1075 return super(HasRepoPermissionAnyApi, self)\
1076 .__call__(check_location=check_location, user=user,
1076 .__call__(check_location=check_location, user=user,
1077 repo_name=repo_name)
1077 repo_name=repo_name)
1078
1078
1079 def check_permissions(self, perm_defs, repo_name):
1079 def check_permissions(self, perm_defs, repo_name):
1080
1080
1081 try:
1081 try:
1082 _user_perms = set(
1082 _user_perms = set(
1083 [perm_defs['repositories'][repo_name]]
1083 [perm_defs['repositories'][repo_name]]
1084 )
1084 )
1085 except KeyError:
1085 except KeyError:
1086 log.warning(traceback.format_exc())
1086 log.warning(traceback.format_exc())
1087 return False
1087 return False
1088 if self.required_perms.intersection(_user_perms):
1088 if self.required_perms.intersection(_user_perms):
1089 return True
1089 return True
1090 return False
1090 return False
1091
1091
1092
1092
1093 def check_ip_access(source_ip, allowed_ips=None):
1093 def check_ip_access(source_ip, allowed_ips=None):
1094 """
1094 """
1095 Checks if source_ip is a subnet of any of allowed_ips.
1095 Checks if source_ip is a subnet of any of allowed_ips.
1096
1096
1097 :param source_ip:
1097 :param source_ip:
1098 :param allowed_ips: list of allowed ips together with mask
1098 :param allowed_ips: list of allowed ips together with mask
1099 """
1099 """
1100 from rhodecode.lib import ipaddr
1100 from rhodecode.lib import ipaddr
1101 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
1101 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
1102 if isinstance(allowed_ips, (tuple, list, set)):
1102 if isinstance(allowed_ips, (tuple, list, set)):
1103 for ip in allowed_ips:
1103 for ip in allowed_ips:
1104 try:
1104 try:
1105 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
1105 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
1106 return True
1106 return True
1107 # for any case we cannot determine the IP, don't crash just
1107 # for any case we cannot determine the IP, don't crash just
1108 # skip it and log as error, we want to say forbidden still when
1108 # skip it and log as error, we want to say forbidden still when
1109 # sending bad IP
1109 # sending bad IP
1110 except Exception:
1110 except Exception:
1111 log.error(traceback.format_exc())
1111 log.error(traceback.format_exc())
1112 continue
1112 continue
1113 return False
1113 return False
@@ -1,2127 +1,2129 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import hashlib
30 import hashlib
31 import time
31 import time
32 from collections import defaultdict
32 from collections import defaultdict
33
33
34 from sqlalchemy import *
34 from sqlalchemy import *
35 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.ext.hybrid import hybrid_property
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
37 from sqlalchemy.exc import DatabaseError
37 from sqlalchemy.exc import DatabaseError
38 from beaker.cache import cache_region, region_invalidate
38 from beaker.cache import cache_region, region_invalidate
39 from webob.exc import HTTPNotFound
39 from webob.exc import HTTPNotFound
40
40
41 from pylons.i18n.translation import lazy_ugettext as _
41 from pylons.i18n.translation import lazy_ugettext as _
42
42
43 from rhodecode.lib.vcs import get_backend
43 from rhodecode.lib.vcs import get_backend
44 from rhodecode.lib.vcs.utils.helpers import get_scm
44 from rhodecode.lib.vcs.utils.helpers import get_scm
45 from rhodecode.lib.vcs.exceptions import VCSError
45 from rhodecode.lib.vcs.exceptions import VCSError
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47 from rhodecode.lib.vcs.backends.base import EmptyChangeset
47 from rhodecode.lib.vcs.backends.base import EmptyChangeset
48
48
49 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
49 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
50 safe_unicode, remove_suffix, remove_prefix, time_to_datetime, _set_extras
50 safe_unicode, remove_suffix, remove_prefix, time_to_datetime, _set_extras
51 from rhodecode.lib.compat import json
51 from rhodecode.lib.compat import json
52 from rhodecode.lib.caching_query import FromCache
52 from rhodecode.lib.caching_query import FromCache
53
53
54 from rhodecode.model.meta import Base, Session
54 from rhodecode.model.meta import Base, Session
55
55
56 URL_SEP = '/'
56 URL_SEP = '/'
57 log = logging.getLogger(__name__)
57 log = logging.getLogger(__name__)
58
58
59 #==============================================================================
59 #==============================================================================
60 # BASE CLASSES
60 # BASE CLASSES
61 #==============================================================================
61 #==============================================================================
62
62
63 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
63 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
64
64
65
65
66 class BaseModel(object):
66 class BaseModel(object):
67 """
67 """
68 Base Model for all classess
68 Base Model for all classess
69 """
69 """
70
70
71 @classmethod
71 @classmethod
72 def _get_keys(cls):
72 def _get_keys(cls):
73 """return column names for this model """
73 """return column names for this model """
74 return class_mapper(cls).c.keys()
74 return class_mapper(cls).c.keys()
75
75
76 def get_dict(self):
76 def get_dict(self):
77 """
77 """
78 return dict with keys and values corresponding
78 return dict with keys and values corresponding
79 to this model data """
79 to this model data """
80
80
81 d = {}
81 d = {}
82 for k in self._get_keys():
82 for k in self._get_keys():
83 d[k] = getattr(self, k)
83 d[k] = getattr(self, k)
84
84
85 # also use __json__() if present to get additional fields
85 # also use __json__() if present to get additional fields
86 _json_attr = getattr(self, '__json__', None)
86 _json_attr = getattr(self, '__json__', None)
87 if _json_attr:
87 if _json_attr:
88 # update with attributes from __json__
88 # update with attributes from __json__
89 if callable(_json_attr):
89 if callable(_json_attr):
90 _json_attr = _json_attr()
90 _json_attr = _json_attr()
91 for k, val in _json_attr.iteritems():
91 for k, val in _json_attr.iteritems():
92 d[k] = val
92 d[k] = val
93 return d
93 return d
94
94
95 def get_appstruct(self):
95 def get_appstruct(self):
96 """return list with keys and values tupples corresponding
96 """return list with keys and values tupples corresponding
97 to this model data """
97 to this model data """
98
98
99 l = []
99 l = []
100 for k in self._get_keys():
100 for k in self._get_keys():
101 l.append((k, getattr(self, k),))
101 l.append((k, getattr(self, k),))
102 return l
102 return l
103
103
104 def populate_obj(self, populate_dict):
104 def populate_obj(self, populate_dict):
105 """populate model with data from given populate_dict"""
105 """populate model with data from given populate_dict"""
106
106
107 for k in self._get_keys():
107 for k in self._get_keys():
108 if k in populate_dict:
108 if k in populate_dict:
109 setattr(self, k, populate_dict[k])
109 setattr(self, k, populate_dict[k])
110
110
111 @classmethod
111 @classmethod
112 def query(cls):
112 def query(cls):
113 return Session().query(cls)
113 return Session().query(cls)
114
114
115 @classmethod
115 @classmethod
116 def get(cls, id_):
116 def get(cls, id_):
117 if id_:
117 if id_:
118 return cls.query().get(id_)
118 return cls.query().get(id_)
119
119
120 @classmethod
120 @classmethod
121 def get_or_404(cls, id_):
121 def get_or_404(cls, id_):
122 try:
122 try:
123 id_ = int(id_)
123 id_ = int(id_)
124 except (TypeError, ValueError):
124 except (TypeError, ValueError):
125 raise HTTPNotFound
125 raise HTTPNotFound
126
126
127 res = cls.query().get(id_)
127 res = cls.query().get(id_)
128 if not res:
128 if not res:
129 raise HTTPNotFound
129 raise HTTPNotFound
130 return res
130 return res
131
131
132 @classmethod
132 @classmethod
133 def getAll(cls):
133 def getAll(cls):
134 # deprecated and left for backward compatibility
134 # deprecated and left for backward compatibility
135 return cls.get_all()
135 return cls.get_all()
136
136
137 @classmethod
137 @classmethod
138 def get_all(cls):
138 def get_all(cls):
139 return cls.query().all()
139 return cls.query().all()
140
140
141 @classmethod
141 @classmethod
142 def delete(cls, id_):
142 def delete(cls, id_):
143 obj = cls.query().get(id_)
143 obj = cls.query().get(id_)
144 Session().delete(obj)
144 Session().delete(obj)
145
145
146 def __repr__(self):
146 def __repr__(self):
147 if hasattr(self, '__unicode__'):
147 if hasattr(self, '__unicode__'):
148 # python repr needs to return str
148 # python repr needs to return str
149 return safe_str(self.__unicode__())
149 return safe_str(self.__unicode__())
150 return '<DB:%s>' % (self.__class__.__name__)
150 return '<DB:%s>' % (self.__class__.__name__)
151
151
152
152
153 class RhodeCodeSetting(Base, BaseModel):
153 class RhodeCodeSetting(Base, BaseModel):
154 __tablename__ = 'rhodecode_settings'
154 __tablename__ = 'rhodecode_settings'
155 __table_args__ = (
155 __table_args__ = (
156 UniqueConstraint('app_settings_name'),
156 UniqueConstraint('app_settings_name'),
157 {'extend_existing': True, 'mysql_engine': 'InnoDB',
157 {'extend_existing': True, 'mysql_engine': 'InnoDB',
158 'mysql_charset': 'utf8'}
158 'mysql_charset': 'utf8'}
159 )
159 )
160 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
160 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
161 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
161 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
162 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
162 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
163
163
164 def __init__(self, k='', v=''):
164 def __init__(self, k='', v=''):
165 self.app_settings_name = k
165 self.app_settings_name = k
166 self.app_settings_value = v
166 self.app_settings_value = v
167
167
168 @validates('_app_settings_value')
168 @validates('_app_settings_value')
169 def validate_settings_value(self, key, val):
169 def validate_settings_value(self, key, val):
170 assert type(val) == unicode
170 assert type(val) == unicode
171 return val
171 return val
172
172
173 @hybrid_property
173 @hybrid_property
174 def app_settings_value(self):
174 def app_settings_value(self):
175 v = self._app_settings_value
175 v = self._app_settings_value
176 if self.app_settings_name in ["ldap_active",
176 if self.app_settings_name in ["ldap_active",
177 "default_repo_enable_statistics",
177 "default_repo_enable_statistics",
178 "default_repo_enable_locking",
178 "default_repo_enable_locking",
179 "default_repo_private",
179 "default_repo_private",
180 "default_repo_enable_downloads"]:
180 "default_repo_enable_downloads"]:
181 v = str2bool(v)
181 v = str2bool(v)
182 return v
182 return v
183
183
184 @app_settings_value.setter
184 @app_settings_value.setter
185 def app_settings_value(self, val):
185 def app_settings_value(self, val):
186 """
186 """
187 Setter that will always make sure we use unicode in app_settings_value
187 Setter that will always make sure we use unicode in app_settings_value
188
188
189 :param val:
189 :param val:
190 """
190 """
191 self._app_settings_value = safe_unicode(val)
191 self._app_settings_value = safe_unicode(val)
192
192
193 def __unicode__(self):
193 def __unicode__(self):
194 return u"<%s('%s:%s')>" % (
194 return u"<%s('%s:%s')>" % (
195 self.__class__.__name__,
195 self.__class__.__name__,
196 self.app_settings_name, self.app_settings_value
196 self.app_settings_name, self.app_settings_value
197 )
197 )
198
198
199 @classmethod
199 @classmethod
200 def get_by_name(cls, key):
200 def get_by_name(cls, key):
201 return cls.query()\
201 return cls.query()\
202 .filter(cls.app_settings_name == key).scalar()
202 .filter(cls.app_settings_name == key).scalar()
203
203
204 @classmethod
204 @classmethod
205 def get_by_name_or_create(cls, key):
205 def get_by_name_or_create(cls, key):
206 res = cls.get_by_name(key)
206 res = cls.get_by_name(key)
207 if not res:
207 if not res:
208 res = cls(key)
208 res = cls(key)
209 return res
209 return res
210
210
211 @classmethod
211 @classmethod
212 def get_app_settings(cls, cache=False):
212 def get_app_settings(cls, cache=False):
213
213
214 ret = cls.query()
214 ret = cls.query()
215
215
216 if cache:
216 if cache:
217 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
217 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
218
218
219 if not ret:
219 if not ret:
220 raise Exception('Could not get application settings !')
220 raise Exception('Could not get application settings !')
221 settings = {}
221 settings = {}
222 for each in ret:
222 for each in ret:
223 settings['rhodecode_' + each.app_settings_name] = \
223 settings['rhodecode_' + each.app_settings_name] = \
224 each.app_settings_value
224 each.app_settings_value
225
225
226 return settings
226 return settings
227
227
228 @classmethod
228 @classmethod
229 def get_ldap_settings(cls, cache=False):
229 def get_ldap_settings(cls, cache=False):
230 ret = cls.query()\
230 ret = cls.query()\
231 .filter(cls.app_settings_name.startswith('ldap_')).all()
231 .filter(cls.app_settings_name.startswith('ldap_')).all()
232 fd = {}
232 fd = {}
233 for row in ret:
233 for row in ret:
234 fd.update({row.app_settings_name: row.app_settings_value})
234 fd.update({row.app_settings_name: row.app_settings_value})
235
235
236 return fd
236 return fd
237
237
238 @classmethod
238 @classmethod
239 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
239 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
240 ret = cls.query()\
240 ret = cls.query()\
241 .filter(cls.app_settings_name.startswith('default_')).all()
241 .filter(cls.app_settings_name.startswith('default_')).all()
242 fd = {}
242 fd = {}
243 for row in ret:
243 for row in ret:
244 key = row.app_settings_name
244 key = row.app_settings_name
245 if strip_prefix:
245 if strip_prefix:
246 key = remove_prefix(key, prefix='default_')
246 key = remove_prefix(key, prefix='default_')
247 fd.update({key: row.app_settings_value})
247 fd.update({key: row.app_settings_value})
248
248
249 return fd
249 return fd
250
250
251
251
252 class RhodeCodeUi(Base, BaseModel):
252 class RhodeCodeUi(Base, BaseModel):
253 __tablename__ = 'rhodecode_ui'
253 __tablename__ = 'rhodecode_ui'
254 __table_args__ = (
254 __table_args__ = (
255 UniqueConstraint('ui_key'),
255 UniqueConstraint('ui_key'),
256 {'extend_existing': True, 'mysql_engine': 'InnoDB',
256 {'extend_existing': True, 'mysql_engine': 'InnoDB',
257 'mysql_charset': 'utf8'}
257 'mysql_charset': 'utf8'}
258 )
258 )
259
259
260 HOOK_UPDATE = 'changegroup.update'
260 HOOK_UPDATE = 'changegroup.update'
261 HOOK_REPO_SIZE = 'changegroup.repo_size'
261 HOOK_REPO_SIZE = 'changegroup.repo_size'
262 HOOK_PUSH = 'changegroup.push_logger'
262 HOOK_PUSH = 'changegroup.push_logger'
263 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
263 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
264 HOOK_PULL = 'outgoing.pull_logger'
264 HOOK_PULL = 'outgoing.pull_logger'
265 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
265 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
266
266
267 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
267 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
268 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
271 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
271 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
272
272
273 @classmethod
273 @classmethod
274 def get_by_key(cls, key):
274 def get_by_key(cls, key):
275 return cls.query().filter(cls.ui_key == key).scalar()
275 return cls.query().filter(cls.ui_key == key).scalar()
276
276
277 @classmethod
277 @classmethod
278 def get_builtin_hooks(cls):
278 def get_builtin_hooks(cls):
279 q = cls.query()
279 q = cls.query()
280 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
280 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
281 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
281 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
282 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
282 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
283 return q.all()
283 return q.all()
284
284
285 @classmethod
285 @classmethod
286 def get_custom_hooks(cls):
286 def get_custom_hooks(cls):
287 q = cls.query()
287 q = cls.query()
288 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
288 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
289 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
289 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
290 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
290 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
291 q = q.filter(cls.ui_section == 'hooks')
291 q = q.filter(cls.ui_section == 'hooks')
292 return q.all()
292 return q.all()
293
293
294 @classmethod
294 @classmethod
295 def get_repos_location(cls):
295 def get_repos_location(cls):
296 return cls.get_by_key('/').ui_value
296 return cls.get_by_key('/').ui_value
297
297
298 @classmethod
298 @classmethod
299 def create_or_update_hook(cls, key, val):
299 def create_or_update_hook(cls, key, val):
300 new_ui = cls.get_by_key(key) or cls()
300 new_ui = cls.get_by_key(key) or cls()
301 new_ui.ui_section = 'hooks'
301 new_ui.ui_section = 'hooks'
302 new_ui.ui_active = True
302 new_ui.ui_active = True
303 new_ui.ui_key = key
303 new_ui.ui_key = key
304 new_ui.ui_value = val
304 new_ui.ui_value = val
305
305
306 Session().add(new_ui)
306 Session().add(new_ui)
307
307
308 def __repr__(self):
308 def __repr__(self):
309 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
309 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
310 self.ui_value)
310 self.ui_value)
311
311
312
312
313 class User(Base, BaseModel):
313 class User(Base, BaseModel):
314 __tablename__ = 'users'
314 __tablename__ = 'users'
315 __table_args__ = (
315 __table_args__ = (
316 UniqueConstraint('username'), UniqueConstraint('email'),
316 UniqueConstraint('username'), UniqueConstraint('email'),
317 Index('u_username_idx', 'username'),
317 Index('u_username_idx', 'username'),
318 Index('u_email_idx', 'email'),
318 Index('u_email_idx', 'email'),
319 {'extend_existing': True, 'mysql_engine': 'InnoDB',
319 {'extend_existing': True, 'mysql_engine': 'InnoDB',
320 'mysql_charset': 'utf8'}
320 'mysql_charset': 'utf8'}
321 )
321 )
322 DEFAULT_USER = 'default'
322 DEFAULT_USER = 'default'
323
323
324 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
324 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
325 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
325 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
326 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
326 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
327 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
327 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
328 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
328 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
329 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
329 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
332 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
332 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
333 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
333 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
334 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
334 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
335 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
335 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
336
336
337 user_log = relationship('UserLog')
337 user_log = relationship('UserLog')
338 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
338 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
339
339
340 repositories = relationship('Repository')
340 repositories = relationship('Repository')
341 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
341 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
342 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
342 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
343
343
344 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
344 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
345 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
345 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
346
346
347 group_member = relationship('UserGroupMember', cascade='all')
347 group_member = relationship('UserGroupMember', cascade='all')
348
348
349 notifications = relationship('UserNotification', cascade='all')
349 notifications = relationship('UserNotification', cascade='all')
350 # notifications assigned to this user
350 # notifications assigned to this user
351 user_created_notifications = relationship('Notification', cascade='all')
351 user_created_notifications = relationship('Notification', cascade='all')
352 # comments created by this user
352 # comments created by this user
353 user_comments = relationship('ChangesetComment', cascade='all')
353 user_comments = relationship('ChangesetComment', cascade='all')
354 #extra emails for this user
354 #extra emails for this user
355 user_emails = relationship('UserEmailMap', cascade='all')
355 user_emails = relationship('UserEmailMap', cascade='all')
356
356
357 @hybrid_property
357 @hybrid_property
358 def email(self):
358 def email(self):
359 return self._email
359 return self._email
360
360
361 @email.setter
361 @email.setter
362 def email(self, val):
362 def email(self, val):
363 self._email = val.lower() if val else None
363 self._email = val.lower() if val else None
364
364
365 @property
365 @property
366 def firstname(self):
366 def firstname(self):
367 # alias for future
367 # alias for future
368 return self.name
368 return self.name
369
369
370 @property
370 @property
371 def emails(self):
371 def emails(self):
372 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
372 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
373 return [self.email] + [x.email for x in other]
373 return [self.email] + [x.email for x in other]
374
374
375 @property
375 @property
376 def ip_addresses(self):
376 def ip_addresses(self):
377 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
377 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
378 return [x.ip_addr for x in ret]
378 return [x.ip_addr for x in ret]
379
379
380 @property
380 @property
381 def username_and_name(self):
381 def username_and_name(self):
382 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
382 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
383
383
384 @property
384 @property
385 def full_name(self):
385 def full_name(self):
386 return '%s %s' % (self.firstname, self.lastname)
386 return '%s %s' % (self.firstname, self.lastname)
387
387
388 @property
388 @property
389 def full_name_or_username(self):
389 def full_name_or_username(self):
390 return ('%s %s' % (self.firstname, self.lastname)
390 return ('%s %s' % (self.firstname, self.lastname)
391 if (self.firstname and self.lastname) else self.username)
391 if (self.firstname and self.lastname) else self.username)
392
392
393 @property
393 @property
394 def full_contact(self):
394 def full_contact(self):
395 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
395 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
396
396
397 @property
397 @property
398 def short_contact(self):
398 def short_contact(self):
399 return '%s %s' % (self.firstname, self.lastname)
399 return '%s %s' % (self.firstname, self.lastname)
400
400
401 @property
401 @property
402 def is_admin(self):
402 def is_admin(self):
403 return self.admin
403 return self.admin
404
404
405 @property
405 @property
406 def AuthUser(self):
406 def AuthUser(self):
407 """
407 """
408 Returns instance of AuthUser for this user
408 Returns instance of AuthUser for this user
409 """
409 """
410 from rhodecode.lib.auth import AuthUser
410 from rhodecode.lib.auth import AuthUser
411 return AuthUser(user_id=self.user_id, api_key=self.api_key,
411 return AuthUser(user_id=self.user_id, api_key=self.api_key,
412 username=self.username)
412 username=self.username)
413
413
414 def __unicode__(self):
414 def __unicode__(self):
415 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
415 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
416 self.user_id, self.username)
416 self.user_id, self.username)
417
417
418 @classmethod
418 @classmethod
419 def get_by_username(cls, username, case_insensitive=False, cache=False):
419 def get_by_username(cls, username, case_insensitive=False, cache=False):
420 if case_insensitive:
420 if case_insensitive:
421 q = cls.query().filter(cls.username.ilike(username))
421 q = cls.query().filter(cls.username.ilike(username))
422 else:
422 else:
423 q = cls.query().filter(cls.username == username)
423 q = cls.query().filter(cls.username == username)
424
424
425 if cache:
425 if cache:
426 q = q.options(FromCache(
426 q = q.options(FromCache(
427 "sql_cache_short",
427 "sql_cache_short",
428 "get_user_%s" % _hash_key(username)
428 "get_user_%s" % _hash_key(username)
429 )
429 )
430 )
430 )
431 return q.scalar()
431 return q.scalar()
432
432
433 @classmethod
433 @classmethod
434 def get_by_api_key(cls, api_key, cache=False):
434 def get_by_api_key(cls, api_key, cache=False):
435 q = cls.query().filter(cls.api_key == api_key)
435 q = cls.query().filter(cls.api_key == api_key)
436
436
437 if cache:
437 if cache:
438 q = q.options(FromCache("sql_cache_short",
438 q = q.options(FromCache("sql_cache_short",
439 "get_api_key_%s" % api_key))
439 "get_api_key_%s" % api_key))
440 return q.scalar()
440 return q.scalar()
441
441
442 @classmethod
442 @classmethod
443 def get_by_email(cls, email, case_insensitive=False, cache=False):
443 def get_by_email(cls, email, case_insensitive=False, cache=False):
444 if case_insensitive:
444 if case_insensitive:
445 q = cls.query().filter(cls.email.ilike(email))
445 q = cls.query().filter(cls.email.ilike(email))
446 else:
446 else:
447 q = cls.query().filter(cls.email == email)
447 q = cls.query().filter(cls.email == email)
448
448
449 if cache:
449 if cache:
450 q = q.options(FromCache("sql_cache_short",
450 q = q.options(FromCache("sql_cache_short",
451 "get_email_key_%s" % email))
451 "get_email_key_%s" % email))
452
452
453 ret = q.scalar()
453 ret = q.scalar()
454 if ret is None:
454 if ret is None:
455 q = UserEmailMap.query()
455 q = UserEmailMap.query()
456 # try fetching in alternate email map
456 # try fetching in alternate email map
457 if case_insensitive:
457 if case_insensitive:
458 q = q.filter(UserEmailMap.email.ilike(email))
458 q = q.filter(UserEmailMap.email.ilike(email))
459 else:
459 else:
460 q = q.filter(UserEmailMap.email == email)
460 q = q.filter(UserEmailMap.email == email)
461 q = q.options(joinedload(UserEmailMap.user))
461 q = q.options(joinedload(UserEmailMap.user))
462 if cache:
462 if cache:
463 q = q.options(FromCache("sql_cache_short",
463 q = q.options(FromCache("sql_cache_short",
464 "get_email_map_key_%s" % email))
464 "get_email_map_key_%s" % email))
465 ret = getattr(q.scalar(), 'user', None)
465 ret = getattr(q.scalar(), 'user', None)
466
466
467 return ret
467 return ret
468
468
469 @classmethod
469 @classmethod
470 def get_from_cs_author(cls, author):
470 def get_from_cs_author(cls, author):
471 """
471 """
472 Tries to get User objects out of commit author string
472 Tries to get User objects out of commit author string
473
473
474 :param author:
474 :param author:
475 """
475 """
476 from rhodecode.lib.helpers import email, author_name
476 from rhodecode.lib.helpers import email, author_name
477 # Valid email in the attribute passed, see if they're in the system
477 # Valid email in the attribute passed, see if they're in the system
478 _email = email(author)
478 _email = email(author)
479 if _email:
479 if _email:
480 user = cls.get_by_email(_email, case_insensitive=True)
480 user = cls.get_by_email(_email, case_insensitive=True)
481 if user:
481 if user:
482 return user
482 return user
483 # Maybe we can match by username?
483 # Maybe we can match by username?
484 _author = author_name(author)
484 _author = author_name(author)
485 user = cls.get_by_username(_author, case_insensitive=True)
485 user = cls.get_by_username(_author, case_insensitive=True)
486 if user:
486 if user:
487 return user
487 return user
488
488
489 def update_lastlogin(self):
489 def update_lastlogin(self):
490 """Update user lastlogin"""
490 """Update user lastlogin"""
491 self.last_login = datetime.datetime.now()
491 self.last_login = datetime.datetime.now()
492 Session().add(self)
492 Session().add(self)
493 log.debug('updated user %s lastlogin' % self.username)
493 log.debug('updated user %s lastlogin' % self.username)
494
494
495 @classmethod
495 @classmethod
496 def get_first_admin(cls):
496 def get_first_admin(cls):
497 user = User.query().filter(User.admin == True).first()
497 user = User.query().filter(User.admin == True).first()
498 if user is None:
498 if user is None:
499 raise Exception('Missing administrative account!')
499 raise Exception('Missing administrative account!')
500 return user
500 return user
501
501
502 @classmethod
502 @classmethod
503 def get_default_user(cls, cache=False):
503 def get_default_user(cls, cache=False):
504 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
504 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
505 if user is None:
505 if user is None:
506 raise Exception('Missing default account!')
506 raise Exception('Missing default account!')
507 return user
507 return user
508
508
509 def get_api_data(self):
509 def get_api_data(self):
510 """
510 """
511 Common function for generating user related data for API
511 Common function for generating user related data for API
512 """
512 """
513 user = self
513 user = self
514 data = dict(
514 data = dict(
515 user_id=user.user_id,
515 user_id=user.user_id,
516 username=user.username,
516 username=user.username,
517 firstname=user.name,
517 firstname=user.name,
518 lastname=user.lastname,
518 lastname=user.lastname,
519 email=user.email,
519 email=user.email,
520 emails=user.emails,
520 emails=user.emails,
521 api_key=user.api_key,
521 api_key=user.api_key,
522 active=user.active,
522 active=user.active,
523 admin=user.admin,
523 admin=user.admin,
524 ldap_dn=user.ldap_dn,
524 ldap_dn=user.ldap_dn,
525 last_login=user.last_login,
525 last_login=user.last_login,
526 ip_addresses=user.ip_addresses
526 ip_addresses=user.ip_addresses
527 )
527 )
528 return data
528 return data
529
529
530 def __json__(self):
530 def __json__(self):
531 data = dict(
531 data = dict(
532 full_name=self.full_name,
532 full_name=self.full_name,
533 full_name_or_username=self.full_name_or_username,
533 full_name_or_username=self.full_name_or_username,
534 short_contact=self.short_contact,
534 short_contact=self.short_contact,
535 full_contact=self.full_contact
535 full_contact=self.full_contact
536 )
536 )
537 data.update(self.get_api_data())
537 data.update(self.get_api_data())
538 return data
538 return data
539
539
540
540
541 class UserEmailMap(Base, BaseModel):
541 class UserEmailMap(Base, BaseModel):
542 __tablename__ = 'user_email_map'
542 __tablename__ = 'user_email_map'
543 __table_args__ = (
543 __table_args__ = (
544 Index('uem_email_idx', 'email'),
544 Index('uem_email_idx', 'email'),
545 UniqueConstraint('email'),
545 UniqueConstraint('email'),
546 {'extend_existing': True, 'mysql_engine': 'InnoDB',
546 {'extend_existing': True, 'mysql_engine': 'InnoDB',
547 'mysql_charset': 'utf8'}
547 'mysql_charset': 'utf8'}
548 )
548 )
549 __mapper_args__ = {}
549 __mapper_args__ = {}
550
550
551 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
551 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
552 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
552 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
553 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
553 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
554 user = relationship('User', lazy='joined')
554 user = relationship('User', lazy='joined')
555
555
556 @validates('_email')
556 @validates('_email')
557 def validate_email(self, key, email):
557 def validate_email(self, key, email):
558 # check if this email is not main one
558 # check if this email is not main one
559 main_email = Session().query(User).filter(User.email == email).scalar()
559 main_email = Session().query(User).filter(User.email == email).scalar()
560 if main_email is not None:
560 if main_email is not None:
561 raise AttributeError('email %s is present is user table' % email)
561 raise AttributeError('email %s is present is user table' % email)
562 return email
562 return email
563
563
564 @hybrid_property
564 @hybrid_property
565 def email(self):
565 def email(self):
566 return self._email
566 return self._email
567
567
568 @email.setter
568 @email.setter
569 def email(self, val):
569 def email(self, val):
570 self._email = val.lower() if val else None
570 self._email = val.lower() if val else None
571
571
572
572
573 class UserIpMap(Base, BaseModel):
573 class UserIpMap(Base, BaseModel):
574 __tablename__ = 'user_ip_map'
574 __tablename__ = 'user_ip_map'
575 __table_args__ = (
575 __table_args__ = (
576 UniqueConstraint('user_id', 'ip_addr'),
576 UniqueConstraint('user_id', 'ip_addr'),
577 {'extend_existing': True, 'mysql_engine': 'InnoDB',
577 {'extend_existing': True, 'mysql_engine': 'InnoDB',
578 'mysql_charset': 'utf8'}
578 'mysql_charset': 'utf8'}
579 )
579 )
580 __mapper_args__ = {}
580 __mapper_args__ = {}
581
581
582 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
582 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
583 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
583 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
584 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
584 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
585 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
585 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
586 user = relationship('User', lazy='joined')
586 user = relationship('User', lazy='joined')
587
587
588 @classmethod
588 @classmethod
589 def _get_ip_range(cls, ip_addr):
589 def _get_ip_range(cls, ip_addr):
590 from rhodecode.lib import ipaddr
590 from rhodecode.lib import ipaddr
591 net = ipaddr.IPNetwork(address=ip_addr)
591 net = ipaddr.IPNetwork(address=ip_addr)
592 return [str(net.network), str(net.broadcast)]
592 return [str(net.network), str(net.broadcast)]
593
593
594 def __json__(self):
594 def __json__(self):
595 return dict(
595 return dict(
596 ip_addr=self.ip_addr,
596 ip_addr=self.ip_addr,
597 ip_range=self._get_ip_range(self.ip_addr)
597 ip_range=self._get_ip_range(self.ip_addr)
598 )
598 )
599
599
600
600
601 class UserLog(Base, BaseModel):
601 class UserLog(Base, BaseModel):
602 __tablename__ = 'user_logs'
602 __tablename__ = 'user_logs'
603 __table_args__ = (
603 __table_args__ = (
604 {'extend_existing': True, 'mysql_engine': 'InnoDB',
604 {'extend_existing': True, 'mysql_engine': 'InnoDB',
605 'mysql_charset': 'utf8'},
605 'mysql_charset': 'utf8'},
606 )
606 )
607 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
607 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
608 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
608 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
609 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
609 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
610 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
610 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
611 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
611 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
612 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
612 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
613 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
613 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
614 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
614 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
615
615
616 @property
616 @property
617 def action_as_day(self):
617 def action_as_day(self):
618 return datetime.date(*self.action_date.timetuple()[:3])
618 return datetime.date(*self.action_date.timetuple()[:3])
619
619
620 user = relationship('User')
620 user = relationship('User')
621 repository = relationship('Repository', cascade='')
621 repository = relationship('Repository', cascade='')
622
622
623
623
624 class UserGroup(Base, BaseModel):
624 class UserGroup(Base, BaseModel):
625 __tablename__ = 'users_groups'
625 __tablename__ = 'users_groups'
626 __table_args__ = (
626 __table_args__ = (
627 {'extend_existing': True, 'mysql_engine': 'InnoDB',
627 {'extend_existing': True, 'mysql_engine': 'InnoDB',
628 'mysql_charset': 'utf8'},
628 'mysql_charset': 'utf8'},
629 )
629 )
630
630
631 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
631 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
632 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
632 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
633 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
633 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
634 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
634 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
635 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
635 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
636
636
637 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
637 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
638 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
638 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
639 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
639 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
640 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
640 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
641 user_user_group_to_perm = relationship('UserUserGroupToPerm ', cascade='all')
641 user_user_group_to_perm = relationship('UserUserGroupToPerm ', cascade='all')
642 user = relationship('User')
642 user = relationship('User')
643
643
644 def __unicode__(self):
644 def __unicode__(self):
645 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
645 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
646 self.users_group_id,
646 self.users_group_id,
647 self.users_group_name)
647 self.users_group_name)
648
648
649 @classmethod
649 @classmethod
650 def get_by_group_name(cls, group_name, cache=False,
650 def get_by_group_name(cls, group_name, cache=False,
651 case_insensitive=False):
651 case_insensitive=False):
652 if case_insensitive:
652 if case_insensitive:
653 q = cls.query().filter(cls.users_group_name.ilike(group_name))
653 q = cls.query().filter(cls.users_group_name.ilike(group_name))
654 else:
654 else:
655 q = cls.query().filter(cls.users_group_name == group_name)
655 q = cls.query().filter(cls.users_group_name == group_name)
656 if cache:
656 if cache:
657 q = q.options(FromCache(
657 q = q.options(FromCache(
658 "sql_cache_short",
658 "sql_cache_short",
659 "get_user_%s" % _hash_key(group_name)
659 "get_user_%s" % _hash_key(group_name)
660 )
660 )
661 )
661 )
662 return q.scalar()
662 return q.scalar()
663
663
664 @classmethod
664 @classmethod
665 def get(cls, users_group_id, cache=False):
665 def get(cls, users_group_id, cache=False):
666 users_group = cls.query()
666 users_group = cls.query()
667 if cache:
667 if cache:
668 users_group = users_group.options(FromCache("sql_cache_short",
668 users_group = users_group.options(FromCache("sql_cache_short",
669 "get_users_group_%s" % users_group_id))
669 "get_users_group_%s" % users_group_id))
670 return users_group.get(users_group_id)
670 return users_group.get(users_group_id)
671
671
672 def get_api_data(self):
672 def get_api_data(self):
673 users_group = self
673 users_group = self
674
674
675 data = dict(
675 data = dict(
676 users_group_id=users_group.users_group_id,
676 users_group_id=users_group.users_group_id,
677 group_name=users_group.users_group_name,
677 group_name=users_group.users_group_name,
678 active=users_group.users_group_active,
678 active=users_group.users_group_active,
679 )
679 )
680
680
681 return data
681 return data
682
682
683
683
684 class UserGroupMember(Base, BaseModel):
684 class UserGroupMember(Base, BaseModel):
685 __tablename__ = 'users_groups_members'
685 __tablename__ = 'users_groups_members'
686 __table_args__ = (
686 __table_args__ = (
687 {'extend_existing': True, 'mysql_engine': 'InnoDB',
687 {'extend_existing': True, 'mysql_engine': 'InnoDB',
688 'mysql_charset': 'utf8'},
688 'mysql_charset': 'utf8'},
689 )
689 )
690
690
691 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
691 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
692 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
692 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
693 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
693 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
694
694
695 user = relationship('User', lazy='joined')
695 user = relationship('User', lazy='joined')
696 users_group = relationship('UserGroup')
696 users_group = relationship('UserGroup')
697
697
698 def __init__(self, gr_id='', u_id=''):
698 def __init__(self, gr_id='', u_id=''):
699 self.users_group_id = gr_id
699 self.users_group_id = gr_id
700 self.user_id = u_id
700 self.user_id = u_id
701
701
702
702
703 class RepositoryField(Base, BaseModel):
703 class RepositoryField(Base, BaseModel):
704 __tablename__ = 'repositories_fields'
704 __tablename__ = 'repositories_fields'
705 __table_args__ = (
705 __table_args__ = (
706 UniqueConstraint('repository_id', 'field_key'), # no-multi field
706 UniqueConstraint('repository_id', 'field_key'), # no-multi field
707 {'extend_existing': True, 'mysql_engine': 'InnoDB',
707 {'extend_existing': True, 'mysql_engine': 'InnoDB',
708 'mysql_charset': 'utf8'},
708 'mysql_charset': 'utf8'},
709 )
709 )
710 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
710 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
711
711
712 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
712 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
713 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
713 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
714 field_key = Column("field_key", String(250, convert_unicode=False, assert_unicode=None))
714 field_key = Column("field_key", String(250, convert_unicode=False, assert_unicode=None))
715 field_label = Column("field_label", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
715 field_label = Column("field_label", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
716 field_value = Column("field_value", String(10000, convert_unicode=False, assert_unicode=None), nullable=False)
716 field_value = Column("field_value", String(10000, convert_unicode=False, assert_unicode=None), nullable=False)
717 field_desc = Column("field_desc", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
717 field_desc = Column("field_desc", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
718 field_type = Column("field_type", String(256), nullable=False, unique=None)
718 field_type = Column("field_type", String(256), nullable=False, unique=None)
719 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
719 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
720
720
721 repository = relationship('Repository')
721 repository = relationship('Repository')
722
722
723 @property
723 @property
724 def field_key_prefixed(self):
724 def field_key_prefixed(self):
725 return 'ex_%s' % self.field_key
725 return 'ex_%s' % self.field_key
726
726
727 @classmethod
727 @classmethod
728 def un_prefix_key(cls, key):
728 def un_prefix_key(cls, key):
729 if key.startswith(cls.PREFIX):
729 if key.startswith(cls.PREFIX):
730 return key[len(cls.PREFIX):]
730 return key[len(cls.PREFIX):]
731 return key
731 return key
732
732
733 @classmethod
733 @classmethod
734 def get_by_key_name(cls, key, repo):
734 def get_by_key_name(cls, key, repo):
735 row = cls.query()\
735 row = cls.query()\
736 .filter(cls.repository == repo)\
736 .filter(cls.repository == repo)\
737 .filter(cls.field_key == key).scalar()
737 .filter(cls.field_key == key).scalar()
738 return row
738 return row
739
739
740
740
741 class Repository(Base, BaseModel):
741 class Repository(Base, BaseModel):
742 __tablename__ = 'repositories'
742 __tablename__ = 'repositories'
743 __table_args__ = (
743 __table_args__ = (
744 UniqueConstraint('repo_name'),
744 UniqueConstraint('repo_name'),
745 Index('r_repo_name_idx', 'repo_name'),
745 Index('r_repo_name_idx', 'repo_name'),
746 {'extend_existing': True, 'mysql_engine': 'InnoDB',
746 {'extend_existing': True, 'mysql_engine': 'InnoDB',
747 'mysql_charset': 'utf8'},
747 'mysql_charset': 'utf8'},
748 )
748 )
749
749
750 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
750 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
751 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
751 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
752 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
752 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
753 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
753 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
754 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
754 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
755 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
755 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
756 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
756 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
757 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
757 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
758 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
758 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
759 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
759 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
760 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
760 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
761 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
761 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
762 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
762 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
763 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
763 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
764 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
764 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
765
765
766 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
766 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
767 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
767 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
768
768
769 user = relationship('User')
769 user = relationship('User')
770 fork = relationship('Repository', remote_side=repo_id)
770 fork = relationship('Repository', remote_side=repo_id)
771 group = relationship('RepoGroup')
771 group = relationship('RepoGroup')
772 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
772 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
773 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
773 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
774 stats = relationship('Statistics', cascade='all', uselist=False)
774 stats = relationship('Statistics', cascade='all', uselist=False)
775
775
776 followers = relationship('UserFollowing',
776 followers = relationship('UserFollowing',
777 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
777 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
778 cascade='all')
778 cascade='all')
779 extra_fields = relationship('RepositoryField',
779 extra_fields = relationship('RepositoryField',
780 cascade="all, delete, delete-orphan")
780 cascade="all, delete, delete-orphan")
781
781
782 logs = relationship('UserLog')
782 logs = relationship('UserLog')
783 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
783 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
784
784
785 pull_requests_org = relationship('PullRequest',
785 pull_requests_org = relationship('PullRequest',
786 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
786 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
787 cascade="all, delete, delete-orphan")
787 cascade="all, delete, delete-orphan")
788
788
789 pull_requests_other = relationship('PullRequest',
789 pull_requests_other = relationship('PullRequest',
790 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
790 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
791 cascade="all, delete, delete-orphan")
791 cascade="all, delete, delete-orphan")
792
792
793 def __unicode__(self):
793 def __unicode__(self):
794 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
794 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
795 self.repo_name)
795 self.repo_name)
796
796
797 @hybrid_property
797 @hybrid_property
798 def locked(self):
798 def locked(self):
799 # always should return [user_id, timelocked]
799 # always should return [user_id, timelocked]
800 if self._locked:
800 if self._locked:
801 _lock_info = self._locked.split(':')
801 _lock_info = self._locked.split(':')
802 return int(_lock_info[0]), _lock_info[1]
802 return int(_lock_info[0]), _lock_info[1]
803 return [None, None]
803 return [None, None]
804
804
805 @locked.setter
805 @locked.setter
806 def locked(self, val):
806 def locked(self, val):
807 if val and isinstance(val, (list, tuple)):
807 if val and isinstance(val, (list, tuple)):
808 self._locked = ':'.join(map(str, val))
808 self._locked = ':'.join(map(str, val))
809 else:
809 else:
810 self._locked = None
810 self._locked = None
811
811
812 @hybrid_property
812 @hybrid_property
813 def changeset_cache(self):
813 def changeset_cache(self):
814 from rhodecode.lib.vcs.backends.base import EmptyChangeset
814 from rhodecode.lib.vcs.backends.base import EmptyChangeset
815 dummy = EmptyChangeset().__json__()
815 dummy = EmptyChangeset().__json__()
816 if not self._changeset_cache:
816 if not self._changeset_cache:
817 return dummy
817 return dummy
818 try:
818 try:
819 return json.loads(self._changeset_cache)
819 return json.loads(self._changeset_cache)
820 except TypeError:
820 except TypeError:
821 return dummy
821 return dummy
822
822
823 @changeset_cache.setter
823 @changeset_cache.setter
824 def changeset_cache(self, val):
824 def changeset_cache(self, val):
825 try:
825 try:
826 self._changeset_cache = json.dumps(val)
826 self._changeset_cache = json.dumps(val)
827 except Exception:
827 except Exception:
828 log.error(traceback.format_exc())
828 log.error(traceback.format_exc())
829
829
830 @classmethod
830 @classmethod
831 def url_sep(cls):
831 def url_sep(cls):
832 return URL_SEP
832 return URL_SEP
833
833
834 @classmethod
834 @classmethod
835 def normalize_repo_name(cls, repo_name):
835 def normalize_repo_name(cls, repo_name):
836 """
836 """
837 Normalizes os specific repo_name to the format internally stored inside
837 Normalizes os specific repo_name to the format internally stored inside
838 dabatabase using URL_SEP
838 dabatabase using URL_SEP
839
839
840 :param cls:
840 :param cls:
841 :param repo_name:
841 :param repo_name:
842 """
842 """
843 return cls.url_sep().join(repo_name.split(os.sep))
843 return cls.url_sep().join(repo_name.split(os.sep))
844
844
845 @classmethod
845 @classmethod
846 def get_by_repo_name(cls, repo_name):
846 def get_by_repo_name(cls, repo_name):
847 q = Session().query(cls).filter(cls.repo_name == repo_name)
847 q = Session().query(cls).filter(cls.repo_name == repo_name)
848 q = q.options(joinedload(Repository.fork))\
848 q = q.options(joinedload(Repository.fork))\
849 .options(joinedload(Repository.user))\
849 .options(joinedload(Repository.user))\
850 .options(joinedload(Repository.group))
850 .options(joinedload(Repository.group))
851 return q.scalar()
851 return q.scalar()
852
852
853 @classmethod
853 @classmethod
854 def get_by_full_path(cls, repo_full_path):
854 def get_by_full_path(cls, repo_full_path):
855 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
855 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
856 repo_name = cls.normalize_repo_name(repo_name)
856 repo_name = cls.normalize_repo_name(repo_name)
857 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
857 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
858
858
859 @classmethod
859 @classmethod
860 def get_repo_forks(cls, repo_id):
860 def get_repo_forks(cls, repo_id):
861 return cls.query().filter(Repository.fork_id == repo_id)
861 return cls.query().filter(Repository.fork_id == repo_id)
862
862
863 @classmethod
863 @classmethod
864 def base_path(cls):
864 def base_path(cls):
865 """
865 """
866 Returns base path when all repos are stored
866 Returns base path when all repos are stored
867
867
868 :param cls:
868 :param cls:
869 """
869 """
870 q = Session().query(RhodeCodeUi)\
870 q = Session().query(RhodeCodeUi)\
871 .filter(RhodeCodeUi.ui_key == cls.url_sep())
871 .filter(RhodeCodeUi.ui_key == cls.url_sep())
872 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
872 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
873 return q.one().ui_value
873 return q.one().ui_value
874
874
875 @property
875 @property
876 def forks(self):
876 def forks(self):
877 """
877 """
878 Return forks of this repo
878 Return forks of this repo
879 """
879 """
880 return Repository.get_repo_forks(self.repo_id)
880 return Repository.get_repo_forks(self.repo_id)
881
881
882 @property
882 @property
883 def parent(self):
883 def parent(self):
884 """
884 """
885 Returns fork parent
885 Returns fork parent
886 """
886 """
887 return self.fork
887 return self.fork
888
888
889 @property
889 @property
890 def just_name(self):
890 def just_name(self):
891 return self.repo_name.split(Repository.url_sep())[-1]
891 return self.repo_name.split(Repository.url_sep())[-1]
892
892
893 @property
893 @property
894 def groups_with_parents(self):
894 def groups_with_parents(self):
895 groups = []
895 groups = []
896 if self.group is None:
896 if self.group is None:
897 return groups
897 return groups
898
898
899 cur_gr = self.group
899 cur_gr = self.group
900 groups.insert(0, cur_gr)
900 groups.insert(0, cur_gr)
901 while 1:
901 while 1:
902 gr = getattr(cur_gr, 'parent_group', None)
902 gr = getattr(cur_gr, 'parent_group', None)
903 cur_gr = cur_gr.parent_group
903 cur_gr = cur_gr.parent_group
904 if gr is None:
904 if gr is None:
905 break
905 break
906 groups.insert(0, gr)
906 groups.insert(0, gr)
907
907
908 return groups
908 return groups
909
909
910 @property
910 @property
911 def groups_and_repo(self):
911 def groups_and_repo(self):
912 return self.groups_with_parents, self.just_name, self.repo_name
912 return self.groups_with_parents, self.just_name, self.repo_name
913
913
914 @LazyProperty
914 @LazyProperty
915 def repo_path(self):
915 def repo_path(self):
916 """
916 """
917 Returns base full path for that repository means where it actually
917 Returns base full path for that repository means where it actually
918 exists on a filesystem
918 exists on a filesystem
919 """
919 """
920 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
920 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
921 Repository.url_sep())
921 Repository.url_sep())
922 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
922 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
923 return q.one().ui_value
923 return q.one().ui_value
924
924
925 @property
925 @property
926 def repo_full_path(self):
926 def repo_full_path(self):
927 p = [self.repo_path]
927 p = [self.repo_path]
928 # we need to split the name by / since this is how we store the
928 # we need to split the name by / since this is how we store the
929 # names in the database, but that eventually needs to be converted
929 # names in the database, but that eventually needs to be converted
930 # into a valid system path
930 # into a valid system path
931 p += self.repo_name.split(Repository.url_sep())
931 p += self.repo_name.split(Repository.url_sep())
932 return os.path.join(*map(safe_unicode, p))
932 return os.path.join(*map(safe_unicode, p))
933
933
934 @property
934 @property
935 def cache_keys(self):
935 def cache_keys(self):
936 """
936 """
937 Returns associated cache keys for that repo
937 Returns associated cache keys for that repo
938 """
938 """
939 return CacheInvalidation.query()\
939 return CacheInvalidation.query()\
940 .filter(CacheInvalidation.cache_args == self.repo_name)\
940 .filter(CacheInvalidation.cache_args == self.repo_name)\
941 .order_by(CacheInvalidation.cache_key)\
941 .order_by(CacheInvalidation.cache_key)\
942 .all()
942 .all()
943
943
944 def get_new_name(self, repo_name):
944 def get_new_name(self, repo_name):
945 """
945 """
946 returns new full repository name based on assigned group and new new
946 returns new full repository name based on assigned group and new new
947
947
948 :param group_name:
948 :param group_name:
949 """
949 """
950 path_prefix = self.group.full_path_splitted if self.group else []
950 path_prefix = self.group.full_path_splitted if self.group else []
951 return Repository.url_sep().join(path_prefix + [repo_name])
951 return Repository.url_sep().join(path_prefix + [repo_name])
952
952
953 @property
953 @property
954 def _ui(self):
954 def _ui(self):
955 """
955 """
956 Creates an db based ui object for this repository
956 Creates an db based ui object for this repository
957 """
957 """
958 from rhodecode.lib.utils import make_ui
958 from rhodecode.lib.utils import make_ui
959 return make_ui('db', clear_session=False)
959 return make_ui('db', clear_session=False)
960
960
961 @classmethod
961 @classmethod
962 def is_valid(cls, repo_name):
962 def is_valid(cls, repo_name):
963 """
963 """
964 returns True if given repo name is a valid filesystem repository
964 returns True if given repo name is a valid filesystem repository
965
965
966 :param cls:
966 :param cls:
967 :param repo_name:
967 :param repo_name:
968 """
968 """
969 from rhodecode.lib.utils import is_valid_repo
969 from rhodecode.lib.utils import is_valid_repo
970
970
971 return is_valid_repo(repo_name, cls.base_path())
971 return is_valid_repo(repo_name, cls.base_path())
972
972
973 def get_api_data(self):
973 def get_api_data(self):
974 """
974 """
975 Common function for generating repo api data
975 Common function for generating repo api data
976
976
977 """
977 """
978 repo = self
978 repo = self
979 data = dict(
979 data = dict(
980 repo_id=repo.repo_id,
980 repo_id=repo.repo_id,
981 repo_name=repo.repo_name,
981 repo_name=repo.repo_name,
982 repo_type=repo.repo_type,
982 repo_type=repo.repo_type,
983 clone_uri=repo.clone_uri,
983 clone_uri=repo.clone_uri,
984 private=repo.private,
984 private=repo.private,
985 created_on=repo.created_on,
985 created_on=repo.created_on,
986 description=repo.description,
986 description=repo.description,
987 landing_rev=repo.landing_rev,
987 landing_rev=repo.landing_rev,
988 owner=repo.user.username,
988 owner=repo.user.username,
989 fork_of=repo.fork.repo_name if repo.fork else None,
989 fork_of=repo.fork.repo_name if repo.fork else None,
990 enable_statistics=repo.enable_statistics,
990 enable_statistics=repo.enable_statistics,
991 enable_locking=repo.enable_locking,
991 enable_locking=repo.enable_locking,
992 enable_downloads=repo.enable_downloads,
992 enable_downloads=repo.enable_downloads,
993 last_changeset=repo.changeset_cache,
993 last_changeset=repo.changeset_cache,
994 locked_by=User.get(self.locked[0]).get_api_data() \
994 locked_by=User.get(self.locked[0]).get_api_data() \
995 if self.locked[0] else None,
995 if self.locked[0] else None,
996 locked_date=time_to_datetime(self.locked[1]) \
996 locked_date=time_to_datetime(self.locked[1]) \
997 if self.locked[1] else None
997 if self.locked[1] else None
998 )
998 )
999 rc_config = RhodeCodeSetting.get_app_settings()
999 rc_config = RhodeCodeSetting.get_app_settings()
1000 repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
1000 repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
1001 if repository_fields:
1001 if repository_fields:
1002 for f in self.extra_fields:
1002 for f in self.extra_fields:
1003 data[f.field_key_prefixed] = f.field_value
1003 data[f.field_key_prefixed] = f.field_value
1004
1004
1005 return data
1005 return data
1006
1006
1007 @classmethod
1007 @classmethod
1008 def lock(cls, repo, user_id):
1008 def lock(cls, repo, user_id):
1009 repo.locked = [user_id, time.time()]
1009 repo.locked = [user_id, time.time()]
1010 Session().add(repo)
1010 Session().add(repo)
1011 Session().commit()
1011 Session().commit()
1012
1012
1013 @classmethod
1013 @classmethod
1014 def unlock(cls, repo):
1014 def unlock(cls, repo):
1015 repo.locked = None
1015 repo.locked = None
1016 Session().add(repo)
1016 Session().add(repo)
1017 Session().commit()
1017 Session().commit()
1018
1018
1019 @classmethod
1019 @classmethod
1020 def getlock(cls, repo):
1020 def getlock(cls, repo):
1021 return repo.locked
1021 return repo.locked
1022
1022
1023 @property
1023 @property
1024 def last_db_change(self):
1024 def last_db_change(self):
1025 return self.updated_on
1025 return self.updated_on
1026
1026
1027 def clone_url(self, **override):
1027 def clone_url(self, **override):
1028 from pylons import url
1028 from pylons import url
1029 from urlparse import urlparse
1029 from urlparse import urlparse
1030 import urllib
1030 import urllib
1031 parsed_url = urlparse(url('home', qualified=True))
1031 parsed_url = urlparse(url('home', qualified=True))
1032 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
1032 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
1033 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
1033 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
1034 args = {
1034 args = {
1035 'user': '',
1035 'user': '',
1036 'pass': '',
1036 'pass': '',
1037 'scheme': parsed_url.scheme,
1037 'scheme': parsed_url.scheme,
1038 'netloc': parsed_url.netloc,
1038 'netloc': parsed_url.netloc,
1039 'prefix': decoded_path,
1039 'prefix': decoded_path,
1040 'path': self.repo_name
1040 'path': self.repo_name
1041 }
1041 }
1042
1042
1043 args.update(override)
1043 args.update(override)
1044 return default_clone_uri % args
1044 return default_clone_uri % args
1045
1045
1046 #==========================================================================
1046 #==========================================================================
1047 # SCM PROPERTIES
1047 # SCM PROPERTIES
1048 #==========================================================================
1048 #==========================================================================
1049
1049
1050 def get_changeset(self, rev=None):
1050 def get_changeset(self, rev=None):
1051 return get_changeset_safe(self.scm_instance, rev)
1051 return get_changeset_safe(self.scm_instance, rev)
1052
1052
1053 def get_landing_changeset(self):
1053 def get_landing_changeset(self):
1054 """
1054 """
1055 Returns landing changeset, or if that doesn't exist returns the tip
1055 Returns landing changeset, or if that doesn't exist returns the tip
1056 """
1056 """
1057 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
1057 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
1058 return cs
1058 return cs
1059
1059
1060 def update_changeset_cache(self, cs_cache=None):
1060 def update_changeset_cache(self, cs_cache=None):
1061 """
1061 """
1062 Update cache of last changeset for repository, keys should be::
1062 Update cache of last changeset for repository, keys should be::
1063
1063
1064 short_id
1064 short_id
1065 raw_id
1065 raw_id
1066 revision
1066 revision
1067 message
1067 message
1068 date
1068 date
1069 author
1069 author
1070
1070
1071 :param cs_cache:
1071 :param cs_cache:
1072 """
1072 """
1073 from rhodecode.lib.vcs.backends.base import BaseChangeset
1073 from rhodecode.lib.vcs.backends.base import BaseChangeset
1074 if cs_cache is None:
1074 if cs_cache is None:
1075 cs_cache = EmptyChangeset()
1075 cs_cache = EmptyChangeset()
1076 # use no-cache version here
1076 # use no-cache version here
1077 scm_repo = self.scm_instance_no_cache()
1077 scm_repo = self.scm_instance_no_cache()
1078 if scm_repo:
1078 if scm_repo:
1079 cs_cache = scm_repo.get_changeset()
1079 cs_cache = scm_repo.get_changeset()
1080
1080
1081 if isinstance(cs_cache, BaseChangeset):
1081 if isinstance(cs_cache, BaseChangeset):
1082 cs_cache = cs_cache.__json__()
1082 cs_cache = cs_cache.__json__()
1083
1083
1084 if (cs_cache != self.changeset_cache or not self.changeset_cache):
1084 if (cs_cache != self.changeset_cache or not self.changeset_cache):
1085 _default = datetime.datetime.fromtimestamp(0)
1085 _default = datetime.datetime.fromtimestamp(0)
1086 last_change = cs_cache.get('date') or _default
1086 last_change = cs_cache.get('date') or _default
1087 log.debug('updated repo %s with new cs cache %s'
1087 log.debug('updated repo %s with new cs cache %s'
1088 % (self.repo_name, cs_cache))
1088 % (self.repo_name, cs_cache))
1089 self.updated_on = last_change
1089 self.updated_on = last_change
1090 self.changeset_cache = cs_cache
1090 self.changeset_cache = cs_cache
1091 Session().add(self)
1091 Session().add(self)
1092 Session().commit()
1092 Session().commit()
1093 else:
1093 else:
1094 log.debug('Skipping repo:%s already with latest changes'
1094 log.debug('Skipping repo:%s already with latest changes'
1095 % self.repo_name)
1095 % self.repo_name)
1096
1096
1097 @property
1097 @property
1098 def tip(self):
1098 def tip(self):
1099 return self.get_changeset('tip')
1099 return self.get_changeset('tip')
1100
1100
1101 @property
1101 @property
1102 def author(self):
1102 def author(self):
1103 return self.tip.author
1103 return self.tip.author
1104
1104
1105 @property
1105 @property
1106 def last_change(self):
1106 def last_change(self):
1107 return self.scm_instance.last_change
1107 return self.scm_instance.last_change
1108
1108
1109 def get_comments(self, revisions=None):
1109 def get_comments(self, revisions=None):
1110 """
1110 """
1111 Returns comments for this repository grouped by revisions
1111 Returns comments for this repository grouped by revisions
1112
1112
1113 :param revisions: filter query by revisions only
1113 :param revisions: filter query by revisions only
1114 """
1114 """
1115 cmts = ChangesetComment.query()\
1115 cmts = ChangesetComment.query()\
1116 .filter(ChangesetComment.repo == self)
1116 .filter(ChangesetComment.repo == self)
1117 if revisions:
1117 if revisions:
1118 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1118 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1119 grouped = defaultdict(list)
1119 grouped = defaultdict(list)
1120 for cmt in cmts.all():
1120 for cmt in cmts.all():
1121 grouped[cmt.revision].append(cmt)
1121 grouped[cmt.revision].append(cmt)
1122 return grouped
1122 return grouped
1123
1123
1124 def statuses(self, revisions=None):
1124 def statuses(self, revisions=None):
1125 """
1125 """
1126 Returns statuses for this repository
1126 Returns statuses for this repository
1127
1127
1128 :param revisions: list of revisions to get statuses for
1128 :param revisions: list of revisions to get statuses for
1129 :type revisions: list
1129 :type revisions: list
1130 """
1130 """
1131
1131
1132 statuses = ChangesetStatus.query()\
1132 statuses = ChangesetStatus.query()\
1133 .filter(ChangesetStatus.repo == self)\
1133 .filter(ChangesetStatus.repo == self)\
1134 .filter(ChangesetStatus.version == 0)
1134 .filter(ChangesetStatus.version == 0)
1135 if revisions:
1135 if revisions:
1136 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1136 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1137 grouped = {}
1137 grouped = {}
1138
1138
1139 #maybe we have open new pullrequest without a status ?
1139 #maybe we have open new pullrequest without a status ?
1140 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1140 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1141 status_lbl = ChangesetStatus.get_status_lbl(stat)
1141 status_lbl = ChangesetStatus.get_status_lbl(stat)
1142 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1142 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1143 for rev in pr.revisions:
1143 for rev in pr.revisions:
1144 pr_id = pr.pull_request_id
1144 pr_id = pr.pull_request_id
1145 pr_repo = pr.other_repo.repo_name
1145 pr_repo = pr.other_repo.repo_name
1146 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1146 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1147
1147
1148 for stat in statuses.all():
1148 for stat in statuses.all():
1149 pr_id = pr_repo = None
1149 pr_id = pr_repo = None
1150 if stat.pull_request:
1150 if stat.pull_request:
1151 pr_id = stat.pull_request.pull_request_id
1151 pr_id = stat.pull_request.pull_request_id
1152 pr_repo = stat.pull_request.other_repo.repo_name
1152 pr_repo = stat.pull_request.other_repo.repo_name
1153 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1153 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1154 pr_id, pr_repo]
1154 pr_id, pr_repo]
1155 return grouped
1155 return grouped
1156
1156
1157 def _repo_size(self):
1157 def _repo_size(self):
1158 from rhodecode.lib import helpers as h
1158 from rhodecode.lib import helpers as h
1159 log.debug('calculating repository size...')
1159 log.debug('calculating repository size...')
1160 return h.format_byte_size(self.scm_instance.size)
1160 return h.format_byte_size(self.scm_instance.size)
1161
1161
1162 #==========================================================================
1162 #==========================================================================
1163 # SCM CACHE INSTANCE
1163 # SCM CACHE INSTANCE
1164 #==========================================================================
1164 #==========================================================================
1165
1165
1166 def set_invalidate(self):
1166 def set_invalidate(self):
1167 """
1167 """
1168 Mark caches of this repo as invalid.
1168 Mark caches of this repo as invalid.
1169 """
1169 """
1170 CacheInvalidation.set_invalidate(self.repo_name)
1170 CacheInvalidation.set_invalidate(self.repo_name)
1171
1171
1172 def scm_instance_no_cache(self):
1172 def scm_instance_no_cache(self):
1173 return self.__get_instance()
1173 return self.__get_instance()
1174
1174
1175 @property
1175 @property
1176 def scm_instance(self):
1176 def scm_instance(self):
1177 import rhodecode
1177 import rhodecode
1178 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1178 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1179 if full_cache:
1179 if full_cache:
1180 return self.scm_instance_cached()
1180 return self.scm_instance_cached()
1181 return self.__get_instance()
1181 return self.__get_instance()
1182
1182
1183 def scm_instance_cached(self, valid_cache_keys=None):
1183 def scm_instance_cached(self, valid_cache_keys=None):
1184 @cache_region('long_term')
1184 @cache_region('long_term')
1185 def _c(repo_name):
1185 def _c(repo_name):
1186 return self.__get_instance()
1186 return self.__get_instance()
1187 rn = self.repo_name
1187 rn = self.repo_name
1188
1188
1189 valid = CacheInvalidation.test_and_set_valid(rn, None, valid_cache_keys=valid_cache_keys)
1189 valid = CacheInvalidation.test_and_set_valid(rn, None, valid_cache_keys=valid_cache_keys)
1190 if not valid:
1190 if not valid:
1191 log.debug('Cache for %s invalidated, getting new object' % (rn))
1191 log.debug('Cache for %s invalidated, getting new object' % (rn))
1192 region_invalidate(_c, None, rn)
1192 region_invalidate(_c, None, rn)
1193 else:
1193 else:
1194 log.debug('Getting obj for %s from cache' % (rn))
1194 log.debug('Getting obj for %s from cache' % (rn))
1195 return _c(rn)
1195 return _c(rn)
1196
1196
1197 def __get_instance(self):
1197 def __get_instance(self):
1198 repo_full_path = self.repo_full_path
1198 repo_full_path = self.repo_full_path
1199 try:
1199 try:
1200 alias = get_scm(repo_full_path)[0]
1200 alias = get_scm(repo_full_path)[0]
1201 log.debug('Creating instance of %s repository from %s'
1201 log.debug('Creating instance of %s repository from %s'
1202 % (alias, repo_full_path))
1202 % (alias, repo_full_path))
1203 backend = get_backend(alias)
1203 backend = get_backend(alias)
1204 except VCSError:
1204 except VCSError:
1205 log.error(traceback.format_exc())
1205 log.error(traceback.format_exc())
1206 log.error('Perhaps this repository is in db and not in '
1206 log.error('Perhaps this repository is in db and not in '
1207 'filesystem run rescan repositories with '
1207 'filesystem run rescan repositories with '
1208 '"destroy old data " option from admin panel')
1208 '"destroy old data " option from admin panel')
1209 return
1209 return
1210
1210
1211 if alias == 'hg':
1211 if alias == 'hg':
1212
1212
1213 repo = backend(safe_str(repo_full_path), create=False,
1213 repo = backend(safe_str(repo_full_path), create=False,
1214 baseui=self._ui)
1214 baseui=self._ui)
1215 # skip hidden web repository
1215 # skip hidden web repository
1216 if repo._get_hidden():
1216 if repo._get_hidden():
1217 return
1217 return
1218 else:
1218 else:
1219 repo = backend(repo_full_path, create=False)
1219 repo = backend(repo_full_path, create=False)
1220
1220
1221 return repo
1221 return repo
1222
1222
1223
1223
1224 class RepoGroup(Base, BaseModel):
1224 class RepoGroup(Base, BaseModel):
1225 __tablename__ = 'groups'
1225 __tablename__ = 'groups'
1226 __table_args__ = (
1226 __table_args__ = (
1227 UniqueConstraint('group_name', 'group_parent_id'),
1227 UniqueConstraint('group_name', 'group_parent_id'),
1228 CheckConstraint('group_id != group_parent_id'),
1228 CheckConstraint('group_id != group_parent_id'),
1229 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1229 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1230 'mysql_charset': 'utf8'},
1230 'mysql_charset': 'utf8'},
1231 )
1231 )
1232 __mapper_args__ = {'order_by': 'group_name'}
1232 __mapper_args__ = {'order_by': 'group_name'}
1233
1233
1234 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1234 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1235 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1235 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1236 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1236 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1237 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1237 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1238 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1238 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1239 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
1239 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
1240
1240
1241 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1241 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1242 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1242 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1243 parent_group = relationship('RepoGroup', remote_side=group_id)
1243 parent_group = relationship('RepoGroup', remote_side=group_id)
1244 user = relationship('User')
1244 user = relationship('User')
1245
1245
1246 def __init__(self, group_name='', parent_group=None):
1246 def __init__(self, group_name='', parent_group=None):
1247 self.group_name = group_name
1247 self.group_name = group_name
1248 self.parent_group = parent_group
1248 self.parent_group = parent_group
1249
1249
1250 def __unicode__(self):
1250 def __unicode__(self):
1251 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
1251 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
1252 self.group_name)
1252 self.group_name)
1253
1253
1254 @classmethod
1254 @classmethod
1255 def groups_choices(cls, groups=None, show_empty_group=True):
1255 def groups_choices(cls, groups=None, show_empty_group=True):
1256 from webhelpers.html import literal as _literal
1256 from webhelpers.html import literal as _literal
1257 if not groups:
1257 if not groups:
1258 groups = cls.query().all()
1258 groups = cls.query().all()
1259
1259
1260 repo_groups = []
1260 repo_groups = []
1261 if show_empty_group:
1261 if show_empty_group:
1262 repo_groups = [('-1', '-- %s --' % _('top level'))]
1262 repo_groups = [('-1', '-- %s --' % _('top level'))]
1263 sep = ' &raquo; '
1263 sep = ' &raquo; '
1264 _name = lambda k: _literal(sep.join(k))
1264 _name = lambda k: _literal(sep.join(k))
1265
1265
1266 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1266 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1267 for x in groups])
1267 for x in groups])
1268
1268
1269 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1269 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1270 return repo_groups
1270 return repo_groups
1271
1271
1272 @classmethod
1272 @classmethod
1273 def url_sep(cls):
1273 def url_sep(cls):
1274 return URL_SEP
1274 return URL_SEP
1275
1275
1276 @classmethod
1276 @classmethod
1277 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1277 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1278 if case_insensitive:
1278 if case_insensitive:
1279 gr = cls.query()\
1279 gr = cls.query()\
1280 .filter(cls.group_name.ilike(group_name))
1280 .filter(cls.group_name.ilike(group_name))
1281 else:
1281 else:
1282 gr = cls.query()\
1282 gr = cls.query()\
1283 .filter(cls.group_name == group_name)
1283 .filter(cls.group_name == group_name)
1284 if cache:
1284 if cache:
1285 gr = gr.options(FromCache(
1285 gr = gr.options(FromCache(
1286 "sql_cache_short",
1286 "sql_cache_short",
1287 "get_group_%s" % _hash_key(group_name)
1287 "get_group_%s" % _hash_key(group_name)
1288 )
1288 )
1289 )
1289 )
1290 return gr.scalar()
1290 return gr.scalar()
1291
1291
1292 @property
1292 @property
1293 def parents(self):
1293 def parents(self):
1294 parents_recursion_limit = 5
1294 parents_recursion_limit = 5
1295 groups = []
1295 groups = []
1296 if self.parent_group is None:
1296 if self.parent_group is None:
1297 return groups
1297 return groups
1298 cur_gr = self.parent_group
1298 cur_gr = self.parent_group
1299 groups.insert(0, cur_gr)
1299 groups.insert(0, cur_gr)
1300 cnt = 0
1300 cnt = 0
1301 while 1:
1301 while 1:
1302 cnt += 1
1302 cnt += 1
1303 gr = getattr(cur_gr, 'parent_group', None)
1303 gr = getattr(cur_gr, 'parent_group', None)
1304 cur_gr = cur_gr.parent_group
1304 cur_gr = cur_gr.parent_group
1305 if gr is None:
1305 if gr is None:
1306 break
1306 break
1307 if cnt == parents_recursion_limit:
1307 if cnt == parents_recursion_limit:
1308 # this will prevent accidental infinit loops
1308 # this will prevent accidental infinit loops
1309 log.error('group nested more than %s' %
1309 log.error('group nested more than %s' %
1310 parents_recursion_limit)
1310 parents_recursion_limit)
1311 break
1311 break
1312
1312
1313 groups.insert(0, gr)
1313 groups.insert(0, gr)
1314 return groups
1314 return groups
1315
1315
1316 @property
1316 @property
1317 def children(self):
1317 def children(self):
1318 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1318 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1319
1319
1320 @property
1320 @property
1321 def name(self):
1321 def name(self):
1322 return self.group_name.split(RepoGroup.url_sep())[-1]
1322 return self.group_name.split(RepoGroup.url_sep())[-1]
1323
1323
1324 @property
1324 @property
1325 def full_path(self):
1325 def full_path(self):
1326 return self.group_name
1326 return self.group_name
1327
1327
1328 @property
1328 @property
1329 def full_path_splitted(self):
1329 def full_path_splitted(self):
1330 return self.group_name.split(RepoGroup.url_sep())
1330 return self.group_name.split(RepoGroup.url_sep())
1331
1331
1332 @property
1332 @property
1333 def repositories(self):
1333 def repositories(self):
1334 return Repository.query()\
1334 return Repository.query()\
1335 .filter(Repository.group == self)\
1335 .filter(Repository.group == self)\
1336 .order_by(Repository.repo_name)
1336 .order_by(Repository.repo_name)
1337
1337
1338 @property
1338 @property
1339 def repositories_recursive_count(self):
1339 def repositories_recursive_count(self):
1340 cnt = self.repositories.count()
1340 cnt = self.repositories.count()
1341
1341
1342 def children_count(group):
1342 def children_count(group):
1343 cnt = 0
1343 cnt = 0
1344 for child in group.children:
1344 for child in group.children:
1345 cnt += child.repositories.count()
1345 cnt += child.repositories.count()
1346 cnt += children_count(child)
1346 cnt += children_count(child)
1347 return cnt
1347 return cnt
1348
1348
1349 return cnt + children_count(self)
1349 return cnt + children_count(self)
1350
1350
1351 def _recursive_objects(self, include_repos=True):
1351 def _recursive_objects(self, include_repos=True):
1352 all_ = []
1352 all_ = []
1353
1353
1354 def _get_members(root_gr):
1354 def _get_members(root_gr):
1355 if include_repos:
1355 if include_repos:
1356 for r in root_gr.repositories:
1356 for r in root_gr.repositories:
1357 all_.append(r)
1357 all_.append(r)
1358 childs = root_gr.children.all()
1358 childs = root_gr.children.all()
1359 if childs:
1359 if childs:
1360 for gr in childs:
1360 for gr in childs:
1361 all_.append(gr)
1361 all_.append(gr)
1362 _get_members(gr)
1362 _get_members(gr)
1363
1363
1364 _get_members(self)
1364 _get_members(self)
1365 return [self] + all_
1365 return [self] + all_
1366
1366
1367 def recursive_groups_and_repos(self):
1367 def recursive_groups_and_repos(self):
1368 """
1368 """
1369 Recursive return all groups, with repositories in those groups
1369 Recursive return all groups, with repositories in those groups
1370 """
1370 """
1371 return self._recursive_objects()
1371 return self._recursive_objects()
1372
1372
1373 def recursive_groups(self):
1373 def recursive_groups(self):
1374 """
1374 """
1375 Returns all children groups for this group including children of children
1375 Returns all children groups for this group including children of children
1376 """
1376 """
1377 return self._recursive_objects(include_repos=False)
1377 return self._recursive_objects(include_repos=False)
1378
1378
1379 def get_new_name(self, group_name):
1379 def get_new_name(self, group_name):
1380 """
1380 """
1381 returns new full group name based on parent and new name
1381 returns new full group name based on parent and new name
1382
1382
1383 :param group_name:
1383 :param group_name:
1384 """
1384 """
1385 path_prefix = (self.parent_group.full_path_splitted if
1385 path_prefix = (self.parent_group.full_path_splitted if
1386 self.parent_group else [])
1386 self.parent_group else [])
1387 return RepoGroup.url_sep().join(path_prefix + [group_name])
1387 return RepoGroup.url_sep().join(path_prefix + [group_name])
1388
1388
1389
1389
1390 class Permission(Base, BaseModel):
1390 class Permission(Base, BaseModel):
1391 __tablename__ = 'permissions'
1391 __tablename__ = 'permissions'
1392 __table_args__ = (
1392 __table_args__ = (
1393 Index('p_perm_name_idx', 'permission_name'),
1393 Index('p_perm_name_idx', 'permission_name'),
1394 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1394 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1395 'mysql_charset': 'utf8'},
1395 'mysql_charset': 'utf8'},
1396 )
1396 )
1397 PERMS = [
1397 PERMS = [
1398 ('hg.admin', _('RhodeCode Administrator')),
1398 ('hg.admin', _('RhodeCode Administrator')),
1399
1399
1400 ('repository.none', _('Repository no access')),
1400 ('repository.none', _('Repository no access')),
1401 ('repository.read', _('Repository read access')),
1401 ('repository.read', _('Repository read access')),
1402 ('repository.write', _('Repository write access')),
1402 ('repository.write', _('Repository write access')),
1403 ('repository.admin', _('Repository admin access')),
1403 ('repository.admin', _('Repository admin access')),
1404
1404
1405 ('group.none', _('Repository group no access')),
1405 ('group.none', _('Repository group no access')),
1406 ('group.read', _('Repository group read access')),
1406 ('group.read', _('Repository group read access')),
1407 ('group.write', _('Repository group write access')),
1407 ('group.write', _('Repository group write access')),
1408 ('group.admin', _('Repository group admin access')),
1408 ('group.admin', _('Repository group admin access')),
1409
1409
1410 ('usergroup.none', _('User group no access')),
1410 ('usergroup.none', _('User group no access')),
1411 ('usergroup.read', _('User group read access')),
1411 ('usergroup.read', _('User group read access')),
1412 ('usergroup.write', _('User group write access')),
1412 ('usergroup.write', _('User group write access')),
1413 ('usergroup.admin', _('User group admin access')),
1413 ('usergroup.admin', _('User group admin access')),
1414
1414
1415 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
1415 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
1416 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
1416 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
1417
1417
1418 ('hg.usergroup.create.false', _('User Group creation disabled')),
1418 ('hg.usergroup.create.false', _('User Group creation disabled')),
1419 ('hg.usergroup.create.true', _('User Group creation enabled')),
1419 ('hg.usergroup.create.true', _('User Group creation enabled')),
1420
1420
1421 ('hg.create.none', _('Repository creation disabled')),
1421 ('hg.create.none', _('Repository creation disabled')),
1422 ('hg.create.repository', _('Repository creation enabled')),
1422 ('hg.create.repository', _('Repository creation enabled')),
1423
1423
1424 ('hg.fork.none', _('Repository forking disabled')),
1424 ('hg.fork.none', _('Repository forking disabled')),
1425 ('hg.fork.repository', _('Repository forking enabled')),
1425 ('hg.fork.repository', _('Repository forking enabled')),
1426
1426
1427 ('hg.register.none', _('Register disabled')),
1427 ('hg.register.none', _('Registration disabled')),
1428 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1428 ('hg.register.manual_activate', _('User Registration with manual account activation')),
1429 'with manual activation')),
1429 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
1430
1430
1431 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1431 ('hg.extern_activate.manual', _('Manual activation of external account')),
1432 'with auto activation')),
1432 ('hg.extern_activate.auto', _('Automatic activation of external account')),
1433
1433 ]
1434 ]
1434
1435
1435 #definition of system default permissions for DEFAULT user
1436 #definition of system default permissions for DEFAULT user
1436 DEFAULT_USER_PERMISSIONS = [
1437 DEFAULT_USER_PERMISSIONS = [
1437 'repository.read',
1438 'repository.read',
1438 'group.read',
1439 'group.read',
1439 'usergroup.read',
1440 'usergroup.read',
1440 'hg.create.repository',
1441 'hg.create.repository',
1441 'hg.fork.repository',
1442 'hg.fork.repository',
1442 'hg.register.manual_activate',
1443 'hg.register.manual_activate',
1444 'hg.extern_activate.auto',
1443 ]
1445 ]
1444
1446
1445 # defines which permissions are more important higher the more important
1447 # defines which permissions are more important higher the more important
1446 # Weight defines which permissions are more important.
1448 # Weight defines which permissions are more important.
1447 # The higher number the more important.
1449 # The higher number the more important.
1448 PERM_WEIGHTS = {
1450 PERM_WEIGHTS = {
1449 'repository.none': 0,
1451 'repository.none': 0,
1450 'repository.read': 1,
1452 'repository.read': 1,
1451 'repository.write': 3,
1453 'repository.write': 3,
1452 'repository.admin': 4,
1454 'repository.admin': 4,
1453
1455
1454 'group.none': 0,
1456 'group.none': 0,
1455 'group.read': 1,
1457 'group.read': 1,
1456 'group.write': 3,
1458 'group.write': 3,
1457 'group.admin': 4,
1459 'group.admin': 4,
1458
1460
1459 'usergroup.none': 0,
1461 'usergroup.none': 0,
1460 'usergroup.read': 1,
1462 'usergroup.read': 1,
1461 'usergroup.write': 3,
1463 'usergroup.write': 3,
1462 'usergroup.admin': 4,
1464 'usergroup.admin': 4,
1463 'hg.repogroup.create.false': 0,
1465 'hg.repogroup.create.false': 0,
1464 'hg.repogroup.create.true': 1,
1466 'hg.repogroup.create.true': 1,
1465
1467
1466 'hg.usergroup.create.false': 0,
1468 'hg.usergroup.create.false': 0,
1467 'hg.usergroup.create.true': 1,
1469 'hg.usergroup.create.true': 1,
1468
1470
1469 'hg.fork.none': 0,
1471 'hg.fork.none': 0,
1470 'hg.fork.repository': 1,
1472 'hg.fork.repository': 1,
1471 'hg.create.none': 0,
1473 'hg.create.none': 0,
1472 'hg.create.repository': 1
1474 'hg.create.repository': 1
1473 }
1475 }
1474
1476
1475 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1477 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1476 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1478 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1477 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1479 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1478
1480
1479 def __unicode__(self):
1481 def __unicode__(self):
1480 return u"<%s('%s:%s')>" % (
1482 return u"<%s('%s:%s')>" % (
1481 self.__class__.__name__, self.permission_id, self.permission_name
1483 self.__class__.__name__, self.permission_id, self.permission_name
1482 )
1484 )
1483
1485
1484 @classmethod
1486 @classmethod
1485 def get_by_key(cls, key):
1487 def get_by_key(cls, key):
1486 return cls.query().filter(cls.permission_name == key).scalar()
1488 return cls.query().filter(cls.permission_name == key).scalar()
1487
1489
1488 @classmethod
1490 @classmethod
1489 def get_default_perms(cls, default_user_id):
1491 def get_default_perms(cls, default_user_id):
1490 q = Session().query(UserRepoToPerm, Repository, cls)\
1492 q = Session().query(UserRepoToPerm, Repository, cls)\
1491 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1493 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1492 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1494 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1493 .filter(UserRepoToPerm.user_id == default_user_id)
1495 .filter(UserRepoToPerm.user_id == default_user_id)
1494
1496
1495 return q.all()
1497 return q.all()
1496
1498
1497 @classmethod
1499 @classmethod
1498 def get_default_group_perms(cls, default_user_id):
1500 def get_default_group_perms(cls, default_user_id):
1499 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1501 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1500 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1502 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1501 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1503 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1502 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1504 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1503
1505
1504 return q.all()
1506 return q.all()
1505
1507
1506 @classmethod
1508 @classmethod
1507 def get_default_user_group_perms(cls, default_user_id):
1509 def get_default_user_group_perms(cls, default_user_id):
1508 q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
1510 q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
1509 .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
1511 .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
1510 .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
1512 .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
1511 .filter(UserUserGroupToPerm.user_id == default_user_id)
1513 .filter(UserUserGroupToPerm.user_id == default_user_id)
1512
1514
1513 return q.all()
1515 return q.all()
1514
1516
1515
1517
1516 class UserRepoToPerm(Base, BaseModel):
1518 class UserRepoToPerm(Base, BaseModel):
1517 __tablename__ = 'repo_to_perm'
1519 __tablename__ = 'repo_to_perm'
1518 __table_args__ = (
1520 __table_args__ = (
1519 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1521 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1520 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1522 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1521 'mysql_charset': 'utf8'}
1523 'mysql_charset': 'utf8'}
1522 )
1524 )
1523 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1525 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1524 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1526 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1525 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1527 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1526 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1528 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1527
1529
1528 user = relationship('User')
1530 user = relationship('User')
1529 repository = relationship('Repository')
1531 repository = relationship('Repository')
1530 permission = relationship('Permission')
1532 permission = relationship('Permission')
1531
1533
1532 @classmethod
1534 @classmethod
1533 def create(cls, user, repository, permission):
1535 def create(cls, user, repository, permission):
1534 n = cls()
1536 n = cls()
1535 n.user = user
1537 n.user = user
1536 n.repository = repository
1538 n.repository = repository
1537 n.permission = permission
1539 n.permission = permission
1538 Session().add(n)
1540 Session().add(n)
1539 return n
1541 return n
1540
1542
1541 def __unicode__(self):
1543 def __unicode__(self):
1542 return u'<%s => %s >' % (self.user, self.repository)
1544 return u'<%s => %s >' % (self.user, self.repository)
1543
1545
1544
1546
1545 class UserUserGroupToPerm(Base, BaseModel):
1547 class UserUserGroupToPerm(Base, BaseModel):
1546 __tablename__ = 'user_user_group_to_perm'
1548 __tablename__ = 'user_user_group_to_perm'
1547 __table_args__ = (
1549 __table_args__ = (
1548 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
1550 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
1549 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1551 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1550 'mysql_charset': 'utf8'}
1552 'mysql_charset': 'utf8'}
1551 )
1553 )
1552 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1554 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1553 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1555 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1554 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1556 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1555 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1557 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1556
1558
1557 user = relationship('User')
1559 user = relationship('User')
1558 user_group = relationship('UserGroup')
1560 user_group = relationship('UserGroup')
1559 permission = relationship('Permission')
1561 permission = relationship('Permission')
1560
1562
1561 @classmethod
1563 @classmethod
1562 def create(cls, user, user_group, permission):
1564 def create(cls, user, user_group, permission):
1563 n = cls()
1565 n = cls()
1564 n.user = user
1566 n.user = user
1565 n.user_group = user_group
1567 n.user_group = user_group
1566 n.permission = permission
1568 n.permission = permission
1567 Session().add(n)
1569 Session().add(n)
1568 return n
1570 return n
1569
1571
1570 def __unicode__(self):
1572 def __unicode__(self):
1571 return u'<%s => %s >' % (self.user, self.user_group)
1573 return u'<%s => %s >' % (self.user, self.user_group)
1572
1574
1573
1575
1574 class UserToPerm(Base, BaseModel):
1576 class UserToPerm(Base, BaseModel):
1575 __tablename__ = 'user_to_perm'
1577 __tablename__ = 'user_to_perm'
1576 __table_args__ = (
1578 __table_args__ = (
1577 UniqueConstraint('user_id', 'permission_id'),
1579 UniqueConstraint('user_id', 'permission_id'),
1578 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1580 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1579 'mysql_charset': 'utf8'}
1581 'mysql_charset': 'utf8'}
1580 )
1582 )
1581 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1583 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1582 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1584 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1583 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1585 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1584
1586
1585 user = relationship('User')
1587 user = relationship('User')
1586 permission = relationship('Permission', lazy='joined')
1588 permission = relationship('Permission', lazy='joined')
1587
1589
1588 def __unicode__(self):
1590 def __unicode__(self):
1589 return u'<%s => %s >' % (self.user, self.permission)
1591 return u'<%s => %s >' % (self.user, self.permission)
1590
1592
1591
1593
1592 class UserGroupRepoToPerm(Base, BaseModel):
1594 class UserGroupRepoToPerm(Base, BaseModel):
1593 __tablename__ = 'users_group_repo_to_perm'
1595 __tablename__ = 'users_group_repo_to_perm'
1594 __table_args__ = (
1596 __table_args__ = (
1595 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1597 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1596 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1598 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1597 'mysql_charset': 'utf8'}
1599 'mysql_charset': 'utf8'}
1598 )
1600 )
1599 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1601 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1600 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1602 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1601 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1603 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1602 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1604 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1603
1605
1604 users_group = relationship('UserGroup')
1606 users_group = relationship('UserGroup')
1605 permission = relationship('Permission')
1607 permission = relationship('Permission')
1606 repository = relationship('Repository')
1608 repository = relationship('Repository')
1607
1609
1608 @classmethod
1610 @classmethod
1609 def create(cls, users_group, repository, permission):
1611 def create(cls, users_group, repository, permission):
1610 n = cls()
1612 n = cls()
1611 n.users_group = users_group
1613 n.users_group = users_group
1612 n.repository = repository
1614 n.repository = repository
1613 n.permission = permission
1615 n.permission = permission
1614 Session().add(n)
1616 Session().add(n)
1615 return n
1617 return n
1616
1618
1617 def __unicode__(self):
1619 def __unicode__(self):
1618 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1620 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1619
1621
1620
1622
1621 #TODO; not sure if this will be ever used
1623 #TODO; not sure if this will be ever used
1622 class UserGroupUserGroupToPerm(Base, BaseModel):
1624 class UserGroupUserGroupToPerm(Base, BaseModel):
1623 __tablename__ = 'user_group_user_group_to_perm'
1625 __tablename__ = 'user_group_user_group_to_perm'
1624 __table_args__ = (
1626 __table_args__ = (
1625 UniqueConstraint('user_group_id', 'user_group_id', 'permission_id'),
1627 UniqueConstraint('user_group_id', 'user_group_id', 'permission_id'),
1626 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1628 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1627 'mysql_charset': 'utf8'}
1629 'mysql_charset': 'utf8'}
1628 )
1630 )
1629 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1631 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1630 target_user_group_id = Column("target_users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1632 target_user_group_id = Column("target_users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1631 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1633 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1632 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1634 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1633
1635
1634 target_user_group = relationship('UserGroup', remote_side=target_user_group_id, primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
1636 target_user_group = relationship('UserGroup', remote_side=target_user_group_id, primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
1635 user_group = relationship('UserGroup', remote_side=user_group_id, primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
1637 user_group = relationship('UserGroup', remote_side=user_group_id, primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
1636 permission = relationship('Permission')
1638 permission = relationship('Permission')
1637
1639
1638 @classmethod
1640 @classmethod
1639 def create(cls, target_user_group, user_group, permission):
1641 def create(cls, target_user_group, user_group, permission):
1640 n = cls()
1642 n = cls()
1641 n.target_user_group = target_user_group
1643 n.target_user_group = target_user_group
1642 n.user_group = user_group
1644 n.user_group = user_group
1643 n.permission = permission
1645 n.permission = permission
1644 Session().add(n)
1646 Session().add(n)
1645 return n
1647 return n
1646
1648
1647 def __unicode__(self):
1649 def __unicode__(self):
1648 return u'<UserGroup:%s => %s >' % (self.target_user_group, self.user_group)
1650 return u'<UserGroup:%s => %s >' % (self.target_user_group, self.user_group)
1649
1651
1650
1652
1651 class UserGroupToPerm(Base, BaseModel):
1653 class UserGroupToPerm(Base, BaseModel):
1652 __tablename__ = 'users_group_to_perm'
1654 __tablename__ = 'users_group_to_perm'
1653 __table_args__ = (
1655 __table_args__ = (
1654 UniqueConstraint('users_group_id', 'permission_id',),
1656 UniqueConstraint('users_group_id', 'permission_id',),
1655 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1657 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1656 'mysql_charset': 'utf8'}
1658 'mysql_charset': 'utf8'}
1657 )
1659 )
1658 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1660 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1659 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1661 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1660 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1662 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1661
1663
1662 users_group = relationship('UserGroup')
1664 users_group = relationship('UserGroup')
1663 permission = relationship('Permission')
1665 permission = relationship('Permission')
1664
1666
1665
1667
1666 class UserRepoGroupToPerm(Base, BaseModel):
1668 class UserRepoGroupToPerm(Base, BaseModel):
1667 __tablename__ = 'user_repo_group_to_perm'
1669 __tablename__ = 'user_repo_group_to_perm'
1668 __table_args__ = (
1670 __table_args__ = (
1669 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1671 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1670 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1672 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1671 'mysql_charset': 'utf8'}
1673 'mysql_charset': 'utf8'}
1672 )
1674 )
1673
1675
1674 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1676 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1675 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1677 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1676 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1678 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1677 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1679 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1678
1680
1679 user = relationship('User')
1681 user = relationship('User')
1680 group = relationship('RepoGroup')
1682 group = relationship('RepoGroup')
1681 permission = relationship('Permission')
1683 permission = relationship('Permission')
1682
1684
1683
1685
1684 class UserGroupRepoGroupToPerm(Base, BaseModel):
1686 class UserGroupRepoGroupToPerm(Base, BaseModel):
1685 __tablename__ = 'users_group_repo_group_to_perm'
1687 __tablename__ = 'users_group_repo_group_to_perm'
1686 __table_args__ = (
1688 __table_args__ = (
1687 UniqueConstraint('users_group_id', 'group_id'),
1689 UniqueConstraint('users_group_id', 'group_id'),
1688 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1690 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1689 'mysql_charset': 'utf8'}
1691 'mysql_charset': 'utf8'}
1690 )
1692 )
1691
1693
1692 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1694 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1693 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1695 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1694 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1696 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1695 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1697 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1696
1698
1697 users_group = relationship('UserGroup')
1699 users_group = relationship('UserGroup')
1698 permission = relationship('Permission')
1700 permission = relationship('Permission')
1699 group = relationship('RepoGroup')
1701 group = relationship('RepoGroup')
1700
1702
1701
1703
1702 class Statistics(Base, BaseModel):
1704 class Statistics(Base, BaseModel):
1703 __tablename__ = 'statistics'
1705 __tablename__ = 'statistics'
1704 __table_args__ = (
1706 __table_args__ = (
1705 UniqueConstraint('repository_id'),
1707 UniqueConstraint('repository_id'),
1706 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1708 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1707 'mysql_charset': 'utf8'}
1709 'mysql_charset': 'utf8'}
1708 )
1710 )
1709 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1711 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1710 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1712 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1711 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1713 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1712 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1714 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1713 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1715 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1714 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1716 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1715
1717
1716 repository = relationship('Repository', single_parent=True)
1718 repository = relationship('Repository', single_parent=True)
1717
1719
1718
1720
1719 class UserFollowing(Base, BaseModel):
1721 class UserFollowing(Base, BaseModel):
1720 __tablename__ = 'user_followings'
1722 __tablename__ = 'user_followings'
1721 __table_args__ = (
1723 __table_args__ = (
1722 UniqueConstraint('user_id', 'follows_repository_id'),
1724 UniqueConstraint('user_id', 'follows_repository_id'),
1723 UniqueConstraint('user_id', 'follows_user_id'),
1725 UniqueConstraint('user_id', 'follows_user_id'),
1724 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1726 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1725 'mysql_charset': 'utf8'}
1727 'mysql_charset': 'utf8'}
1726 )
1728 )
1727
1729
1728 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1730 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1729 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1731 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1730 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1732 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1731 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1733 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1732 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1734 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1733
1735
1734 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1736 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1735
1737
1736 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1738 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1737 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1739 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1738
1740
1739 @classmethod
1741 @classmethod
1740 def get_repo_followers(cls, repo_id):
1742 def get_repo_followers(cls, repo_id):
1741 return cls.query().filter(cls.follows_repo_id == repo_id)
1743 return cls.query().filter(cls.follows_repo_id == repo_id)
1742
1744
1743
1745
1744 class CacheInvalidation(Base, BaseModel):
1746 class CacheInvalidation(Base, BaseModel):
1745 __tablename__ = 'cache_invalidation'
1747 __tablename__ = 'cache_invalidation'
1746 __table_args__ = (
1748 __table_args__ = (
1747 UniqueConstraint('cache_key'),
1749 UniqueConstraint('cache_key'),
1748 Index('key_idx', 'cache_key'),
1750 Index('key_idx', 'cache_key'),
1749 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1751 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1750 'mysql_charset': 'utf8'},
1752 'mysql_charset': 'utf8'},
1751 )
1753 )
1752 # cache_id, not used
1754 # cache_id, not used
1753 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1755 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1754 # cache_key as created by _get_cache_key
1756 # cache_key as created by _get_cache_key
1755 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1757 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1756 # cache_args is a repo_name
1758 # cache_args is a repo_name
1757 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1759 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1758 # instance sets cache_active True when it is caching,
1760 # instance sets cache_active True when it is caching,
1759 # other instances set cache_active to False to indicate that this cache is invalid
1761 # other instances set cache_active to False to indicate that this cache is invalid
1760 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1762 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1761
1763
1762 def __init__(self, cache_key, repo_name=''):
1764 def __init__(self, cache_key, repo_name=''):
1763 self.cache_key = cache_key
1765 self.cache_key = cache_key
1764 self.cache_args = repo_name
1766 self.cache_args = repo_name
1765 self.cache_active = False
1767 self.cache_active = False
1766
1768
1767 def __unicode__(self):
1769 def __unicode__(self):
1768 return u"<%s('%s:%s[%s]')>" % (self.__class__.__name__,
1770 return u"<%s('%s:%s[%s]')>" % (self.__class__.__name__,
1769 self.cache_id, self.cache_key, self.cache_active)
1771 self.cache_id, self.cache_key, self.cache_active)
1770
1772
1771 def _cache_key_partition(self):
1773 def _cache_key_partition(self):
1772 prefix, repo_name, suffix = self.cache_key.partition(self.cache_args)
1774 prefix, repo_name, suffix = self.cache_key.partition(self.cache_args)
1773 return prefix, repo_name, suffix
1775 return prefix, repo_name, suffix
1774
1776
1775 def get_prefix(self):
1777 def get_prefix(self):
1776 """
1778 """
1777 get prefix that might have been used in _get_cache_key to
1779 get prefix that might have been used in _get_cache_key to
1778 generate self.cache_key. Only used for informational purposes
1780 generate self.cache_key. Only used for informational purposes
1779 in repo_edit.html.
1781 in repo_edit.html.
1780 """
1782 """
1781 # prefix, repo_name, suffix
1783 # prefix, repo_name, suffix
1782 return self._cache_key_partition()[0]
1784 return self._cache_key_partition()[0]
1783
1785
1784 def get_suffix(self):
1786 def get_suffix(self):
1785 """
1787 """
1786 get suffix that might have been used in _get_cache_key to
1788 get suffix that might have been used in _get_cache_key to
1787 generate self.cache_key. Only used for informational purposes
1789 generate self.cache_key. Only used for informational purposes
1788 in repo_edit.html.
1790 in repo_edit.html.
1789 """
1791 """
1790 # prefix, repo_name, suffix
1792 # prefix, repo_name, suffix
1791 return self._cache_key_partition()[2]
1793 return self._cache_key_partition()[2]
1792
1794
1793 @classmethod
1795 @classmethod
1794 def clear_cache(cls):
1796 def clear_cache(cls):
1795 """
1797 """
1796 Delete all cache keys from database.
1798 Delete all cache keys from database.
1797 Should only be run when all instances are down and all entries thus stale.
1799 Should only be run when all instances are down and all entries thus stale.
1798 """
1800 """
1799 cls.query().delete()
1801 cls.query().delete()
1800 Session().commit()
1802 Session().commit()
1801
1803
1802 @classmethod
1804 @classmethod
1803 def _get_cache_key(cls, key):
1805 def _get_cache_key(cls, key):
1804 """
1806 """
1805 Wrapper for generating a unique cache key for this instance and "key".
1807 Wrapper for generating a unique cache key for this instance and "key".
1806 key must / will start with a repo_name which will be stored in .cache_args .
1808 key must / will start with a repo_name which will be stored in .cache_args .
1807 """
1809 """
1808 import rhodecode
1810 import rhodecode
1809 prefix = rhodecode.CONFIG.get('instance_id', '')
1811 prefix = rhodecode.CONFIG.get('instance_id', '')
1810 return "%s%s" % (prefix, key)
1812 return "%s%s" % (prefix, key)
1811
1813
1812 @classmethod
1814 @classmethod
1813 def set_invalidate(cls, repo_name):
1815 def set_invalidate(cls, repo_name):
1814 """
1816 """
1815 Mark all caches of a repo as invalid in the database.
1817 Mark all caches of a repo as invalid in the database.
1816 """
1818 """
1817 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1819 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1818
1820
1819 try:
1821 try:
1820 for inv_obj in inv_objs:
1822 for inv_obj in inv_objs:
1821 log.debug('marking %s key for invalidation based on repo_name=%s'
1823 log.debug('marking %s key for invalidation based on repo_name=%s'
1822 % (inv_obj, safe_str(repo_name)))
1824 % (inv_obj, safe_str(repo_name)))
1823 inv_obj.cache_active = False
1825 inv_obj.cache_active = False
1824 Session().add(inv_obj)
1826 Session().add(inv_obj)
1825 Session().commit()
1827 Session().commit()
1826 except Exception:
1828 except Exception:
1827 log.error(traceback.format_exc())
1829 log.error(traceback.format_exc())
1828 Session().rollback()
1830 Session().rollback()
1829
1831
1830 @classmethod
1832 @classmethod
1831 def test_and_set_valid(cls, repo_name, kind, valid_cache_keys=None):
1833 def test_and_set_valid(cls, repo_name, kind, valid_cache_keys=None):
1832 """
1834 """
1833 Mark this cache key as active and currently cached.
1835 Mark this cache key as active and currently cached.
1834 Return True if the existing cache registration still was valid.
1836 Return True if the existing cache registration still was valid.
1835 Return False to indicate that it had been invalidated and caches should be refreshed.
1837 Return False to indicate that it had been invalidated and caches should be refreshed.
1836 """
1838 """
1837
1839
1838 key = (repo_name + '_' + kind) if kind else repo_name
1840 key = (repo_name + '_' + kind) if kind else repo_name
1839 cache_key = cls._get_cache_key(key)
1841 cache_key = cls._get_cache_key(key)
1840
1842
1841 if valid_cache_keys and cache_key in valid_cache_keys:
1843 if valid_cache_keys and cache_key in valid_cache_keys:
1842 return True
1844 return True
1843
1845
1844 try:
1846 try:
1845 inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
1847 inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
1846 if not inv_obj:
1848 if not inv_obj:
1847 inv_obj = CacheInvalidation(cache_key, repo_name)
1849 inv_obj = CacheInvalidation(cache_key, repo_name)
1848 was_valid = inv_obj.cache_active
1850 was_valid = inv_obj.cache_active
1849 inv_obj.cache_active = True
1851 inv_obj.cache_active = True
1850 Session().add(inv_obj)
1852 Session().add(inv_obj)
1851 Session().commit()
1853 Session().commit()
1852 return was_valid
1854 return was_valid
1853 except Exception:
1855 except Exception:
1854 log.error(traceback.format_exc())
1856 log.error(traceback.format_exc())
1855 Session().rollback()
1857 Session().rollback()
1856 return False
1858 return False
1857
1859
1858 @classmethod
1860 @classmethod
1859 def get_valid_cache_keys(cls):
1861 def get_valid_cache_keys(cls):
1860 """
1862 """
1861 Return opaque object with information of which caches still are valid
1863 Return opaque object with information of which caches still are valid
1862 and can be used without checking for invalidation.
1864 and can be used without checking for invalidation.
1863 """
1865 """
1864 return set(inv_obj.cache_key for inv_obj in cls.query().filter(cls.cache_active).all())
1866 return set(inv_obj.cache_key for inv_obj in cls.query().filter(cls.cache_active).all())
1865
1867
1866
1868
1867 class ChangesetComment(Base, BaseModel):
1869 class ChangesetComment(Base, BaseModel):
1868 __tablename__ = 'changeset_comments'
1870 __tablename__ = 'changeset_comments'
1869 __table_args__ = (
1871 __table_args__ = (
1870 Index('cc_revision_idx', 'revision'),
1872 Index('cc_revision_idx', 'revision'),
1871 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1873 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1872 'mysql_charset': 'utf8'},
1874 'mysql_charset': 'utf8'},
1873 )
1875 )
1874 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1876 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1875 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1877 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1876 revision = Column('revision', String(40), nullable=True)
1878 revision = Column('revision', String(40), nullable=True)
1877 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1879 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1878 line_no = Column('line_no', Unicode(10), nullable=True)
1880 line_no = Column('line_no', Unicode(10), nullable=True)
1879 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1881 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1880 f_path = Column('f_path', Unicode(1000), nullable=True)
1882 f_path = Column('f_path', Unicode(1000), nullable=True)
1881 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1883 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1882 text = Column('text', UnicodeText(25000), nullable=False)
1884 text = Column('text', UnicodeText(25000), nullable=False)
1883 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1885 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1884 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1886 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1885
1887
1886 author = relationship('User', lazy='joined')
1888 author = relationship('User', lazy='joined')
1887 repo = relationship('Repository')
1889 repo = relationship('Repository')
1888 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1890 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1889 pull_request = relationship('PullRequest', lazy='joined')
1891 pull_request = relationship('PullRequest', lazy='joined')
1890
1892
1891 @classmethod
1893 @classmethod
1892 def get_users(cls, revision=None, pull_request_id=None):
1894 def get_users(cls, revision=None, pull_request_id=None):
1893 """
1895 """
1894 Returns user associated with this ChangesetComment. ie those
1896 Returns user associated with this ChangesetComment. ie those
1895 who actually commented
1897 who actually commented
1896
1898
1897 :param cls:
1899 :param cls:
1898 :param revision:
1900 :param revision:
1899 """
1901 """
1900 q = Session().query(User)\
1902 q = Session().query(User)\
1901 .join(ChangesetComment.author)
1903 .join(ChangesetComment.author)
1902 if revision:
1904 if revision:
1903 q = q.filter(cls.revision == revision)
1905 q = q.filter(cls.revision == revision)
1904 elif pull_request_id:
1906 elif pull_request_id:
1905 q = q.filter(cls.pull_request_id == pull_request_id)
1907 q = q.filter(cls.pull_request_id == pull_request_id)
1906 return q.all()
1908 return q.all()
1907
1909
1908
1910
1909 class ChangesetStatus(Base, BaseModel):
1911 class ChangesetStatus(Base, BaseModel):
1910 __tablename__ = 'changeset_statuses'
1912 __tablename__ = 'changeset_statuses'
1911 __table_args__ = (
1913 __table_args__ = (
1912 Index('cs_revision_idx', 'revision'),
1914 Index('cs_revision_idx', 'revision'),
1913 Index('cs_version_idx', 'version'),
1915 Index('cs_version_idx', 'version'),
1914 UniqueConstraint('repo_id', 'revision', 'version'),
1916 UniqueConstraint('repo_id', 'revision', 'version'),
1915 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1917 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1916 'mysql_charset': 'utf8'}
1918 'mysql_charset': 'utf8'}
1917 )
1919 )
1918 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1920 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1919 STATUS_APPROVED = 'approved'
1921 STATUS_APPROVED = 'approved'
1920 STATUS_REJECTED = 'rejected'
1922 STATUS_REJECTED = 'rejected'
1921 STATUS_UNDER_REVIEW = 'under_review'
1923 STATUS_UNDER_REVIEW = 'under_review'
1922
1924
1923 STATUSES = [
1925 STATUSES = [
1924 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1926 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1925 (STATUS_APPROVED, _("Approved")),
1927 (STATUS_APPROVED, _("Approved")),
1926 (STATUS_REJECTED, _("Rejected")),
1928 (STATUS_REJECTED, _("Rejected")),
1927 (STATUS_UNDER_REVIEW, _("Under Review")),
1929 (STATUS_UNDER_REVIEW, _("Under Review")),
1928 ]
1930 ]
1929
1931
1930 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1932 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1931 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1933 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1932 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1934 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1933 revision = Column('revision', String(40), nullable=False)
1935 revision = Column('revision', String(40), nullable=False)
1934 status = Column('status', String(128), nullable=False, default=DEFAULT)
1936 status = Column('status', String(128), nullable=False, default=DEFAULT)
1935 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1937 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1936 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1938 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1937 version = Column('version', Integer(), nullable=False, default=0)
1939 version = Column('version', Integer(), nullable=False, default=0)
1938 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1940 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1939
1941
1940 author = relationship('User', lazy='joined')
1942 author = relationship('User', lazy='joined')
1941 repo = relationship('Repository')
1943 repo = relationship('Repository')
1942 comment = relationship('ChangesetComment', lazy='joined')
1944 comment = relationship('ChangesetComment', lazy='joined')
1943 pull_request = relationship('PullRequest', lazy='joined')
1945 pull_request = relationship('PullRequest', lazy='joined')
1944
1946
1945 def __unicode__(self):
1947 def __unicode__(self):
1946 return u"<%s('%s:%s')>" % (
1948 return u"<%s('%s:%s')>" % (
1947 self.__class__.__name__,
1949 self.__class__.__name__,
1948 self.status, self.author
1950 self.status, self.author
1949 )
1951 )
1950
1952
1951 @classmethod
1953 @classmethod
1952 def get_status_lbl(cls, value):
1954 def get_status_lbl(cls, value):
1953 return dict(cls.STATUSES).get(value)
1955 return dict(cls.STATUSES).get(value)
1954
1956
1955 @property
1957 @property
1956 def status_lbl(self):
1958 def status_lbl(self):
1957 return ChangesetStatus.get_status_lbl(self.status)
1959 return ChangesetStatus.get_status_lbl(self.status)
1958
1960
1959
1961
1960 class PullRequest(Base, BaseModel):
1962 class PullRequest(Base, BaseModel):
1961 __tablename__ = 'pull_requests'
1963 __tablename__ = 'pull_requests'
1962 __table_args__ = (
1964 __table_args__ = (
1963 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1965 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1964 'mysql_charset': 'utf8'},
1966 'mysql_charset': 'utf8'},
1965 )
1967 )
1966
1968
1967 STATUS_NEW = u'new'
1969 STATUS_NEW = u'new'
1968 STATUS_OPEN = u'open'
1970 STATUS_OPEN = u'open'
1969 STATUS_CLOSED = u'closed'
1971 STATUS_CLOSED = u'closed'
1970
1972
1971 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1973 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1972 title = Column('title', Unicode(256), nullable=True)
1974 title = Column('title', Unicode(256), nullable=True)
1973 description = Column('description', UnicodeText(10240), nullable=True)
1975 description = Column('description', UnicodeText(10240), nullable=True)
1974 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1976 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1975 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1977 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1976 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1978 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1977 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1979 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1978 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1980 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1979 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1981 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1980 org_ref = Column('org_ref', Unicode(256), nullable=False)
1982 org_ref = Column('org_ref', Unicode(256), nullable=False)
1981 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1983 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1982 other_ref = Column('other_ref', Unicode(256), nullable=False)
1984 other_ref = Column('other_ref', Unicode(256), nullable=False)
1983
1985
1984 @hybrid_property
1986 @hybrid_property
1985 def revisions(self):
1987 def revisions(self):
1986 return self._revisions.split(':')
1988 return self._revisions.split(':')
1987
1989
1988 @revisions.setter
1990 @revisions.setter
1989 def revisions(self, val):
1991 def revisions(self, val):
1990 self._revisions = ':'.join(val)
1992 self._revisions = ':'.join(val)
1991
1993
1992 @property
1994 @property
1993 def org_ref_parts(self):
1995 def org_ref_parts(self):
1994 return self.org_ref.split(':')
1996 return self.org_ref.split(':')
1995
1997
1996 @property
1998 @property
1997 def other_ref_parts(self):
1999 def other_ref_parts(self):
1998 return self.other_ref.split(':')
2000 return self.other_ref.split(':')
1999
2001
2000 author = relationship('User', lazy='joined')
2002 author = relationship('User', lazy='joined')
2001 reviewers = relationship('PullRequestReviewers',
2003 reviewers = relationship('PullRequestReviewers',
2002 cascade="all, delete, delete-orphan")
2004 cascade="all, delete, delete-orphan")
2003 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
2005 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
2004 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
2006 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
2005 statuses = relationship('ChangesetStatus')
2007 statuses = relationship('ChangesetStatus')
2006 comments = relationship('ChangesetComment',
2008 comments = relationship('ChangesetComment',
2007 cascade="all, delete, delete-orphan")
2009 cascade="all, delete, delete-orphan")
2008
2010
2009 def is_closed(self):
2011 def is_closed(self):
2010 return self.status == self.STATUS_CLOSED
2012 return self.status == self.STATUS_CLOSED
2011
2013
2012 @property
2014 @property
2013 def last_review_status(self):
2015 def last_review_status(self):
2014 return self.statuses[-1].status if self.statuses else ''
2016 return self.statuses[-1].status if self.statuses else ''
2015
2017
2016 def __json__(self):
2018 def __json__(self):
2017 return dict(
2019 return dict(
2018 revisions=self.revisions
2020 revisions=self.revisions
2019 )
2021 )
2020
2022
2021
2023
2022 class PullRequestReviewers(Base, BaseModel):
2024 class PullRequestReviewers(Base, BaseModel):
2023 __tablename__ = 'pull_request_reviewers'
2025 __tablename__ = 'pull_request_reviewers'
2024 __table_args__ = (
2026 __table_args__ = (
2025 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2027 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2026 'mysql_charset': 'utf8'},
2028 'mysql_charset': 'utf8'},
2027 )
2029 )
2028
2030
2029 def __init__(self, user=None, pull_request=None):
2031 def __init__(self, user=None, pull_request=None):
2030 self.user = user
2032 self.user = user
2031 self.pull_request = pull_request
2033 self.pull_request = pull_request
2032
2034
2033 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
2035 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
2034 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
2036 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
2035 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
2037 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
2036
2038
2037 user = relationship('User')
2039 user = relationship('User')
2038 pull_request = relationship('PullRequest')
2040 pull_request = relationship('PullRequest')
2039
2041
2040
2042
2041 class Notification(Base, BaseModel):
2043 class Notification(Base, BaseModel):
2042 __tablename__ = 'notifications'
2044 __tablename__ = 'notifications'
2043 __table_args__ = (
2045 __table_args__ = (
2044 Index('notification_type_idx', 'type'),
2046 Index('notification_type_idx', 'type'),
2045 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2047 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2046 'mysql_charset': 'utf8'},
2048 'mysql_charset': 'utf8'},
2047 )
2049 )
2048
2050
2049 TYPE_CHANGESET_COMMENT = u'cs_comment'
2051 TYPE_CHANGESET_COMMENT = u'cs_comment'
2050 TYPE_MESSAGE = u'message'
2052 TYPE_MESSAGE = u'message'
2051 TYPE_MENTION = u'mention'
2053 TYPE_MENTION = u'mention'
2052 TYPE_REGISTRATION = u'registration'
2054 TYPE_REGISTRATION = u'registration'
2053 TYPE_PULL_REQUEST = u'pull_request'
2055 TYPE_PULL_REQUEST = u'pull_request'
2054 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
2056 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
2055
2057
2056 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
2058 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
2057 subject = Column('subject', Unicode(512), nullable=True)
2059 subject = Column('subject', Unicode(512), nullable=True)
2058 body = Column('body', UnicodeText(50000), nullable=True)
2060 body = Column('body', UnicodeText(50000), nullable=True)
2059 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
2061 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
2060 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2062 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2061 type_ = Column('type', Unicode(256))
2063 type_ = Column('type', Unicode(256))
2062
2064
2063 created_by_user = relationship('User')
2065 created_by_user = relationship('User')
2064 notifications_to_users = relationship('UserNotification', lazy='joined',
2066 notifications_to_users = relationship('UserNotification', lazy='joined',
2065 cascade="all, delete, delete-orphan")
2067 cascade="all, delete, delete-orphan")
2066
2068
2067 @property
2069 @property
2068 def recipients(self):
2070 def recipients(self):
2069 return [x.user for x in UserNotification.query()\
2071 return [x.user for x in UserNotification.query()\
2070 .filter(UserNotification.notification == self)\
2072 .filter(UserNotification.notification == self)\
2071 .order_by(UserNotification.user_id.asc()).all()]
2073 .order_by(UserNotification.user_id.asc()).all()]
2072
2074
2073 @classmethod
2075 @classmethod
2074 def create(cls, created_by, subject, body, recipients, type_=None):
2076 def create(cls, created_by, subject, body, recipients, type_=None):
2075 if type_ is None:
2077 if type_ is None:
2076 type_ = Notification.TYPE_MESSAGE
2078 type_ = Notification.TYPE_MESSAGE
2077
2079
2078 notification = cls()
2080 notification = cls()
2079 notification.created_by_user = created_by
2081 notification.created_by_user = created_by
2080 notification.subject = subject
2082 notification.subject = subject
2081 notification.body = body
2083 notification.body = body
2082 notification.type_ = type_
2084 notification.type_ = type_
2083 notification.created_on = datetime.datetime.now()
2085 notification.created_on = datetime.datetime.now()
2084
2086
2085 for u in recipients:
2087 for u in recipients:
2086 assoc = UserNotification()
2088 assoc = UserNotification()
2087 assoc.notification = notification
2089 assoc.notification = notification
2088 u.notifications.append(assoc)
2090 u.notifications.append(assoc)
2089 Session().add(notification)
2091 Session().add(notification)
2090 return notification
2092 return notification
2091
2093
2092 @property
2094 @property
2093 def description(self):
2095 def description(self):
2094 from rhodecode.model.notification import NotificationModel
2096 from rhodecode.model.notification import NotificationModel
2095 return NotificationModel().make_description(self)
2097 return NotificationModel().make_description(self)
2096
2098
2097
2099
2098 class UserNotification(Base, BaseModel):
2100 class UserNotification(Base, BaseModel):
2099 __tablename__ = 'user_to_notification'
2101 __tablename__ = 'user_to_notification'
2100 __table_args__ = (
2102 __table_args__ = (
2101 UniqueConstraint('user_id', 'notification_id'),
2103 UniqueConstraint('user_id', 'notification_id'),
2102 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2104 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2103 'mysql_charset': 'utf8'}
2105 'mysql_charset': 'utf8'}
2104 )
2106 )
2105 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
2107 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
2106 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
2108 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
2107 read = Column('read', Boolean, default=False)
2109 read = Column('read', Boolean, default=False)
2108 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
2110 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
2109
2111
2110 user = relationship('User', lazy="joined")
2112 user = relationship('User', lazy="joined")
2111 notification = relationship('Notification', lazy="joined",
2113 notification = relationship('Notification', lazy="joined",
2112 order_by=lambda: Notification.created_on.desc(),)
2114 order_by=lambda: Notification.created_on.desc(),)
2113
2115
2114 def mark_as_read(self):
2116 def mark_as_read(self):
2115 self.read = True
2117 self.read = True
2116 Session().add(self)
2118 Session().add(self)
2117
2119
2118
2120
2119 class DbMigrateVersion(Base, BaseModel):
2121 class DbMigrateVersion(Base, BaseModel):
2120 __tablename__ = 'db_migrate_version'
2122 __tablename__ = 'db_migrate_version'
2121 __table_args__ = (
2123 __table_args__ = (
2122 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2124 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2123 'mysql_charset': 'utf8'},
2125 'mysql_charset': 'utf8'},
2124 )
2126 )
2125 repository_id = Column('repository_id', String(250), primary_key=True)
2127 repository_id = Column('repository_id', String(250), primary_key=True)
2126 repository_path = Column('repository_path', Text)
2128 repository_path = Column('repository_path', Text)
2127 version = Column('version', Integer)
2129 version = Column('version', Integer)
@@ -1,420 +1,421 b''
1 """ this is forms validation classes
1 """ this is forms validation classes
2 http://formencode.org/module-formencode.validators.html
2 http://formencode.org/module-formencode.validators.html
3 for list off all availible validators
3 for list off all availible validators
4
4
5 we can create our own validators
5 we can create our own validators
6
6
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 pre_validators [] These validators will be applied before the schema
8 pre_validators [] These validators will be applied before the schema
9 chained_validators [] These validators will be applied after the schema
9 chained_validators [] These validators will be applied after the schema
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 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.
12 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.
13 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
13 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
14
14
15
15
16 <name> = formencode.validators.<name of validator>
16 <name> = formencode.validators.<name of validator>
17 <name> must equal form name
17 <name> must equal form name
18 list=[1,2,3,4,5]
18 list=[1,2,3,4,5]
19 for SELECT use formencode.All(OneOf(list), Int())
19 for SELECT use formencode.All(OneOf(list), Int())
20
20
21 """
21 """
22 import logging
22 import logging
23
23
24 import formencode
24 import formencode
25 from formencode import All
25 from formencode import All
26
26
27 from pylons.i18n.translation import _
27 from pylons.i18n.translation import _
28
28
29 from rhodecode.model import validators as v
29 from rhodecode.model import validators as v
30 from rhodecode import BACKENDS
30 from rhodecode import BACKENDS
31
31
32 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
33
33
34
34
35 class LoginForm(formencode.Schema):
35 class LoginForm(formencode.Schema):
36 allow_extra_fields = True
36 allow_extra_fields = True
37 filter_extra_fields = True
37 filter_extra_fields = True
38 username = v.UnicodeString(
38 username = v.UnicodeString(
39 strip=True,
39 strip=True,
40 min=1,
40 min=1,
41 not_empty=True,
41 not_empty=True,
42 messages={
42 messages={
43 'empty': _(u'Please enter a login'),
43 'empty': _(u'Please enter a login'),
44 'tooShort': _(u'Enter a value %(min)i characters long or more')}
44 'tooShort': _(u'Enter a value %(min)i characters long or more')}
45 )
45 )
46
46
47 password = v.UnicodeString(
47 password = v.UnicodeString(
48 strip=False,
48 strip=False,
49 min=3,
49 min=3,
50 not_empty=True,
50 not_empty=True,
51 messages={
51 messages={
52 'empty': _(u'Please enter a password'),
52 'empty': _(u'Please enter a password'),
53 'tooShort': _(u'Enter %(min)i characters or more')}
53 'tooShort': _(u'Enter %(min)i characters or more')}
54 )
54 )
55
55
56 remember = v.StringBoolean(if_missing=False)
56 remember = v.StringBoolean(if_missing=False)
57
57
58 chained_validators = [v.ValidAuth()]
58 chained_validators = [v.ValidAuth()]
59
59
60
60
61 def UserForm(edit=False, old_data={}):
61 def UserForm(edit=False, old_data={}):
62 class _UserForm(formencode.Schema):
62 class _UserForm(formencode.Schema):
63 allow_extra_fields = True
63 allow_extra_fields = True
64 filter_extra_fields = True
64 filter_extra_fields = True
65 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
65 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
66 v.ValidUsername(edit, old_data))
66 v.ValidUsername(edit, old_data))
67 if edit:
67 if edit:
68 new_password = All(
68 new_password = All(
69 v.ValidPassword(),
69 v.ValidPassword(),
70 v.UnicodeString(strip=False, min=6, not_empty=False)
70 v.UnicodeString(strip=False, min=6, not_empty=False)
71 )
71 )
72 password_confirmation = All(
72 password_confirmation = All(
73 v.ValidPassword(),
73 v.ValidPassword(),
74 v.UnicodeString(strip=False, min=6, not_empty=False),
74 v.UnicodeString(strip=False, min=6, not_empty=False),
75 )
75 )
76 admin = v.StringBoolean(if_missing=False)
76 admin = v.StringBoolean(if_missing=False)
77 else:
77 else:
78 password = All(
78 password = All(
79 v.ValidPassword(),
79 v.ValidPassword(),
80 v.UnicodeString(strip=False, min=6, not_empty=True)
80 v.UnicodeString(strip=False, min=6, not_empty=True)
81 )
81 )
82 password_confirmation = All(
82 password_confirmation = All(
83 v.ValidPassword(),
83 v.ValidPassword(),
84 v.UnicodeString(strip=False, min=6, not_empty=False)
84 v.UnicodeString(strip=False, min=6, not_empty=False)
85 )
85 )
86
86
87 active = v.StringBoolean(if_missing=False)
87 active = v.StringBoolean(if_missing=False)
88 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
88 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
89 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
89 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
90 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
90 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
91
91
92 chained_validators = [v.ValidPasswordsMatch()]
92 chained_validators = [v.ValidPasswordsMatch()]
93
93
94 return _UserForm
94 return _UserForm
95
95
96
96
97 def UserGroupForm(edit=False, old_data={}, available_members=[]):
97 def UserGroupForm(edit=False, old_data={}, available_members=[]):
98 class _UserGroupForm(formencode.Schema):
98 class _UserGroupForm(formencode.Schema):
99 allow_extra_fields = True
99 allow_extra_fields = True
100 filter_extra_fields = True
100 filter_extra_fields = True
101
101
102 users_group_name = All(
102 users_group_name = All(
103 v.UnicodeString(strip=True, min=1, not_empty=True),
103 v.UnicodeString(strip=True, min=1, not_empty=True),
104 v.ValidUserGroup(edit, old_data)
104 v.ValidUserGroup(edit, old_data)
105 )
105 )
106
106
107 users_group_active = v.StringBoolean(if_missing=False)
107 users_group_active = v.StringBoolean(if_missing=False)
108
108
109 if edit:
109 if edit:
110 users_group_members = v.OneOf(
110 users_group_members = v.OneOf(
111 available_members, hideList=False, testValueList=True,
111 available_members, hideList=False, testValueList=True,
112 if_missing=None, not_empty=False
112 if_missing=None, not_empty=False
113 )
113 )
114
114
115 return _UserGroupForm
115 return _UserGroupForm
116
116
117
117
118 def ReposGroupForm(edit=False, old_data={}, available_groups=[],
118 def ReposGroupForm(edit=False, old_data={}, available_groups=[],
119 can_create_in_root=False):
119 can_create_in_root=False):
120 class _ReposGroupForm(formencode.Schema):
120 class _ReposGroupForm(formencode.Schema):
121 allow_extra_fields = True
121 allow_extra_fields = True
122 filter_extra_fields = False
122 filter_extra_fields = False
123
123
124 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
124 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
125 v.SlugifyName())
125 v.SlugifyName())
126 group_description = v.UnicodeString(strip=True, min=1,
126 group_description = v.UnicodeString(strip=True, min=1,
127 not_empty=False)
127 not_empty=False)
128 if edit:
128 if edit:
129 #FIXME: do a special check that we cannot move a group to one of
129 #FIXME: do a special check that we cannot move a group to one of
130 #it's children
130 #it's children
131 pass
131 pass
132 group_parent_id = All(v.CanCreateGroup(can_create_in_root),
132 group_parent_id = All(v.CanCreateGroup(can_create_in_root),
133 v.OneOf(available_groups, hideList=False,
133 v.OneOf(available_groups, hideList=False,
134 testValueList=True,
134 testValueList=True,
135 if_missing=None, not_empty=True))
135 if_missing=None, not_empty=True))
136 enable_locking = v.StringBoolean(if_missing=False)
136 enable_locking = v.StringBoolean(if_missing=False)
137 chained_validators = [v.ValidReposGroup(edit, old_data)]
137 chained_validators = [v.ValidReposGroup(edit, old_data)]
138
138
139 return _ReposGroupForm
139 return _ReposGroupForm
140
140
141
141
142 def RegisterForm(edit=False, old_data={}):
142 def RegisterForm(edit=False, old_data={}):
143 class _RegisterForm(formencode.Schema):
143 class _RegisterForm(formencode.Schema):
144 allow_extra_fields = True
144 allow_extra_fields = True
145 filter_extra_fields = True
145 filter_extra_fields = True
146 username = All(
146 username = All(
147 v.ValidUsername(edit, old_data),
147 v.ValidUsername(edit, old_data),
148 v.UnicodeString(strip=True, min=1, not_empty=True)
148 v.UnicodeString(strip=True, min=1, not_empty=True)
149 )
149 )
150 password = All(
150 password = All(
151 v.ValidPassword(),
151 v.ValidPassword(),
152 v.UnicodeString(strip=False, min=6, not_empty=True)
152 v.UnicodeString(strip=False, min=6, not_empty=True)
153 )
153 )
154 password_confirmation = All(
154 password_confirmation = All(
155 v.ValidPassword(),
155 v.ValidPassword(),
156 v.UnicodeString(strip=False, min=6, not_empty=True)
156 v.UnicodeString(strip=False, min=6, not_empty=True)
157 )
157 )
158 active = v.StringBoolean(if_missing=False)
158 active = v.StringBoolean(if_missing=False)
159 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
159 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
160 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
160 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
161 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
161 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
162
162
163 chained_validators = [v.ValidPasswordsMatch()]
163 chained_validators = [v.ValidPasswordsMatch()]
164
164
165 return _RegisterForm
165 return _RegisterForm
166
166
167
167
168 def PasswordResetForm():
168 def PasswordResetForm():
169 class _PasswordResetForm(formencode.Schema):
169 class _PasswordResetForm(formencode.Schema):
170 allow_extra_fields = True
170 allow_extra_fields = True
171 filter_extra_fields = True
171 filter_extra_fields = True
172 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
172 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
173 return _PasswordResetForm
173 return _PasswordResetForm
174
174
175
175
176 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
176 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
177 repo_groups=[], landing_revs=[]):
177 repo_groups=[], landing_revs=[]):
178 class _RepoForm(formencode.Schema):
178 class _RepoForm(formencode.Schema):
179 allow_extra_fields = True
179 allow_extra_fields = True
180 filter_extra_fields = False
180 filter_extra_fields = False
181 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
181 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
182 v.SlugifyName())
182 v.SlugifyName())
183 repo_group = All(v.CanWriteGroup(old_data),
183 repo_group = All(v.CanWriteGroup(old_data),
184 v.OneOf(repo_groups, hideList=True))
184 v.OneOf(repo_groups, hideList=True))
185 repo_type = v.OneOf(supported_backends)
185 repo_type = v.OneOf(supported_backends)
186 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
186 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
187 repo_private = v.StringBoolean(if_missing=False)
187 repo_private = v.StringBoolean(if_missing=False)
188 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
188 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
189 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
189 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
190
190
191 repo_enable_statistics = v.StringBoolean(if_missing=False)
191 repo_enable_statistics = v.StringBoolean(if_missing=False)
192 repo_enable_downloads = v.StringBoolean(if_missing=False)
192 repo_enable_downloads = v.StringBoolean(if_missing=False)
193 repo_enable_locking = v.StringBoolean(if_missing=False)
193 repo_enable_locking = v.StringBoolean(if_missing=False)
194
194
195 if edit:
195 if edit:
196 #this is repo owner
196 #this is repo owner
197 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
197 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
198
198
199 chained_validators = [v.ValidCloneUri(),
199 chained_validators = [v.ValidCloneUri(),
200 v.ValidRepoName(edit, old_data)]
200 v.ValidRepoName(edit, old_data)]
201 return _RepoForm
201 return _RepoForm
202
202
203
203
204 def RepoPermsForm():
204 def RepoPermsForm():
205 class _RepoPermsForm(formencode.Schema):
205 class _RepoPermsForm(formencode.Schema):
206 allow_extra_fields = True
206 allow_extra_fields = True
207 filter_extra_fields = False
207 filter_extra_fields = False
208 chained_validators = [v.ValidPerms(type_='repo')]
208 chained_validators = [v.ValidPerms(type_='repo')]
209 return _RepoPermsForm
209 return _RepoPermsForm
210
210
211
211
212 def RepoGroupPermsForm():
212 def RepoGroupPermsForm():
213 class _RepoGroupPermsForm(formencode.Schema):
213 class _RepoGroupPermsForm(formencode.Schema):
214 allow_extra_fields = True
214 allow_extra_fields = True
215 filter_extra_fields = False
215 filter_extra_fields = False
216 recursive = v.StringBoolean(if_missing=False)
216 recursive = v.StringBoolean(if_missing=False)
217 chained_validators = [v.ValidPerms(type_='repo_group')]
217 chained_validators = [v.ValidPerms(type_='repo_group')]
218 return _RepoGroupPermsForm
218 return _RepoGroupPermsForm
219
219
220
220
221 def UserGroupPermsForm():
221 def UserGroupPermsForm():
222 class _UserPermsForm(formencode.Schema):
222 class _UserPermsForm(formencode.Schema):
223 allow_extra_fields = True
223 allow_extra_fields = True
224 filter_extra_fields = False
224 filter_extra_fields = False
225 chained_validators = [v.ValidPerms(type_='user_group')]
225 chained_validators = [v.ValidPerms(type_='user_group')]
226 return _UserPermsForm
226 return _UserPermsForm
227
227
228
228
229 def RepoFieldForm():
229 def RepoFieldForm():
230 class _RepoFieldForm(formencode.Schema):
230 class _RepoFieldForm(formencode.Schema):
231 filter_extra_fields = True
231 filter_extra_fields = True
232 allow_extra_fields = True
232 allow_extra_fields = True
233
233
234 new_field_key = All(v.FieldKey(),
234 new_field_key = All(v.FieldKey(),
235 v.UnicodeString(strip=True, min=3, not_empty=True))
235 v.UnicodeString(strip=True, min=3, not_empty=True))
236 new_field_value = v.UnicodeString(not_empty=False, if_missing='')
236 new_field_value = v.UnicodeString(not_empty=False, if_missing='')
237 new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'],
237 new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'],
238 if_missing='str')
238 if_missing='str')
239 new_field_label = v.UnicodeString(not_empty=False)
239 new_field_label = v.UnicodeString(not_empty=False)
240 new_field_desc = v.UnicodeString(not_empty=False)
240 new_field_desc = v.UnicodeString(not_empty=False)
241
241
242 return _RepoFieldForm
242 return _RepoFieldForm
243
243
244
244
245 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
245 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
246 repo_groups=[], landing_revs=[]):
246 repo_groups=[], landing_revs=[]):
247 class _RepoForkForm(formencode.Schema):
247 class _RepoForkForm(formencode.Schema):
248 allow_extra_fields = True
248 allow_extra_fields = True
249 filter_extra_fields = False
249 filter_extra_fields = False
250 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
250 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
251 v.SlugifyName())
251 v.SlugifyName())
252 repo_group = All(v.CanWriteGroup(),
252 repo_group = All(v.CanWriteGroup(),
253 v.OneOf(repo_groups, hideList=True))
253 v.OneOf(repo_groups, hideList=True))
254 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
254 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
255 description = v.UnicodeString(strip=True, min=1, not_empty=True)
255 description = v.UnicodeString(strip=True, min=1, not_empty=True)
256 private = v.StringBoolean(if_missing=False)
256 private = v.StringBoolean(if_missing=False)
257 copy_permissions = v.StringBoolean(if_missing=False)
257 copy_permissions = v.StringBoolean(if_missing=False)
258 update_after_clone = v.StringBoolean(if_missing=False)
258 update_after_clone = v.StringBoolean(if_missing=False)
259 fork_parent_id = v.UnicodeString()
259 fork_parent_id = v.UnicodeString()
260 chained_validators = [v.ValidForkName(edit, old_data)]
260 chained_validators = [v.ValidForkName(edit, old_data)]
261 landing_rev = v.OneOf(landing_revs, hideList=True)
261 landing_rev = v.OneOf(landing_revs, hideList=True)
262
262
263 return _RepoForkForm
263 return _RepoForkForm
264
264
265
265
266 def ApplicationSettingsForm():
266 def ApplicationSettingsForm():
267 class _ApplicationSettingsForm(formencode.Schema):
267 class _ApplicationSettingsForm(formencode.Schema):
268 allow_extra_fields = True
268 allow_extra_fields = True
269 filter_extra_fields = False
269 filter_extra_fields = False
270 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
270 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
271 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
271 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
272 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
272 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
273
273
274 return _ApplicationSettingsForm
274 return _ApplicationSettingsForm
275
275
276
276
277 def ApplicationVisualisationForm():
277 def ApplicationVisualisationForm():
278 class _ApplicationVisualisationForm(formencode.Schema):
278 class _ApplicationVisualisationForm(formencode.Schema):
279 allow_extra_fields = True
279 allow_extra_fields = True
280 filter_extra_fields = False
280 filter_extra_fields = False
281 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
281 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
282 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
282 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
283 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
283 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
284
284
285 rhodecode_repository_fields = v.StringBoolean(if_missing=False)
285 rhodecode_repository_fields = v.StringBoolean(if_missing=False)
286 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
286 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
287
287
288 return _ApplicationVisualisationForm
288 return _ApplicationVisualisationForm
289
289
290
290
291 def ApplicationUiSettingsForm():
291 def ApplicationUiSettingsForm():
292 class _ApplicationUiSettingsForm(formencode.Schema):
292 class _ApplicationUiSettingsForm(formencode.Schema):
293 allow_extra_fields = True
293 allow_extra_fields = True
294 filter_extra_fields = False
294 filter_extra_fields = False
295 web_push_ssl = v.StringBoolean(if_missing=False)
295 web_push_ssl = v.StringBoolean(if_missing=False)
296 paths_root_path = All(
296 paths_root_path = All(
297 v.ValidPath(),
297 v.ValidPath(),
298 v.UnicodeString(strip=True, min=1, not_empty=True)
298 v.UnicodeString(strip=True, min=1, not_empty=True)
299 )
299 )
300 hooks_changegroup_update = v.StringBoolean(if_missing=False)
300 hooks_changegroup_update = v.StringBoolean(if_missing=False)
301 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
301 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
302 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
302 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
303 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
303 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
304
304
305 extensions_largefiles = v.StringBoolean(if_missing=False)
305 extensions_largefiles = v.StringBoolean(if_missing=False)
306 extensions_hgsubversion = v.StringBoolean(if_missing=False)
306 extensions_hgsubversion = v.StringBoolean(if_missing=False)
307 extensions_hggit = v.StringBoolean(if_missing=False)
307 extensions_hggit = v.StringBoolean(if_missing=False)
308
308
309 return _ApplicationUiSettingsForm
309 return _ApplicationUiSettingsForm
310
310
311
311
312 def DefaultPermissionsForm(repo_perms_choices, group_perms_choices,
312 def DefaultPermissionsForm(repo_perms_choices, group_perms_choices,
313 user_group_perms_choices, create_choices,
313 user_group_perms_choices, create_choices,
314 repo_group_create_choices, user_group_create_choices,
314 repo_group_create_choices, user_group_create_choices,
315 fork_choices, register_choices):
315 fork_choices, register_choices, extern_activate_choices):
316 class _DefaultPermissionsForm(formencode.Schema):
316 class _DefaultPermissionsForm(formencode.Schema):
317 allow_extra_fields = True
317 allow_extra_fields = True
318 filter_extra_fields = True
318 filter_extra_fields = True
319 overwrite_default_repo = v.StringBoolean(if_missing=False)
319 overwrite_default_repo = v.StringBoolean(if_missing=False)
320 overwrite_default_group = v.StringBoolean(if_missing=False)
320 overwrite_default_group = v.StringBoolean(if_missing=False)
321 overwrite_default_user_group = v.StringBoolean(if_missing=False)
321 overwrite_default_user_group = v.StringBoolean(if_missing=False)
322 anonymous = v.StringBoolean(if_missing=False)
322 anonymous = v.StringBoolean(if_missing=False)
323 default_repo_perm = v.OneOf(repo_perms_choices)
323 default_repo_perm = v.OneOf(repo_perms_choices)
324 default_group_perm = v.OneOf(group_perms_choices)
324 default_group_perm = v.OneOf(group_perms_choices)
325 default_user_group_perm = v.OneOf(user_group_perms_choices)
325 default_user_group_perm = v.OneOf(user_group_perms_choices)
326
326
327 default_repo_create = v.OneOf(create_choices)
327 default_repo_create = v.OneOf(create_choices)
328 default_user_group_create = v.OneOf(user_group_create_choices)
328 default_user_group_create = v.OneOf(user_group_create_choices)
329 #default_repo_group_create = v.OneOf(repo_group_create_choices) #not impl. yet
329 #default_repo_group_create = v.OneOf(repo_group_create_choices) #not impl. yet
330 default_fork = v.OneOf(fork_choices)
330 default_fork = v.OneOf(fork_choices)
331
331
332 default_register = v.OneOf(register_choices)
332 default_register = v.OneOf(register_choices)
333 default_extern_activate = v.OneOf(extern_activate_choices)
333 return _DefaultPermissionsForm
334 return _DefaultPermissionsForm
334
335
335
336
336 def CustomDefaultPermissionsForm():
337 def CustomDefaultPermissionsForm():
337 class _CustomDefaultPermissionsForm(formencode.Schema):
338 class _CustomDefaultPermissionsForm(formencode.Schema):
338 filter_extra_fields = True
339 filter_extra_fields = True
339 allow_extra_fields = True
340 allow_extra_fields = True
340 inherit_default_permissions = v.StringBoolean(if_missing=False)
341 inherit_default_permissions = v.StringBoolean(if_missing=False)
341
342
342 create_repo_perm = v.StringBoolean(if_missing=False)
343 create_repo_perm = v.StringBoolean(if_missing=False)
343 create_user_group_perm = v.StringBoolean(if_missing=False)
344 create_user_group_perm = v.StringBoolean(if_missing=False)
344 #create_repo_group_perm Impl. later
345 #create_repo_group_perm Impl. later
345
346
346 fork_repo_perm = v.StringBoolean(if_missing=False)
347 fork_repo_perm = v.StringBoolean(if_missing=False)
347
348
348 return _CustomDefaultPermissionsForm
349 return _CustomDefaultPermissionsForm
349
350
350
351
351 def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
352 def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
352 class _DefaultsForm(formencode.Schema):
353 class _DefaultsForm(formencode.Schema):
353 allow_extra_fields = True
354 allow_extra_fields = True
354 filter_extra_fields = True
355 filter_extra_fields = True
355 default_repo_type = v.OneOf(supported_backends)
356 default_repo_type = v.OneOf(supported_backends)
356 default_repo_private = v.StringBoolean(if_missing=False)
357 default_repo_private = v.StringBoolean(if_missing=False)
357 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
358 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
358 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
359 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
359 default_repo_enable_locking = v.StringBoolean(if_missing=False)
360 default_repo_enable_locking = v.StringBoolean(if_missing=False)
360
361
361 return _DefaultsForm
362 return _DefaultsForm
362
363
363
364
364 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
365 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
365 tls_kind_choices):
366 tls_kind_choices):
366 class _LdapSettingsForm(formencode.Schema):
367 class _LdapSettingsForm(formencode.Schema):
367 allow_extra_fields = True
368 allow_extra_fields = True
368 filter_extra_fields = True
369 filter_extra_fields = True
369 #pre_validators = [LdapLibValidator]
370 #pre_validators = [LdapLibValidator]
370 ldap_active = v.StringBoolean(if_missing=False)
371 ldap_active = v.StringBoolean(if_missing=False)
371 ldap_host = v.UnicodeString(strip=True,)
372 ldap_host = v.UnicodeString(strip=True,)
372 ldap_port = v.Number(strip=True,)
373 ldap_port = v.Number(strip=True,)
373 ldap_tls_kind = v.OneOf(tls_kind_choices)
374 ldap_tls_kind = v.OneOf(tls_kind_choices)
374 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
375 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
375 ldap_dn_user = v.UnicodeString(strip=True,)
376 ldap_dn_user = v.UnicodeString(strip=True,)
376 ldap_dn_pass = v.UnicodeString(strip=True,)
377 ldap_dn_pass = v.UnicodeString(strip=True,)
377 ldap_base_dn = v.UnicodeString(strip=True,)
378 ldap_base_dn = v.UnicodeString(strip=True,)
378 ldap_filter = v.UnicodeString(strip=True,)
379 ldap_filter = v.UnicodeString(strip=True,)
379 ldap_search_scope = v.OneOf(search_scope_choices)
380 ldap_search_scope = v.OneOf(search_scope_choices)
380 ldap_attr_login = v.AttrLoginValidator()(not_empty=True)
381 ldap_attr_login = v.AttrLoginValidator()(not_empty=True)
381 ldap_attr_firstname = v.UnicodeString(strip=True,)
382 ldap_attr_firstname = v.UnicodeString(strip=True,)
382 ldap_attr_lastname = v.UnicodeString(strip=True,)
383 ldap_attr_lastname = v.UnicodeString(strip=True,)
383 ldap_attr_email = v.UnicodeString(strip=True,)
384 ldap_attr_email = v.UnicodeString(strip=True,)
384
385
385 return _LdapSettingsForm
386 return _LdapSettingsForm
386
387
387
388
388 def UserExtraEmailForm():
389 def UserExtraEmailForm():
389 class _UserExtraEmailForm(formencode.Schema):
390 class _UserExtraEmailForm(formencode.Schema):
390 email = All(v.UniqSystemEmail(), v.Email(not_empty=True))
391 email = All(v.UniqSystemEmail(), v.Email(not_empty=True))
391 return _UserExtraEmailForm
392 return _UserExtraEmailForm
392
393
393
394
394 def UserExtraIpForm():
395 def UserExtraIpForm():
395 class _UserExtraIpForm(formencode.Schema):
396 class _UserExtraIpForm(formencode.Schema):
396 ip = v.ValidIp()(not_empty=True)
397 ip = v.ValidIp()(not_empty=True)
397 return _UserExtraIpForm
398 return _UserExtraIpForm
398
399
399
400
400 def PullRequestForm(repo_id):
401 def PullRequestForm(repo_id):
401 class _PullRequestForm(formencode.Schema):
402 class _PullRequestForm(formencode.Schema):
402 allow_extra_fields = True
403 allow_extra_fields = True
403 filter_extra_fields = True
404 filter_extra_fields = True
404
405
405 user = v.UnicodeString(strip=True, required=True)
406 user = v.UnicodeString(strip=True, required=True)
406 org_repo = v.UnicodeString(strip=True, required=True)
407 org_repo = v.UnicodeString(strip=True, required=True)
407 org_ref = v.UnicodeString(strip=True, required=True)
408 org_ref = v.UnicodeString(strip=True, required=True)
408 other_repo = v.UnicodeString(strip=True, required=True)
409 other_repo = v.UnicodeString(strip=True, required=True)
409 other_ref = v.UnicodeString(strip=True, required=True)
410 other_ref = v.UnicodeString(strip=True, required=True)
410 revisions = All(#v.NotReviewedRevisions(repo_id)(),
411 revisions = All(#v.NotReviewedRevisions(repo_id)(),
411 v.UniqueList(not_empty=True))
412 v.UniqueList(not_empty=True))
412 review_members = v.UniqueList(not_empty=True)
413 review_members = v.UniqueList(not_empty=True)
413
414
414 pullrequest_title = v.UnicodeString(strip=True, required=True, min=3)
415 pullrequest_title = v.UnicodeString(strip=True, required=True, min=3)
415 pullrequest_desc = v.UnicodeString(strip=True, required=False)
416 pullrequest_desc = v.UnicodeString(strip=True, required=False)
416
417
417 ancestor_rev = v.UnicodeString(strip=True, required=True)
418 ancestor_rev = v.UnicodeString(strip=True, required=True)
418 merge_rev = v.UnicodeString(strip=True, required=True)
419 merge_rev = v.UnicodeString(strip=True, required=True)
419
420
420 return _PullRequestForm
421 return _PullRequestForm
@@ -1,162 +1,163 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.permission
3 rhodecode.model.permission
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 permissions model for RhodeCode
6 permissions model for RhodeCode
7
7
8 :created_on: Aug 20, 2010
8 :created_on: Aug 20, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28
28
29 from sqlalchemy.exc import DatabaseError
29 from sqlalchemy.exc import DatabaseError
30
30
31 from rhodecode.model import BaseModel
31 from rhodecode.model import BaseModel
32 from rhodecode.model.db import User, Permission, UserToPerm, UserRepoToPerm,\
32 from rhodecode.model.db import User, Permission, UserToPerm, UserRepoToPerm,\
33 UserRepoGroupToPerm, UserUserGroupToPerm
33 UserRepoGroupToPerm, UserUserGroupToPerm
34 from rhodecode.lib.utils2 import str2bool
34 from rhodecode.lib.utils2 import str2bool
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38
38
39 class PermissionModel(BaseModel):
39 class PermissionModel(BaseModel):
40 """
40 """
41 Permissions model for RhodeCode
41 Permissions model for RhodeCode
42 """
42 """
43
43
44 cls = Permission
44 cls = Permission
45
45
46 def create_permissions(self):
46 def create_permissions(self):
47 """
47 """
48 Create permissions for whole system
48 Create permissions for whole system
49 """
49 """
50 for p in Permission.PERMS:
50 for p in Permission.PERMS:
51 if not Permission.get_by_key(p[0]):
51 if not Permission.get_by_key(p[0]):
52 new_perm = Permission()
52 new_perm = Permission()
53 new_perm.permission_name = p[0]
53 new_perm.permission_name = p[0]
54 new_perm.permission_longname = p[0] #translation err with p[1]
54 new_perm.permission_longname = p[0] #translation err with p[1]
55 self.sa.add(new_perm)
55 self.sa.add(new_perm)
56
56
57 def create_default_permissions(self, user):
57 def create_default_permissions(self, user):
58 """
58 """
59 Creates only missing default permissions for user
59 Creates only missing default permissions for user
60
60
61 :param user:
61 :param user:
62 """
62 """
63 user = self._get_user(user)
63 user = self._get_user(user)
64
64
65 def _make_perm(perm):
65 def _make_perm(perm):
66 new_perm = UserToPerm()
66 new_perm = UserToPerm()
67 new_perm.user = user
67 new_perm.user = user
68 new_perm.permission = Permission.get_by_key(perm)
68 new_perm.permission = Permission.get_by_key(perm)
69 return new_perm
69 return new_perm
70
70
71 def _get_group(perm_name):
71 def _get_group(perm_name):
72 return '.'.join(perm_name.split('.')[:1])
72 return '.'.join(perm_name.split('.')[:1])
73
73
74 perms = UserToPerm.query().filter(UserToPerm.user == user).all()
74 perms = UserToPerm.query().filter(UserToPerm.user == user).all()
75 defined_perms_groups = map(_get_group,
75 defined_perms_groups = map(_get_group,
76 (x.permission.permission_name for x in perms))
76 (x.permission.permission_name for x in perms))
77 log.debug('GOT ALREADY DEFINED:%s' % perms)
77 log.debug('GOT ALREADY DEFINED:%s' % perms)
78 DEFAULT_PERMS = Permission.DEFAULT_USER_PERMISSIONS
78 DEFAULT_PERMS = Permission.DEFAULT_USER_PERMISSIONS
79
79
80 # for every default permission that needs to be created, we check if
80 # for every default permission that needs to be created, we check if
81 # it's group is already defined, if it's not we create default perm
81 # it's group is already defined, if it's not we create default perm
82 for perm_name in DEFAULT_PERMS:
82 for perm_name in DEFAULT_PERMS:
83 gr = _get_group(perm_name)
83 gr = _get_group(perm_name)
84 if gr not in defined_perms_groups:
84 if gr not in defined_perms_groups:
85 log.debug('GR:%s not found, creating permission %s'
85 log.debug('GR:%s not found, creating permission %s'
86 % (gr, perm_name))
86 % (gr, perm_name))
87 new_perm = _make_perm(perm_name)
87 new_perm = _make_perm(perm_name)
88 self.sa.add(new_perm)
88 self.sa.add(new_perm)
89
89
90 def update(self, form_result):
90 def update(self, form_result):
91 perm_user = User.get_by_username(username=form_result['perm_user_name'])
91 perm_user = User.get_by_username(username=form_result['perm_user_name'])
92
92
93 try:
93 try:
94 # stage 1 set anonymous access
94 # stage 1 set anonymous access
95 if perm_user.username == 'default':
95 if perm_user.username == 'default':
96 perm_user.active = str2bool(form_result['anonymous'])
96 perm_user.active = str2bool(form_result['anonymous'])
97 self.sa.add(perm_user)
97 self.sa.add(perm_user)
98
98
99 # stage 2 reset defaults and set them from form data
99 # stage 2 reset defaults and set them from form data
100 def _make_new(usr, perm_name):
100 def _make_new(usr, perm_name):
101 log.debug('Creating new permission:%s' % (perm_name))
101 log.debug('Creating new permission:%s' % (perm_name))
102 new = UserToPerm()
102 new = UserToPerm()
103 new.user = usr
103 new.user = usr
104 new.permission = Permission.get_by_key(perm_name)
104 new.permission = Permission.get_by_key(perm_name)
105 return new
105 return new
106 # clear current entries, to make this function idempotent
106 # clear current entries, to make this function idempotent
107 # it will fix even if we define more permissions or permissions
107 # it will fix even if we define more permissions or permissions
108 # are somehow missing
108 # are somehow missing
109 u2p = self.sa.query(UserToPerm)\
109 u2p = self.sa.query(UserToPerm)\
110 .filter(UserToPerm.user == perm_user)\
110 .filter(UserToPerm.user == perm_user)\
111 .all()
111 .all()
112 for p in u2p:
112 for p in u2p:
113 self.sa.delete(p)
113 self.sa.delete(p)
114 #create fresh set of permissions
114 #create fresh set of permissions
115 for def_perm_key in ['default_repo_perm', 'default_group_perm',
115 for def_perm_key in ['default_repo_perm', 'default_group_perm',
116 'default_user_group_perm',
116 'default_user_group_perm',
117 'default_repo_create',
117 'default_repo_create',
118 #'default_repo_group_create', #not implemented yet
118 #'default_repo_group_create', #not implemented yet
119 'default_user_group_create',
119 'default_user_group_create',
120 'default_fork', 'default_register']:
120 'default_fork', 'default_register',
121 'default_extern_activate']:
121 p = _make_new(perm_user, form_result[def_perm_key])
122 p = _make_new(perm_user, form_result[def_perm_key])
122 self.sa.add(p)
123 self.sa.add(p)
123
124
124 #stage 3 update all default permissions for repos if checked
125 #stage 3 update all default permissions for repos if checked
125 if form_result['overwrite_default_repo'] == True:
126 if form_result['overwrite_default_repo'] == True:
126 _def_name = form_result['default_repo_perm'].split('repository.')[-1]
127 _def_name = form_result['default_repo_perm'].split('repository.')[-1]
127 _def = Permission.get_by_key('repository.' + _def_name)
128 _def = Permission.get_by_key('repository.' + _def_name)
128 # repos
129 # repos
129 for r2p in self.sa.query(UserRepoToPerm)\
130 for r2p in self.sa.query(UserRepoToPerm)\
130 .filter(UserRepoToPerm.user == perm_user)\
131 .filter(UserRepoToPerm.user == perm_user)\
131 .all():
132 .all():
132
133
133 #don't reset PRIVATE repositories
134 #don't reset PRIVATE repositories
134 if not r2p.repository.private:
135 if not r2p.repository.private:
135 r2p.permission = _def
136 r2p.permission = _def
136 self.sa.add(r2p)
137 self.sa.add(r2p)
137
138
138 if form_result['overwrite_default_group'] == True:
139 if form_result['overwrite_default_group'] == True:
139 _def_name = form_result['default_group_perm'].split('group.')[-1]
140 _def_name = form_result['default_group_perm'].split('group.')[-1]
140 # groups
141 # groups
141 _def = Permission.get_by_key('group.' + _def_name)
142 _def = Permission.get_by_key('group.' + _def_name)
142 for g2p in self.sa.query(UserRepoGroupToPerm)\
143 for g2p in self.sa.query(UserRepoGroupToPerm)\
143 .filter(UserRepoGroupToPerm.user == perm_user)\
144 .filter(UserRepoGroupToPerm.user == perm_user)\
144 .all():
145 .all():
145 g2p.permission = _def
146 g2p.permission = _def
146 self.sa.add(g2p)
147 self.sa.add(g2p)
147
148
148 if form_result['overwrite_default_user_group'] == True:
149 if form_result['overwrite_default_user_group'] == True:
149 _def_name = form_result['default_user_group_perm'].split('usergroup.')[-1]
150 _def_name = form_result['default_user_group_perm'].split('usergroup.')[-1]
150 # groups
151 # groups
151 _def = Permission.get_by_key('usergroup.' + _def_name)
152 _def = Permission.get_by_key('usergroup.' + _def_name)
152 for g2p in self.sa.query(UserUserGroupToPerm)\
153 for g2p in self.sa.query(UserUserGroupToPerm)\
153 .filter(UserUserGroupToPerm.user == perm_user)\
154 .filter(UserUserGroupToPerm.user == perm_user)\
154 .all():
155 .all():
155 g2p.permission = _def
156 g2p.permission = _def
156 self.sa.add(g2p)
157 self.sa.add(g2p)
157
158
158 self.sa.commit()
159 self.sa.commit()
159 except (DatabaseError,):
160 except (DatabaseError,):
160 log.error(traceback.format_exc())
161 log.error(traceback.format_exc())
161 self.sa.rollback()
162 self.sa.rollback()
162 raise
163 raise
@@ -1,183 +1,191 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Permissions administration')} &middot; ${c.rhodecode_name}
5 ${_('Permissions administration')} &middot; ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 &raquo;
10 &raquo;
11 ${_('Permissions')}
11 ${_('Permissions')}
12 </%def>
12 </%def>
13
13
14 <%def name="page_nav()">
14 <%def name="page_nav()">
15 ${self.menu('admin')}
15 ${self.menu('admin')}
16 </%def>
16 </%def>
17
17
18 <%def name="main()">
18 <%def name="main()">
19 <div class="box box-left">
19 <div class="box box-left">
20 <!-- box / title -->
20 <!-- box / title -->
21 <div class="title">
21 <div class="title">
22 ${self.breadcrumbs()}
22 ${self.breadcrumbs()}
23 </div>
23 </div>
24 <h3>${_('Default permissions')}</h3>
24 <h3>${_('Default permissions')}</h3>
25 ${h.form(url('permission', id='default'),method='put')}
25 ${h.form(url('permission', id='default'),method='put')}
26 <div class="form">
26 <div class="form">
27 <!-- fields -->
27 <!-- fields -->
28 <div class="fields">
28 <div class="fields">
29 <div class="field">
29 <div class="field">
30 <div class="label label-checkbox">
30 <div class="label label-checkbox">
31 <label for="anonymous">${_('Anonymous access')}:</label>
31 <label for="anonymous">${_('Anonymous access')}:</label>
32 </div>
32 </div>
33 <div class="checkboxes">
33 <div class="checkboxes">
34 <div class="checkbox">
34 <div class="checkbox">
35 ${h.checkbox('anonymous',True)}
35 ${h.checkbox('anonymous',True)}
36 </div>
36 </div>
37 </div>
37 </div>
38 </div>
38 </div>
39 <div class="field">
39 <div class="field">
40 <div class="label">
40 <div class="label">
41 <label for="default_repo_perm">${_('Repository')}:</label>
41 <label for="default_repo_perm">${_('Repository')}:</label>
42 </div>
42 </div>
43 <div class="select">
43 <div class="select">
44 ${h.select('default_repo_perm','',c.repo_perms_choices)}
44 ${h.select('default_repo_perm','',c.repo_perms_choices)}
45
45
46 ${h.checkbox('overwrite_default_repo','true')}
46 ${h.checkbox('overwrite_default_repo','true')}
47 <label for="overwrite_default_repo">
47 <label for="overwrite_default_repo">
48 <span class="tooltip"
48 <span class="tooltip"
49 title="${h.tooltip(_('All default permissions on each repository will be reset to chosen permission, note that all custom default permission on repositories will be lost'))}">
49 title="${h.tooltip(_('All default permissions on each repository will be reset to chosen permission, note that all custom default permission on repositories will be lost'))}">
50 ${_('Overwrite existing settings')}</span> </label>
50 ${_('Overwrite existing settings')}</span> </label>
51 </div>
51 </div>
52 </div>
52 </div>
53 <div class="field">
53 <div class="field">
54 <div class="label">
54 <div class="label">
55 <label for="default_group_perm">${_('Repository group')}:</label>
55 <label for="default_group_perm">${_('Repository group')}:</label>
56 </div>
56 </div>
57 <div class="select">
57 <div class="select">
58 ${h.select('default_group_perm','',c.group_perms_choices)}
58 ${h.select('default_group_perm','',c.group_perms_choices)}
59 ${h.checkbox('overwrite_default_group','true')}
59 ${h.checkbox('overwrite_default_group','true')}
60 <label for="overwrite_default_group">
60 <label for="overwrite_default_group">
61 <span class="tooltip"
61 <span class="tooltip"
62 title="${h.tooltip(_('All default permissions on each repository group will be reset to chosen permission, note that all custom default permission on repository groups will be lost'))}">
62 title="${h.tooltip(_('All default permissions on each repository group will be reset to chosen permission, note that all custom default permission on repository groups will be lost'))}">
63 ${_('Overwrite existing settings')}</span> </label>
63 ${_('Overwrite existing settings')}</span> </label>
64
64
65 </div>
65 </div>
66 </div>
66 </div>
67 <div class="field">
67 <div class="field">
68 <div class="label">
68 <div class="label">
69 <label for="default_group_perm">${_('User group')}:</label>
69 <label for="default_group_perm">${_('User group')}:</label>
70 </div>
70 </div>
71 <div class="select">
71 <div class="select">
72 ${h.select('default_user_group_perm','',c.user_group_perms_choices)}
72 ${h.select('default_user_group_perm','',c.user_group_perms_choices)}
73 ${h.checkbox('overwrite_default_user_group','true')}
73 ${h.checkbox('overwrite_default_user_group','true')}
74 <label for="overwrite_default_user_group">
74 <label for="overwrite_default_user_group">
75 <span class="tooltip"
75 <span class="tooltip"
76 title="${h.tooltip(_('All default permissions on each user group will be reset to chosen permission, note that all custom default permission on repository groups will be lost'))}">
76 title="${h.tooltip(_('All default permissions on each user group will be reset to chosen permission, note that all custom default permission on repository groups will be lost'))}">
77 ${_('Overwrite existing settings')}</span> </label>
77 ${_('Overwrite existing settings')}</span> </label>
78
78
79 </div>
79 </div>
80 </div>
80 </div>
81 <div class="field">
81 <div class="field">
82 <div class="label">
82 <div class="label">
83 <label for="default_repo_create">${_('Repository creation')}:</label>
83 <label for="default_repo_create">${_('Repository creation')}:</label>
84 </div>
84 </div>
85 <div class="select">
85 <div class="select">
86 ${h.select('default_repo_create','',c.repo_create_choices)}
86 ${h.select('default_repo_create','',c.repo_create_choices)}
87 </div>
87 </div>
88 </div>
88 </div>
89 <div class="field">
89 <div class="field">
90 <div class="label">
90 <div class="label">
91 <label for="default_user_group_create">${_('User group creation')}:</label>
91 <label for="default_user_group_create">${_('User group creation')}:</label>
92 </div>
92 </div>
93 <div class="select">
93 <div class="select">
94 ${h.select('default_user_group_create','',c.user_group_create_choices)}
94 ${h.select('default_user_group_create','',c.user_group_create_choices)}
95 </div>
95 </div>
96 </div>
96 </div>
97 <div class="field">
97 <div class="field">
98 <div class="label">
98 <div class="label">
99 <label for="default_fork">${_('Repository forking')}:</label>
99 <label for="default_fork">${_('Repository forking')}:</label>
100 </div>
100 </div>
101 <div class="select">
101 <div class="select">
102 ${h.select('default_fork','',c.fork_choices)}
102 ${h.select('default_fork','',c.fork_choices)}
103 </div>
103 </div>
104 </div>
104 </div>
105 <div class="field">
105 <div class="field">
106 <div class="label">
106 <div class="label">
107 <label for="default_register">${_('Registration')}:</label>
107 <label for="default_register">${_('Registration')}:</label>
108 </div>
108 </div>
109 <div class="select">
109 <div class="select">
110 ${h.select('default_register','',c.register_choices)}
110 ${h.select('default_register','',c.register_choices)}
111 </div>
111 </div>
112 </div>
112 </div>
113 <div class="field">
114 <div class="label">
115 <label for="default_extern_activate">${_('External auth account activation')}:</label>
116 </div>
117 <div class="select">
118 ${h.select('default_extern_activate','',c.extern_activate_choices)}
119 </div>
120 </div>
113 <div class="buttons">
121 <div class="buttons">
114 ${h.submit('save',_('Save'),class_="ui-btn large")}
122 ${h.submit('save',_('Save'),class_="ui-btn large")}
115 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
123 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
116 </div>
124 </div>
117 </div>
125 </div>
118 </div>
126 </div>
119 ${h.end_form()}
127 ${h.end_form()}
120 </div>
128 </div>
121
129
122 <div style="min-height:780px" class="box box-right">
130 <div style="min-height:780px" class="box box-right">
123 <!-- box / title -->
131 <!-- box / title -->
124 <div class="title">
132 <div class="title">
125 <h5>${_('Default User Permissions')}</h5>
133 <h5>${_('Default User Permissions')}</h5>
126 </div>
134 </div>
127
135
128 ## permissions overview
136 ## permissions overview
129 <%namespace name="p" file="/base/perms_summary.html"/>
137 <%namespace name="p" file="/base/perms_summary.html"/>
130 ${p.perms_summary(c.perm_user.permissions)}
138 ${p.perms_summary(c.perm_user.permissions)}
131
139
132 </div>
140 </div>
133 <div class="box box-left" style="clear:left">
141 <div class="box box-left" style="clear:left">
134 <!-- box / title -->
142 <!-- box / title -->
135 <div class="title">
143 <div class="title">
136 <h5>${_('Allowed IP addresses')}</h5>
144 <h5>${_('Allowed IP addresses')}</h5>
137 </div>
145 </div>
138
146
139 <div class="ips_wrap">
147 <div class="ips_wrap">
140 <table class="noborder">
148 <table class="noborder">
141 %if c.user_ip_map:
149 %if c.user_ip_map:
142 %for ip in c.user_ip_map:
150 %for ip in c.user_ip_map:
143 <tr>
151 <tr>
144 <td><div class="ip">${ip.ip_addr}</div></td>
152 <td><div class="ip">${ip.ip_addr}</div></td>
145 <td><div class="ip">${h.ip_range(ip.ip_addr)}</div></td>
153 <td><div class="ip">${h.ip_range(ip.ip_addr)}</div></td>
146 <td>
154 <td>
147 ${h.form(url('user_ips_delete', id=c.user.user_id),method='delete')}
155 ${h.form(url('user_ips_delete', id=c.user.user_id),method='delete')}
148 ${h.hidden('del_ip',ip.ip_id)}
156 ${h.hidden('del_ip',ip.ip_id)}
149 ${h.hidden('default_user', 'True')}
157 ${h.hidden('default_user', 'True')}
150 ${h.submit('remove_',_('delete'),id="remove_ip_%s" % ip.ip_id,
158 ${h.submit('remove_',_('delete'),id="remove_ip_%s" % ip.ip_id,
151 class_="delete_icon action_button", onclick="return confirm('"+_('Confirm to delete this ip: %s') % ip.ip_addr+"');")}
159 class_="delete_icon action_button", onclick="return confirm('"+_('Confirm to delete this ip: %s') % ip.ip_addr+"');")}
152 ${h.end_form()}
160 ${h.end_form()}
153 </td>
161 </td>
154 </tr>
162 </tr>
155 %endfor
163 %endfor
156 %else:
164 %else:
157 <tr><td><div class="ip">${_('All IP addresses are allowed')}</div></td></tr>
165 <tr><td><div class="ip">${_('All IP addresses are allowed')}</div></td></tr>
158 %endif
166 %endif
159 </table>
167 </table>
160 </div>
168 </div>
161
169
162 ${h.form(url('user_ips', id=c.user.user_id),method='put')}
170 ${h.form(url('user_ips', id=c.user.user_id),method='put')}
163 <div class="form">
171 <div class="form">
164 <!-- fields -->
172 <!-- fields -->
165 <div class="fields">
173 <div class="fields">
166 <div class="field">
174 <div class="field">
167 <div class="label">
175 <div class="label">
168 <label for="new_ip">${_('New ip address')}:</label>
176 <label for="new_ip">${_('New ip address')}:</label>
169 </div>
177 </div>
170 <div class="input">
178 <div class="input">
171 ${h.hidden('default_user', 'True')}
179 ${h.hidden('default_user', 'True')}
172 ${h.text('new_ip', class_='medium')}
180 ${h.text('new_ip', class_='medium')}
173 </div>
181 </div>
174 </div>
182 </div>
175 <div class="buttons">
183 <div class="buttons">
176 ${h.submit('save',_('Add'),class_="ui-btn large")}
184 ${h.submit('save',_('Add'),class_="ui-btn large")}
177 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
185 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
178 </div>
186 </div>
179 </div>
187 </div>
180 </div>
188 </div>
181 ${h.end_form()}
189 ${h.end_form()}
182 </div>
190 </div>
183 </%def>
191 </%def>
General Comments 0
You need to be logged in to leave comments. Login now