repositories: ported repo_creating checks to pyramid.
- fixed security issues with resource discovery. - ported repository creating check into base view for repos.
This commit is contained in:
parent
21efeeab37
commit
3430eb74e5
14 changed files with 199 additions and 77 deletions
|
|
@ -362,14 +362,22 @@ class RepoRoutePredicate(object):
|
|||
repo_model = repo.RepoModel()
|
||||
by_name_match = repo_model.get_by_repo_name(repo_name, cache=True)
|
||||
|
||||
def redirect_if_creating(db_repo):
|
||||
if db_repo.repo_state in [repo.Repository.STATE_PENDING]:
|
||||
raise HTTPFound(
|
||||
request.route_path('repo_creating',
|
||||
repo_name=db_repo.repo_name))
|
||||
|
||||
if by_name_match:
|
||||
# register this as request object we can re-use later
|
||||
request.db_repo = by_name_match
|
||||
redirect_if_creating(by_name_match)
|
||||
return True
|
||||
|
||||
by_id_match = repo_model.get_repo_by_id(repo_name)
|
||||
if by_id_match:
|
||||
request.db_repo = by_id_match
|
||||
redirect_if_creating(by_id_match)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -22,6 +22,15 @@ from rhodecode.apps._base import add_route_with_slash
|
|||
|
||||
def includeme(config):
|
||||
|
||||
# repo creating checks, special cases that aren't repo routes
|
||||
config.add_route(
|
||||
name='repo_creating',
|
||||
pattern='/{repo_name:.*?[^/]}/repo_creating')
|
||||
|
||||
config.add_route(
|
||||
name='repo_creating_check',
|
||||
pattern='/{repo_name:.*?[^/]}/repo_creating_check')
|
||||
|
||||
# Summary
|
||||
# NOTE(marcink): one additional route is defined in very bottom, catch
|
||||
# all pattern
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ def route_path(name, params=None, **kwargs):
|
|||
'repo_summary': '/{repo_name}',
|
||||
'repo_stats': '/{repo_name}/repo_stats/{commit_id}',
|
||||
'repo_refs_data': '/{repo_name}/refs-data',
|
||||
'repo_refs_changelog_data': '/{repo_name}/refs-data-changelog'
|
||||
|
||||
'repo_refs_changelog_data': '/{repo_name}/refs-data-changelog',
|
||||
'repo_creating_check': '/{repo_name}/repo_creating_check',
|
||||
}[name].format(**kwargs)
|
||||
|
||||
if params:
|
||||
|
|
|
|||
110
rhodecode/apps/repository/views/repo_checks.py
Normal file
110
rhodecode/apps/repository/views/repo_checks.py
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2011-2017 RhodeCode GmbH
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License, version 3
|
||||
# (only), as published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# This program is dual-licensed. If you wish to learn more about the
|
||||
# RhodeCode Enterprise Edition, including its added features, Support services,
|
||||
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
||||
|
||||
import logging
|
||||
|
||||
from pyramid.view import view_config
|
||||
from pyramid.httpexceptions import HTTPFound, HTTPNotFound
|
||||
|
||||
from rhodecode.apps._base import BaseAppView
|
||||
from rhodecode.lib import helpers as h
|
||||
from rhodecode.lib.auth import (NotAnonymous, HasRepoPermissionAny)
|
||||
from rhodecode.model.db import Repository
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RepoChecksView(BaseAppView):
|
||||
def load_default_context(self):
|
||||
c = self._get_local_tmpl_context()
|
||||
self._register_global_c(c)
|
||||
return c
|
||||
|
||||
@NotAnonymous()
|
||||
@view_config(
|
||||
route_name='repo_creating', request_method='GET',
|
||||
renderer='rhodecode:templates/admin/repos/repo_creating.mako')
|
||||
def repo_creating(self):
|
||||
c = self.load_default_context()
|
||||
|
||||
repo_name = self.request.matchdict['repo_name']
|
||||
db_repo = Repository.get_by_repo_name(repo_name)
|
||||
if not db_repo:
|
||||
raise HTTPNotFound()
|
||||
|
||||
# check if maybe repo is already created
|
||||
if db_repo.repo_state in [Repository.STATE_CREATED]:
|
||||
# re-check permissions before redirecting to prevent resource
|
||||
# discovery by checking the 302 code
|
||||
perm_set = ['repository.read', 'repository.write', 'repository.admin']
|
||||
has_perm = HasRepoPermissionAny(*perm_set)(
|
||||
db_repo.repo_name, 'Repo Creating check')
|
||||
if not has_perm:
|
||||
raise HTTPNotFound()
|
||||
|
||||
raise HTTPFound(h.route_path(
|
||||
'repo_summary', repo_name=db_repo.repo_name))
|
||||
|
||||
c.task_id = self.request.GET.get('task_id')
|
||||
c.repo_name = repo_name
|
||||
|
||||
return self._get_template_context(c)
|
||||
|
||||
@NotAnonymous()
|
||||
@view_config(
|
||||
route_name='repo_creating_check', request_method='GET',
|
||||
renderer='json_ext')
|
||||
def repo_creating_check(self):
|
||||
_ = self.request.translate
|
||||
task_id = self.request.GET.get('task_id')
|
||||
self.load_default_context()
|
||||
|
||||
repo_name = self.request.matchdict['repo_name']
|
||||
|
||||
if task_id and task_id not in ['None']:
|
||||
import rhodecode
|
||||
from celery.result import AsyncResult
|
||||
if rhodecode.CELERY_ENABLED:
|
||||
task = AsyncResult(task_id)
|
||||
if task.failed():
|
||||
msg = self._log_creation_exception(task.result, repo_name)
|
||||
h.flash(msg, category='error')
|
||||
raise HTTPFound(h.route_path('home'), code=501)
|
||||
|
||||
db_repo = Repository.get_by_repo_name(repo_name)
|
||||
if db_repo and db_repo.repo_state == Repository.STATE_CREATED:
|
||||
if db_repo.clone_uri:
|
||||
clone_uri = db_repo.clone_uri_hidden
|
||||
h.flash(_('Created repository %s from %s')
|
||||
% (db_repo.repo_name, clone_uri), category='success')
|
||||
else:
|
||||
repo_url = h.link_to(
|
||||
db_repo.repo_name,
|
||||
h.route_path('repo_summary', repo_name=db_repo.repo_name))
|
||||
fork = db_repo.fork
|
||||
if fork:
|
||||
fork_name = fork.repo_name
|
||||
h.flash(h.literal(_('Forked repository %s as %s')
|
||||
% (fork_name, repo_url)), category='success')
|
||||
else:
|
||||
h.flash(h.literal(_('Created repository %s') % repo_url),
|
||||
category='success')
|
||||
return {'result': True}
|
||||
return {'result': False}
|
||||
|
|
@ -416,13 +416,6 @@ def make_map(config):
|
|||
# REPOSITORY ROUTES
|
||||
#==========================================================================
|
||||
|
||||
rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
|
||||
controller='admin/repos', action='repo_creating',
|
||||
requirements=URL_NAME_REQUIREMENTS)
|
||||
rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
|
||||
controller='admin/repos', action='repo_check',
|
||||
requirements=URL_NAME_REQUIREMENTS)
|
||||
|
||||
# repo edit options
|
||||
rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
|
||||
controller='admin/repos', action='edit_fields',
|
||||
|
|
|
|||
|
|
@ -31,8 +31,9 @@ from formencode import htmlfill
|
|||
from pylons import request, tmpl_context as c, url
|
||||
from pylons.controllers.util import redirect
|
||||
from pylons.i18n.translation import _
|
||||
from webob.exc import HTTPForbidden, HTTPNotFound, HTTPBadRequest
|
||||
from webob.exc import HTTPForbidden, HTTPBadRequest
|
||||
|
||||
from pyramid.httpexceptions import HTTPFound
|
||||
import rhodecode
|
||||
from rhodecode.lib import auth, helpers as h
|
||||
from rhodecode.lib.auth import (
|
||||
|
|
@ -183,9 +184,10 @@ class ReposController(BaseRepoController):
|
|||
h.flash(msg, category='error')
|
||||
return redirect(h.route_path('home'))
|
||||
|
||||
return redirect(h.url('repo_creating_home',
|
||||
repo_name=form_result['repo_name_full'],
|
||||
task_id=task_id))
|
||||
raise HTTPFound(
|
||||
h.route_path('repo_creating',
|
||||
repo_name=form_result['repo_name_full'],
|
||||
_query=dict(task_id=task_id)))
|
||||
|
||||
# perms check inside
|
||||
@NotAnonymous()
|
||||
|
|
@ -239,51 +241,6 @@ class ReposController(BaseRepoController):
|
|||
force_defaults=False
|
||||
)
|
||||
|
||||
@NotAnonymous()
|
||||
def repo_creating(self, repo_name):
|
||||
c.repo = repo_name
|
||||
c.task_id = request.GET.get('task_id')
|
||||
if not c.repo:
|
||||
raise HTTPNotFound()
|
||||
return render('admin/repos/repo_creating.mako')
|
||||
|
||||
@NotAnonymous()
|
||||
@jsonify
|
||||
def repo_check(self, repo_name):
|
||||
c.repo = repo_name
|
||||
task_id = request.GET.get('task_id')
|
||||
|
||||
if task_id and task_id not in ['None']:
|
||||
import rhodecode
|
||||
from celery.result import AsyncResult
|
||||
if rhodecode.CELERY_ENABLED:
|
||||
task = AsyncResult(task_id)
|
||||
if task.failed():
|
||||
msg = self._log_creation_exception(task.result, c.repo)
|
||||
h.flash(msg, category='error')
|
||||
return redirect(h.route_path('home'), code=501)
|
||||
|
||||
repo = Repository.get_by_repo_name(repo_name)
|
||||
if repo and repo.repo_state == Repository.STATE_CREATED:
|
||||
if repo.clone_uri:
|
||||
clone_uri = repo.clone_uri_hidden
|
||||
h.flash(_('Created repository %s from %s')
|
||||
% (repo.repo_name, clone_uri), category='success')
|
||||
else:
|
||||
repo_url = h.link_to(
|
||||
repo.repo_name,
|
||||
h.route_path('repo_summary',repo_name=repo.repo_name))
|
||||
fork = repo.fork
|
||||
if fork:
|
||||
fork_name = fork.repo_name
|
||||
h.flash(h.literal(_('Forked repository %s as %s')
|
||||
% (fork_name, repo_url)), category='success')
|
||||
else:
|
||||
h.flash(h.literal(_('Created repository %s') % repo_url),
|
||||
category='success')
|
||||
return {'result': True}
|
||||
return {'result': False}
|
||||
|
||||
@HasPermissionAllDecorator('hg.admin')
|
||||
def show(self, repo_name, format='html'):
|
||||
"""GET /repos/repo_name: Show a specific item"""
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ from pylons import tmpl_context as c, request, url
|
|||
from pylons.controllers.util import redirect
|
||||
from pylons.i18n.translation import _
|
||||
|
||||
from pyramid.httpexceptions import HTTPFound
|
||||
import rhodecode.lib.helpers as h
|
||||
|
||||
from rhodecode.lib import auth
|
||||
|
|
@ -191,6 +192,7 @@ class ForksController(BaseRepoController):
|
|||
(repo_name, ))
|
||||
h.flash(msg, category='error')
|
||||
|
||||
return redirect(h.url('repo_creating_home',
|
||||
repo_name=form_result['repo_name_full'],
|
||||
task_id=task_id))
|
||||
raise HTTPFound(
|
||||
h.route_path('repo_creating',
|
||||
repo_name=form_result['repo_name_full'],
|
||||
_query=dict(task_id=task_id)))
|
||||
|
|
|
|||
|
|
@ -1833,6 +1833,11 @@ BIN_FILENODE = 7
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
.creation_in_progress {
|
||||
color: @grey4
|
||||
}
|
||||
|
||||
.status_box_menu {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,6 +96,8 @@ function registerRCRoutes() {
|
|||
pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
|
||||
pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
|
||||
pyroutes.register('toggle_following', '/_admin/toggle_following', []);
|
||||
pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
|
||||
pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
|
||||
pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
|
||||
pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
|
||||
pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@
|
|||
<%inherit file="/base/base.mako"/>
|
||||
|
||||
<%def name="title()">
|
||||
${_('%s Creating repository') % c.repo_name}
|
||||
${_('{} Creating repository').format(c.repo_name)}
|
||||
%if c.rhodecode_name:
|
||||
· ${h.branding(c.rhodecode_name)}
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
<%def name="breadcrumbs_links()">
|
||||
${_('Creating repository')} ${c.repo}
|
||||
${_('Creating repository')} ${c.repo_name}
|
||||
</%def>
|
||||
|
||||
<%def name="menu_bar_nav()">
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
<script>
|
||||
(function worker() {
|
||||
var skipCheck = false;
|
||||
var url = "${h.url('repo_check_home', repo_name=c.repo_name, repo=c.repo, task_id=c.task_id)}";
|
||||
var url = "${h.route_path('repo_creating_check', repo_name=c.repo_name, _query=dict(task_id=c.task_id))}";
|
||||
$.ajax({
|
||||
url: url,
|
||||
complete: function(resp) {
|
||||
|
|
@ -48,12 +48,12 @@
|
|||
if (jsonResponse === undefined) {
|
||||
setTimeout(function () {
|
||||
// we might have a backend problem, try dashboard again
|
||||
window.location = "${h.route_path('repo_summary', repo_name = c.repo)}";
|
||||
window.location = "${h.route_path('repo_summary', repo_name = c.repo_name)}";
|
||||
}, 3000);
|
||||
} else {
|
||||
if (skipCheck || jsonResponse.result === true) {
|
||||
// success, means go to dashboard
|
||||
window.location = "${h.route_path('repo_summary', repo_name = c.repo)}";
|
||||
window.location = "${h.route_path('repo_summary', repo_name = c.repo_name)}";
|
||||
} else {
|
||||
// Schedule the next request when the current one's complete
|
||||
setTimeout(worker, 1000);
|
||||
|
|
@ -61,10 +61,17 @@
|
|||
}
|
||||
}
|
||||
else {
|
||||
window.location = "${h.route_path('home')}";
|
||||
var payload = {
|
||||
message: {
|
||||
message: _gettext('Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.').format(resp.status, resp.statusText),
|
||||
level: 'error',
|
||||
force: true
|
||||
}
|
||||
};
|
||||
$.Topic('/notifications').publish(payload);
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</%def>
|
||||
</%def>
|
||||
|
|
|
|||
|
|
@ -67,7 +67,9 @@
|
|||
<a href="${h.route_path('repo_summary',repo_name=fork_of.repo_name)}"><i class="icon-code-fork"></i></a>
|
||||
%endif
|
||||
%if rstate == 'repo_state_pending':
|
||||
<i class="icon-cogs" title="${_('Repository creating in progress...')}"></i>
|
||||
<span class="creation_in_progress tooltip" title="${_('This repository is being created in a background task')}">
|
||||
(${_('creating...')})
|
||||
</span>
|
||||
%endif
|
||||
</div>
|
||||
</%def>
|
||||
|
|
|
|||
|
|
@ -122,8 +122,6 @@ class _BaseTest(TestController):
|
|||
csrf_token=self.csrf_token))
|
||||
|
||||
# run the check page that triggers the flash message
|
||||
# response = self.app.get(url('repo_check_home', repo_name=repo_name))
|
||||
# assert response.json == {u'result': True}
|
||||
repo_gr_url = h.route_path(
|
||||
'repo_group_home', repo_group_name=repo_group_name)
|
||||
|
||||
|
|
|
|||
|
|
@ -35,14 +35,26 @@ from rhodecode.model.settings import SettingsModel, VcsSettingsModel
|
|||
from rhodecode.model.user import UserModel
|
||||
from rhodecode.tests import (
|
||||
login_user_session, url, assert_session_flash, TEST_USER_ADMIN_LOGIN,
|
||||
TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, HG_REPO, GIT_REPO,
|
||||
logout_user_session)
|
||||
TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, logout_user_session)
|
||||
from rhodecode.tests.fixture import Fixture, error_function
|
||||
from rhodecode.tests.utils import AssertResponse, repo_on_filesystem
|
||||
|
||||
fixture = Fixture()
|
||||
|
||||
|
||||
def route_path(name, params=None, **kwargs):
|
||||
import urllib
|
||||
|
||||
base_url = {
|
||||
'repo_summary': '/{repo_name}',
|
||||
'repo_creating_check': '/{repo_name}/repo_creating_check',
|
||||
}[name].format(**kwargs)
|
||||
|
||||
if params:
|
||||
base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
|
||||
return base_url
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("app")
|
||||
class TestAdminRepos(object):
|
||||
|
||||
|
|
@ -461,7 +473,8 @@ class TestAdminRepos(object):
|
|||
repo_name_utf8 = safe_str(repo_name)
|
||||
|
||||
# run the check page that triggers the flash message
|
||||
response = self.app.get(url('repo_check_home', repo_name=repo_name))
|
||||
response = self.app.get(
|
||||
route_path('repo_creating_check', repo_name=safe_str(repo_name)))
|
||||
assert response.json == {u'result': True}
|
||||
|
||||
flash_msg = u'Created repository <a href="/{}">{}</a>'.format(
|
||||
|
|
@ -475,7 +488,8 @@ class TestAdminRepos(object):
|
|||
assert new_repo.description == description
|
||||
|
||||
# test if the repository is visible in the list ?
|
||||
response = self.app.get(h.route_path('repo_summary', repo_name=repo_name))
|
||||
response = self.app.get(
|
||||
h.route_path('repo_summary', repo_name=safe_str(repo_name)))
|
||||
response.mustcontain(repo_name)
|
||||
response.mustcontain(backend.alias)
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,19 @@ from rhodecode.model.meta import Session
|
|||
fixture = Fixture()
|
||||
|
||||
|
||||
def route_path(name, params=None, **kwargs):
|
||||
import urllib
|
||||
|
||||
base_url = {
|
||||
'repo_summary': '/{repo_name}',
|
||||
'repo_creating_check': '/{repo_name}/repo_creating_check',
|
||||
}[name].format(**kwargs)
|
||||
|
||||
if params:
|
||||
base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
|
||||
return base_url
|
||||
|
||||
|
||||
class _BaseTest(TestController):
|
||||
|
||||
REPO = None
|
||||
|
|
@ -134,7 +147,8 @@ class _BaseTest(TestController):
|
|||
assert repo.fork.repo_name == self.REPO
|
||||
|
||||
# run the check page that triggers the flash message
|
||||
response = self.app.get(url('repo_check_home', repo_name=fork_name_full))
|
||||
response = self.app.get(
|
||||
route_path('repo_creating_check', repo_name=fork_name_full))
|
||||
# test if we have a message that fork is ok
|
||||
assert_session_flash(response,
|
||||
'Forked repository %s as <a href="/%s">%s</a>'
|
||||
|
|
@ -180,7 +194,8 @@ class _BaseTest(TestController):
|
|||
assert repo.fork.repo_name == self.REPO
|
||||
|
||||
# run the check page that triggers the flash message
|
||||
response = self.app.get(url('repo_check_home', repo_name=fork_name))
|
||||
response = self.app.get(
|
||||
route_path('repo_creating_check', repo_name=fork_name))
|
||||
# test if we have a message that fork is ok
|
||||
assert_session_flash(response,
|
||||
'Forked repository %s as <a href="/%s">%s</a>'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue