##// END OF EJS Templates
tests: remove usage of pylons context var in home app test.
marcink -
r1893:eb462cc2 default
parent child Browse files
Show More
@@ -1,140 +1,141 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-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 import pytest
22 import pytest
23
23
24 import rhodecode
24 import rhodecode
25 from rhodecode.model.db import Repository
25 from rhodecode.model.db import Repository
26 from rhodecode.model.meta import Session
26 from rhodecode.model.meta import Session
27 from rhodecode.model.repo import RepoModel
27 from rhodecode.model.repo import RepoModel
28 from rhodecode.model.repo_group import RepoGroupModel
28 from rhodecode.model.repo_group import RepoGroupModel
29 from rhodecode.model.settings import SettingsModel
29 from rhodecode.model.settings import SettingsModel
30 from rhodecode.tests import TestController
30 from rhodecode.tests import TestController
31 from rhodecode.tests.fixture import Fixture
31 from rhodecode.tests.fixture import Fixture
32 from rhodecode.lib import helpers as h
32 from rhodecode.lib import helpers as h
33
33
34 fixture = Fixture()
34 fixture = Fixture()
35
35
36
36
37 def route_path(name, **kwargs):
37 def route_path(name, **kwargs):
38 return {
38 return {
39 'home': '/',
39 'home': '/',
40 'repo_group_home': '/{repo_group_name}'
40 'repo_group_home': '/{repo_group_name}'
41 }[name].format(**kwargs)
41 }[name].format(**kwargs)
42
42
43
43
44 class TestHomeController(TestController):
44 class TestHomeController(TestController):
45
45
46 def test_index(self):
46 def test_index(self):
47 self.log_user()
47 self.log_user()
48 response = self.app.get(route_path('home'))
48 response = self.app.get(route_path('home'))
49 # if global permission is set
49 # if global permission is set
50 response.mustcontain('Add Repository')
50 response.mustcontain('Add Repository')
51
51
52 # search for objects inside the JavaScript JSON
52 # search for objects inside the JavaScript JSON
53 for repo in Repository.getAll():
53 for repo in Repository.getAll():
54 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
54 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
55
55
56 def test_index_contains_statics_with_ver(self):
56 def test_index_contains_statics_with_ver(self):
57 from pylons import tmpl_context as c
57 from rhodecode.lib.base import calculate_version_hash
58
58
59 self.log_user()
59 self.log_user()
60 response = self.app.get(route_path('home'))
60 response = self.app.get(route_path('home'))
61
61
62 rhodecode_version_hash = c.rhodecode_version_hash
62 rhodecode_version_hash = calculate_version_hash()
63 response.mustcontain('style.css?ver={0}'.format(rhodecode_version_hash))
63 response.mustcontain('style.css?ver={0}'.format(rhodecode_version_hash))
64 response.mustcontain('rhodecode-components.js?ver={0}'.format(rhodecode_version_hash))
64 response.mustcontain('rhodecode-components.js?ver={0}'.format(
65 rhodecode_version_hash))
65
66
66 def test_index_contains_backend_specific_details(self, backend):
67 def test_index_contains_backend_specific_details(self, backend):
67 self.log_user()
68 self.log_user()
68 response = self.app.get(route_path('home'))
69 response = self.app.get(route_path('home'))
69 tip = backend.repo.get_commit().raw_id
70 tip = backend.repo.get_commit().raw_id
70
71
71 # html in javascript variable:
72 # html in javascript variable:
72 response.mustcontain(r'<i class=\"icon-%s\"' % (backend.alias, ))
73 response.mustcontain(r'<i class=\"icon-%s\"' % (backend.alias, ))
73 response.mustcontain(r'href=\"/%s\"' % (backend.repo_name, ))
74 response.mustcontain(r'href=\"/%s\"' % (backend.repo_name, ))
74
75
75 response.mustcontain("""/%s/changeset/%s""" % (backend.repo_name, tip))
76 response.mustcontain("""/%s/changeset/%s""" % (backend.repo_name, tip))
76 response.mustcontain("""Added a symlink""")
77 response.mustcontain("""Added a symlink""")
77
78
78 def test_index_with_anonymous_access_disabled(self):
79 def test_index_with_anonymous_access_disabled(self):
79 with fixture.anon_access(False):
80 with fixture.anon_access(False):
80 response = self.app.get(route_path('home'), status=302)
81 response = self.app.get(route_path('home'), status=302)
81 assert 'login' in response.location
82 assert 'login' in response.location
82
83
83 def test_index_page_on_groups(self, autologin_user, repo_group):
84 def test_index_page_on_groups(self, autologin_user, repo_group):
84 response = self.app.get(route_path('repo_group_home', repo_group_name='gr1'))
85 response = self.app.get(route_path('repo_group_home', repo_group_name='gr1'))
85 response.mustcontain("gr1/repo_in_group")
86 response.mustcontain("gr1/repo_in_group")
86
87
87 def test_index_page_on_group_with_trailing_slash(
88 def test_index_page_on_group_with_trailing_slash(
88 self, autologin_user, repo_group):
89 self, autologin_user, repo_group):
89 response = self.app.get(route_path('repo_group_home', repo_group_name='gr1') + '/')
90 response = self.app.get(route_path('repo_group_home', repo_group_name='gr1') + '/')
90 response.mustcontain("gr1/repo_in_group")
91 response.mustcontain("gr1/repo_in_group")
91
92
92 @pytest.fixture(scope='class')
93 @pytest.fixture(scope='class')
93 def repo_group(self, request):
94 def repo_group(self, request):
94 gr = fixture.create_repo_group('gr1')
95 gr = fixture.create_repo_group('gr1')
95 fixture.create_repo(name='gr1/repo_in_group', repo_group=gr)
96 fixture.create_repo(name='gr1/repo_in_group', repo_group=gr)
96
97
97 @request.addfinalizer
98 @request.addfinalizer
98 def cleanup():
99 def cleanup():
99 RepoModel().delete('gr1/repo_in_group')
100 RepoModel().delete('gr1/repo_in_group')
100 RepoGroupModel().delete(repo_group='gr1', force_delete=True)
101 RepoGroupModel().delete(repo_group='gr1', force_delete=True)
101 Session().commit()
102 Session().commit()
102
103
103 def test_index_with_name_with_tags(self, user_util, autologin_user):
104 def test_index_with_name_with_tags(self, user_util, autologin_user):
104 user = user_util.create_user()
105 user = user_util.create_user()
105 username = user.username
106 username = user.username
106 user.name = '<img src="/image1" onload="alert(\'Hello, World!\');">'
107 user.name = '<img src="/image1" onload="alert(\'Hello, World!\');">'
107 user.lastname = '#"><img src=x onerror=prompt(document.cookie);>'
108 user.lastname = '#"><img src=x onerror=prompt(document.cookie);>'
108
109
109 Session().add(user)
110 Session().add(user)
110 Session().commit()
111 Session().commit()
111 user_util.create_repo(owner=username)
112 user_util.create_repo(owner=username)
112
113
113 response = self.app.get(route_path('home'))
114 response = self.app.get(route_path('home'))
114 response.mustcontain(h.html_escape(user.first_name))
115 response.mustcontain(h.html_escape(user.first_name))
115 response.mustcontain(h.html_escape(user.last_name))
116 response.mustcontain(h.html_escape(user.last_name))
116
117
117 @pytest.mark.parametrize("name, state", [
118 @pytest.mark.parametrize("name, state", [
118 ('Disabled', False),
119 ('Disabled', False),
119 ('Enabled', True),
120 ('Enabled', True),
120 ])
121 ])
121 def test_index_show_version(self, autologin_user, name, state):
122 def test_index_show_version(self, autologin_user, name, state):
122 version_string = 'RhodeCode Enterprise %s' % rhodecode.__version__
123 version_string = 'RhodeCode Enterprise %s' % rhodecode.__version__
123
124
124 sett = SettingsModel().create_or_update_setting(
125 sett = SettingsModel().create_or_update_setting(
125 'show_version', state, 'bool')
126 'show_version', state, 'bool')
126 Session().add(sett)
127 Session().add(sett)
127 Session().commit()
128 Session().commit()
128 SettingsModel().invalidate_settings_cache()
129 SettingsModel().invalidate_settings_cache()
129
130
130 response = self.app.get(route_path('home'))
131 response = self.app.get(route_path('home'))
131 if state is True:
132 if state is True:
132 response.mustcontain(version_string)
133 response.mustcontain(version_string)
133 if state is False:
134 if state is False:
134 response.mustcontain(no=[version_string])
135 response.mustcontain(no=[version_string])
135
136
136 def test_logout_form_contains_csrf(self, autologin_user, csrf_token):
137 def test_logout_form_contains_csrf(self, autologin_user, csrf_token):
137 response = self.app.get(route_path('home'))
138 response = self.app.get(route_path('home'))
138 assert_response = response.assert_response()
139 assert_response = response.assert_response()
139 element = assert_response.get_element('.logout #csrf_token')
140 element = assert_response.get_element('.logout #csrf_token')
140 assert element.value == csrf_token
141 assert element.value == csrf_token
@@ -1,601 +1,605 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-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 The base Controller API
22 The base Controller API
23 Provides the BaseController class for subclassing. And usage in different
23 Provides the BaseController class for subclassing. And usage in different
24 controllers
24 controllers
25 """
25 """
26
26
27 import logging
27 import logging
28 import socket
28 import socket
29
29
30 import ipaddress
30 import ipaddress
31 import pyramid.threadlocal
31 import pyramid.threadlocal
32
32
33 from paste.auth.basic import AuthBasicAuthenticator
33 from paste.auth.basic import AuthBasicAuthenticator
34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
35 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
35 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
36 from pylons import config, tmpl_context as c, request, session, url
36 from pylons import config, tmpl_context as c, request, session, url
37 from pylons.controllers import WSGIController
37 from pylons.controllers import WSGIController
38 from pylons.controllers.util import redirect
38 from pylons.controllers.util import redirect
39 from pylons.i18n import translation
39 from pylons.i18n import translation
40 # marcink: don't remove this import
40 # marcink: don't remove this import
41 from pylons.templating import render_mako as render # noqa
41 from pylons.templating import render_mako as render # noqa
42 from pylons.i18n.translation import _
42 from pylons.i18n.translation import _
43 from webob.exc import HTTPFound
43 from webob.exc import HTTPFound
44
44
45
45
46 import rhodecode
46 import rhodecode
47 from rhodecode.authentication.base import VCS_TYPE
47 from rhodecode.authentication.base import VCS_TYPE
48 from rhodecode.lib import auth, utils2
48 from rhodecode.lib import auth, utils2
49 from rhodecode.lib import helpers as h
49 from rhodecode.lib import helpers as h
50 from rhodecode.lib.auth import AuthUser, CookieStoreWrapper
50 from rhodecode.lib.auth import AuthUser, CookieStoreWrapper
51 from rhodecode.lib.exceptions import UserCreationError
51 from rhodecode.lib.exceptions import UserCreationError
52 from rhodecode.lib.utils import (
52 from rhodecode.lib.utils import (
53 get_repo_slug, set_rhodecode_config, password_changed,
53 get_repo_slug, set_rhodecode_config, password_changed,
54 get_enabled_hook_classes)
54 get_enabled_hook_classes)
55 from rhodecode.lib.utils2 import (
55 from rhodecode.lib.utils2 import (
56 str2bool, safe_unicode, AttributeDict, safe_int, md5, aslist)
56 str2bool, safe_unicode, AttributeDict, safe_int, md5, aslist)
57 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
57 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
58 from rhodecode.model import meta
58 from rhodecode.model import meta
59 from rhodecode.model.db import Repository, User, ChangesetComment
59 from rhodecode.model.db import Repository, User, ChangesetComment
60 from rhodecode.model.notification import NotificationModel
60 from rhodecode.model.notification import NotificationModel
61 from rhodecode.model.scm import ScmModel
61 from rhodecode.model.scm import ScmModel
62 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
62 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
63
63
64
64
65 log = logging.getLogger(__name__)
65 log = logging.getLogger(__name__)
66
66
67
67
68 def _filter_proxy(ip):
68 def _filter_proxy(ip):
69 """
69 """
70 Passed in IP addresses in HEADERS can be in a special format of multiple
70 Passed in IP addresses in HEADERS can be in a special format of multiple
71 ips. Those comma separated IPs are passed from various proxies in the
71 ips. Those comma separated IPs are passed from various proxies in the
72 chain of request processing. The left-most being the original client.
72 chain of request processing. The left-most being the original client.
73 We only care about the first IP which came from the org. client.
73 We only care about the first IP which came from the org. client.
74
74
75 :param ip: ip string from headers
75 :param ip: ip string from headers
76 """
76 """
77 if ',' in ip:
77 if ',' in ip:
78 _ips = ip.split(',')
78 _ips = ip.split(',')
79 _first_ip = _ips[0].strip()
79 _first_ip = _ips[0].strip()
80 log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
80 log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
81 return _first_ip
81 return _first_ip
82 return ip
82 return ip
83
83
84
84
85 def _filter_port(ip):
85 def _filter_port(ip):
86 """
86 """
87 Removes a port from ip, there are 4 main cases to handle here.
87 Removes a port from ip, there are 4 main cases to handle here.
88 - ipv4 eg. 127.0.0.1
88 - ipv4 eg. 127.0.0.1
89 - ipv6 eg. ::1
89 - ipv6 eg. ::1
90 - ipv4+port eg. 127.0.0.1:8080
90 - ipv4+port eg. 127.0.0.1:8080
91 - ipv6+port eg. [::1]:8080
91 - ipv6+port eg. [::1]:8080
92
92
93 :param ip:
93 :param ip:
94 """
94 """
95 def is_ipv6(ip_addr):
95 def is_ipv6(ip_addr):
96 if hasattr(socket, 'inet_pton'):
96 if hasattr(socket, 'inet_pton'):
97 try:
97 try:
98 socket.inet_pton(socket.AF_INET6, ip_addr)
98 socket.inet_pton(socket.AF_INET6, ip_addr)
99 except socket.error:
99 except socket.error:
100 return False
100 return False
101 else:
101 else:
102 # fallback to ipaddress
102 # fallback to ipaddress
103 try:
103 try:
104 ipaddress.IPv6Address(ip_addr)
104 ipaddress.IPv6Address(ip_addr)
105 except Exception:
105 except Exception:
106 return False
106 return False
107 return True
107 return True
108
108
109 if ':' not in ip: # must be ipv4 pure ip
109 if ':' not in ip: # must be ipv4 pure ip
110 return ip
110 return ip
111
111
112 if '[' in ip and ']' in ip: # ipv6 with port
112 if '[' in ip and ']' in ip: # ipv6 with port
113 return ip.split(']')[0][1:].lower()
113 return ip.split(']')[0][1:].lower()
114
114
115 # must be ipv6 or ipv4 with port
115 # must be ipv6 or ipv4 with port
116 if is_ipv6(ip):
116 if is_ipv6(ip):
117 return ip
117 return ip
118 else:
118 else:
119 ip, _port = ip.split(':')[:2] # means ipv4+port
119 ip, _port = ip.split(':')[:2] # means ipv4+port
120 return ip
120 return ip
121
121
122
122
123 def get_ip_addr(environ):
123 def get_ip_addr(environ):
124 proxy_key = 'HTTP_X_REAL_IP'
124 proxy_key = 'HTTP_X_REAL_IP'
125 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
125 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
126 def_key = 'REMOTE_ADDR'
126 def_key = 'REMOTE_ADDR'
127 _filters = lambda x: _filter_port(_filter_proxy(x))
127 _filters = lambda x: _filter_port(_filter_proxy(x))
128
128
129 ip = environ.get(proxy_key)
129 ip = environ.get(proxy_key)
130 if ip:
130 if ip:
131 return _filters(ip)
131 return _filters(ip)
132
132
133 ip = environ.get(proxy_key2)
133 ip = environ.get(proxy_key2)
134 if ip:
134 if ip:
135 return _filters(ip)
135 return _filters(ip)
136
136
137 ip = environ.get(def_key, '0.0.0.0')
137 ip = environ.get(def_key, '0.0.0.0')
138 return _filters(ip)
138 return _filters(ip)
139
139
140
140
141 def get_server_ip_addr(environ, log_errors=True):
141 def get_server_ip_addr(environ, log_errors=True):
142 hostname = environ.get('SERVER_NAME')
142 hostname = environ.get('SERVER_NAME')
143 try:
143 try:
144 return socket.gethostbyname(hostname)
144 return socket.gethostbyname(hostname)
145 except Exception as e:
145 except Exception as e:
146 if log_errors:
146 if log_errors:
147 # in some cases this lookup is not possible, and we don't want to
147 # in some cases this lookup is not possible, and we don't want to
148 # make it an exception in logs
148 # make it an exception in logs
149 log.exception('Could not retrieve server ip address: %s', e)
149 log.exception('Could not retrieve server ip address: %s', e)
150 return hostname
150 return hostname
151
151
152
152
153 def get_server_port(environ):
153 def get_server_port(environ):
154 return environ.get('SERVER_PORT')
154 return environ.get('SERVER_PORT')
155
155
156
156
157 def get_access_path(environ):
157 def get_access_path(environ):
158 path = environ.get('PATH_INFO')
158 path = environ.get('PATH_INFO')
159 org_req = environ.get('pylons.original_request')
159 org_req = environ.get('pylons.original_request')
160 if org_req:
160 if org_req:
161 path = org_req.environ.get('PATH_INFO')
161 path = org_req.environ.get('PATH_INFO')
162 return path
162 return path
163
163
164
164
165 def get_user_agent(environ):
165 def get_user_agent(environ):
166 return environ.get('HTTP_USER_AGENT')
166 return environ.get('HTTP_USER_AGENT')
167
167
168
168
169 def vcs_operation_context(
169 def vcs_operation_context(
170 environ, repo_name, username, action, scm, check_locking=True,
170 environ, repo_name, username, action, scm, check_locking=True,
171 is_shadow_repo=False):
171 is_shadow_repo=False):
172 """
172 """
173 Generate the context for a vcs operation, e.g. push or pull.
173 Generate the context for a vcs operation, e.g. push or pull.
174
174
175 This context is passed over the layers so that hooks triggered by the
175 This context is passed over the layers so that hooks triggered by the
176 vcs operation know details like the user, the user's IP address etc.
176 vcs operation know details like the user, the user's IP address etc.
177
177
178 :param check_locking: Allows to switch of the computation of the locking
178 :param check_locking: Allows to switch of the computation of the locking
179 data. This serves mainly the need of the simplevcs middleware to be
179 data. This serves mainly the need of the simplevcs middleware to be
180 able to disable this for certain operations.
180 able to disable this for certain operations.
181
181
182 """
182 """
183 # Tri-state value: False: unlock, None: nothing, True: lock
183 # Tri-state value: False: unlock, None: nothing, True: lock
184 make_lock = None
184 make_lock = None
185 locked_by = [None, None, None]
185 locked_by = [None, None, None]
186 is_anonymous = username == User.DEFAULT_USER
186 is_anonymous = username == User.DEFAULT_USER
187 if not is_anonymous and check_locking:
187 if not is_anonymous and check_locking:
188 log.debug('Checking locking on repository "%s"', repo_name)
188 log.debug('Checking locking on repository "%s"', repo_name)
189 user = User.get_by_username(username)
189 user = User.get_by_username(username)
190 repo = Repository.get_by_repo_name(repo_name)
190 repo = Repository.get_by_repo_name(repo_name)
191 make_lock, __, locked_by = repo.get_locking_state(
191 make_lock, __, locked_by = repo.get_locking_state(
192 action, user.user_id)
192 action, user.user_id)
193
193
194 settings_model = VcsSettingsModel(repo=repo_name)
194 settings_model = VcsSettingsModel(repo=repo_name)
195 ui_settings = settings_model.get_ui_settings()
195 ui_settings = settings_model.get_ui_settings()
196
196
197 extras = {
197 extras = {
198 'ip': get_ip_addr(environ),
198 'ip': get_ip_addr(environ),
199 'username': username,
199 'username': username,
200 'action': action,
200 'action': action,
201 'repository': repo_name,
201 'repository': repo_name,
202 'scm': scm,
202 'scm': scm,
203 'config': rhodecode.CONFIG['__file__'],
203 'config': rhodecode.CONFIG['__file__'],
204 'make_lock': make_lock,
204 'make_lock': make_lock,
205 'locked_by': locked_by,
205 'locked_by': locked_by,
206 'server_url': utils2.get_server_url(environ),
206 'server_url': utils2.get_server_url(environ),
207 'user_agent': get_user_agent(environ),
207 'user_agent': get_user_agent(environ),
208 'hooks': get_enabled_hook_classes(ui_settings),
208 'hooks': get_enabled_hook_classes(ui_settings),
209 'is_shadow_repo': is_shadow_repo,
209 'is_shadow_repo': is_shadow_repo,
210 }
210 }
211 return extras
211 return extras
212
212
213
213
214 class BasicAuth(AuthBasicAuthenticator):
214 class BasicAuth(AuthBasicAuthenticator):
215
215
216 def __init__(self, realm, authfunc, registry, auth_http_code=None,
216 def __init__(self, realm, authfunc, registry, auth_http_code=None,
217 initial_call_detection=False, acl_repo_name=None):
217 initial_call_detection=False, acl_repo_name=None):
218 self.realm = realm
218 self.realm = realm
219 self.initial_call = initial_call_detection
219 self.initial_call = initial_call_detection
220 self.authfunc = authfunc
220 self.authfunc = authfunc
221 self.registry = registry
221 self.registry = registry
222 self.acl_repo_name = acl_repo_name
222 self.acl_repo_name = acl_repo_name
223 self._rc_auth_http_code = auth_http_code
223 self._rc_auth_http_code = auth_http_code
224
224
225 def _get_response_from_code(self, http_code):
225 def _get_response_from_code(self, http_code):
226 try:
226 try:
227 return get_exception(safe_int(http_code))
227 return get_exception(safe_int(http_code))
228 except Exception:
228 except Exception:
229 log.exception('Failed to fetch response for code %s' % http_code)
229 log.exception('Failed to fetch response for code %s' % http_code)
230 return HTTPForbidden
230 return HTTPForbidden
231
231
232 def build_authentication(self):
232 def build_authentication(self):
233 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
233 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
234 if self._rc_auth_http_code and not self.initial_call:
234 if self._rc_auth_http_code and not self.initial_call:
235 # return alternative HTTP code if alternative http return code
235 # return alternative HTTP code if alternative http return code
236 # is specified in RhodeCode config, but ONLY if it's not the
236 # is specified in RhodeCode config, but ONLY if it's not the
237 # FIRST call
237 # FIRST call
238 custom_response_klass = self._get_response_from_code(
238 custom_response_klass = self._get_response_from_code(
239 self._rc_auth_http_code)
239 self._rc_auth_http_code)
240 return custom_response_klass(headers=head)
240 return custom_response_klass(headers=head)
241 return HTTPUnauthorized(headers=head)
241 return HTTPUnauthorized(headers=head)
242
242
243 def authenticate(self, environ):
243 def authenticate(self, environ):
244 authorization = AUTHORIZATION(environ)
244 authorization = AUTHORIZATION(environ)
245 if not authorization:
245 if not authorization:
246 return self.build_authentication()
246 return self.build_authentication()
247 (authmeth, auth) = authorization.split(' ', 1)
247 (authmeth, auth) = authorization.split(' ', 1)
248 if 'basic' != authmeth.lower():
248 if 'basic' != authmeth.lower():
249 return self.build_authentication()
249 return self.build_authentication()
250 auth = auth.strip().decode('base64')
250 auth = auth.strip().decode('base64')
251 _parts = auth.split(':', 1)
251 _parts = auth.split(':', 1)
252 if len(_parts) == 2:
252 if len(_parts) == 2:
253 username, password = _parts
253 username, password = _parts
254 if self.authfunc(
254 if self.authfunc(
255 username, password, environ, VCS_TYPE,
255 username, password, environ, VCS_TYPE,
256 registry=self.registry, acl_repo_name=self.acl_repo_name):
256 registry=self.registry, acl_repo_name=self.acl_repo_name):
257 return username
257 return username
258 if username and password:
258 if username and password:
259 # we mark that we actually executed authentication once, at
259 # we mark that we actually executed authentication once, at
260 # that point we can use the alternative auth code
260 # that point we can use the alternative auth code
261 self.initial_call = False
261 self.initial_call = False
262
262
263 return self.build_authentication()
263 return self.build_authentication()
264
264
265 __call__ = authenticate
265 __call__ = authenticate
266
266
267
267
268 def attach_context_attributes(context, request, user_id, attach_to_request=False):
268 def attach_context_attributes(context, request, user_id, attach_to_request=False):
269 def calculate_version_hash():
270 return md5(
271 config.get('beaker.session.secret', '') +
272 rhodecode.__version__)[:8]
273
274
269 """
275 """
270 Attach variables into template context called `c`, please note that
276 Attach variables into template context called `c`, please note that
271 request could be pylons or pyramid request in here.
277 request could be pylons or pyramid request in here.
272 """
278 """
273 rc_config = SettingsModel().get_all_settings(cache=True)
279 rc_config = SettingsModel().get_all_settings(cache=True)
274
280
275 context.rhodecode_version = rhodecode.__version__
281 context.rhodecode_version = rhodecode.__version__
276 context.rhodecode_edition = config.get('rhodecode.edition')
282 context.rhodecode_edition = config.get('rhodecode.edition')
277 # unique secret + version does not leak the version but keep consistency
283 # unique secret + version does not leak the version but keep consistency
278 context.rhodecode_version_hash = md5(
284 context.rhodecode_version_hash = calculate_version_hash()
279 config.get('beaker.session.secret', '') +
280 rhodecode.__version__)[:8]
281
285
282 # Default language set for the incoming request
286 # Default language set for the incoming request
283 context.language = translation.get_lang()[0]
287 context.language = translation.get_lang()[0]
284
288
285 # Visual options
289 # Visual options
286 context.visual = AttributeDict({})
290 context.visual = AttributeDict({})
287
291
288 # DB stored Visual Items
292 # DB stored Visual Items
289 context.visual.show_public_icon = str2bool(
293 context.visual.show_public_icon = str2bool(
290 rc_config.get('rhodecode_show_public_icon'))
294 rc_config.get('rhodecode_show_public_icon'))
291 context.visual.show_private_icon = str2bool(
295 context.visual.show_private_icon = str2bool(
292 rc_config.get('rhodecode_show_private_icon'))
296 rc_config.get('rhodecode_show_private_icon'))
293 context.visual.stylify_metatags = str2bool(
297 context.visual.stylify_metatags = str2bool(
294 rc_config.get('rhodecode_stylify_metatags'))
298 rc_config.get('rhodecode_stylify_metatags'))
295 context.visual.dashboard_items = safe_int(
299 context.visual.dashboard_items = safe_int(
296 rc_config.get('rhodecode_dashboard_items', 100))
300 rc_config.get('rhodecode_dashboard_items', 100))
297 context.visual.admin_grid_items = safe_int(
301 context.visual.admin_grid_items = safe_int(
298 rc_config.get('rhodecode_admin_grid_items', 100))
302 rc_config.get('rhodecode_admin_grid_items', 100))
299 context.visual.repository_fields = str2bool(
303 context.visual.repository_fields = str2bool(
300 rc_config.get('rhodecode_repository_fields'))
304 rc_config.get('rhodecode_repository_fields'))
301 context.visual.show_version = str2bool(
305 context.visual.show_version = str2bool(
302 rc_config.get('rhodecode_show_version'))
306 rc_config.get('rhodecode_show_version'))
303 context.visual.use_gravatar = str2bool(
307 context.visual.use_gravatar = str2bool(
304 rc_config.get('rhodecode_use_gravatar'))
308 rc_config.get('rhodecode_use_gravatar'))
305 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
309 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
306 context.visual.default_renderer = rc_config.get(
310 context.visual.default_renderer = rc_config.get(
307 'rhodecode_markup_renderer', 'rst')
311 'rhodecode_markup_renderer', 'rst')
308 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
312 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
309 context.visual.rhodecode_support_url = \
313 context.visual.rhodecode_support_url = \
310 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
314 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
311
315
312 context.pre_code = rc_config.get('rhodecode_pre_code')
316 context.pre_code = rc_config.get('rhodecode_pre_code')
313 context.post_code = rc_config.get('rhodecode_post_code')
317 context.post_code = rc_config.get('rhodecode_post_code')
314 context.rhodecode_name = rc_config.get('rhodecode_title')
318 context.rhodecode_name = rc_config.get('rhodecode_title')
315 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
319 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
316 # if we have specified default_encoding in the request, it has more
320 # if we have specified default_encoding in the request, it has more
317 # priority
321 # priority
318 if request.GET.get('default_encoding'):
322 if request.GET.get('default_encoding'):
319 context.default_encodings.insert(0, request.GET.get('default_encoding'))
323 context.default_encodings.insert(0, request.GET.get('default_encoding'))
320 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
324 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
321
325
322 # INI stored
326 # INI stored
323 context.labs_active = str2bool(
327 context.labs_active = str2bool(
324 config.get('labs_settings_active', 'false'))
328 config.get('labs_settings_active', 'false'))
325 context.visual.allow_repo_location_change = str2bool(
329 context.visual.allow_repo_location_change = str2bool(
326 config.get('allow_repo_location_change', True))
330 config.get('allow_repo_location_change', True))
327 context.visual.allow_custom_hooks_settings = str2bool(
331 context.visual.allow_custom_hooks_settings = str2bool(
328 config.get('allow_custom_hooks_settings', True))
332 config.get('allow_custom_hooks_settings', True))
329 context.debug_style = str2bool(config.get('debug_style', False))
333 context.debug_style = str2bool(config.get('debug_style', False))
330
334
331 context.rhodecode_instanceid = config.get('instance_id')
335 context.rhodecode_instanceid = config.get('instance_id')
332
336
333 context.visual.cut_off_limit_diff = safe_int(
337 context.visual.cut_off_limit_diff = safe_int(
334 config.get('cut_off_limit_diff'))
338 config.get('cut_off_limit_diff'))
335 context.visual.cut_off_limit_file = safe_int(
339 context.visual.cut_off_limit_file = safe_int(
336 config.get('cut_off_limit_file'))
340 config.get('cut_off_limit_file'))
337
341
338 # AppEnlight
342 # AppEnlight
339 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
343 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
340 context.appenlight_api_public_key = config.get(
344 context.appenlight_api_public_key = config.get(
341 'appenlight.api_public_key', '')
345 'appenlight.api_public_key', '')
342 context.appenlight_server_url = config.get('appenlight.server_url', '')
346 context.appenlight_server_url = config.get('appenlight.server_url', '')
343
347
344 # JS template context
348 # JS template context
345 context.template_context = {
349 context.template_context = {
346 'repo_name': None,
350 'repo_name': None,
347 'repo_type': None,
351 'repo_type': None,
348 'repo_landing_commit': None,
352 'repo_landing_commit': None,
349 'rhodecode_user': {
353 'rhodecode_user': {
350 'username': None,
354 'username': None,
351 'email': None,
355 'email': None,
352 'notification_status': False
356 'notification_status': False
353 },
357 },
354 'visual': {
358 'visual': {
355 'default_renderer': None
359 'default_renderer': None
356 },
360 },
357 'commit_data': {
361 'commit_data': {
358 'commit_id': None
362 'commit_id': None
359 },
363 },
360 'pull_request_data': {'pull_request_id': None},
364 'pull_request_data': {'pull_request_id': None},
361 'timeago': {
365 'timeago': {
362 'refresh_time': 120 * 1000,
366 'refresh_time': 120 * 1000,
363 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
367 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
364 },
368 },
365 'pylons_dispatch': {
369 'pylons_dispatch': {
366 # 'controller': request.environ['pylons.routes_dict']['controller'],
370 # 'controller': request.environ['pylons.routes_dict']['controller'],
367 # 'action': request.environ['pylons.routes_dict']['action'],
371 # 'action': request.environ['pylons.routes_dict']['action'],
368 },
372 },
369 'pyramid_dispatch': {
373 'pyramid_dispatch': {
370
374
371 },
375 },
372 'extra': {'plugins': {}}
376 'extra': {'plugins': {}}
373 }
377 }
374 # END CONFIG VARS
378 # END CONFIG VARS
375
379
376 # TODO: This dosn't work when called from pylons compatibility tween.
380 # TODO: This dosn't work when called from pylons compatibility tween.
377 # Fix this and remove it from base controller.
381 # Fix this and remove it from base controller.
378 # context.repo_name = get_repo_slug(request) # can be empty
382 # context.repo_name = get_repo_slug(request) # can be empty
379
383
380 diffmode = 'sideside'
384 diffmode = 'sideside'
381 if request.GET.get('diffmode'):
385 if request.GET.get('diffmode'):
382 if request.GET['diffmode'] == 'unified':
386 if request.GET['diffmode'] == 'unified':
383 diffmode = 'unified'
387 diffmode = 'unified'
384 elif request.session.get('diffmode'):
388 elif request.session.get('diffmode'):
385 diffmode = request.session['diffmode']
389 diffmode = request.session['diffmode']
386
390
387 context.diffmode = diffmode
391 context.diffmode = diffmode
388
392
389 if request.session.get('diffmode') != diffmode:
393 if request.session.get('diffmode') != diffmode:
390 request.session['diffmode'] = diffmode
394 request.session['diffmode'] = diffmode
391
395
392 context.csrf_token = auth.get_csrf_token()
396 context.csrf_token = auth.get_csrf_token()
393 context.backends = rhodecode.BACKENDS.keys()
397 context.backends = rhodecode.BACKENDS.keys()
394 context.backends.sort()
398 context.backends.sort()
395 context.unread_notifications = NotificationModel().get_unread_cnt_for_user(user_id)
399 context.unread_notifications = NotificationModel().get_unread_cnt_for_user(user_id)
396 if attach_to_request:
400 if attach_to_request:
397 request.call_context = context
401 request.call_context = context
398 else:
402 else:
399 context.pyramid_request = pyramid.threadlocal.get_current_request()
403 context.pyramid_request = pyramid.threadlocal.get_current_request()
400
404
401
405
402
406
403 def get_auth_user(environ):
407 def get_auth_user(environ):
404 ip_addr = get_ip_addr(environ)
408 ip_addr = get_ip_addr(environ)
405 # make sure that we update permissions each time we call controller
409 # make sure that we update permissions each time we call controller
406 _auth_token = (request.GET.get('auth_token', '') or
410 _auth_token = (request.GET.get('auth_token', '') or
407 request.GET.get('api_key', ''))
411 request.GET.get('api_key', ''))
408
412
409 if _auth_token:
413 if _auth_token:
410 # when using API_KEY we assume user exists, and
414 # when using API_KEY we assume user exists, and
411 # doesn't need auth based on cookies.
415 # doesn't need auth based on cookies.
412 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
416 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
413 authenticated = False
417 authenticated = False
414 else:
418 else:
415 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
419 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
416 try:
420 try:
417 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
421 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
418 ip_addr=ip_addr)
422 ip_addr=ip_addr)
419 except UserCreationError as e:
423 except UserCreationError as e:
420 h.flash(e, 'error')
424 h.flash(e, 'error')
421 # container auth or other auth functions that create users
425 # container auth or other auth functions that create users
422 # on the fly can throw this exception signaling that there's
426 # on the fly can throw this exception signaling that there's
423 # issue with user creation, explanation should be provided
427 # issue with user creation, explanation should be provided
424 # in Exception itself. We then create a simple blank
428 # in Exception itself. We then create a simple blank
425 # AuthUser
429 # AuthUser
426 auth_user = AuthUser(ip_addr=ip_addr)
430 auth_user = AuthUser(ip_addr=ip_addr)
427
431
428 if password_changed(auth_user, session):
432 if password_changed(auth_user, session):
429 session.invalidate()
433 session.invalidate()
430 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
434 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
431 auth_user = AuthUser(ip_addr=ip_addr)
435 auth_user = AuthUser(ip_addr=ip_addr)
432
436
433 authenticated = cookie_store.get('is_authenticated')
437 authenticated = cookie_store.get('is_authenticated')
434
438
435 if not auth_user.is_authenticated and auth_user.is_user_object:
439 if not auth_user.is_authenticated and auth_user.is_user_object:
436 # user is not authenticated and not empty
440 # user is not authenticated and not empty
437 auth_user.set_authenticated(authenticated)
441 auth_user.set_authenticated(authenticated)
438
442
439 return auth_user
443 return auth_user
440
444
441
445
442 class BaseController(WSGIController):
446 class BaseController(WSGIController):
443
447
444 def __before__(self):
448 def __before__(self):
445 """
449 """
446 __before__ is called before controller methods and after __call__
450 __before__ is called before controller methods and after __call__
447 """
451 """
448 # on each call propagate settings calls into global settings.
452 # on each call propagate settings calls into global settings.
449 set_rhodecode_config(config)
453 set_rhodecode_config(config)
450 attach_context_attributes(c, request, c.rhodecode_user.user_id)
454 attach_context_attributes(c, request, c.rhodecode_user.user_id)
451
455
452 # TODO: Remove this when fixed in attach_context_attributes()
456 # TODO: Remove this when fixed in attach_context_attributes()
453 c.repo_name = get_repo_slug(request) # can be empty
457 c.repo_name = get_repo_slug(request) # can be empty
454
458
455 self.cut_off_limit_diff = safe_int(config.get('cut_off_limit_diff'))
459 self.cut_off_limit_diff = safe_int(config.get('cut_off_limit_diff'))
456 self.cut_off_limit_file = safe_int(config.get('cut_off_limit_file'))
460 self.cut_off_limit_file = safe_int(config.get('cut_off_limit_file'))
457 self.sa = meta.Session
461 self.sa = meta.Session
458 self.scm_model = ScmModel(self.sa)
462 self.scm_model = ScmModel(self.sa)
459
463
460 # set user language
464 # set user language
461 user_lang = getattr(c.pyramid_request, '_LOCALE_', None)
465 user_lang = getattr(c.pyramid_request, '_LOCALE_', None)
462 if user_lang:
466 if user_lang:
463 translation.set_lang(user_lang)
467 translation.set_lang(user_lang)
464 log.debug('set language to %s for user %s',
468 log.debug('set language to %s for user %s',
465 user_lang, self._rhodecode_user)
469 user_lang, self._rhodecode_user)
466
470
467 def _dispatch_redirect(self, with_url, environ, start_response):
471 def _dispatch_redirect(self, with_url, environ, start_response):
468 resp = HTTPFound(with_url)
472 resp = HTTPFound(with_url)
469 environ['SCRIPT_NAME'] = '' # handle prefix middleware
473 environ['SCRIPT_NAME'] = '' # handle prefix middleware
470 environ['PATH_INFO'] = with_url
474 environ['PATH_INFO'] = with_url
471 return resp(environ, start_response)
475 return resp(environ, start_response)
472
476
473 def __call__(self, environ, start_response):
477 def __call__(self, environ, start_response):
474 """Invoke the Controller"""
478 """Invoke the Controller"""
475 # WSGIController.__call__ dispatches to the Controller method
479 # WSGIController.__call__ dispatches to the Controller method
476 # the request is routed to. This routing information is
480 # the request is routed to. This routing information is
477 # available in environ['pylons.routes_dict']
481 # available in environ['pylons.routes_dict']
478 from rhodecode.lib import helpers as h
482 from rhodecode.lib import helpers as h
479
483
480 # Provide the Pylons context to Pyramid's debugtoolbar if it asks
484 # Provide the Pylons context to Pyramid's debugtoolbar if it asks
481 if environ.get('debugtoolbar.wants_pylons_context', False):
485 if environ.get('debugtoolbar.wants_pylons_context', False):
482 environ['debugtoolbar.pylons_context'] = c._current_obj()
486 environ['debugtoolbar.pylons_context'] = c._current_obj()
483
487
484 _route_name = '.'.join([environ['pylons.routes_dict']['controller'],
488 _route_name = '.'.join([environ['pylons.routes_dict']['controller'],
485 environ['pylons.routes_dict']['action']])
489 environ['pylons.routes_dict']['action']])
486
490
487 self.rc_config = SettingsModel().get_all_settings(cache=True)
491 self.rc_config = SettingsModel().get_all_settings(cache=True)
488 self.ip_addr = get_ip_addr(environ)
492 self.ip_addr = get_ip_addr(environ)
489
493
490 # The rhodecode auth user is looked up and passed through the
494 # The rhodecode auth user is looked up and passed through the
491 # environ by the pylons compatibility tween in pyramid.
495 # environ by the pylons compatibility tween in pyramid.
492 # So we can just grab it from there.
496 # So we can just grab it from there.
493 auth_user = environ['rc_auth_user']
497 auth_user = environ['rc_auth_user']
494
498
495 # set globals for auth user
499 # set globals for auth user
496 request.user = auth_user
500 request.user = auth_user
497 c.rhodecode_user = self._rhodecode_user = auth_user
501 c.rhodecode_user = self._rhodecode_user = auth_user
498
502
499 log.info('IP: %s User: %s accessed %s [%s]' % (
503 log.info('IP: %s User: %s accessed %s [%s]' % (
500 self.ip_addr, auth_user, safe_unicode(get_access_path(environ)),
504 self.ip_addr, auth_user, safe_unicode(get_access_path(environ)),
501 _route_name)
505 _route_name)
502 )
506 )
503
507
504 user_obj = auth_user.get_instance()
508 user_obj = auth_user.get_instance()
505 if user_obj and user_obj.user_data.get('force_password_change'):
509 if user_obj and user_obj.user_data.get('force_password_change'):
506 h.flash('You are required to change your password', 'warning',
510 h.flash('You are required to change your password', 'warning',
507 ignore_duplicate=True)
511 ignore_duplicate=True)
508 return self._dispatch_redirect(
512 return self._dispatch_redirect(
509 url('my_account_password'), environ, start_response)
513 url('my_account_password'), environ, start_response)
510
514
511 return WSGIController.__call__(self, environ, start_response)
515 return WSGIController.__call__(self, environ, start_response)
512
516
513
517
514 class BaseRepoController(BaseController):
518 class BaseRepoController(BaseController):
515 """
519 """
516 Base class for controllers responsible for loading all needed data for
520 Base class for controllers responsible for loading all needed data for
517 repository loaded items are
521 repository loaded items are
518
522
519 c.rhodecode_repo: instance of scm repository
523 c.rhodecode_repo: instance of scm repository
520 c.rhodecode_db_repo: instance of db
524 c.rhodecode_db_repo: instance of db
521 c.repository_requirements_missing: shows that repository specific data
525 c.repository_requirements_missing: shows that repository specific data
522 could not be displayed due to the missing requirements
526 could not be displayed due to the missing requirements
523 c.repository_pull_requests: show number of open pull requests
527 c.repository_pull_requests: show number of open pull requests
524 """
528 """
525
529
526 def __before__(self):
530 def __before__(self):
527 super(BaseRepoController, self).__before__()
531 super(BaseRepoController, self).__before__()
528 if c.repo_name: # extracted from routes
532 if c.repo_name: # extracted from routes
529 db_repo = Repository.get_by_repo_name(c.repo_name)
533 db_repo = Repository.get_by_repo_name(c.repo_name)
530 if not db_repo:
534 if not db_repo:
531 return
535 return
532
536
533 log.debug(
537 log.debug(
534 'Found repository in database %s with state `%s`',
538 'Found repository in database %s with state `%s`',
535 safe_unicode(db_repo), safe_unicode(db_repo.repo_state))
539 safe_unicode(db_repo), safe_unicode(db_repo.repo_state))
536 route = getattr(request.environ.get('routes.route'), 'name', '')
540 route = getattr(request.environ.get('routes.route'), 'name', '')
537
541
538 # allow to delete repos that are somehow damages in filesystem
542 # allow to delete repos that are somehow damages in filesystem
539 if route in ['delete_repo']:
543 if route in ['delete_repo']:
540 return
544 return
541
545
542 if db_repo.repo_state in [Repository.STATE_PENDING]:
546 if db_repo.repo_state in [Repository.STATE_PENDING]:
543 if route in ['repo_creating_home']:
547 if route in ['repo_creating_home']:
544 return
548 return
545 check_url = url('repo_creating_home', repo_name=c.repo_name)
549 check_url = url('repo_creating_home', repo_name=c.repo_name)
546 return redirect(check_url)
550 return redirect(check_url)
547
551
548 self.rhodecode_db_repo = db_repo
552 self.rhodecode_db_repo = db_repo
549
553
550 missing_requirements = False
554 missing_requirements = False
551 try:
555 try:
552 self.rhodecode_repo = self.rhodecode_db_repo.scm_instance()
556 self.rhodecode_repo = self.rhodecode_db_repo.scm_instance()
553 except RepositoryRequirementError as e:
557 except RepositoryRequirementError as e:
554 missing_requirements = True
558 missing_requirements = True
555 self._handle_missing_requirements(e)
559 self._handle_missing_requirements(e)
556
560
557 if self.rhodecode_repo is None and not missing_requirements:
561 if self.rhodecode_repo is None and not missing_requirements:
558 log.error('%s this repository is present in database but it '
562 log.error('%s this repository is present in database but it '
559 'cannot be created as an scm instance', c.repo_name)
563 'cannot be created as an scm instance', c.repo_name)
560
564
561 h.flash(_(
565 h.flash(_(
562 "The repository at %(repo_name)s cannot be located.") %
566 "The repository at %(repo_name)s cannot be located.") %
563 {'repo_name': c.repo_name},
567 {'repo_name': c.repo_name},
564 category='error', ignore_duplicate=True)
568 category='error', ignore_duplicate=True)
565 redirect(h.route_path('home'))
569 redirect(h.route_path('home'))
566
570
567 # update last change according to VCS data
571 # update last change according to VCS data
568 if not missing_requirements:
572 if not missing_requirements:
569 commit = db_repo.get_commit(
573 commit = db_repo.get_commit(
570 pre_load=["author", "date", "message", "parents"])
574 pre_load=["author", "date", "message", "parents"])
571 db_repo.update_commit_cache(commit)
575 db_repo.update_commit_cache(commit)
572
576
573 # Prepare context
577 # Prepare context
574 c.rhodecode_db_repo = db_repo
578 c.rhodecode_db_repo = db_repo
575 c.rhodecode_repo = self.rhodecode_repo
579 c.rhodecode_repo = self.rhodecode_repo
576 c.repository_requirements_missing = missing_requirements
580 c.repository_requirements_missing = missing_requirements
577
581
578 self._update_global_counters(self.scm_model, db_repo)
582 self._update_global_counters(self.scm_model, db_repo)
579
583
580 def _update_global_counters(self, scm_model, db_repo):
584 def _update_global_counters(self, scm_model, db_repo):
581 """
585 """
582 Base variables that are exposed to every page of repository
586 Base variables that are exposed to every page of repository
583 """
587 """
584 c.repository_pull_requests = scm_model.get_pull_requests(db_repo)
588 c.repository_pull_requests = scm_model.get_pull_requests(db_repo)
585
589
586 def _handle_missing_requirements(self, error):
590 def _handle_missing_requirements(self, error):
587 self.rhodecode_repo = None
591 self.rhodecode_repo = None
588 log.error(
592 log.error(
589 'Requirements are missing for repository %s: %s',
593 'Requirements are missing for repository %s: %s',
590 c.repo_name, error.message)
594 c.repo_name, error.message)
591
595
592 summary_url = h.route_path('repo_summary', repo_name=c.repo_name)
596 summary_url = h.route_path('repo_summary', repo_name=c.repo_name)
593 statistics_url = url('edit_repo_statistics', repo_name=c.repo_name)
597 statistics_url = url('edit_repo_statistics', repo_name=c.repo_name)
594 settings_update_url = url('repo', repo_name=c.repo_name)
598 settings_update_url = url('repo', repo_name=c.repo_name)
595 path = request.path
599 path = request.path
596 should_redirect = (
600 should_redirect = (
597 path not in (summary_url, settings_update_url)
601 path not in (summary_url, settings_update_url)
598 and '/settings' not in path or path == statistics_url
602 and '/settings' not in path or path == statistics_url
599 )
603 )
600 if should_redirect:
604 if should_redirect:
601 redirect(summary_url)
605 redirect(summary_url)
General Comments 0
You need to be logged in to leave comments. Login now