test_admin_gists.py
389 lines
| 15.3 KiB
| text/x-python
|
PythonLexer
r5088 | # Copyright (C) 2010-2023 RhodeCode GmbH | |||
r1891 | # | |||
# 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 mock | ||||
import pytest | ||||
from rhodecode.lib import helpers as h | ||||
from rhodecode.model.db import User, Gist | ||||
from rhodecode.model.gist import GistModel | ||||
from rhodecode.model.meta import Session | ||||
from rhodecode.tests import ( | ||||
TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, | ||||
TestController, assert_session_flash) | ||||
def route_path(name, params=None, **kwargs): | ||||
r5087 | import urllib.parse | |||
import urllib.error | ||||
r1891 | from rhodecode.apps._base import ADMIN_PREFIX | |||
base_url = { | ||||
'gists_show': ADMIN_PREFIX + '/gists', | ||||
'gists_new': ADMIN_PREFIX + '/gists/new', | ||||
'gists_create': ADMIN_PREFIX + '/gists/create', | ||||
'gist_show': ADMIN_PREFIX + '/gists/{gist_id}', | ||||
'gist_delete': ADMIN_PREFIX + '/gists/{gist_id}/delete', | ||||
'gist_edit': ADMIN_PREFIX + '/gists/{gist_id}/edit', | ||||
'gist_edit_check_revision': ADMIN_PREFIX + '/gists/{gist_id}/edit/check_revision', | ||||
'gist_update': ADMIN_PREFIX + '/gists/{gist_id}/update', | ||||
r4610 | 'gist_show_rev': ADMIN_PREFIX + '/gists/{gist_id}/rev/{revision}', | |||
'gist_show_formatted': ADMIN_PREFIX + '/gists/{gist_id}/rev/{revision}/{format}', | ||||
'gist_show_formatted_path': ADMIN_PREFIX + '/gists/{gist_id}/rev/{revision}/{format}/{f_path}', | ||||
r1891 | ||||
}[name].format(**kwargs) | ||||
if params: | ||||
r4914 | base_url = '{}?{}'.format(base_url, urllib.parse.urlencode(params)) | |||
r1891 | return base_url | |||
class GistUtility(object): | ||||
def __init__(self): | ||||
self._gist_ids = [] | ||||
def __call__( | ||||
r5087 | self, f_name: bytes, content: bytes = b'some gist', lifetime=-1, | |||
r1891 | description='gist-desc', gist_type='public', | |||
acl_level=Gist.GIST_PUBLIC, owner=TEST_USER_ADMIN_LOGIN): | ||||
gist_mapping = { | ||||
f_name: {'content': content} | ||||
} | ||||
user = User.get_by_username(owner) | ||||
gist = GistModel().create( | ||||
description, owner=user, gist_mapping=gist_mapping, | ||||
gist_type=gist_type, lifetime=lifetime, gist_acl_level=acl_level) | ||||
Session().commit() | ||||
self._gist_ids.append(gist.gist_id) | ||||
return gist | ||||
def cleanup(self): | ||||
for gist_id in self._gist_ids: | ||||
gist = Gist.get(gist_id) | ||||
if gist: | ||||
Session().delete(gist) | ||||
Session().commit() | ||||
r3946 | @pytest.fixture() | |||
r1891 | def create_gist(request): | |||
gist_utility = GistUtility() | ||||
request.addfinalizer(gist_utility.cleanup) | ||||
return gist_utility | ||||
class TestGistsController(TestController): | ||||
def test_index_empty(self, create_gist): | ||||
self.log_user() | ||||
response = self.app.get(route_path('gists_show')) | ||||
r5087 | response.mustcontain('var gist_data = [];') | |||
r1891 | ||||
def test_index(self, create_gist): | ||||
self.log_user() | ||||
r5087 | g1 = create_gist(b'gist1') | |||
g2 = create_gist(b'gist2', lifetime=1400) | ||||
g3 = create_gist(b'gist3', description='gist3-desc') | ||||
g4 = create_gist(b'gist4', gist_type='private').gist_access_id | ||||
r1891 | response = self.app.get(route_path('gists_show')) | |||
r4193 | response.mustcontain(g1.gist_access_id) | |||
response.mustcontain(g2.gist_access_id) | ||||
response.mustcontain(g3.gist_access_id) | ||||
r1891 | response.mustcontain('gist3-desc') | |||
r4193 | response.mustcontain(no=[g4]) | |||
r1891 | ||||
# Expiration information should be visible | ||||
r5087 | expires_tag = str(h.age_component(h.time_to_utcdatetime(g2.gist_expires))) | |||
r1891 | response.mustcontain(expires_tag.replace('"', '\\"')) | |||
def test_index_private_gists(self, create_gist): | ||||
self.log_user() | ||||
r5087 | gist = create_gist(b'gist5', gist_type='private') | |||
r1891 | response = self.app.get(route_path('gists_show', params=dict(private=1))) | |||
# and privates | ||||
r4193 | response.mustcontain(gist.gist_access_id) | |||
r1891 | ||||
def test_index_show_all(self, create_gist): | ||||
self.log_user() | ||||
r5087 | create_gist(b'gist1') | |||
create_gist(b'gist2', lifetime=1400) | ||||
create_gist(b'gist3', description='gist3-desc') | ||||
create_gist(b'gist4', gist_type='private') | ||||
r1891 | ||||
response = self.app.get(route_path('gists_show', params=dict(all=1))) | ||||
assert len(GistModel.get_all()) == 4 | ||||
# and privates | ||||
for gist in GistModel.get_all(): | ||||
r4193 | response.mustcontain(gist.gist_access_id) | |||
r1891 | ||||
def test_index_show_all_hidden_from_regular(self, create_gist): | ||||
self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | ||||
r5087 | create_gist(b'gist2', gist_type='private') | |||
create_gist(b'gist3', gist_type='private') | ||||
create_gist(b'gist4', gist_type='private') | ||||
r1891 | ||||
response = self.app.get(route_path('gists_show', params=dict(all=1))) | ||||
assert len(GistModel.get_all()) == 3 | ||||
# since we don't have access to private in this view, we | ||||
# should see nothing | ||||
for gist in GistModel.get_all(): | ||||
r4193 | response.mustcontain(no=[gist.gist_access_id]) | |||
r1891 | ||||
def test_create(self): | ||||
self.log_user() | ||||
response = self.app.post( | ||||
route_path('gists_create'), | ||||
params={'lifetime': -1, | ||||
'content': 'gist test', | ||||
'filename': 'foo', | ||||
r4083 | 'gist_type': 'public', | |||
r1891 | 'gist_acl_level': Gist.ACL_LEVEL_PUBLIC, | |||
'csrf_token': self.csrf_token}, | ||||
status=302) | ||||
response = response.follow() | ||||
response.mustcontain('added file: foo') | ||||
response.mustcontain('gist test') | ||||
def test_create_with_path_with_dirs(self): | ||||
self.log_user() | ||||
response = self.app.post( | ||||
route_path('gists_create'), | ||||
params={'lifetime': -1, | ||||
'content': 'gist test', | ||||
'filename': '/home/foo', | ||||
r4083 | 'gist_type': 'public', | |||
r1891 | 'gist_acl_level': Gist.ACL_LEVEL_PUBLIC, | |||
'csrf_token': self.csrf_token}, | ||||
status=200) | ||||
response.mustcontain('Filename /home/foo cannot be inside a directory') | ||||
def test_access_expired_gist(self, create_gist): | ||||
self.log_user() | ||||
r5087 | gist = create_gist(b'never-see-me') | |||
r1891 | gist.gist_expires = 0 # 1970 | |||
Session().add(gist) | ||||
Session().commit() | ||||
self.app.get(route_path('gist_show', gist_id=gist.gist_access_id), | ||||
status=404) | ||||
def test_create_private(self): | ||||
self.log_user() | ||||
response = self.app.post( | ||||
route_path('gists_create'), | ||||
params={'lifetime': -1, | ||||
'content': 'private gist test', | ||||
'filename': 'private-foo', | ||||
r4083 | 'gist_type': 'private', | |||
r1891 | 'gist_acl_level': Gist.ACL_LEVEL_PUBLIC, | |||
'csrf_token': self.csrf_token}, | ||||
status=302) | ||||
response = response.follow() | ||||
response.mustcontain('added file: private-foo<') | ||||
response.mustcontain('private gist test') | ||||
response.mustcontain('Private Gist') | ||||
# Make sure private gists are not indexed by robots | ||||
response.mustcontain( | ||||
'<meta name="robots" content="noindex, nofollow">') | ||||
def test_create_private_acl_private(self): | ||||
self.log_user() | ||||
response = self.app.post( | ||||
route_path('gists_create'), | ||||
params={'lifetime': -1, | ||||
'content': 'private gist test', | ||||
'filename': 'private-foo', | ||||
r4083 | 'gist_type': 'private', | |||
r1891 | 'gist_acl_level': Gist.ACL_LEVEL_PRIVATE, | |||
'csrf_token': self.csrf_token}, | ||||
status=302) | ||||
response = response.follow() | ||||
response.mustcontain('added file: private-foo<') | ||||
response.mustcontain('private gist test') | ||||
response.mustcontain('Private Gist') | ||||
# Make sure private gists are not indexed by robots | ||||
response.mustcontain( | ||||
'<meta name="robots" content="noindex, nofollow">') | ||||
def test_create_with_description(self): | ||||
self.log_user() | ||||
response = self.app.post( | ||||
route_path('gists_create'), | ||||
params={'lifetime': -1, | ||||
'content': 'gist test', | ||||
'filename': 'foo-desc', | ||||
'description': 'gist-desc', | ||||
r4083 | 'gist_type': 'public', | |||
r1891 | 'gist_acl_level': Gist.ACL_LEVEL_PUBLIC, | |||
'csrf_token': self.csrf_token}, | ||||
status=302) | ||||
response = response.follow() | ||||
response.mustcontain('added file: foo-desc') | ||||
response.mustcontain('gist test') | ||||
response.mustcontain('gist-desc') | ||||
def test_create_public_with_anonymous_access(self): | ||||
self.log_user() | ||||
params = { | ||||
'lifetime': -1, | ||||
'content': 'gist test', | ||||
'filename': 'foo-desc', | ||||
'description': 'gist-desc', | ||||
r4083 | 'gist_type': 'public', | |||
r1891 | 'gist_acl_level': Gist.ACL_LEVEL_PUBLIC, | |||
'csrf_token': self.csrf_token | ||||
} | ||||
response = self.app.post( | ||||
route_path('gists_create'), params=params, status=302) | ||||
self.logout_user() | ||||
response = response.follow() | ||||
response.mustcontain('added file: foo-desc') | ||||
response.mustcontain('gist test') | ||||
response.mustcontain('gist-desc') | ||||
def test_new(self): | ||||
self.log_user() | ||||
self.app.get(route_path('gists_new')) | ||||
def test_delete(self, create_gist): | ||||
self.log_user() | ||||
r5087 | gist = create_gist(b'delete-me') | |||
r1891 | response = self.app.post( | |||
route_path('gist_delete', gist_id=gist.gist_id), | ||||
params={'csrf_token': self.csrf_token}) | ||||
assert_session_flash(response, 'Deleted gist %s' % gist.gist_id) | ||||
def test_delete_normal_user_his_gist(self, create_gist): | ||||
self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | ||||
r5087 | gist = create_gist(b'delete-me', owner=TEST_USER_REGULAR_LOGIN) | |||
r1891 | ||||
response = self.app.post( | ||||
route_path('gist_delete', gist_id=gist.gist_id), | ||||
params={'csrf_token': self.csrf_token}) | ||||
assert_session_flash(response, 'Deleted gist %s' % gist.gist_id) | ||||
def test_delete_normal_user_not_his_own_gist(self, create_gist): | ||||
self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | ||||
r5087 | gist = create_gist(b'delete-me-2') | |||
r1891 | ||||
self.app.post( | ||||
route_path('gist_delete', gist_id=gist.gist_id), | ||||
params={'csrf_token': self.csrf_token}, status=404) | ||||
def test_show(self, create_gist): | ||||
r5087 | gist = create_gist(b'gist-show-me') | |||
r1891 | response = self.app.get(route_path('gist_show', gist_id=gist.gist_access_id)) | |||
response.mustcontain('added file: gist-show-me<') | ||||
assert_response = response.assert_response() | ||||
assert_response.element_equals_to( | ||||
'div.rc-user span.user', | ||||
r4422 | '<a href="/_profiles/test_admin">test_admin</a>') | |||
r1891 | ||||
response.mustcontain('gist-desc') | ||||
def test_show_without_hg(self, create_gist): | ||||
with mock.patch( | ||||
'rhodecode.lib.vcs.settings.ALIASES', ['git']): | ||||
r5087 | gist = create_gist(b'gist-show-me-again') | |||
r1891 | self.app.get( | |||
route_path('gist_show', gist_id=gist.gist_access_id), status=200) | ||||
def test_show_acl_private(self, create_gist): | ||||
r5087 | gist = create_gist(b'gist-show-me-only-when-im-logged-in', | |||
r1891 | acl_level=Gist.ACL_LEVEL_PRIVATE) | |||
self.app.get( | ||||
route_path('gist_show', gist_id=gist.gist_access_id), status=404) | ||||
# now we log-in we should see thi gist | ||||
self.log_user() | ||||
response = self.app.get( | ||||
route_path('gist_show', gist_id=gist.gist_access_id)) | ||||
response.mustcontain('added file: gist-show-me-only-when-im-logged-in') | ||||
assert_response = response.assert_response() | ||||
assert_response.element_equals_to( | ||||
'div.rc-user span.user', | ||||
r4422 | '<a href="/_profiles/test_admin">test_admin</a>') | |||
r1891 | response.mustcontain('gist-desc') | |||
def test_show_as_raw(self, create_gist): | ||||
r5087 | gist = create_gist(b'gist-show-me', content=b'GIST CONTENT') | |||
r1891 | response = self.app.get( | |||
route_path('gist_show_formatted', | ||||
gist_id=gist.gist_access_id, revision='tip', | ||||
format='raw')) | ||||
r4996 | assert response.text == 'GIST CONTENT' | |||
r1891 | ||||
def test_show_as_raw_individual_file(self, create_gist): | ||||
r5087 | gist = create_gist(b'gist-show-me-raw', content=b'GIST BODY') | |||
r1891 | response = self.app.get( | |||
route_path('gist_show_formatted_path', | ||||
gist_id=gist.gist_access_id, format='raw', | ||||
revision='tip', f_path='gist-show-me-raw')) | ||||
r4996 | assert response.text == 'GIST BODY' | |||
r1891 | ||||
def test_edit_page(self, create_gist): | ||||
self.log_user() | ||||
r5087 | gist = create_gist(b'gist-for-edit', content=b'GIST EDIT BODY') | |||
r1891 | response = self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id)) | |||
response.mustcontain('GIST EDIT BODY') | ||||
def test_edit_page_non_logged_user(self, create_gist): | ||||
r5087 | gist = create_gist(b'gist-for-edit', content=b'GIST EDIT BODY') | |||
r1891 | self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id), | |||
status=302) | ||||
def test_edit_normal_user_his_gist(self, create_gist): | ||||
self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | ||||
r5087 | gist = create_gist(b'gist-for-edit', owner=TEST_USER_REGULAR_LOGIN) | |||
r1891 | self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id, | |||
status=200)) | ||||
def test_edit_normal_user_not_his_own_gist(self, create_gist): | ||||
self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | ||||
r5087 | gist = create_gist(b'delete-me') | |||
r1891 | self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id), | |||
status=404) | ||||
def test_user_first_name_is_escaped(self, user_util, create_gist): | ||||
xss_atack_string = '"><script>alert(\'First Name\')</script>' | ||||
xss_escaped_string = h.html_escape(h.escape(xss_atack_string)) | ||||
password = 'test' | ||||
user = user_util.create_user( | ||||
firstname=xss_atack_string, password=password) | ||||
r5087 | create_gist(b'gist', gist_type='public', owner=user.username) | |||
r1891 | response = self.app.get(route_path('gists_show')) | |||
response.mustcontain(xss_escaped_string) | ||||
def test_user_last_name_is_escaped(self, user_util, create_gist): | ||||
xss_atack_string = '"><script>alert(\'Last Name\')</script>' | ||||
xss_escaped_string = h.html_escape(h.escape(xss_atack_string)) | ||||
password = 'test' | ||||
user = user_util.create_user( | ||||
lastname=xss_atack_string, password=password) | ||||
r5087 | create_gist(b'gist', gist_type='public', owner=user.username) | |||
r1891 | response = self.app.get(route_path('gists_show')) | |||
response.mustcontain(xss_escaped_string) | ||||