##// END OF EJS Templates
repo-forks: stable, security, fix issue when forging fork_repo_id could allow reading other people forks.
marcink -
r2195:af6ecbb0 stable
parent child Browse files
Show More
@@ -1,196 +1,199 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
3 # Copyright (C) 2011-2017 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 """
21 """
22 forks controller for rhodecode
22 forks controller for rhodecode
23 """
23 """
24
24
25 import formencode
25 import formencode
26 import logging
26 import logging
27 from formencode import htmlfill
27 from formencode import htmlfill
28
28
29 from pylons import tmpl_context as c, request, url
29 from pylons import tmpl_context as c, request, url
30 from pylons.controllers.util import redirect
30 from pylons.controllers.util import redirect
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32
32
33 import rhodecode.lib.helpers as h
33 import rhodecode.lib.helpers as h
34
34
35 from rhodecode.lib import auth
35 from rhodecode.lib import auth
36 from rhodecode.lib.helpers import Page
36 from rhodecode.lib.helpers import Page
37 from rhodecode.lib.auth import (
37 from rhodecode.lib.auth import (
38 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous,
38 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous,
39 HasRepoPermissionAny, HasPermissionAnyDecorator, HasAcceptedRepoType)
39 HasRepoPermissionAny, HasPermissionAnyDecorator, HasAcceptedRepoType)
40 from rhodecode.lib.base import BaseRepoController, render
40 from rhodecode.lib.base import BaseRepoController, render
41 from rhodecode.model.db import Repository, RepoGroup, UserFollowing, User
41 from rhodecode.model.db import Repository, RepoGroup, UserFollowing, User
42 from rhodecode.model.repo import RepoModel
42 from rhodecode.model.repo import RepoModel
43 from rhodecode.model.forms import RepoForkForm
43 from rhodecode.model.forms import RepoForkForm
44 from rhodecode.model.scm import ScmModel, RepoGroupList
44 from rhodecode.model.scm import ScmModel, RepoGroupList
45 from rhodecode.lib.utils2 import safe_int
45 from rhodecode.lib.utils2 import safe_int
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 class ForksController(BaseRepoController):
50 class ForksController(BaseRepoController):
51
51
52 def __before__(self):
52 def __before__(self):
53 super(ForksController, self).__before__()
53 super(ForksController, self).__before__()
54
54
55 def __load_defaults(self):
55 def __load_defaults(self):
56 acl_groups = RepoGroupList(
56 acl_groups = RepoGroupList(
57 RepoGroup.query().all(),
57 RepoGroup.query().all(),
58 perm_set=['group.write', 'group.admin'])
58 perm_set=['group.write', 'group.admin'])
59 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
59 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
60 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
60 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
61 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
61 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
62 c.landing_revs_choices = choices
62 c.landing_revs_choices = choices
63 c.personal_repo_group = c.rhodecode_user.personal_repo_group
63 c.personal_repo_group = c.rhodecode_user.personal_repo_group
64
64
65 def __load_data(self, repo_name=None):
65 def __load_data(self, repo_name=None):
66 """
66 """
67 Load defaults settings for edit, and update
67 Load defaults settings for edit, and update
68
68
69 :param repo_name:
69 :param repo_name:
70 """
70 """
71 self.__load_defaults()
71 self.__load_defaults()
72
72
73 c.repo_info = Repository.get_by_repo_name(repo_name)
73 c.repo_info = Repository.get_by_repo_name(repo_name)
74 repo = c.repo_info.scm_instance()
74 repo = c.repo_info.scm_instance()
75
75
76 if c.repo_info is None:
76 if c.repo_info is None:
77 h.not_mapped_error(repo_name)
77 h.not_mapped_error(repo_name)
78 return redirect(url('repos'))
78 return redirect(url('repos'))
79
79
80 c.default_user_id = User.get_default_user().user_id
80 c.default_user_id = User.get_default_user().user_id
81 c.in_public_journal = UserFollowing.query()\
81 c.in_public_journal = UserFollowing.query()\
82 .filter(UserFollowing.user_id == c.default_user_id)\
82 .filter(UserFollowing.user_id == c.default_user_id)\
83 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
83 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
84
84
85 if c.repo_info.stats:
85 if c.repo_info.stats:
86 last_rev = c.repo_info.stats.stat_on_revision+1
86 last_rev = c.repo_info.stats.stat_on_revision+1
87 else:
87 else:
88 last_rev = 0
88 last_rev = 0
89 c.stats_revision = last_rev
89 c.stats_revision = last_rev
90
90
91 c.repo_last_rev = repo.count()
91 c.repo_last_rev = repo.count()
92
92
93 if last_rev == 0 or c.repo_last_rev == 0:
93 if last_rev == 0 or c.repo_last_rev == 0:
94 c.stats_percentage = 0
94 c.stats_percentage = 0
95 else:
95 else:
96 c.stats_percentage = '%.2f' % ((float((last_rev)) /
96 c.stats_percentage = '%.2f' % ((float((last_rev)) /
97 c.repo_last_rev) * 100)
97 c.repo_last_rev) * 100)
98
98
99 defaults = RepoModel()._get_defaults(repo_name)
99 defaults = RepoModel()._get_defaults(repo_name)
100 # alter the description to indicate a fork
100 # alter the description to indicate a fork
101 defaults['description'] = ('fork of repository: %s \n%s'
101 defaults['description'] = ('fork of repository: %s \n%s'
102 % (defaults['repo_name'],
102 % (defaults['repo_name'],
103 defaults['description']))
103 defaults['description']))
104 # add suffix to fork
104 # add suffix to fork
105 defaults['repo_name'] = '%s-fork' % defaults['repo_name']
105 defaults['repo_name'] = '%s-fork' % defaults['repo_name']
106
106
107 return defaults
107 return defaults
108
108
109 @LoginRequired()
109 @LoginRequired()
110 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
110 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
111 'repository.admin')
111 'repository.admin')
112 @HasAcceptedRepoType('git', 'hg')
112 @HasAcceptedRepoType('git', 'hg')
113 def forks(self, repo_name):
113 def forks(self, repo_name):
114 p = safe_int(request.GET.get('page', 1), 1)
114 p = safe_int(request.GET.get('page', 1), 1)
115 repo_id = c.rhodecode_db_repo.repo_id
115 repo_id = c.rhodecode_db_repo.repo_id
116 d = []
116 d = []
117 for r in Repository.get_repo_forks(repo_id):
117 for r in Repository.get_repo_forks(repo_id):
118 if not HasRepoPermissionAny(
118 if not HasRepoPermissionAny(
119 'repository.read', 'repository.write', 'repository.admin'
119 'repository.read', 'repository.write', 'repository.admin'
120 )(r.repo_name, 'get forks check'):
120 )(r.repo_name, 'get forks check'):
121 continue
121 continue
122 d.append(r)
122 d.append(r)
123 c.forks_pager = Page(d, page=p, items_per_page=20)
123 c.forks_pager = Page(d, page=p, items_per_page=20)
124
124
125 c.forks_data = render('/forks/forks_data.mako')
125 c.forks_data = render('/forks/forks_data.mako')
126
126
127 if request.environ.get('HTTP_X_PJAX'):
127 if request.environ.get('HTTP_X_PJAX'):
128 return c.forks_data
128 return c.forks_data
129
129
130 return render('/forks/forks.mako')
130 return render('/forks/forks.mako')
131
131
132 @LoginRequired()
132 @LoginRequired()
133 @NotAnonymous()
133 @NotAnonymous()
134 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
134 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
135 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
135 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
136 'repository.admin')
136 'repository.admin')
137 @HasAcceptedRepoType('git', 'hg')
137 @HasAcceptedRepoType('git', 'hg')
138 def fork(self, repo_name):
138 def fork(self, repo_name):
139 c.repo_info = Repository.get_by_repo_name(repo_name)
139 c.repo_info = Repository.get_by_repo_name(repo_name)
140 if not c.repo_info:
140 if not c.repo_info:
141 h.not_mapped_error(repo_name)
141 h.not_mapped_error(repo_name)
142 return redirect(h.route_path('home'))
142 return redirect(h.route_path('home'))
143
143
144 defaults = self.__load_data(repo_name)
144 defaults = self.__load_data(repo_name)
145
145
146 return htmlfill.render(
146 return htmlfill.render(
147 render('forks/fork.mako'),
147 render('forks/fork.mako'),
148 defaults=defaults,
148 defaults=defaults,
149 encoding="UTF-8",
149 encoding="UTF-8",
150 force_defaults=False
150 force_defaults=False
151 )
151 )
152
152
153 @LoginRequired()
153 @LoginRequired()
154 @NotAnonymous()
154 @NotAnonymous()
155 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
155 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
156 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
156 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
157 'repository.admin')
157 'repository.admin')
158 @HasAcceptedRepoType('git', 'hg')
158 @HasAcceptedRepoType('git', 'hg')
159 @auth.CSRFRequired()
159 @auth.CSRFRequired()
160 def fork_create(self, repo_name):
160 def fork_create(self, repo_name):
161 self.__load_defaults()
161 self.__load_defaults()
162 c.repo_info = Repository.get_by_repo_name(repo_name)
162 c.repo_info = Repository.get_by_repo_name(repo_name)
163 _form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type},
163 _form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type},
164 repo_groups=c.repo_groups_choices,
164 repo_groups=c.repo_groups_choices,
165 landing_revs=c.landing_revs_choices)()
165 landing_revs=c.landing_revs_choices)()
166 post_data = dict(request.POST)
167 post_data['fork_parent_id'] = c.repo_info.repo_id
168
166 form_result = {}
169 form_result = {}
167 task_id = None
170 task_id = None
168 try:
171 try:
169 form_result = _form.to_python(dict(request.POST))
172 form_result = _form.to_python(post_data)
170 # create fork is done sometimes async on celery, db transaction
173 # create fork is done sometimes async on celery, db transaction
171 # management is handled there.
174 # management is handled there.
172 task = RepoModel().create_fork(
175 task = RepoModel().create_fork(
173 form_result, c.rhodecode_user.user_id)
176 form_result, c.rhodecode_user.user_id)
174 from celery.result import BaseAsyncResult
177 from celery.result import BaseAsyncResult
175 if isinstance(task, BaseAsyncResult):
178 if isinstance(task, BaseAsyncResult):
176 task_id = task.task_id
179 task_id = task.task_id
177 except formencode.Invalid as errors:
180 except formencode.Invalid as errors:
178 c.new_repo = errors.value['repo_name']
181 c.new_repo = errors.value['repo_name']
179 return htmlfill.render(
182 return htmlfill.render(
180 render('forks/fork.mako'),
183 render('forks/fork.mako'),
181 defaults=errors.value,
184 defaults=errors.value,
182 errors=errors.error_dict or {},
185 errors=errors.error_dict or {},
183 prefix_error=False,
186 prefix_error=False,
184 encoding="UTF-8",
187 encoding="UTF-8",
185 force_defaults=False)
188 force_defaults=False)
186 except Exception:
189 except Exception:
187 log.exception(
190 log.exception(
188 u'Exception while trying to fork the repository %s', repo_name)
191 u'Exception while trying to fork the repository %s', repo_name)
189 msg = (
192 msg = (
190 _('An error occurred during repository forking %s') %
193 _('An error occurred during repository forking %s') %
191 (repo_name, ))
194 (repo_name, ))
192 h.flash(msg, category='error')
195 h.flash(msg, category='error')
193
196
194 return redirect(h.url('repo_creating_home',
197 return redirect(h.url('repo_creating_home',
195 repo_name=form_result['repo_name_full'],
198 repo_name=form_result['repo_name_full'],
196 task_id=task_id))
199 task_id=task_id))
General Comments 0
You need to be logged in to leave comments. Login now