##// END OF EJS Templates
fix(permissions): fixed security problem with apply-to-children functionality breaking permissions for private repositories...
super-admin -
r5550:cb083474 default
parent child Browse files
Show More
@@ -1,886 +1,889 b''
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19
19
20 """
20 """
21 repo group model for RhodeCode
21 repo group model for RhodeCode
22 """
22 """
23
23
24 import os
24 import os
25 import datetime
25 import datetime
26 import itertools
26 import itertools
27 import logging
27 import logging
28 import shutil
28 import shutil
29 import time
29 import time
30 import traceback
30 import traceback
31 import string
31 import string
32
32
33 from zope.cachedescriptors.property import Lazy as LazyProperty
34
33
35 from rhodecode import events
34 from rhodecode import events
36 from rhodecode.model import BaseModel
35 from rhodecode.model import BaseModel
37 from rhodecode.model.db import (_hash_key, func, or_, in_filter_generator,
36 from rhodecode.model.db import (_hash_key, func, or_, in_filter_generator,
38 Session, RepoGroup, UserRepoGroupToPerm, User, Permission, UserGroupRepoGroupToPerm,
37 Session, RepoGroup, UserRepoGroupToPerm, User, Permission, UserGroupRepoGroupToPerm,
39 UserGroup, Repository)
38 UserGroup, Repository)
40 from rhodecode.model.permission import PermissionModel
39 from rhodecode.model.permission import PermissionModel
41 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
40 from rhodecode.model.settings import SettingsModel
42 from rhodecode.lib.caching_query import FromCache
41 from rhodecode.lib.caching_query import FromCache
43 from rhodecode.lib.utils2 import action_logger_generic
42 from rhodecode.lib.utils2 import action_logger_generic
44
43
45 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
46
45
47
46
48 class RepoGroupModel(BaseModel):
47 class RepoGroupModel(BaseModel):
49
48
50 cls = RepoGroup
49 cls = RepoGroup
51 PERSONAL_GROUP_DESC = 'personal repo group of user `%(username)s`'
50 PERSONAL_GROUP_DESC = 'personal repo group of user `%(username)s`'
52 PERSONAL_GROUP_PATTERN = '${username}' # default
51 PERSONAL_GROUP_PATTERN = '${username}' # default
53
52
54 def _get_user_group(self, users_group):
53 def _get_user_group(self, users_group):
55 return self._get_instance(UserGroup, users_group,
54 return self._get_instance(UserGroup, users_group,
56 callback=UserGroup.get_by_group_name)
55 callback=UserGroup.get_by_group_name)
57
56
58 def _get_repo_group(self, repo_group):
57 def _get_repo_group(self, repo_group):
59 return self._get_instance(RepoGroup, repo_group,
58 return self._get_instance(RepoGroup, repo_group,
60 callback=RepoGroup.get_by_group_name)
59 callback=RepoGroup.get_by_group_name)
61
60
62 def get_repo_group(self, repo_group):
61 def get_repo_group(self, repo_group):
63 return self._get_repo_group(repo_group)
62 return self._get_repo_group(repo_group)
64
63
65 def get_by_group_name(self, repo_group_name, cache=None):
64 def get_by_group_name(self, repo_group_name, cache=None):
66 repo = self.sa.query(RepoGroup) \
65 repo = self.sa.query(RepoGroup) \
67 .filter(RepoGroup.group_name == repo_group_name)
66 .filter(RepoGroup.group_name == repo_group_name)
68
67
69 if cache:
68 if cache:
70 name_key = _hash_key(repo_group_name)
69 name_key = _hash_key(repo_group_name)
71 repo = repo.options(
70 repo = repo.options(
72 FromCache("sql_cache_short", f"get_repo_group_{name_key}"))
71 FromCache("sql_cache_short", f"get_repo_group_{name_key}"))
73 return repo.scalar()
72 return repo.scalar()
74
73
75 def get_default_create_personal_repo_group(self):
74 def get_default_create_personal_repo_group(self):
76 value = SettingsModel().get_setting_by_name(
75 value = SettingsModel().get_setting_by_name(
77 'create_personal_repo_group')
76 'create_personal_repo_group')
78 return value.app_settings_value if value else None or False
77 return value.app_settings_value if value else None or False
79
78
80 def get_personal_group_name_pattern(self):
79 def get_personal_group_name_pattern(self):
81 value = SettingsModel().get_setting_by_name(
80 value = SettingsModel().get_setting_by_name(
82 'personal_repo_group_pattern')
81 'personal_repo_group_pattern')
83 val = value.app_settings_value if value else None
82 val = value.app_settings_value if value else None
84 group_template = val or self.PERSONAL_GROUP_PATTERN
83 group_template = val or self.PERSONAL_GROUP_PATTERN
85
84
86 group_template = group_template.lstrip('/')
85 group_template = group_template.lstrip('/')
87 return group_template
86 return group_template
88
87
89 def get_personal_group_name(self, user):
88 def get_personal_group_name(self, user):
90 template = self.get_personal_group_name_pattern()
89 template = self.get_personal_group_name_pattern()
91 return string.Template(template).safe_substitute(
90 return string.Template(template).safe_substitute(
92 username=user.username,
91 username=user.username,
93 user_id=user.user_id,
92 user_id=user.user_id,
94 first_name=user.first_name,
93 first_name=user.first_name,
95 last_name=user.last_name,
94 last_name=user.last_name,
96 )
95 )
97
96
98 def create_personal_repo_group(self, user, commit_early=True):
97 def create_personal_repo_group(self, user, commit_early=True):
99 desc = self.PERSONAL_GROUP_DESC % {'username': user.username}
98 desc = self.PERSONAL_GROUP_DESC % {'username': user.username}
100 personal_repo_group_name = self.get_personal_group_name(user)
99 personal_repo_group_name = self.get_personal_group_name(user)
101
100
102 # create a new one
101 # create a new one
103 RepoGroupModel().create(
102 RepoGroupModel().create(
104 group_name=personal_repo_group_name,
103 group_name=personal_repo_group_name,
105 group_description=desc,
104 group_description=desc,
106 owner=user.username,
105 owner=user.username,
107 personal=True,
106 personal=True,
108 commit_early=commit_early)
107 commit_early=commit_early)
109
108
110 def _create_default_perms(self, new_group):
109 def _create_default_perms(self, new_group):
111 # create default permission
110 # create default permission
112 default_perm = 'group.read'
111 default_perm = 'group.read'
113 def_user = User.get_default_user()
112 def_user = User.get_default_user()
114 for p in def_user.user_perms:
113 for p in def_user.user_perms:
115 if p.permission.permission_name.startswith('group.'):
114 if p.permission.permission_name.startswith('group.'):
116 default_perm = p.permission.permission_name
115 default_perm = p.permission.permission_name
117 break
116 break
118
117
119 repo_group_to_perm = UserRepoGroupToPerm()
118 repo_group_to_perm = UserRepoGroupToPerm()
120 repo_group_to_perm.permission = Permission.get_by_key(default_perm)
119 repo_group_to_perm.permission = Permission.get_by_key(default_perm)
121
120
122 repo_group_to_perm.group = new_group
121 repo_group_to_perm.group = new_group
123 repo_group_to_perm.user = def_user
122 repo_group_to_perm.user = def_user
124 return repo_group_to_perm
123 return repo_group_to_perm
125
124
126 def _get_group_name_and_parent(self, group_name_full, repo_in_path=False,
125 def _get_group_name_and_parent(self, group_name_full, repo_in_path=False,
127 get_object=False):
126 get_object=False):
128 """
127 """
129 Get's the group name and a parent group name from given group name.
128 Get's the group name and a parent group name from given group name.
130 If repo_in_path is set to truth, we asume the full path also includes
129 If repo_in_path is set to truth, we asume the full path also includes
131 repo name, in such case we clean the last element.
130 repo name, in such case we clean the last element.
132
131
133 :param group_name_full:
132 :param group_name_full:
134 """
133 """
135 split_paths = 1
134 split_paths = 1
136 if repo_in_path:
135 if repo_in_path:
137 split_paths = 2
136 split_paths = 2
138 _parts = group_name_full.rsplit(RepoGroup.url_sep(), split_paths)
137 _parts = group_name_full.rsplit(RepoGroup.url_sep(), split_paths)
139
138
140 if repo_in_path and len(_parts) > 1:
139 if repo_in_path and len(_parts) > 1:
141 # such case last element is the repo_name
140 # such case last element is the repo_name
142 _parts.pop(-1)
141 _parts.pop(-1)
143 group_name_cleaned = _parts[-1] # just the group name
142 group_name_cleaned = _parts[-1] # just the group name
144 parent_repo_group_name = None
143 parent_repo_group_name = None
145
144
146 if len(_parts) > 1:
145 if len(_parts) > 1:
147 parent_repo_group_name = _parts[0]
146 parent_repo_group_name = _parts[0]
148
147
149 parent_group = None
148 parent_group = None
150 if parent_repo_group_name:
149 if parent_repo_group_name:
151 parent_group = RepoGroup.get_by_group_name(parent_repo_group_name)
150 parent_group = RepoGroup.get_by_group_name(parent_repo_group_name)
152
151
153 if get_object:
152 if get_object:
154 return group_name_cleaned, parent_repo_group_name, parent_group
153 return group_name_cleaned, parent_repo_group_name, parent_group
155
154
156 return group_name_cleaned, parent_repo_group_name
155 return group_name_cleaned, parent_repo_group_name
157
156
158 def check_exist_filesystem(self, group_name, exc_on_failure=True):
157 def check_exist_filesystem(self, group_name, exc_on_failure=True):
159 create_path = os.path.join(self.repos_path, group_name)
158 create_path = os.path.join(self.repos_path, group_name)
160 log.debug('creating new group in %s', create_path)
159 log.debug('creating new group in %s', create_path)
161
160
162 if os.path.isdir(create_path):
161 if os.path.isdir(create_path):
163 if exc_on_failure:
162 if exc_on_failure:
164 abs_create_path = os.path.abspath(create_path)
163 abs_create_path = os.path.abspath(create_path)
165 raise Exception(f'Directory `{abs_create_path}` already exists !')
164 raise Exception(f'Directory `{abs_create_path}` already exists !')
166 return False
165 return False
167 return True
166 return True
168
167
169 def _create_group(self, group_name):
168 def _create_group(self, group_name):
170 """
169 """
171 makes repository group on filesystem
170 makes repository group on filesystem
172
171
173 :param repo_name:
172 :param repo_name:
174 :param parent_id:
173 :param parent_id:
175 """
174 """
176
175
177 self.check_exist_filesystem(group_name)
176 self.check_exist_filesystem(group_name)
178 create_path = os.path.join(self.repos_path, group_name)
177 create_path = os.path.join(self.repos_path, group_name)
179 log.debug('creating new group in %s', create_path)
178 log.debug('creating new group in %s', create_path)
180 os.makedirs(create_path, mode=0o755)
179 os.makedirs(create_path, mode=0o755)
181 log.debug('created group in %s', create_path)
180 log.debug('created group in %s', create_path)
182
181
183 def _rename_group(self, old, new):
182 def _rename_group(self, old, new):
184 """
183 """
185 Renames a group on filesystem
184 Renames a group on filesystem
186
185
187 :param group_name:
186 :param group_name:
188 """
187 """
189
188
190 if old == new:
189 if old == new:
191 log.debug('skipping group rename')
190 log.debug('skipping group rename')
192 return
191 return
193
192
194 log.debug('renaming repository group from %s to %s', old, new)
193 log.debug('renaming repository group from %s to %s', old, new)
195
194
196 old_path = os.path.join(self.repos_path, old)
195 old_path = os.path.join(self.repos_path, old)
197 new_path = os.path.join(self.repos_path, new)
196 new_path = os.path.join(self.repos_path, new)
198
197
199 log.debug('renaming repos paths from %s to %s', old_path, new_path)
198 log.debug('renaming repos paths from %s to %s', old_path, new_path)
200
199
201 if os.path.isdir(new_path):
200 if os.path.isdir(new_path):
202 raise Exception('Was trying to rename to already '
201 raise Exception('Was trying to rename to already '
203 'existing dir %s' % new_path)
202 'existing dir %s' % new_path)
204 shutil.move(old_path, new_path)
203 shutil.move(old_path, new_path)
205
204
206 def _delete_filesystem_group(self, group, force_delete=False):
205 def _delete_filesystem_group(self, group, force_delete=False):
207 """
206 """
208 Deletes a group from a filesystem
207 Deletes a group from a filesystem
209
208
210 :param group: instance of group from database
209 :param group: instance of group from database
211 :param force_delete: use shutil rmtree to remove all objects
210 :param force_delete: use shutil rmtree to remove all objects
212 """
211 """
213 paths = group.full_path.split(RepoGroup.url_sep())
212 paths = group.full_path.split(RepoGroup.url_sep())
214 paths = os.sep.join(paths)
213 paths = os.sep.join(paths)
215
214
216 rm_path = os.path.join(self.repos_path, paths)
215 rm_path = os.path.join(self.repos_path, paths)
217 log.info("Removing group %s", rm_path)
216 log.info("Removing group %s", rm_path)
218 # delete only if that path really exists
217 # delete only if that path really exists
219 if os.path.isdir(rm_path):
218 if os.path.isdir(rm_path):
220 if force_delete:
219 if force_delete:
221 shutil.rmtree(rm_path)
220 shutil.rmtree(rm_path)
222 else:
221 else:
223 # archive that group`
222 # archive that group`
224 _now = datetime.datetime.now()
223 _now = datetime.datetime.now()
225 _ms = str(_now.microsecond).rjust(6, '0')
224 _ms = str(_now.microsecond).rjust(6, '0')
226 _d = 'rm__{}_GROUP_{}'.format(
225 _d = 'rm__{}_GROUP_{}'.format(
227 _now.strftime('%Y%m%d_%H%M%S_' + _ms), group.name)
226 _now.strftime('%Y%m%d_%H%M%S_' + _ms), group.name)
228 shutil.move(rm_path, os.path.join(self.repos_path, _d))
227 shutil.move(rm_path, os.path.join(self.repos_path, _d))
229
228
230 def create(self, group_name, group_description, owner, just_db=False,
229 def create(self, group_name, group_description, owner, just_db=False,
231 copy_permissions=False, personal=None, commit_early=True):
230 copy_permissions=False, personal=None, commit_early=True):
232
231
233 (group_name_cleaned,
232 (group_name_cleaned,
234 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(group_name)
233 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(group_name)
235
234
236 parent_group = None
235 parent_group = None
237 if parent_group_name:
236 if parent_group_name:
238 parent_group = self._get_repo_group(parent_group_name)
237 parent_group = self._get_repo_group(parent_group_name)
239 if not parent_group:
238 if not parent_group:
240 # we tried to create a nested group, but the parent is not
239 # we tried to create a nested group, but the parent is not
241 # existing
240 # existing
242 raise ValueError(
241 raise ValueError(
243 'Parent group `%s` given in `%s` group name '
242 'Parent group `%s` given in `%s` group name '
244 'is not yet existing.' % (parent_group_name, group_name))
243 'is not yet existing.' % (parent_group_name, group_name))
245
244
246 # because we are doing a cleanup, we need to check if such directory
245 # because we are doing a cleanup, we need to check if such directory
247 # already exists. If we don't do that we can accidentally delete
246 # already exists. If we don't do that we can accidentally delete
248 # existing directory via cleanup that can cause data issues, since
247 # existing directory via cleanup that can cause data issues, since
249 # delete does a folder rename to special syntax later cleanup
248 # delete does a folder rename to special syntax later cleanup
250 # functions can delete this
249 # functions can delete this
251 cleanup_group = self.check_exist_filesystem(group_name,
250 cleanup_group = self.check_exist_filesystem(group_name,
252 exc_on_failure=False)
251 exc_on_failure=False)
253 user = self._get_user(owner)
252 user = self._get_user(owner)
254 if not user:
253 if not user:
255 raise ValueError('Owner %s not found as rhodecode user', owner)
254 raise ValueError('Owner %s not found as rhodecode user', owner)
256
255
257 try:
256 try:
258 new_repo_group = RepoGroup()
257 new_repo_group = RepoGroup()
259 new_repo_group.user = user
258 new_repo_group.user = user
260 new_repo_group.group_description = group_description or group_name
259 new_repo_group.group_description = group_description or group_name
261 new_repo_group.parent_group = parent_group
260 new_repo_group.parent_group = parent_group
262 new_repo_group.group_name = group_name
261 new_repo_group.group_name = group_name
263 new_repo_group.personal = personal
262 new_repo_group.personal = personal
264
263
265 self.sa.add(new_repo_group)
264 self.sa.add(new_repo_group)
266
265
267 # create an ADMIN permission for owner except if we're super admin,
266 # create an ADMIN permission for owner except if we're super admin,
268 # later owner should go into the owner field of groups
267 # later owner should go into the owner field of groups
269 if not user.is_admin:
268 if not user.is_admin:
270 self.grant_user_permission(repo_group=new_repo_group,
269 self.grant_user_permission(repo_group=new_repo_group,
271 user=owner, perm='group.admin')
270 user=owner, perm='group.admin')
272
271
273 if parent_group and copy_permissions:
272 if parent_group and copy_permissions:
274 # copy permissions from parent
273 # copy permissions from parent
275 user_perms = UserRepoGroupToPerm.query() \
274 user_perms = UserRepoGroupToPerm.query() \
276 .filter(UserRepoGroupToPerm.group == parent_group).all()
275 .filter(UserRepoGroupToPerm.group == parent_group).all()
277
276
278 group_perms = UserGroupRepoGroupToPerm.query() \
277 group_perms = UserGroupRepoGroupToPerm.query() \
279 .filter(UserGroupRepoGroupToPerm.group == parent_group).all()
278 .filter(UserGroupRepoGroupToPerm.group == parent_group).all()
280
279
281 for perm in user_perms:
280 for perm in user_perms:
282 # don't copy over the permission for user who is creating
281 # don't copy over the permission for user who is creating
283 # this group, if he is not super admin he get's admin
282 # this group, if he is not super admin he get's admin
284 # permission set above
283 # permission set above
285 if perm.user != user or user.is_admin:
284 if perm.user != user or user.is_admin:
286 UserRepoGroupToPerm.create(
285 UserRepoGroupToPerm.create(
287 perm.user, new_repo_group, perm.permission)
286 perm.user, new_repo_group, perm.permission)
288
287
289 for perm in group_perms:
288 for perm in group_perms:
290 UserGroupRepoGroupToPerm.create(
289 UserGroupRepoGroupToPerm.create(
291 perm.users_group, new_repo_group, perm.permission)
290 perm.users_group, new_repo_group, perm.permission)
292 else:
291 else:
293 perm_obj = self._create_default_perms(new_repo_group)
292 perm_obj = self._create_default_perms(new_repo_group)
294 self.sa.add(perm_obj)
293 self.sa.add(perm_obj)
295
294
296 # now commit the changes, earlier so we are sure everything is in
295 # now commit the changes, earlier so we are sure everything is in
297 # the database.
296 # the database.
298 if commit_early:
297 if commit_early:
299 self.sa.commit()
298 self.sa.commit()
300 if not just_db:
299 if not just_db:
301 self._create_group(new_repo_group.group_name)
300 self._create_group(new_repo_group.group_name)
302
301
303 # trigger the post hook
302 # trigger the post hook
304 from rhodecode.lib import hooks_base
303 from rhodecode.lib import hooks_base
305 repo_group = RepoGroup.get_by_group_name(group_name)
304 repo_group = RepoGroup.get_by_group_name(group_name)
306
305
307 # update repo group commit caches initially
306 # update repo group commit caches initially
308 repo_group.update_commit_cache()
307 repo_group.update_commit_cache()
309
308
310 hooks_base.create_repository_group(
309 hooks_base.create_repository_group(
311 created_by=user.username, **repo_group.get_dict())
310 created_by=user.username, **repo_group.get_dict())
312
311
313 # Trigger create event.
312 # Trigger create event.
314 events.trigger(events.RepoGroupCreateEvent(repo_group))
313 events.trigger(events.RepoGroupCreateEvent(repo_group))
315
314
316 return new_repo_group
315 return new_repo_group
317 except Exception:
316 except Exception:
318 self.sa.rollback()
317 self.sa.rollback()
319 log.exception('Exception occurred when creating repository group, '
318 log.exception('Exception occurred when creating repository group, '
320 'doing cleanup...')
319 'doing cleanup...')
321 # rollback things manually !
320 # rollback things manually !
322 repo_group = RepoGroup.get_by_group_name(group_name)
321 repo_group = RepoGroup.get_by_group_name(group_name)
323 if repo_group:
322 if repo_group:
324 RepoGroup.delete(repo_group.group_id)
323 RepoGroup.delete(repo_group.group_id)
325 self.sa.commit()
324 self.sa.commit()
326 if cleanup_group:
325 if cleanup_group:
327 RepoGroupModel()._delete_filesystem_group(repo_group)
326 RepoGroupModel()._delete_filesystem_group(repo_group)
328 raise
327 raise
329
328
330 def update_permissions(
329 def update_permissions(
331 self, repo_group, perm_additions=None, perm_updates=None,
330 self, repo_group, perm_additions=None, perm_updates=None,
332 perm_deletions=None, recursive=None, check_perms=True,
331 perm_deletions=None, recursive=None, check_perms=True,
333 cur_user=None):
332 cur_user=None):
334 from rhodecode.model.repo import RepoModel
333 from rhodecode.model.repo import RepoModel
335 from rhodecode.lib.auth import HasUserGroupPermissionAny
334 from rhodecode.lib.auth import HasUserGroupPermissionAny
336
335
337 if not perm_additions:
336 if not perm_additions:
338 perm_additions = []
337 perm_additions = []
339 if not perm_updates:
338 if not perm_updates:
340 perm_updates = []
339 perm_updates = []
341 if not perm_deletions:
340 if not perm_deletions:
342 perm_deletions = []
341 perm_deletions = []
343
342
344 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
343 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
345
344
346 changes = {
345 changes = {
347 'added': [],
346 'added': [],
348 'updated': [],
347 'updated': [],
349 'deleted': [],
348 'deleted': [],
350 'default_user_changed': None
349 'default_user_changed': None
351 }
350 }
352
351
353 def _set_perm_user(obj, user, perm):
352 def _set_perm_user(_obj: RepoGroup | Repository, _user_obj: User, _perm):
354 if isinstance(obj, RepoGroup):
353
355 self.grant_user_permission(
354 if isinstance(_obj, RepoGroup):
356 repo_group=obj, user=user, perm=perm)
355 self.grant_user_permission(repo_group=_obj, user=_user_obj, perm=_perm)
357 elif isinstance(obj, Repository):
356 elif isinstance(_obj, Repository):
358 # private repos will not allow to change the default
357 # private repos will not allow to change the default
359 # permissions using recursive mode
358 # permissions using recursive mode
360 if obj.private and user == User.DEFAULT_USER:
359 if _obj.private and _user_obj.username == User.DEFAULT_USER:
360 log.debug('Skipping private repo %s for user %s', _obj, _user_obj)
361 return
361 return
362
362
363 # we set group permission but we have to switch to repo
363 # we set group permission, we have to switch to repo permission definition
364 # permission
364 new_perm = _perm.replace('group.', 'repository.')
365 perm = perm.replace('group.', 'repository.')
365 RepoModel().grant_user_permission(repo=_obj, user=_user_obj, perm=new_perm)
366 RepoModel().grant_user_permission(
366
367 repo=obj, user=user, perm=perm)
367 def _set_perm_group(_obj: RepoGroup | Repository, users_group: UserGroup, _perm):
368 if isinstance(_obj, RepoGroup):
369 self.grant_user_group_permission(repo_group=_obj, group_name=users_group, perm=_perm)
370 elif isinstance(_obj, Repository):
371 # we set group permission, we have to switch to repo permission definition
372 new_perm = _perm.replace('group.', 'repository.')
373 RepoModel().grant_user_group_permission(repo=_obj, group_name=users_group, perm=new_perm)
368
374
369 def _set_perm_group(obj, users_group, perm):
375 def _revoke_perm_user(_obj: RepoGroup | Repository, _user_obj: User):
370 if isinstance(obj, RepoGroup):
376 if isinstance(_obj, RepoGroup):
371 self.grant_user_group_permission(
377 self.revoke_user_permission(repo_group=_obj, user=_user_obj)
372 repo_group=obj, group_name=users_group, perm=perm)
378 elif isinstance(_obj, Repository):
373 elif isinstance(obj, Repository):
379 # private repos will not allow to change the default
374 # we set group permission but we have to switch to repo
380 # permissions using recursive mode, also there's no revocation fo default user, just update
375 # permission
381 if _user_obj.username == User.DEFAULT_USER:
376 perm = perm.replace('group.', 'repository.')
382 log.debug('Skipping private repo %s for user %s', _obj, _user_obj)
377 RepoModel().grant_user_group_permission(
383 return
378 repo=obj, group_name=users_group, perm=perm)
384 RepoModel().revoke_user_permission(repo=_obj, user=_user_obj)
379
385
380 def _revoke_perm_user(obj, user):
386 def _revoke_perm_group(_obj: RepoGroup | Repository, user_group: UserGroup):
381 if isinstance(obj, RepoGroup):
387 if isinstance(_obj, RepoGroup):
382 self.revoke_user_permission(repo_group=obj, user=user)
388 self.revoke_user_group_permission(repo_group=_obj, group_name=user_group)
383 elif isinstance(obj, Repository):
389 elif isinstance(_obj, Repository):
384 RepoModel().revoke_user_permission(repo=obj, user=user)
390 RepoModel().revoke_user_group_permission(repo=_obj, group_name=user_group)
385
386 def _revoke_perm_group(obj, user_group):
387 if isinstance(obj, RepoGroup):
388 self.revoke_user_group_permission(
389 repo_group=obj, group_name=user_group)
390 elif isinstance(obj, Repository):
391 RepoModel().revoke_user_group_permission(
392 repo=obj, group_name=user_group)
393
391
394 # start updates
392 # start updates
395 log.debug('Now updating permissions for %s in recursive mode:%s',
393 log.debug('Now updating permissions for %s in recursive mode:%s',
396 repo_group, recursive)
394 repo_group, recursive)
397
395
398 # initialize check function, we'll call that multiple times
396 # initialize check function, we'll call that multiple times
399 has_group_perm = HasUserGroupPermissionAny(*req_perms)
397 has_group_perm = HasUserGroupPermissionAny(*req_perms)
400
398
401 for obj in repo_group.recursive_groups_and_repos():
399 for obj in repo_group.recursive_groups_and_repos():
402 # iterated obj is an instance of a repos group or repository in
400 # iterated obj is an instance of a repos group or repository in
403 # that group, recursive option can be: none, repos, groups, all
401 # that group, recursive option can be: none, repos, groups, all
404 if recursive == 'all':
402 if recursive == 'all':
405 obj = obj
403 obj = obj
406 elif recursive == 'repos':
404 elif recursive == 'repos':
407 # skip groups, other than this one
405 # skip groups, other than this one
408 if isinstance(obj, RepoGroup) and not obj == repo_group:
406 if isinstance(obj, RepoGroup) and not obj == repo_group:
409 continue
407 continue
410 elif recursive == 'groups':
408 elif recursive == 'groups':
411 # skip repos
409 # skip repos
412 if isinstance(obj, Repository):
410 if isinstance(obj, Repository):
413 continue
411 continue
414 else: # recursive == 'none':
412 else: # recursive == 'none':
415 # DEFAULT option - don't apply to iterated objects
413 # DEFAULT option - don't apply to iterated objects
416 # also we do a break at the end of this loop. if we are not
414 # also we do a break at the end of this loop. if we are not
417 # in recursive mode
415 # in recursive mode
418 obj = repo_group
416 obj = repo_group
419
417
420 change_obj = obj.get_api_data()
418 change_obj = obj.get_api_data()
421
419
422 # update permissions
420 # update permissions
423 for member_id, perm, member_type in perm_updates:
421 for member_id, perm, member_type in perm_updates:
424 member_id = int(member_id)
422 member_id = int(member_id)
425 if member_type == 'user':
423 if member_type == 'user':
426 member_name = User.get(member_id).username
424 member_obj = User.get(member_id)
425 member_name = member_obj.username
427 if isinstance(obj, RepoGroup) and obj == repo_group and member_name == User.DEFAULT_USER:
426 if isinstance(obj, RepoGroup) and obj == repo_group and member_name == User.DEFAULT_USER:
428 # NOTE(dan): detect if we changed permissions for default user
427 # NOTE(dan): detect if we changed permissions for default user
429 perm_obj = self.sa.query(UserRepoGroupToPerm) \
428 perm_obj = self.sa.query(UserRepoGroupToPerm) \
430 .filter(UserRepoGroupToPerm.user_id == member_id) \
429 .filter(UserRepoGroupToPerm.user_id == member_id) \
431 .filter(UserRepoGroupToPerm.group == repo_group) \
430 .filter(UserRepoGroupToPerm.group == repo_group) \
432 .scalar()
431 .scalar()
433 if perm_obj and perm_obj.permission.permission_name != perm:
432 if perm_obj and perm_obj.permission.permission_name != perm:
434 changes['default_user_changed'] = True
433 changes['default_user_changed'] = True
435
434
436 # this updates also current one if found
435 # this updates also current one if found
437 _set_perm_user(obj, user=member_id, perm=perm)
436 _set_perm_user(obj, member_obj, perm)
438 elif member_type == 'user_group':
437 elif member_type == 'user_group':
439 member_name = UserGroup.get(member_id).users_group_name
438 member_obj = UserGroup.get(member_id)
440 if not check_perms or has_group_perm(member_name,
439 member_name = member_obj.users_group_name
441 user=cur_user):
440 if not check_perms or has_group_perm(member_name, user=cur_user):
442 _set_perm_group(obj, users_group=member_id, perm=perm)
441 _set_perm_group(obj, member_obj, perm)
443 else:
442 else:
444 raise ValueError("member_type must be 'user' or 'user_group' "
443 raise ValueError(
445 "got {} instead".format(member_type))
444 f"member_type must be 'user' or 'user_group' got {member_type} instead"
445 )
446
446
447 changes['updated'].append(
447 changes['updated'].append(
448 {'change_obj': change_obj, 'type': member_type,
448 {'change_obj': change_obj, 'type': member_type,
449 'id': member_id, 'name': member_name, 'new_perm': perm})
449 'id': member_id, 'name': member_name, 'new_perm': perm})
450
450
451 # set new permissions
451 # set new permissions
452 for member_id, perm, member_type in perm_additions:
452 for member_id, perm, member_type in perm_additions:
453 member_id = int(member_id)
453 member_id = int(member_id)
454 if member_type == 'user':
454 if member_type == 'user':
455 member_name = User.get(member_id).username
455 member_obj = User.get(member_id)
456 _set_perm_user(obj, user=member_id, perm=perm)
456 member_name = member_obj.username
457 _set_perm_user(obj, member_obj, perm)
457 elif member_type == 'user_group':
458 elif member_type == 'user_group':
458 # check if we have permissions to alter this usergroup
459 # check if we have permissions to alter this usergroup
459 member_name = UserGroup.get(member_id).users_group_name
460 member_obj = UserGroup.get(member_id)
460 if not check_perms or has_group_perm(member_name,
461 member_name = member_obj.users_group_name
461 user=cur_user):
462 if not check_perms or has_group_perm(member_name, user=cur_user):
462 _set_perm_group(obj, users_group=member_id, perm=perm)
463 _set_perm_group(obj, member_obj, perm)
463 else:
464 else:
464 raise ValueError("member_type must be 'user' or 'user_group' "
465 raise ValueError(
465 "got {} instead".format(member_type))
466 f"member_type must be 'user' or 'user_group' got {member_type} instead"
467 )
466
468
467 changes['added'].append(
469 changes['added'].append(
468 {'change_obj': change_obj, 'type': member_type,
470 {'change_obj': change_obj, 'type': member_type,
469 'id': member_id, 'name': member_name, 'new_perm': perm})
471 'id': member_id, 'name': member_name, 'new_perm': perm})
470
472
471 # delete permissions
473 # delete permissions
472 for member_id, perm, member_type in perm_deletions:
474 for member_id, perm, member_type in perm_deletions:
473 member_id = int(member_id)
475 member_id = int(member_id)
474 if member_type == 'user':
476 if member_type == 'user':
475 member_name = User.get(member_id).username
477 member_obj = User.get(member_id)
476 _revoke_perm_user(obj, user=member_id)
478 member_name = member_obj.username
479 _revoke_perm_user(obj, member_obj)
477 elif member_type == 'user_group':
480 elif member_type == 'user_group':
478 # check if we have permissions to alter this usergroup
481 # check if we have permissions to alter this usergroup
479 member_name = UserGroup.get(member_id).users_group_name
482 member_obj = UserGroup.get(member_id)
480 if not check_perms or has_group_perm(member_name,
483 member_name = member_obj.users_group_name
481 user=cur_user):
484 if not check_perms or has_group_perm(member_name, user=cur_user):
482 _revoke_perm_group(obj, user_group=member_id)
485 _revoke_perm_group(obj, member_obj)
483 else:
486 else:
484 raise ValueError("member_type must be 'user' or 'user_group' "
487 raise ValueError(
485 "got {} instead".format(member_type))
488 f"member_type must be 'user' or 'user_group' got {member_type} instead"
486
489 )
487 changes['deleted'].append(
490 changes['deleted'].append(
488 {'change_obj': change_obj, 'type': member_type,
491 {'change_obj': change_obj, 'type': member_type,
489 'id': member_id, 'name': member_name, 'new_perm': perm})
492 'id': member_id, 'name': member_name, 'new_perm': perm})
490
493
491 # if it's not recursive call for all,repos,groups
494 # if it's not recursive call for all,repos,groups
492 # break the loop and don't proceed with other changes
495 # break the loop and don't proceed with other changes
493 if recursive not in ['all', 'repos', 'groups']:
496 if recursive not in ['all', 'repos', 'groups']:
494 break
497 break
495
498
496 return changes
499 return changes
497
500
498 def update(self, repo_group, form_data):
501 def update(self, repo_group, form_data):
499 try:
502 try:
500 repo_group = self._get_repo_group(repo_group)
503 repo_group = self._get_repo_group(repo_group)
501 old_path = repo_group.full_path
504 old_path = repo_group.full_path
502
505
503 # change properties
506 # change properties
504 if 'group_description' in form_data:
507 if 'group_description' in form_data:
505 repo_group.group_description = form_data['group_description']
508 repo_group.group_description = form_data['group_description']
506
509
507 if 'enable_locking' in form_data:
510 if 'enable_locking' in form_data:
508 repo_group.enable_locking = form_data['enable_locking']
511 repo_group.enable_locking = form_data['enable_locking']
509
512
510 if 'group_parent_id' in form_data:
513 if 'group_parent_id' in form_data:
511 parent_group = (
514 parent_group = (
512 self._get_repo_group(form_data['group_parent_id']))
515 self._get_repo_group(form_data['group_parent_id']))
513 repo_group.group_parent_id = (
516 repo_group.group_parent_id = (
514 parent_group.group_id if parent_group else None)
517 parent_group.group_id if parent_group else None)
515 repo_group.parent_group = parent_group
518 repo_group.parent_group = parent_group
516
519
517 # mikhail: to update the full_path, we have to explicitly
520 # mikhail: to update the full_path, we have to explicitly
518 # update group_name
521 # update group_name
519 group_name = form_data.get('group_name', repo_group.name)
522 group_name = form_data.get('group_name', repo_group.name)
520 repo_group.group_name = repo_group.get_new_name(group_name)
523 repo_group.group_name = repo_group.get_new_name(group_name)
521
524
522 new_path = repo_group.full_path
525 new_path = repo_group.full_path
523
526
524 affected_user_ids = []
527 affected_user_ids = []
525 if 'user' in form_data:
528 if 'user' in form_data:
526 old_owner_id = repo_group.user.user_id
529 old_owner_id = repo_group.user.user_id
527 new_owner = User.get_by_username(form_data['user'])
530 new_owner = User.get_by_username(form_data['user'])
528 repo_group.user = new_owner
531 repo_group.user = new_owner
529
532
530 if old_owner_id != new_owner.user_id:
533 if old_owner_id != new_owner.user_id:
531 affected_user_ids = [new_owner.user_id, old_owner_id]
534 affected_user_ids = [new_owner.user_id, old_owner_id]
532
535
533 self.sa.add(repo_group)
536 self.sa.add(repo_group)
534
537
535 # iterate over all members of this groups and do fixes
538 # iterate over all members of this groups and do fixes
536 # set locking if given
539 # set locking if given
537 # if obj is a repoGroup also fix the name of the group according
540 # if obj is a repoGroup also fix the name of the group according
538 # to the parent
541 # to the parent
539 # if obj is a Repo fix it's name
542 # if obj is a Repo fix it's name
540 # this can be potentially heavy operation
543 # this can be potentially heavy operation
541 for obj in repo_group.recursive_groups_and_repos():
544 for obj in repo_group.recursive_groups_and_repos():
542 # set the value from it's parent
545 # set the value from it's parent
543 obj.enable_locking = repo_group.enable_locking
546 obj.enable_locking = repo_group.enable_locking
544 if isinstance(obj, RepoGroup):
547 if isinstance(obj, RepoGroup):
545 new_name = obj.get_new_name(obj.name)
548 new_name = obj.get_new_name(obj.name)
546 log.debug('Fixing group %s to new name %s',
549 log.debug('Fixing group %s to new name %s',
547 obj.group_name, new_name)
550 obj.group_name, new_name)
548 obj.group_name = new_name
551 obj.group_name = new_name
549
552
550 elif isinstance(obj, Repository):
553 elif isinstance(obj, Repository):
551 # we need to get all repositories from this new group and
554 # we need to get all repositories from this new group and
552 # rename them accordingly to new group path
555 # rename them accordingly to new group path
553 new_name = obj.get_new_name(obj.just_name)
556 new_name = obj.get_new_name(obj.just_name)
554 log.debug('Fixing repo %s to new name %s',
557 log.debug('Fixing repo %s to new name %s',
555 obj.repo_name, new_name)
558 obj.repo_name, new_name)
556 obj.repo_name = new_name
559 obj.repo_name = new_name
557
560
558 self.sa.add(obj)
561 self.sa.add(obj)
559
562
560 self._rename_group(old_path, new_path)
563 self._rename_group(old_path, new_path)
561
564
562 # Trigger update event.
565 # Trigger update event.
563 events.trigger(events.RepoGroupUpdateEvent(repo_group))
566 events.trigger(events.RepoGroupUpdateEvent(repo_group))
564
567
565 if affected_user_ids:
568 if affected_user_ids:
566 PermissionModel().trigger_permission_flush(affected_user_ids)
569 PermissionModel().trigger_permission_flush(affected_user_ids)
567
570
568 return repo_group
571 return repo_group
569 except Exception:
572 except Exception:
570 log.error(traceback.format_exc())
573 log.error(traceback.format_exc())
571 raise
574 raise
572
575
573 def delete(self, repo_group, force_delete=False, fs_remove=True):
576 def delete(self, repo_group, force_delete=False, fs_remove=True):
574 repo_group = self._get_repo_group(repo_group)
577 repo_group = self._get_repo_group(repo_group)
575 if not repo_group:
578 if not repo_group:
576 return False
579 return False
577 try:
580 try:
578 self.sa.delete(repo_group)
581 self.sa.delete(repo_group)
579 if fs_remove:
582 if fs_remove:
580 self._delete_filesystem_group(repo_group, force_delete)
583 self._delete_filesystem_group(repo_group, force_delete)
581 else:
584 else:
582 log.debug('skipping removal from filesystem')
585 log.debug('skipping removal from filesystem')
583
586
584 # Trigger delete event.
587 # Trigger delete event.
585 events.trigger(events.RepoGroupDeleteEvent(repo_group))
588 events.trigger(events.RepoGroupDeleteEvent(repo_group))
586 return True
589 return True
587
590
588 except Exception:
591 except Exception:
589 log.error('Error removing repo_group %s', repo_group)
592 log.error('Error removing repo_group %s', repo_group)
590 raise
593 raise
591
594
592 def grant_user_permission(self, repo_group, user, perm):
595 def grant_user_permission(self, repo_group, user, perm):
593 """
596 """
594 Grant permission for user on given repository group, or update
597 Grant permission for user on given repository group, or update
595 existing one if found
598 existing one if found
596
599
597 :param repo_group: Instance of RepoGroup, repositories_group_id,
600 :param repo_group: Instance of RepoGroup, repositories_group_id,
598 or repositories_group name
601 or repositories_group name
599 :param user: Instance of User, user_id or username
602 :param user: Instance of User, user_id or username
600 :param perm: Instance of Permission, or permission_name
603 :param perm: Instance of Permission, or permission_name
601 """
604 """
602
605
603 repo_group = self._get_repo_group(repo_group)
606 repo_group = self._get_repo_group(repo_group)
604 user = self._get_user(user)
607 user = self._get_user(user)
605 permission = self._get_perm(perm)
608 permission = self._get_perm(perm)
606
609
607 # check if we have that permission already
610 # check if we have that permission already
608 obj = self.sa.query(UserRepoGroupToPerm)\
611 obj = self.sa.query(UserRepoGroupToPerm)\
609 .filter(UserRepoGroupToPerm.user == user)\
612 .filter(UserRepoGroupToPerm.user == user)\
610 .filter(UserRepoGroupToPerm.group == repo_group)\
613 .filter(UserRepoGroupToPerm.group == repo_group)\
611 .scalar()
614 .scalar()
612 if obj is None:
615 if obj is None:
613 # create new !
616 # create new !
614 obj = UserRepoGroupToPerm()
617 obj = UserRepoGroupToPerm()
615 obj.group = repo_group
618 obj.group = repo_group
616 obj.user = user
619 obj.user = user
617 obj.permission = permission
620 obj.permission = permission
618 self.sa.add(obj)
621 self.sa.add(obj)
619 log.debug('Granted perm %s to %s on %s', perm, user, repo_group)
622 log.debug('Granted perm %s to %s on %s', perm, user, repo_group)
620 action_logger_generic(
623 action_logger_generic(
621 'granted permission: {} to user: {} on repogroup: {}'.format(
624 'granted permission: {} to user: {} on repogroup: {}'.format(
622 perm, user, repo_group), namespace='security.repogroup')
625 perm, user, repo_group), namespace='security.repogroup')
623 return obj
626 return obj
624
627
625 def revoke_user_permission(self, repo_group, user):
628 def revoke_user_permission(self, repo_group, user):
626 """
629 """
627 Revoke permission for user on given repository group
630 Revoke permission for user on given repository group
628
631
629 :param repo_group: Instance of RepoGroup, repositories_group_id,
632 :param repo_group: Instance of RepoGroup, repositories_group_id,
630 or repositories_group name
633 or repositories_group name
631 :param user: Instance of User, user_id or username
634 :param user: Instance of User, user_id or username
632 """
635 """
633
636
634 repo_group = self._get_repo_group(repo_group)
637 repo_group = self._get_repo_group(repo_group)
635 user = self._get_user(user)
638 user = self._get_user(user)
636
639
637 obj = self.sa.query(UserRepoGroupToPerm)\
640 obj = self.sa.query(UserRepoGroupToPerm)\
638 .filter(UserRepoGroupToPerm.user == user)\
641 .filter(UserRepoGroupToPerm.user == user)\
639 .filter(UserRepoGroupToPerm.group == repo_group)\
642 .filter(UserRepoGroupToPerm.group == repo_group)\
640 .scalar()
643 .scalar()
641 if obj:
644 if obj:
642 self.sa.delete(obj)
645 self.sa.delete(obj)
643 log.debug('Revoked perm on %s on %s', repo_group, user)
646 log.debug('Revoked perm on %s on %s', repo_group, user)
644 action_logger_generic(
647 action_logger_generic(
645 'revoked permission from user: {} on repogroup: {}'.format(
648 'revoked permission from user: {} on repogroup: {}'.format(
646 user, repo_group), namespace='security.repogroup')
649 user, repo_group), namespace='security.repogroup')
647
650
648 def grant_user_group_permission(self, repo_group, group_name, perm):
651 def grant_user_group_permission(self, repo_group, group_name, perm):
649 """
652 """
650 Grant permission for user group on given repository group, or update
653 Grant permission for user group on given repository group, or update
651 existing one if found
654 existing one if found
652
655
653 :param repo_group: Instance of RepoGroup, repositories_group_id,
656 :param repo_group: Instance of RepoGroup, repositories_group_id,
654 or repositories_group name
657 or repositories_group name
655 :param group_name: Instance of UserGroup, users_group_id,
658 :param group_name: Instance of UserGroup, users_group_id,
656 or user group name
659 or user group name
657 :param perm: Instance of Permission, or permission_name
660 :param perm: Instance of Permission, or permission_name
658 """
661 """
659 repo_group = self._get_repo_group(repo_group)
662 repo_group = self._get_repo_group(repo_group)
660 group_name = self._get_user_group(group_name)
663 group_name = self._get_user_group(group_name)
661 permission = self._get_perm(perm)
664 permission = self._get_perm(perm)
662
665
663 # check if we have that permission already
666 # check if we have that permission already
664 obj = self.sa.query(UserGroupRepoGroupToPerm)\
667 obj = self.sa.query(UserGroupRepoGroupToPerm)\
665 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
668 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
666 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
669 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
667 .scalar()
670 .scalar()
668
671
669 if obj is None:
672 if obj is None:
670 # create new
673 # create new
671 obj = UserGroupRepoGroupToPerm()
674 obj = UserGroupRepoGroupToPerm()
672
675
673 obj.group = repo_group
676 obj.group = repo_group
674 obj.users_group = group_name
677 obj.users_group = group_name
675 obj.permission = permission
678 obj.permission = permission
676 self.sa.add(obj)
679 self.sa.add(obj)
677 log.debug('Granted perm %s to %s on %s', perm, group_name, repo_group)
680 log.debug('Granted perm %s to %s on %s', perm, group_name, repo_group)
678 action_logger_generic(
681 action_logger_generic(
679 'granted permission: {} to usergroup: {} on repogroup: {}'.format(
682 'granted permission: {} to usergroup: {} on repogroup: {}'.format(
680 perm, group_name, repo_group), namespace='security.repogroup')
683 perm, group_name, repo_group), namespace='security.repogroup')
681 return obj
684 return obj
682
685
683 def revoke_user_group_permission(self, repo_group, group_name):
686 def revoke_user_group_permission(self, repo_group, group_name):
684 """
687 """
685 Revoke permission for user group on given repository group
688 Revoke permission for user group on given repository group
686
689
687 :param repo_group: Instance of RepoGroup, repositories_group_id,
690 :param repo_group: Instance of RepoGroup, repositories_group_id,
688 or repositories_group name
691 or repositories_group name
689 :param group_name: Instance of UserGroup, users_group_id,
692 :param group_name: Instance of UserGroup, users_group_id,
690 or user group name
693 or user group name
691 """
694 """
692 repo_group = self._get_repo_group(repo_group)
695 repo_group = self._get_repo_group(repo_group)
693 group_name = self._get_user_group(group_name)
696 group_name = self._get_user_group(group_name)
694
697
695 obj = self.sa.query(UserGroupRepoGroupToPerm)\
698 obj = self.sa.query(UserGroupRepoGroupToPerm)\
696 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
699 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
697 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
700 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
698 .scalar()
701 .scalar()
699 if obj:
702 if obj:
700 self.sa.delete(obj)
703 self.sa.delete(obj)
701 log.debug('Revoked perm to %s on %s', repo_group, group_name)
704 log.debug('Revoked perm to %s on %s', repo_group, group_name)
702 action_logger_generic(
705 action_logger_generic(
703 'revoked permission from usergroup: {} on repogroup: {}'.format(
706 'revoked permission from usergroup: {} on repogroup: {}'.format(
704 group_name, repo_group), namespace='security.repogroup')
707 group_name, repo_group), namespace='security.repogroup')
705
708
706 @classmethod
709 @classmethod
707 def update_commit_cache(cls, repo_groups=None):
710 def update_commit_cache(cls, repo_groups=None):
708 if not repo_groups:
711 if not repo_groups:
709 repo_groups = RepoGroup.getAll()
712 repo_groups = RepoGroup.getAll()
710 for repo_group in repo_groups:
713 for repo_group in repo_groups:
711 repo_group.update_commit_cache()
714 repo_group.update_commit_cache()
712
715
713 def get_repo_groups_as_dict(self, repo_group_list=None, admin=False,
716 def get_repo_groups_as_dict(self, repo_group_list=None, admin=False,
714 super_user_actions=False):
717 super_user_actions=False):
715
718
716 from pyramid.threadlocal import get_current_request
719 from pyramid.threadlocal import get_current_request
717 _render = get_current_request().get_partial_renderer(
720 _render = get_current_request().get_partial_renderer(
718 'rhodecode:templates/data_table/_dt_elements.mako')
721 'rhodecode:templates/data_table/_dt_elements.mako')
719 c = _render.get_call_context()
722 c = _render.get_call_context()
720 h = _render.get_helpers()
723 h = _render.get_helpers()
721
724
722 def quick_menu(repo_group_name):
725 def quick_menu(repo_group_name):
723 return _render('quick_repo_group_menu', repo_group_name)
726 return _render('quick_repo_group_menu', repo_group_name)
724
727
725 def repo_group_lnk(repo_group_name):
728 def repo_group_lnk(repo_group_name):
726 return _render('repo_group_name', repo_group_name)
729 return _render('repo_group_name', repo_group_name)
727
730
728 def last_change(last_change):
731 def last_change(last_change):
729 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
732 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
730 ts = time.time()
733 ts = time.time()
731 utc_offset = (datetime.datetime.fromtimestamp(ts)
734 utc_offset = (datetime.datetime.fromtimestamp(ts)
732 - datetime.datetime.utcfromtimestamp(ts)).total_seconds()
735 - datetime.datetime.utcfromtimestamp(ts)).total_seconds()
733 last_change = last_change + datetime.timedelta(seconds=utc_offset)
736 last_change = last_change + datetime.timedelta(seconds=utc_offset)
734 return _render("last_change", last_change)
737 return _render("last_change", last_change)
735
738
736 def desc(desc, personal):
739 def desc(desc, personal):
737 return _render(
740 return _render(
738 'repo_group_desc', desc, personal, c.visual.stylify_metatags)
741 'repo_group_desc', desc, personal, c.visual.stylify_metatags)
739
742
740 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
743 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
741 return _render(
744 return _render(
742 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
745 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
743
746
744 def repo_group_name(repo_group_name, children_groups):
747 def repo_group_name(repo_group_name, children_groups):
745 return _render("repo_group_name", repo_group_name, children_groups)
748 return _render("repo_group_name", repo_group_name, children_groups)
746
749
747 def user_profile(username):
750 def user_profile(username):
748 return _render('user_profile', username)
751 return _render('user_profile', username)
749
752
750 repo_group_data = []
753 repo_group_data = []
751 for group in repo_group_list:
754 for group in repo_group_list:
752 # NOTE(marcink): because we use only raw column we need to load it like that
755 # NOTE(marcink): because we use only raw column we need to load it like that
753 changeset_cache = RepoGroup._load_changeset_cache(
756 changeset_cache = RepoGroup._load_changeset_cache(
754 '', group._changeset_cache)
757 '', group._changeset_cache)
755 last_commit_change = RepoGroup._load_commit_change(changeset_cache)
758 last_commit_change = RepoGroup._load_commit_change(changeset_cache)
756 row = {
759 row = {
757 "menu": quick_menu(group.group_name),
760 "menu": quick_menu(group.group_name),
758 "name": repo_group_lnk(group.group_name),
761 "name": repo_group_lnk(group.group_name),
759 "name_raw": group.group_name,
762 "name_raw": group.group_name,
760
763
761 "last_change": last_change(last_commit_change),
764 "last_change": last_change(last_commit_change),
762
765
763 "last_changeset": "",
766 "last_changeset": "",
764 "last_changeset_raw": "",
767 "last_changeset_raw": "",
765
768
766 "desc": desc(h.escape(group.group_description), group.personal),
769 "desc": desc(h.escape(group.group_description), group.personal),
767 "top_level_repos": 0,
770 "top_level_repos": 0,
768 "owner": user_profile(group.User.username)
771 "owner": user_profile(group.User.username)
769 }
772 }
770 if admin:
773 if admin:
771 repo_count = group.repositories.count()
774 repo_count = group.repositories.count()
772 children_groups = list(map(
775 children_groups = list(map(
773 h.safe_str,
776 h.safe_str,
774 itertools.chain((g.name for g in group.parents),
777 itertools.chain((g.name for g in group.parents),
775 (x.name for x in [group]))))
778 (x.name for x in [group]))))
776 row.update({
779 row.update({
777 "action": repo_group_actions(
780 "action": repo_group_actions(
778 group.group_id, group.group_name, repo_count),
781 group.group_id, group.group_name, repo_count),
779 "top_level_repos": repo_count,
782 "top_level_repos": repo_count,
780 "name": repo_group_name(group.group_name, children_groups),
783 "name": repo_group_name(group.group_name, children_groups),
781
784
782 })
785 })
783 repo_group_data.append(row)
786 repo_group_data.append(row)
784
787
785 return repo_group_data
788 return repo_group_data
786
789
787 def get_repo_groups_data_table(
790 def get_repo_groups_data_table(
788 self, draw, start, limit,
791 self, draw, start, limit,
789 search_q, order_by, order_dir,
792 search_q, order_by, order_dir,
790 auth_user, repo_group_id):
793 auth_user, repo_group_id):
791 from rhodecode.model.scm import RepoGroupList
794 from rhodecode.model.scm import RepoGroupList
792
795
793 _perms = ['group.read', 'group.write', 'group.admin']
796 _perms = ['group.read', 'group.write', 'group.admin']
794 repo_groups = RepoGroup.query() \
797 repo_groups = RepoGroup.query() \
795 .filter(RepoGroup.group_parent_id == repo_group_id) \
798 .filter(RepoGroup.group_parent_id == repo_group_id) \
796 .all()
799 .all()
797 auth_repo_group_list = RepoGroupList(
800 auth_repo_group_list = RepoGroupList(
798 repo_groups, perm_set=_perms,
801 repo_groups, perm_set=_perms,
799 extra_kwargs=dict(user=auth_user))
802 extra_kwargs=dict(user=auth_user))
800
803
801 allowed_ids = [-1]
804 allowed_ids = [-1]
802 for repo_group in auth_repo_group_list:
805 for repo_group in auth_repo_group_list:
803 allowed_ids.append(repo_group.group_id)
806 allowed_ids.append(repo_group.group_id)
804
807
805 repo_groups_data_total_count = RepoGroup.query() \
808 repo_groups_data_total_count = RepoGroup.query() \
806 .filter(RepoGroup.group_parent_id == repo_group_id) \
809 .filter(RepoGroup.group_parent_id == repo_group_id) \
807 .filter(or_(
810 .filter(or_(
808 # generate multiple IN to fix limitation problems
811 # generate multiple IN to fix limitation problems
809 *in_filter_generator(RepoGroup.group_id, allowed_ids))
812 *in_filter_generator(RepoGroup.group_id, allowed_ids))
810 ) \
813 ) \
811 .count()
814 .count()
812
815
813 base_q = Session.query(
816 base_q = Session.query(
814 RepoGroup.group_name,
817 RepoGroup.group_name,
815 RepoGroup.group_name_hash,
818 RepoGroup.group_name_hash,
816 RepoGroup.group_description,
819 RepoGroup.group_description,
817 RepoGroup.group_id,
820 RepoGroup.group_id,
818 RepoGroup.personal,
821 RepoGroup.personal,
819 RepoGroup.updated_on,
822 RepoGroup.updated_on,
820 RepoGroup._changeset_cache,
823 RepoGroup._changeset_cache,
821 User,
824 User,
822 ) \
825 ) \
823 .filter(RepoGroup.group_parent_id == repo_group_id) \
826 .filter(RepoGroup.group_parent_id == repo_group_id) \
824 .filter(or_(
827 .filter(or_(
825 # generate multiple IN to fix limitation problems
828 # generate multiple IN to fix limitation problems
826 *in_filter_generator(RepoGroup.group_id, allowed_ids))
829 *in_filter_generator(RepoGroup.group_id, allowed_ids))
827 ) \
830 ) \
828 .join(User, User.user_id == RepoGroup.user_id) \
831 .join(User, User.user_id == RepoGroup.user_id) \
829 .group_by(RepoGroup, User)
832 .group_by(RepoGroup, User)
830
833
831 repo_groups_data_total_filtered_count = base_q.count()
834 repo_groups_data_total_filtered_count = base_q.count()
832
835
833 sort_defined = False
836 sort_defined = False
834
837
835 if order_by == 'group_name':
838 if order_by == 'group_name':
836 sort_col = func.lower(RepoGroup.group_name)
839 sort_col = func.lower(RepoGroup.group_name)
837 sort_defined = True
840 sort_defined = True
838 elif order_by == 'user_username':
841 elif order_by == 'user_username':
839 sort_col = User.username
842 sort_col = User.username
840 else:
843 else:
841 sort_col = getattr(RepoGroup, order_by, None)
844 sort_col = getattr(RepoGroup, order_by, None)
842
845
843 if sort_defined or sort_col:
846 if sort_defined or sort_col:
844 if order_dir == 'asc':
847 if order_dir == 'asc':
845 sort_col = sort_col.asc()
848 sort_col = sort_col.asc()
846 else:
849 else:
847 sort_col = sort_col.desc()
850 sort_col = sort_col.desc()
848
851
849 base_q = base_q.order_by(sort_col)
852 base_q = base_q.order_by(sort_col)
850 base_q = base_q.offset(start).limit(limit)
853 base_q = base_q.offset(start).limit(limit)
851
854
852 repo_group_list = base_q.all()
855 repo_group_list = base_q.all()
853
856
854 repo_groups_data = RepoGroupModel().get_repo_groups_as_dict(
857 repo_groups_data = RepoGroupModel().get_repo_groups_as_dict(
855 repo_group_list=repo_group_list, admin=False)
858 repo_group_list=repo_group_list, admin=False)
856
859
857 data = ({
860 data = ({
858 'draw': draw,
861 'draw': draw,
859 'data': repo_groups_data,
862 'data': repo_groups_data,
860 'recordsTotal': repo_groups_data_total_count,
863 'recordsTotal': repo_groups_data_total_count,
861 'recordsFiltered': repo_groups_data_total_filtered_count,
864 'recordsFiltered': repo_groups_data_total_filtered_count,
862 })
865 })
863 return data
866 return data
864
867
865 def _get_defaults(self, repo_group_name):
868 def _get_defaults(self, repo_group_name):
866 repo_group = RepoGroup.get_by_group_name(repo_group_name)
869 repo_group = RepoGroup.get_by_group_name(repo_group_name)
867
870
868 if repo_group is None:
871 if repo_group is None:
869 return None
872 return None
870
873
871 defaults = repo_group.get_dict()
874 defaults = repo_group.get_dict()
872 defaults['repo_group_name'] = repo_group.name
875 defaults['repo_group_name'] = repo_group.name
873 defaults['repo_group_description'] = repo_group.group_description
876 defaults['repo_group_description'] = repo_group.group_description
874 defaults['repo_group_enable_locking'] = repo_group.enable_locking
877 defaults['repo_group_enable_locking'] = repo_group.enable_locking
875
878
876 # we use -1 as this is how in HTML, we mark an empty group
879 # we use -1 as this is how in HTML, we mark an empty group
877 defaults['repo_group'] = defaults['group_parent_id'] or -1
880 defaults['repo_group'] = defaults['group_parent_id'] or -1
878
881
879 # fill owner
882 # fill owner
880 if repo_group.user:
883 if repo_group.user:
881 defaults.update({'user': repo_group.user.username})
884 defaults.update({'user': repo_group.user.username})
882 else:
885 else:
883 replacement_user = User.get_first_super_admin().username
886 replacement_user = User.get_first_super_admin().username
884 defaults.update({'user': replacement_user})
887 defaults.update({'user': replacement_user})
885
888
886 return defaults
889 return defaults
General Comments 0
You need to be logged in to leave comments. Login now