##// END OF EJS Templates
permissions: flush permissions correctly for forks/repos when celery is used.
marcink -
r3388:70a576f9 default
parent child Browse files
Show More
@@ -1,194 +1,189 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import formencode
22 import formencode
23 import formencode.htmlfill
23 import formencode.htmlfill
24
24
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
26 from pyramid.view import view_config
26 from pyramid.view import view_config
27 from pyramid.renderers import render
27 from pyramid.renderers import render
28 from pyramid.response import Response
28 from pyramid.response import Response
29
29
30 from rhodecode import events
30 from rhodecode import events
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode.lib.celerylib.utils import get_task_id
32 from rhodecode.lib.celerylib.utils import get_task_id
33
33
34 from rhodecode.lib.ext_json import json
34 from rhodecode.lib.ext_json import json
35 from rhodecode.lib.auth import (
35 from rhodecode.lib.auth import (
36 LoginRequired, CSRFRequired, NotAnonymous,
36 LoginRequired, CSRFRequired, NotAnonymous,
37 HasPermissionAny, HasRepoGroupPermissionAny)
37 HasPermissionAny, HasRepoGroupPermissionAny)
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.utils import repo_name_slug
39 from rhodecode.lib.utils import repo_name_slug
40 from rhodecode.lib.utils2 import safe_int, safe_unicode
40 from rhodecode.lib.utils2 import safe_int, safe_unicode
41 from rhodecode.model.forms import RepoForm
41 from rhodecode.model.forms import RepoForm
42 from rhodecode.model.repo import RepoModel
42 from rhodecode.model.repo import RepoModel
43 from rhodecode.model.scm import RepoList, RepoGroupList, ScmModel
43 from rhodecode.model.scm import RepoList, RepoGroupList, ScmModel
44 from rhodecode.model.settings import SettingsModel
44 from rhodecode.model.settings import SettingsModel
45 from rhodecode.model.db import Repository, RepoGroup
45 from rhodecode.model.db import Repository, RepoGroup
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 class AdminReposView(BaseAppView, DataGridAppView):
50 class AdminReposView(BaseAppView, DataGridAppView):
51
51
52 def load_default_context(self):
52 def load_default_context(self):
53 c = self._get_local_tmpl_context()
53 c = self._get_local_tmpl_context()
54
54
55 return c
55 return c
56
56
57 def _load_form_data(self, c):
57 def _load_form_data(self, c):
58 acl_groups = RepoGroupList(RepoGroup.query().all(),
58 acl_groups = RepoGroupList(RepoGroup.query().all(),
59 perm_set=['group.write', 'group.admin'])
59 perm_set=['group.write', 'group.admin'])
60 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
60 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
61 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
61 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
62 c.landing_revs_choices, c.landing_revs = \
62 c.landing_revs_choices, c.landing_revs = \
63 ScmModel().get_repo_landing_revs(self.request.translate)
63 ScmModel().get_repo_landing_revs(self.request.translate)
64 c.personal_repo_group = self._rhodecode_user.personal_repo_group
64 c.personal_repo_group = self._rhodecode_user.personal_repo_group
65
65
66 @LoginRequired()
66 @LoginRequired()
67 @NotAnonymous()
67 @NotAnonymous()
68 # perms check inside
68 # perms check inside
69 @view_config(
69 @view_config(
70 route_name='repos', request_method='GET',
70 route_name='repos', request_method='GET',
71 renderer='rhodecode:templates/admin/repos/repos.mako')
71 renderer='rhodecode:templates/admin/repos/repos.mako')
72 def repository_list(self):
72 def repository_list(self):
73 c = self.load_default_context()
73 c = self.load_default_context()
74
74
75 repo_list = Repository.get_all_repos()
75 repo_list = Repository.get_all_repos()
76 c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
76 c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
77 repos_data = RepoModel().get_repos_as_dict(
77 repos_data = RepoModel().get_repos_as_dict(
78 repo_list=c.repo_list, admin=True, super_user_actions=True)
78 repo_list=c.repo_list, admin=True, super_user_actions=True)
79 # json used to render the grid
79 # json used to render the grid
80 c.data = json.dumps(repos_data)
80 c.data = json.dumps(repos_data)
81
81
82 return self._get_template_context(c)
82 return self._get_template_context(c)
83
83
84 @LoginRequired()
84 @LoginRequired()
85 @NotAnonymous()
85 @NotAnonymous()
86 # perms check inside
86 # perms check inside
87 @view_config(
87 @view_config(
88 route_name='repo_new', request_method='GET',
88 route_name='repo_new', request_method='GET',
89 renderer='rhodecode:templates/admin/repos/repo_add.mako')
89 renderer='rhodecode:templates/admin/repos/repo_add.mako')
90 def repository_new(self):
90 def repository_new(self):
91 c = self.load_default_context()
91 c = self.load_default_context()
92
92
93 new_repo = self.request.GET.get('repo', '')
93 new_repo = self.request.GET.get('repo', '')
94 parent_group = safe_int(self.request.GET.get('parent_group'))
94 parent_group = safe_int(self.request.GET.get('parent_group'))
95 _gr = RepoGroup.get(parent_group)
95 _gr = RepoGroup.get(parent_group)
96
96
97 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
97 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
98 # you're not super admin nor have global create permissions,
98 # you're not super admin nor have global create permissions,
99 # but maybe you have at least write permission to a parent group ?
99 # but maybe you have at least write permission to a parent group ?
100
100
101 gr_name = _gr.group_name if _gr else None
101 gr_name = _gr.group_name if _gr else None
102 # create repositories with write permission on group is set to true
102 # create repositories with write permission on group is set to true
103 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
103 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
104 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
104 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
105 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
105 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
106 if not (group_admin or (group_write and create_on_write)):
106 if not (group_admin or (group_write and create_on_write)):
107 raise HTTPForbidden()
107 raise HTTPForbidden()
108
108
109 self._load_form_data(c)
109 self._load_form_data(c)
110 c.new_repo = repo_name_slug(new_repo)
110 c.new_repo = repo_name_slug(new_repo)
111
111
112 # apply the defaults from defaults page
112 # apply the defaults from defaults page
113 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
113 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
114 # set checkbox to autochecked
114 # set checkbox to autochecked
115 defaults['repo_copy_permissions'] = True
115 defaults['repo_copy_permissions'] = True
116
116
117 parent_group_choice = '-1'
117 parent_group_choice = '-1'
118 if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
118 if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
119 parent_group_choice = self._rhodecode_user.personal_repo_group
119 parent_group_choice = self._rhodecode_user.personal_repo_group
120
120
121 if parent_group and _gr:
121 if parent_group and _gr:
122 if parent_group in [x[0] for x in c.repo_groups]:
122 if parent_group in [x[0] for x in c.repo_groups]:
123 parent_group_choice = safe_unicode(parent_group)
123 parent_group_choice = safe_unicode(parent_group)
124
124
125 defaults.update({'repo_group': parent_group_choice})
125 defaults.update({'repo_group': parent_group_choice})
126
126
127 data = render('rhodecode:templates/admin/repos/repo_add.mako',
127 data = render('rhodecode:templates/admin/repos/repo_add.mako',
128 self._get_template_context(c), self.request)
128 self._get_template_context(c), self.request)
129 html = formencode.htmlfill.render(
129 html = formencode.htmlfill.render(
130 data,
130 data,
131 defaults=defaults,
131 defaults=defaults,
132 encoding="UTF-8",
132 encoding="UTF-8",
133 force_defaults=False
133 force_defaults=False
134 )
134 )
135 return Response(html)
135 return Response(html)
136
136
137 @LoginRequired()
137 @LoginRequired()
138 @NotAnonymous()
138 @NotAnonymous()
139 @CSRFRequired()
139 @CSRFRequired()
140 # perms check inside
140 # perms check inside
141 @view_config(
141 @view_config(
142 route_name='repo_create', request_method='POST',
142 route_name='repo_create', request_method='POST',
143 renderer='rhodecode:templates/admin/repos/repos.mako')
143 renderer='rhodecode:templates/admin/repos/repos.mako')
144 def repository_create(self):
144 def repository_create(self):
145 c = self.load_default_context()
145 c = self.load_default_context()
146
146
147 form_result = {}
147 form_result = {}
148 self._load_form_data(c)
148 self._load_form_data(c)
149
149
150 try:
150 try:
151 # CanWriteToGroup validators checks permissions of this POST
151 # CanWriteToGroup validators checks permissions of this POST
152 form = RepoForm(
152 form = RepoForm(
153 self.request.translate, repo_groups=c.repo_groups_choices,
153 self.request.translate, repo_groups=c.repo_groups_choices,
154 landing_revs=c.landing_revs_choices)()
154 landing_revs=c.landing_revs_choices)()
155 form_result = form.to_python(dict(self.request.POST))
155 form_result = form.to_python(dict(self.request.POST))
156 copy_permissions = form_result.get('repo_copy_permissions')
156 copy_permissions = form_result.get('repo_copy_permissions')
157 # create is done sometimes async on celery, db transaction
157 # create is done sometimes async on celery, db transaction
158 # management is handled there.
158 # management is handled there.
159 task = RepoModel().create(form_result, self._rhodecode_user.user_id)
159 task = RepoModel().create(form_result, self._rhodecode_user.user_id)
160 task_id = get_task_id(task)
160 task_id = get_task_id(task)
161 except formencode.Invalid as errors:
161 except formencode.Invalid as errors:
162 data = render('rhodecode:templates/admin/repos/repo_add.mako',
162 data = render('rhodecode:templates/admin/repos/repo_add.mako',
163 self._get_template_context(c), self.request)
163 self._get_template_context(c), self.request)
164 html = formencode.htmlfill.render(
164 html = formencode.htmlfill.render(
165 data,
165 data,
166 defaults=errors.value,
166 defaults=errors.value,
167 errors=errors.error_dict or {},
167 errors=errors.error_dict or {},
168 prefix_error=False,
168 prefix_error=False,
169 encoding="UTF-8",
169 encoding="UTF-8",
170 force_defaults=False
170 force_defaults=False
171 )
171 )
172 return Response(html)
172 return Response(html)
173
173
174 except Exception as e:
174 except Exception as e:
175 msg = self._log_creation_exception(e, form_result.get('repo_name'))
175 msg = self._log_creation_exception(e, form_result.get('repo_name'))
176 h.flash(msg, category='error')
176 h.flash(msg, category='error')
177 raise HTTPFound(h.route_path('home'))
177 raise HTTPFound(h.route_path('home'))
178
178
179 repo_name = form_result.get('repo_name_full')
179 repo_name = form_result.get('repo_name_full')
180
180
181 affected_user_ids = [self._rhodecode_user.user_id]
181 affected_user_ids = [self._rhodecode_user.user_id]
182 if copy_permissions:
182 if copy_permissions:
183 repository = Repository.get_by_repo_name(repo_name)
183 # permission flush is done in repo creating
184 # also include those newly created by copy
184 pass
185 user_group_perms = repository.permissions(expand_from_user_groups=True)
186 copy_perms = [perm['user_id'] for perm in user_group_perms]
187 # also include those newly created by copy
188 affected_user_ids.extend(copy_perms)
189
190 events.trigger(events.UserPermissionsChange(affected_user_ids))
185 events.trigger(events.UserPermissionsChange(affected_user_ids))
191
186
192 raise HTTPFound(
187 raise HTTPFound(
193 h.route_path('repo_creating', repo_name=repo_name,
188 h.route_path('repo_creating', repo_name=repo_name,
194 _query=dict(task_id=task_id)))
189 _query=dict(task_id=task_id)))
@@ -1,114 +1,125 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 from pyramid.view import view_config
23 from pyramid.view import view_config
24 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
24 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
25
25
26 from rhodecode import events
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps._base import BaseAppView
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib import helpers as h
28 from rhodecode.lib.auth import (NotAnonymous, HasRepoPermissionAny)
29 from rhodecode.lib.auth import (NotAnonymous, HasRepoPermissionAny)
29 from rhodecode.model.db import Repository
30 from rhodecode.model.db import Repository
30 from rhodecode.model.validation_schema.types import RepoNameType
31 from rhodecode.model.validation_schema.types import RepoNameType
31
32
32 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
33
34
34
35
35 class RepoChecksView(BaseAppView):
36 class RepoChecksView(BaseAppView):
36 def load_default_context(self):
37 def load_default_context(self):
37 c = self._get_local_tmpl_context()
38 c = self._get_local_tmpl_context()
38
39
39 return c
40 return c
40
41
41 @NotAnonymous()
42 @NotAnonymous()
42 @view_config(
43 @view_config(
43 route_name='repo_creating', request_method='GET',
44 route_name='repo_creating', request_method='GET',
44 renderer='rhodecode:templates/admin/repos/repo_creating.mako')
45 renderer='rhodecode:templates/admin/repos/repo_creating.mako')
45 def repo_creating(self):
46 def repo_creating(self):
46 c = self.load_default_context()
47 c = self.load_default_context()
47 repo_name = self.request.matchdict['repo_name']
48 repo_name = self.request.matchdict['repo_name']
48 repo_name = RepoNameType().deserialize(None, repo_name)
49 repo_name = RepoNameType().deserialize(None, repo_name)
49 db_repo = Repository.get_by_repo_name(repo_name)
50 db_repo = Repository.get_by_repo_name(repo_name)
50
51
51 # check if maybe repo is already created
52 # check if maybe repo is already created
52 if db_repo and db_repo.repo_state in [Repository.STATE_CREATED]:
53 if db_repo and db_repo.repo_state in [Repository.STATE_CREATED]:
54 self.flush_permissions_on_creation(db_repo)
55
53 # re-check permissions before redirecting to prevent resource
56 # re-check permissions before redirecting to prevent resource
54 # discovery by checking the 302 code
57 # discovery by checking the 302 code
55 perm_set = ['repository.read', 'repository.write', 'repository.admin']
58 perm_set = ['repository.read', 'repository.write', 'repository.admin']
56 has_perm = HasRepoPermissionAny(*perm_set)(
59 has_perm = HasRepoPermissionAny(*perm_set)(
57 db_repo.repo_name, 'Repo Creating check')
60 db_repo.repo_name, 'Repo Creating check')
58 if not has_perm:
61 if not has_perm:
59 raise HTTPNotFound()
62 raise HTTPNotFound()
60
63
61 raise HTTPFound(h.route_path(
64 raise HTTPFound(h.route_path(
62 'repo_summary', repo_name=db_repo.repo_name))
65 'repo_summary', repo_name=db_repo.repo_name))
63
66
64 c.task_id = self.request.GET.get('task_id')
67 c.task_id = self.request.GET.get('task_id')
65 c.repo_name = repo_name
68 c.repo_name = repo_name
66
69
67 return self._get_template_context(c)
70 return self._get_template_context(c)
68
71
69 @NotAnonymous()
72 @NotAnonymous()
70 @view_config(
73 @view_config(
71 route_name='repo_creating_check', request_method='GET',
74 route_name='repo_creating_check', request_method='GET',
72 renderer='json_ext')
75 renderer='json_ext')
73 def repo_creating_check(self):
76 def repo_creating_check(self):
74 _ = self.request.translate
77 _ = self.request.translate
75 task_id = self.request.GET.get('task_id')
78 task_id = self.request.GET.get('task_id')
76 self.load_default_context()
79 self.load_default_context()
77
80
78 repo_name = self.request.matchdict['repo_name']
81 repo_name = self.request.matchdict['repo_name']
79
82
80 if task_id and task_id not in ['None']:
83 if task_id and task_id not in ['None']:
81 import rhodecode
84 import rhodecode
82 from rhodecode.lib.celerylib.loader import celery_app, exceptions
85 from rhodecode.lib.celerylib.loader import celery_app, exceptions
83 if rhodecode.CELERY_ENABLED:
86 if rhodecode.CELERY_ENABLED:
84 log.debug('celery: checking result for task:%s', task_id)
87 log.debug('celery: checking result for task:%s', task_id)
85 task = celery_app.AsyncResult(task_id)
88 task = celery_app.AsyncResult(task_id)
86 try:
89 try:
87 task.get(timeout=10)
90 task.get(timeout=10)
88 except exceptions.TimeoutError:
91 except exceptions.TimeoutError:
89 task = None
92 task = None
90 if task and task.failed():
93 if task and task.failed():
91 msg = self._log_creation_exception(task.result, repo_name)
94 msg = self._log_creation_exception(task.result, repo_name)
92 h.flash(msg, category='error')
95 h.flash(msg, category='error')
93 raise HTTPFound(h.route_path('home'), code=501)
96 raise HTTPFound(h.route_path('home'), code=501)
94
97
95 db_repo = Repository.get_by_repo_name(repo_name)
98 db_repo = Repository.get_by_repo_name(repo_name)
96 if db_repo and db_repo.repo_state == Repository.STATE_CREATED:
99 if db_repo and db_repo.repo_state == Repository.STATE_CREATED:
97 if db_repo.clone_uri:
100 if db_repo.clone_uri:
98 clone_uri = db_repo.clone_uri_hidden
101 clone_uri = db_repo.clone_uri_hidden
99 h.flash(_('Created repository %s from %s')
102 h.flash(_('Created repository %s from %s')
100 % (db_repo.repo_name, clone_uri), category='success')
103 % (db_repo.repo_name, clone_uri), category='success')
101 else:
104 else:
102 repo_url = h.link_to(
105 repo_url = h.link_to(
103 db_repo.repo_name,
106 db_repo.repo_name,
104 h.route_path('repo_summary', repo_name=db_repo.repo_name))
107 h.route_path('repo_summary', repo_name=db_repo.repo_name))
105 fork = db_repo.fork
108 fork = db_repo.fork
106 if fork:
109 if fork:
107 fork_name = fork.repo_name
110 fork_name = fork.repo_name
108 h.flash(h.literal(_('Forked repository %s as %s')
111 h.flash(h.literal(_('Forked repository %s as %s')
109 % (fork_name, repo_url)), category='success')
112 % (fork_name, repo_url)), category='success')
110 else:
113 else:
111 h.flash(h.literal(_('Created repository %s') % repo_url),
114 h.flash(h.literal(_('Created repository %s') % repo_url),
112 category='success')
115 category='success')
116 self.flush_permissions_on_creation(db_repo)
117
113 return {'result': True}
118 return {'result': True}
114 return {'result': False}
119 return {'result': False}
120
121 def flush_permissions_on_creation(self, db_repo):
122 # repo is finished and created, we flush the permissions now
123 user_group_perms = db_repo.permissions(expand_from_user_groups=True)
124 affected_user_ids = [perm['user_id'] for perm in user_group_perms]
125 events.trigger(events.UserPermissionsChange(affected_user_ids))
@@ -1,270 +1,266 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import datetime
22 import datetime
23 import formencode
23 import formencode
24 import formencode.htmlfill
24 import formencode.htmlfill
25
25
26 from pyramid.httpexceptions import HTTPFound
26 from pyramid.httpexceptions import HTTPFound
27 from pyramid.view import view_config
27 from pyramid.view import view_config
28 from pyramid.renderers import render
28 from pyramid.renderers import render
29 from pyramid.response import Response
29 from pyramid.response import Response
30
30
31 from rhodecode import events
31 from rhodecode import events
32 from rhodecode.apps._base import RepoAppView, DataGridAppView
32 from rhodecode.apps._base import RepoAppView, DataGridAppView
33 from rhodecode.lib.auth import (
33 from rhodecode.lib.auth import (
34 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous,
34 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous,
35 HasRepoPermissionAny, HasPermissionAnyDecorator, CSRFRequired)
35 HasRepoPermissionAny, HasPermissionAnyDecorator, CSRFRequired)
36 import rhodecode.lib.helpers as h
36 import rhodecode.lib.helpers as h
37 from rhodecode.lib.celerylib.utils import get_task_id
37 from rhodecode.lib.celerylib.utils import get_task_id
38 from rhodecode.model.db import coalesce, or_, Repository, RepoGroup
38 from rhodecode.model.db import coalesce, or_, Repository, RepoGroup
39 from rhodecode.model.repo import RepoModel
39 from rhodecode.model.repo import RepoModel
40 from rhodecode.model.forms import RepoForkForm
40 from rhodecode.model.forms import RepoForkForm
41 from rhodecode.model.scm import ScmModel, RepoGroupList
41 from rhodecode.model.scm import ScmModel, RepoGroupList
42 from rhodecode.lib.utils2 import safe_int, safe_unicode
42 from rhodecode.lib.utils2 import safe_int, safe_unicode
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46
46
47 class RepoForksView(RepoAppView, DataGridAppView):
47 class RepoForksView(RepoAppView, DataGridAppView):
48
48
49 def load_default_context(self):
49 def load_default_context(self):
50 c = self._get_local_tmpl_context(include_app_defaults=True)
50 c = self._get_local_tmpl_context(include_app_defaults=True)
51 c.rhodecode_repo = self.rhodecode_vcs_repo
51 c.rhodecode_repo = self.rhodecode_vcs_repo
52
52
53 acl_groups = RepoGroupList(
53 acl_groups = RepoGroupList(
54 RepoGroup.query().all(),
54 RepoGroup.query().all(),
55 perm_set=['group.write', 'group.admin'])
55 perm_set=['group.write', 'group.admin'])
56 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
56 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
57 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
57 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
58 choices, c.landing_revs = ScmModel().get_repo_landing_revs(
58 choices, c.landing_revs = ScmModel().get_repo_landing_revs(
59 self.request.translate)
59 self.request.translate)
60 c.landing_revs_choices = choices
60 c.landing_revs_choices = choices
61 c.personal_repo_group = c.rhodecode_user.personal_repo_group
61 c.personal_repo_group = c.rhodecode_user.personal_repo_group
62
62
63 return c
63 return c
64
64
65 @LoginRequired()
65 @LoginRequired()
66 @HasRepoPermissionAnyDecorator(
66 @HasRepoPermissionAnyDecorator(
67 'repository.read', 'repository.write', 'repository.admin')
67 'repository.read', 'repository.write', 'repository.admin')
68 @view_config(
68 @view_config(
69 route_name='repo_forks_show_all', request_method='GET',
69 route_name='repo_forks_show_all', request_method='GET',
70 renderer='rhodecode:templates/forks/forks.mako')
70 renderer='rhodecode:templates/forks/forks.mako')
71 def repo_forks_show_all(self):
71 def repo_forks_show_all(self):
72 c = self.load_default_context()
72 c = self.load_default_context()
73 return self._get_template_context(c)
73 return self._get_template_context(c)
74
74
75 @LoginRequired()
75 @LoginRequired()
76 @HasRepoPermissionAnyDecorator(
76 @HasRepoPermissionAnyDecorator(
77 'repository.read', 'repository.write', 'repository.admin')
77 'repository.read', 'repository.write', 'repository.admin')
78 @view_config(
78 @view_config(
79 route_name='repo_forks_data', request_method='GET',
79 route_name='repo_forks_data', request_method='GET',
80 renderer='json_ext', xhr=True)
80 renderer='json_ext', xhr=True)
81 def repo_forks_data(self):
81 def repo_forks_data(self):
82 _ = self.request.translate
82 _ = self.request.translate
83 self.load_default_context()
83 self.load_default_context()
84 column_map = {
84 column_map = {
85 'fork_name': 'repo_name',
85 'fork_name': 'repo_name',
86 'fork_date': 'created_on',
86 'fork_date': 'created_on',
87 'last_activity': 'updated_on'
87 'last_activity': 'updated_on'
88 }
88 }
89 draw, start, limit = self._extract_chunk(self.request)
89 draw, start, limit = self._extract_chunk(self.request)
90 search_q, order_by, order_dir = self._extract_ordering(
90 search_q, order_by, order_dir = self._extract_ordering(
91 self.request, column_map=column_map)
91 self.request, column_map=column_map)
92
92
93 acl_check = HasRepoPermissionAny(
93 acl_check = HasRepoPermissionAny(
94 'repository.read', 'repository.write', 'repository.admin')
94 'repository.read', 'repository.write', 'repository.admin')
95 repo_id = self.db_repo.repo_id
95 repo_id = self.db_repo.repo_id
96 allowed_ids = [-1]
96 allowed_ids = [-1]
97 for f in Repository.query().filter(Repository.fork_id == repo_id):
97 for f in Repository.query().filter(Repository.fork_id == repo_id):
98 if acl_check(f.repo_name, 'get forks check'):
98 if acl_check(f.repo_name, 'get forks check'):
99 allowed_ids.append(f.repo_id)
99 allowed_ids.append(f.repo_id)
100
100
101 forks_data_total_count = Repository.query()\
101 forks_data_total_count = Repository.query()\
102 .filter(Repository.fork_id == repo_id)\
102 .filter(Repository.fork_id == repo_id)\
103 .filter(Repository.repo_id.in_(allowed_ids))\
103 .filter(Repository.repo_id.in_(allowed_ids))\
104 .count()
104 .count()
105
105
106 # json generate
106 # json generate
107 base_q = Repository.query()\
107 base_q = Repository.query()\
108 .filter(Repository.fork_id == repo_id)\
108 .filter(Repository.fork_id == repo_id)\
109 .filter(Repository.repo_id.in_(allowed_ids))\
109 .filter(Repository.repo_id.in_(allowed_ids))\
110
110
111 if search_q:
111 if search_q:
112 like_expression = u'%{}%'.format(safe_unicode(search_q))
112 like_expression = u'%{}%'.format(safe_unicode(search_q))
113 base_q = base_q.filter(or_(
113 base_q = base_q.filter(or_(
114 Repository.repo_name.ilike(like_expression),
114 Repository.repo_name.ilike(like_expression),
115 Repository.description.ilike(like_expression),
115 Repository.description.ilike(like_expression),
116 ))
116 ))
117
117
118 forks_data_total_filtered_count = base_q.count()
118 forks_data_total_filtered_count = base_q.count()
119
119
120 sort_col = getattr(Repository, order_by, None)
120 sort_col = getattr(Repository, order_by, None)
121 if sort_col:
121 if sort_col:
122 if order_dir == 'asc':
122 if order_dir == 'asc':
123 # handle null values properly to order by NULL last
123 # handle null values properly to order by NULL last
124 if order_by in ['last_activity']:
124 if order_by in ['last_activity']:
125 sort_col = coalesce(sort_col, datetime.date.max)
125 sort_col = coalesce(sort_col, datetime.date.max)
126 sort_col = sort_col.asc()
126 sort_col = sort_col.asc()
127 else:
127 else:
128 # handle null values properly to order by NULL last
128 # handle null values properly to order by NULL last
129 if order_by in ['last_activity']:
129 if order_by in ['last_activity']:
130 sort_col = coalesce(sort_col, datetime.date.min)
130 sort_col = coalesce(sort_col, datetime.date.min)
131 sort_col = sort_col.desc()
131 sort_col = sort_col.desc()
132
132
133 base_q = base_q.order_by(sort_col)
133 base_q = base_q.order_by(sort_col)
134 base_q = base_q.offset(start).limit(limit)
134 base_q = base_q.offset(start).limit(limit)
135
135
136 fork_list = base_q.all()
136 fork_list = base_q.all()
137
137
138 def fork_actions(fork):
138 def fork_actions(fork):
139 url_link = h.route_path(
139 url_link = h.route_path(
140 'repo_compare',
140 'repo_compare',
141 repo_name=fork.repo_name,
141 repo_name=fork.repo_name,
142 source_ref_type=self.db_repo.landing_rev[0],
142 source_ref_type=self.db_repo.landing_rev[0],
143 source_ref=self.db_repo.landing_rev[1],
143 source_ref=self.db_repo.landing_rev[1],
144 target_ref_type=self.db_repo.landing_rev[0],
144 target_ref_type=self.db_repo.landing_rev[0],
145 target_ref=self.db_repo.landing_rev[1],
145 target_ref=self.db_repo.landing_rev[1],
146 _query=dict(merge=1, target_repo=f.repo_name))
146 _query=dict(merge=1, target_repo=f.repo_name))
147 return h.link_to(_('Compare fork'), url_link, class_='btn-link')
147 return h.link_to(_('Compare fork'), url_link, class_='btn-link')
148
148
149 def fork_name(fork):
149 def fork_name(fork):
150 return h.link_to(fork.repo_name,
150 return h.link_to(fork.repo_name,
151 h.route_path('repo_summary', repo_name=fork.repo_name))
151 h.route_path('repo_summary', repo_name=fork.repo_name))
152
152
153 forks_data = []
153 forks_data = []
154 for fork in fork_list:
154 for fork in fork_list:
155 forks_data.append({
155 forks_data.append({
156 "username": h.gravatar_with_user(self.request, fork.user.username),
156 "username": h.gravatar_with_user(self.request, fork.user.username),
157 "fork_name": fork_name(fork),
157 "fork_name": fork_name(fork),
158 "description": fork.description_safe,
158 "description": fork.description_safe,
159 "fork_date": h.age_component(fork.created_on, time_is_local=True),
159 "fork_date": h.age_component(fork.created_on, time_is_local=True),
160 "last_activity": h.format_date(fork.updated_on),
160 "last_activity": h.format_date(fork.updated_on),
161 "action": fork_actions(fork),
161 "action": fork_actions(fork),
162 })
162 })
163
163
164 data = ({
164 data = ({
165 'draw': draw,
165 'draw': draw,
166 'data': forks_data,
166 'data': forks_data,
167 'recordsTotal': forks_data_total_count,
167 'recordsTotal': forks_data_total_count,
168 'recordsFiltered': forks_data_total_filtered_count,
168 'recordsFiltered': forks_data_total_filtered_count,
169 })
169 })
170
170
171 return data
171 return data
172
172
173 @LoginRequired()
173 @LoginRequired()
174 @NotAnonymous()
174 @NotAnonymous()
175 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
175 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
176 @HasRepoPermissionAnyDecorator(
176 @HasRepoPermissionAnyDecorator(
177 'repository.read', 'repository.write', 'repository.admin')
177 'repository.read', 'repository.write', 'repository.admin')
178 @view_config(
178 @view_config(
179 route_name='repo_fork_new', request_method='GET',
179 route_name='repo_fork_new', request_method='GET',
180 renderer='rhodecode:templates/forks/forks.mako')
180 renderer='rhodecode:templates/forks/forks.mako')
181 def repo_fork_new(self):
181 def repo_fork_new(self):
182 c = self.load_default_context()
182 c = self.load_default_context()
183
183
184 defaults = RepoModel()._get_defaults(self.db_repo_name)
184 defaults = RepoModel()._get_defaults(self.db_repo_name)
185 # alter the description to indicate a fork
185 # alter the description to indicate a fork
186 defaults['description'] = (
186 defaults['description'] = (
187 'fork of repository: %s \n%s' % (
187 'fork of repository: %s \n%s' % (
188 defaults['repo_name'], defaults['description']))
188 defaults['repo_name'], defaults['description']))
189 # add suffix to fork
189 # add suffix to fork
190 defaults['repo_name'] = '%s-fork' % defaults['repo_name']
190 defaults['repo_name'] = '%s-fork' % defaults['repo_name']
191
191
192 data = render('rhodecode:templates/forks/fork.mako',
192 data = render('rhodecode:templates/forks/fork.mako',
193 self._get_template_context(c), self.request)
193 self._get_template_context(c), self.request)
194 html = formencode.htmlfill.render(
194 html = formencode.htmlfill.render(
195 data,
195 data,
196 defaults=defaults,
196 defaults=defaults,
197 encoding="UTF-8",
197 encoding="UTF-8",
198 force_defaults=False
198 force_defaults=False
199 )
199 )
200 return Response(html)
200 return Response(html)
201
201
202 @LoginRequired()
202 @LoginRequired()
203 @NotAnonymous()
203 @NotAnonymous()
204 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
204 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
205 @HasRepoPermissionAnyDecorator(
205 @HasRepoPermissionAnyDecorator(
206 'repository.read', 'repository.write', 'repository.admin')
206 'repository.read', 'repository.write', 'repository.admin')
207 @CSRFRequired()
207 @CSRFRequired()
208 @view_config(
208 @view_config(
209 route_name='repo_fork_create', request_method='POST',
209 route_name='repo_fork_create', request_method='POST',
210 renderer='rhodecode:templates/forks/fork.mako')
210 renderer='rhodecode:templates/forks/fork.mako')
211 def repo_fork_create(self):
211 def repo_fork_create(self):
212 _ = self.request.translate
212 _ = self.request.translate
213 c = self.load_default_context()
213 c = self.load_default_context()
214
214
215 _form = RepoForkForm(self.request.translate, old_data={'repo_type': self.db_repo.repo_type},
215 _form = RepoForkForm(self.request.translate, old_data={'repo_type': self.db_repo.repo_type},
216 repo_groups=c.repo_groups_choices,
216 repo_groups=c.repo_groups_choices,
217 landing_revs=c.landing_revs_choices)()
217 landing_revs=c.landing_revs_choices)()
218 post_data = dict(self.request.POST)
218 post_data = dict(self.request.POST)
219
219
220 # forbid injecting other repo by forging a request
220 # forbid injecting other repo by forging a request
221 post_data['fork_parent_id'] = self.db_repo.repo_id
221 post_data['fork_parent_id'] = self.db_repo.repo_id
222
222
223 form_result = {}
223 form_result = {}
224 task_id = None
224 task_id = None
225 try:
225 try:
226 form_result = _form.to_python(post_data)
226 form_result = _form.to_python(post_data)
227 copy_permissions = form_result.get('copy_permissions')
227 copy_permissions = form_result.get('copy_permissions')
228 # create fork is done sometimes async on celery, db transaction
228 # create fork is done sometimes async on celery, db transaction
229 # management is handled there.
229 # management is handled there.
230 task = RepoModel().create_fork(
230 task = RepoModel().create_fork(
231 form_result, c.rhodecode_user.user_id)
231 form_result, c.rhodecode_user.user_id)
232
232
233 task_id = get_task_id(task)
233 task_id = get_task_id(task)
234 except formencode.Invalid as errors:
234 except formencode.Invalid as errors:
235 c.rhodecode_db_repo = self.db_repo
235 c.rhodecode_db_repo = self.db_repo
236
236
237 data = render('rhodecode:templates/forks/fork.mako',
237 data = render('rhodecode:templates/forks/fork.mako',
238 self._get_template_context(c), self.request)
238 self._get_template_context(c), self.request)
239 html = formencode.htmlfill.render(
239 html = formencode.htmlfill.render(
240 data,
240 data,
241 defaults=errors.value,
241 defaults=errors.value,
242 errors=errors.error_dict or {},
242 errors=errors.error_dict or {},
243 prefix_error=False,
243 prefix_error=False,
244 encoding="UTF-8",
244 encoding="UTF-8",
245 force_defaults=False
245 force_defaults=False
246 )
246 )
247 return Response(html)
247 return Response(html)
248 except Exception:
248 except Exception:
249 log.exception(
249 log.exception(
250 u'Exception while trying to fork the repository %s', self.db_repo_name)
250 u'Exception while trying to fork the repository %s', self.db_repo_name)
251 msg = _('An error occurred during repository forking %s') % (self.db_repo_name, )
251 msg = _('An error occurred during repository forking %s') % (self.db_repo_name, )
252 h.flash(msg, category='error')
252 h.flash(msg, category='error')
253 raise HTTPFound(h.route_path('home'))
253 raise HTTPFound(h.route_path('home'))
254
254
255 repo_name = form_result.get('repo_name_full', self.db_repo_name)
255 repo_name = form_result.get('repo_name_full', self.db_repo_name)
256
256
257 affected_user_ids = [self._rhodecode_user.user_id]
257 affected_user_ids = [self._rhodecode_user.user_id]
258 if copy_permissions:
258 if copy_permissions:
259 repository = Repository.get_by_repo_name(repo_name)
259 # permission flush is done in repo creating
260 # also include those newly created by copy
260 pass
261 user_group_perms = repository.permissions(expand_from_user_groups=True)
262 copy_perms = [perm['user_id'] for perm in user_group_perms]
263 # also include those newly created by copy
264 affected_user_ids.extend(copy_perms)
265
261
266 events.trigger(events.UserPermissionsChange(affected_user_ids))
262 events.trigger(events.UserPermissionsChange(affected_user_ids))
267
263
268 raise HTTPFound(
264 raise HTTPFound(
269 h.route_path('repo_creating', repo_name=repo_name,
265 h.route_path('repo_creating', repo_name=repo_name,
270 _query=dict(task_id=task_id)))
266 _query=dict(task_id=task_id)))
General Comments 0
You need to be logged in to leave comments. Login now