Show More
@@ -1,76 +1,76 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2015-2016 RhodeCode GmbH |
|
3 | # Copyright (C) 2015-2016 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 | Disable VCS pages when VCS Server is not available |
|
22 | Disable VCS pages when VCS Server is not available | |
23 | """ |
|
23 | """ | |
24 |
|
24 | |||
25 | import logging |
|
25 | import logging | |
26 | import re |
|
26 | import re | |
27 | from pyramid.httpexceptions import HTTPBadGateway |
|
27 | from pyramid.httpexceptions import HTTPBadGateway | |
28 |
|
28 | |||
29 | log = logging.getLogger(__name__) |
|
29 | log = logging.getLogger(__name__) | |
30 |
|
30 | |||
31 |
|
31 | |||
32 | class VCSServerUnavailable(HTTPBadGateway): |
|
32 | class VCSServerUnavailable(HTTPBadGateway): | |
33 | """ HTTP Exception class for when VCS Server is unavailable """ |
|
33 | """ HTTP Exception class for when VCS Server is unavailable """ | |
34 | code = 502 |
|
34 | code = 502 | |
35 | title = 'VCS Server Required' |
|
35 | title = 'VCS Server Required' | |
36 | explanation = 'A VCS Server is required for this action. There is currently no VCS Server configured.' |
|
36 | explanation = 'A VCS Server is required for this action. There is currently no VCS Server configured.' | |
37 |
|
37 | |||
38 | class DisableVCSPagesWrapper(object): |
|
38 | class DisableVCSPagesWrapper(object): | |
39 | """ |
|
39 | """ | |
40 | Pyramid view wrapper to disable all pages that require VCS Server to be |
|
40 | Pyramid view wrapper to disable all pages that require VCS Server to be | |
41 | running, avoiding that errors explode to the user. |
|
41 | running, avoiding that errors explode to the user. | |
42 |
|
42 | |||
43 | This Wrapper should be enabled only in case VCS Server is not available |
|
43 | This Wrapper should be enabled only in case VCS Server is not available | |
44 | for the instance. |
|
44 | for the instance. | |
45 | """ |
|
45 | """ | |
46 |
|
46 | |||
47 | VCS_NOT_REQUIRED = [ |
|
47 | VCS_NOT_REQUIRED = [ | |
48 | '^/$', |
|
48 | '^/$', | |
49 | ('/_admin(?!/settings/mapping)(?!/my_account/repos)' |
|
49 | ('/_admin(?!/settings/mapping)(?!/my_account/repos)' | |
50 | '(?!/create_repository)(?!/gists)(?!/notifications/)' |
|
50 | '(?!/create_repository)(?!/gists)(?!/notifications/)' | |
51 | ), |
|
51 | ), | |
52 | ] |
|
52 | ] | |
53 | _REGEX_VCS_NOT_REQUIRED = [re.compile(path) for path in VCS_NOT_REQUIRED] |
|
53 | _REGEX_VCS_NOT_REQUIRED = [re.compile(path) for path in VCS_NOT_REQUIRED] | |
54 |
|
54 | |||
55 | def _check_vcs_requirement(self, path_info): |
|
55 | def _check_vcs_requirement(self, path_info): | |
56 | """ |
|
56 | """ | |
57 | Tries to match the current path to one of the safe URLs to be rendered. |
|
57 | Tries to match the current path to one of the safe URLs to be rendered. | |
58 | Displays an error message in case |
|
58 | Displays an error message in case | |
59 | """ |
|
59 | """ | |
60 | for regex in self._REGEX_VCS_NOT_REQUIRED: |
|
60 | for regex in self._REGEX_VCS_NOT_REQUIRED: | |
61 | safe_url = regex.match(path_info) |
|
61 | safe_url = regex.match(path_info) | |
62 | if safe_url: |
|
62 | if safe_url: | |
63 | return True |
|
63 | return True | |
64 |
|
64 | |||
65 | # Url is not safe to be rendered without VCS Server |
|
65 | # Url is not safe to be rendered without VCS Server | |
66 | log.debug('accessing: `%s` with VCS Server disabled', path_info) |
|
66 | log.debug('accessing: `%s` with VCS Server disabled', path_info) | |
67 | return False |
|
67 | return False | |
68 |
|
68 | |||
69 | def __init__(self, handler): |
|
69 | def __init__(self, handler): | |
70 | self.handler = handler |
|
70 | self.handler = handler | |
71 |
|
71 | |||
72 | def __call__(self, context, request): |
|
72 | def __call__(self, context, request): | |
73 |
if not self._check_vcs_requirement(request. |
|
73 | if not self._check_vcs_requirement(request.path): | |
74 | raise VCSServerUnavailable('VCS Server is not available') |
|
74 | raise VCSServerUnavailable('VCS Server is not available') | |
75 |
|
75 | |||
76 | return self.handler(context, request) |
|
76 | return self.handler(context, request) |
@@ -1,54 +1,53 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2010-2016 RhodeCode GmbH |
|
3 | # Copyright (C) 2010-2016 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 pytest |
|
21 | import pytest | |
22 | from rhodecode.lib.middleware.disable_vcs import DisableVCSPagesWrapper |
|
22 | from pyramid.response import Response | |
|
23 | from pyramid.testing import DummyRequest | |||
|
24 | from rhodecode.lib.middleware.disable_vcs import ( | |||
|
25 | DisableVCSPagesWrapper, VCSServerUnavailable) | |||
23 |
|
26 | |||
24 |
|
27 | |||
25 |
@pytest.mark.parametrize('url, |
|
28 | @pytest.mark.parametrize('url, should_raise', [ | |
26 |
('/', |
|
29 | ('/', False), | |
27 |
('/_admin/settings', |
|
30 | ('/_admin/settings', False), | |
28 |
('/_admin/i_am_fine', |
|
31 | ('/_admin/i_am_fine', False), | |
29 |
('/_admin/settings/mappings', |
|
32 | ('/_admin/settings/mappings', True), | |
30 |
('/_admin/my_account/repos', |
|
33 | ('/_admin/my_account/repos', True), | |
31 |
('/_admin/create_repository', |
|
34 | ('/_admin/create_repository', True), | |
32 |
('/_admin/gists/1', |
|
35 | ('/_admin/gists/1', True), | |
33 |
('/_admin/notifications/1', |
|
36 | ('/_admin/notifications/1', True), | |
34 | ]) |
|
37 | ]) | |
35 |
def test_vcs_disabled(url, |
|
38 | def test_vcs_disabled(url, should_raise): | |
36 |
app = DisableVCSPagesWrapper( |
|
39 | wrapped_view = DisableVCSPagesWrapper(pyramid_view) | |
37 | assert expected_url == app(get_environ(url), None) |
|
40 | request = DummyRequest(path=url) | |
38 |
|
||||
39 |
|
41 | |||
40 | def get_environ(url): |
|
42 | if should_raise: | |
41 | """Construct a minimum WSGI environ based on the URL.""" |
|
43 | with pytest.raises(VCSServerUnavailable): | |
42 | environ = { |
|
44 | response = wrapped_view(None, request) | |
43 | 'PATH_INFO': url, |
|
45 | else: | |
44 | } |
|
46 | response = wrapped_view(None, request) | |
45 | return environ |
|
47 | assert response.status_int == 200 | |
46 |
|
||||
47 |
|
48 | |||
48 | class SimpleApp(object): |
|
49 | def pyramid_view(context, request): | |
49 | """ |
|
50 | """ | |
50 |
A mock |
|
51 | A mock pyramid view to be used in the wrapper | |
51 | from the middleware |
|
|||
52 | """ |
|
52 | """ | |
53 | def __call__(self, environ, start_response): |
|
53 | return Response('success') | |
54 | return environ['PATH_INFO'] |
|
General Comments 0
You need to be logged in to leave comments.
Login now