##// END OF EJS Templates
ssh: don't show the SSH clone url if ssh is disabled
marcink -
r2498:5fe36cd7 default
parent child Browse files
Show More
@@ -1,375 +1,372 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import string
23 23
24 24 from pyramid.view import view_config
25 25 from beaker.cache import cache_region
26 26
27 27 from rhodecode.controllers import utils
28 28 from rhodecode.apps._base import RepoAppView
29 29 from rhodecode.config.conf import (LANGUAGES_EXTENSIONS_MAP)
30 30 from rhodecode.lib import caches, helpers as h
31 31 from rhodecode.lib.helpers import RepoPage
32 32 from rhodecode.lib.utils2 import safe_str, safe_int
33 33 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
34 34 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
35 35 from rhodecode.lib.ext_json import json
36 36 from rhodecode.lib.vcs.backends.base import EmptyCommit
37 37 from rhodecode.lib.vcs.exceptions import CommitError, EmptyRepositoryError, \
38 38 CommitDoesNotExistError
39 39 from rhodecode.model.db import Statistics, CacheKey, User
40 40 from rhodecode.model.meta import Session
41 41 from rhodecode.model.repo import ReadmeFinder
42 42 from rhodecode.model.scm import ScmModel
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46
47 47 class RepoSummaryView(RepoAppView):
48 48
49 49 def load_default_context(self):
50 50 c = self._get_local_tmpl_context(include_app_defaults=True)
51
52 51 c.rhodecode_repo = None
53 52 if not c.repository_requirements_missing:
54 53 c.rhodecode_repo = self.rhodecode_vcs_repo
55
56
57 54 return c
58 55
59 56 def _get_readme_data(self, db_repo, default_renderer):
60 57 repo_name = db_repo.repo_name
61 58 log.debug('Looking for README file')
62 59
63 60 @cache_region('long_term')
64 61 def _generate_readme(cache_key):
65 62 readme_data = None
66 63 readme_node = None
67 64 readme_filename = None
68 65 commit = self._get_landing_commit_or_none(db_repo)
69 66 if commit:
70 67 log.debug("Searching for a README file.")
71 68 readme_node = ReadmeFinder(default_renderer).search(commit)
72 69 if readme_node:
73 70 relative_urls = {
74 71 'raw': h.route_path(
75 72 'repo_file_raw', repo_name=repo_name,
76 73 commit_id=commit.raw_id, f_path=readme_node.path),
77 74 'standard': h.route_path(
78 75 'repo_files', repo_name=repo_name,
79 76 commit_id=commit.raw_id, f_path=readme_node.path),
80 77 }
81 78 readme_data = self._render_readme_or_none(
82 79 commit, readme_node, relative_urls)
83 80 readme_filename = readme_node.path
84 81 return readme_data, readme_filename
85 82
86 83 invalidator_context = CacheKey.repo_context_cache(
87 84 _generate_readme, repo_name, CacheKey.CACHE_TYPE_README)
88 85
89 86 with invalidator_context as context:
90 87 context.invalidate()
91 88 computed = context.compute()
92 89
93 90 return computed
94 91
95 92 def _get_landing_commit_or_none(self, db_repo):
96 93 log.debug("Getting the landing commit.")
97 94 try:
98 95 commit = db_repo.get_landing_commit()
99 96 if not isinstance(commit, EmptyCommit):
100 97 return commit
101 98 else:
102 99 log.debug("Repository is empty, no README to render.")
103 100 except CommitError:
104 101 log.exception(
105 102 "Problem getting commit when trying to render the README.")
106 103
107 104 def _render_readme_or_none(self, commit, readme_node, relative_urls):
108 105 log.debug(
109 106 'Found README file `%s` rendering...', readme_node.path)
110 107 renderer = MarkupRenderer()
111 108 try:
112 109 html_source = renderer.render(
113 110 readme_node.content, filename=readme_node.path)
114 111 if relative_urls:
115 112 return relative_links(html_source, relative_urls)
116 113 return html_source
117 114 except Exception:
118 115 log.exception(
119 116 "Exception while trying to render the README")
120 117
121 118 def _load_commits_context(self, c):
122 119 p = safe_int(self.request.GET.get('page'), 1)
123 120 size = safe_int(self.request.GET.get('size'), 10)
124 121
125 122 def url_generator(**kw):
126 123 query_params = {
127 124 'size': size
128 125 }
129 126 query_params.update(kw)
130 127 return h.route_path(
131 128 'repo_summary_commits',
132 129 repo_name=c.rhodecode_db_repo.repo_name, _query=query_params)
133 130
134 131 pre_load = ['author', 'branch', 'date', 'message']
135 132 try:
136 133 collection = self.rhodecode_vcs_repo.get_commits(pre_load=pre_load)
137 134 except EmptyRepositoryError:
138 135 collection = self.rhodecode_vcs_repo
139 136
140 137 c.repo_commits = RepoPage(
141 138 collection, page=p, items_per_page=size, url=url_generator)
142 139 page_ids = [x.raw_id for x in c.repo_commits]
143 140 c.comments = self.db_repo.get_comments(page_ids)
144 141 c.statuses = self.db_repo.statuses(page_ids)
145 142
146 143 @LoginRequired()
147 144 @HasRepoPermissionAnyDecorator(
148 145 'repository.read', 'repository.write', 'repository.admin')
149 146 @view_config(
150 147 route_name='repo_summary_commits', request_method='GET',
151 148 renderer='rhodecode:templates/summary/summary_commits.mako')
152 149 def summary_commits(self):
153 150 c = self.load_default_context()
154 151 self._load_commits_context(c)
155 152 return self._get_template_context(c)
156 153
157 154 @LoginRequired()
158 155 @HasRepoPermissionAnyDecorator(
159 156 'repository.read', 'repository.write', 'repository.admin')
160 157 @view_config(
161 158 route_name='repo_summary', request_method='GET',
162 159 renderer='rhodecode:templates/summary/summary.mako')
163 160 @view_config(
164 161 route_name='repo_summary_slash', request_method='GET',
165 162 renderer='rhodecode:templates/summary/summary.mako')
166 163 @view_config(
167 164 route_name='repo_summary_explicit', request_method='GET',
168 165 renderer='rhodecode:templates/summary/summary.mako')
169 166 def summary(self):
170 167 c = self.load_default_context()
171 168
172 169 # Prepare the clone URL
173 170 username = ''
174 171 if self._rhodecode_user.username != User.DEFAULT_USER:
175 172 username = safe_str(self._rhodecode_user.username)
176 173
177 174 _def_clone_uri = _def_clone_uri_id = c.clone_uri_tmpl
178 175 _def_clone_uri_ssh = c.clone_uri_ssh_tmpl
179 176
180 177 if '{repo}' in _def_clone_uri:
181 178 _def_clone_uri_id = _def_clone_uri.replace(
182 179 '{repo}', '_{repoid}')
183 180 elif '{repoid}' in _def_clone_uri:
184 181 _def_clone_uri_id = _def_clone_uri.replace(
185 182 '_{repoid}', '{repo}')
186 183
187 184 c.clone_repo_url = self.db_repo.clone_url(
188 185 user=username, uri_tmpl=_def_clone_uri)
189 186 c.clone_repo_url_id = self.db_repo.clone_url(
190 187 user=username, uri_tmpl=_def_clone_uri_id)
191 188 c.clone_repo_url_ssh = self.db_repo.clone_url(
192 189 uri_tmpl=_def_clone_uri_ssh, ssh=True)
193 190
194 191 # If enabled, get statistics data
195 192
196 193 c.show_stats = bool(self.db_repo.enable_statistics)
197 194
198 195 stats = Session().query(Statistics) \
199 196 .filter(Statistics.repository == self.db_repo) \
200 197 .scalar()
201 198
202 199 c.stats_percentage = 0
203 200
204 201 if stats and stats.languages:
205 202 c.no_data = False is self.db_repo.enable_statistics
206 203 lang_stats_d = json.loads(stats.languages)
207 204
208 205 # Sort first by decreasing count and second by the file extension,
209 206 # so we have a consistent output.
210 207 lang_stats_items = sorted(lang_stats_d.iteritems(),
211 208 key=lambda k: (-k[1], k[0]))[:10]
212 209 lang_stats = [(x, {"count": y,
213 210 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
214 211 for x, y in lang_stats_items]
215 212
216 213 c.trending_languages = json.dumps(lang_stats)
217 214 else:
218 215 c.no_data = True
219 216 c.trending_languages = json.dumps({})
220 217
221 218 scm_model = ScmModel()
222 219 c.enable_downloads = self.db_repo.enable_downloads
223 220 c.repository_followers = scm_model.get_followers(self.db_repo)
224 221 c.repository_forks = scm_model.get_forks(self.db_repo)
225 222 c.repository_is_user_following = scm_model.is_following_repo(
226 223 self.db_repo_name, self._rhodecode_user.user_id)
227 224
228 225 # first interaction with the VCS instance after here...
229 226 if c.repository_requirements_missing:
230 227 self.request.override_renderer = \
231 228 'rhodecode:templates/summary/missing_requirements.mako'
232 229 return self._get_template_context(c)
233 230
234 231 c.readme_data, c.readme_file = \
235 232 self._get_readme_data(self.db_repo, c.visual.default_renderer)
236 233
237 234 # loads the summary commits template context
238 235 self._load_commits_context(c)
239 236
240 237 return self._get_template_context(c)
241 238
242 239 def get_request_commit_id(self):
243 240 return self.request.matchdict['commit_id']
244 241
245 242 @LoginRequired()
246 243 @HasRepoPermissionAnyDecorator(
247 244 'repository.read', 'repository.write', 'repository.admin')
248 245 @view_config(
249 246 route_name='repo_stats', request_method='GET',
250 247 renderer='json_ext')
251 248 def repo_stats(self):
252 249 commit_id = self.get_request_commit_id()
253 250
254 251 _namespace = caches.get_repo_namespace_key(
255 252 caches.SUMMARY_STATS, self.db_repo_name)
256 253 show_stats = bool(self.db_repo.enable_statistics)
257 254 cache_manager = caches.get_cache_manager(
258 255 'repo_cache_long', _namespace)
259 256 _cache_key = caches.compute_key_from_params(
260 257 self.db_repo_name, commit_id, show_stats)
261 258
262 259 def compute_stats():
263 260 code_stats = {}
264 261 size = 0
265 262 try:
266 263 scm_instance = self.db_repo.scm_instance()
267 264 commit = scm_instance.get_commit(commit_id)
268 265
269 266 for node in commit.get_filenodes_generator():
270 267 size += node.size
271 268 if not show_stats:
272 269 continue
273 270 ext = string.lower(node.extension)
274 271 ext_info = LANGUAGES_EXTENSIONS_MAP.get(ext)
275 272 if ext_info:
276 273 if ext in code_stats:
277 274 code_stats[ext]['count'] += 1
278 275 else:
279 276 code_stats[ext] = {"count": 1, "desc": ext_info}
280 277 except (EmptyRepositoryError, CommitDoesNotExistError):
281 278 pass
282 279 return {'size': h.format_byte_size_binary(size),
283 280 'code_stats': code_stats}
284 281
285 282 stats = cache_manager.get(_cache_key, createfunc=compute_stats)
286 283 return stats
287 284
288 285 @LoginRequired()
289 286 @HasRepoPermissionAnyDecorator(
290 287 'repository.read', 'repository.write', 'repository.admin')
291 288 @view_config(
292 289 route_name='repo_refs_data', request_method='GET',
293 290 renderer='json_ext')
294 291 def repo_refs_data(self):
295 292 _ = self.request.translate
296 293 self.load_default_context()
297 294
298 295 repo = self.rhodecode_vcs_repo
299 296 refs_to_create = [
300 297 (_("Branch"), repo.branches, 'branch'),
301 298 (_("Tag"), repo.tags, 'tag'),
302 299 (_("Bookmark"), repo.bookmarks, 'book'),
303 300 ]
304 301 res = self._create_reference_data(
305 302 repo, self.db_repo_name, refs_to_create)
306 303 data = {
307 304 'more': False,
308 305 'results': res
309 306 }
310 307 return data
311 308
312 309 @LoginRequired()
313 310 @HasRepoPermissionAnyDecorator(
314 311 'repository.read', 'repository.write', 'repository.admin')
315 312 @view_config(
316 313 route_name='repo_refs_changelog_data', request_method='GET',
317 314 renderer='json_ext')
318 315 def repo_refs_changelog_data(self):
319 316 _ = self.request.translate
320 317 self.load_default_context()
321 318
322 319 repo = self.rhodecode_vcs_repo
323 320
324 321 refs_to_create = [
325 322 (_("Branches"), repo.branches, 'branch'),
326 323 (_("Closed branches"), repo.branches_closed, 'branch_closed'),
327 324 # TODO: enable when vcs can handle bookmarks filters
328 325 # (_("Bookmarks"), repo.bookmarks, "book"),
329 326 ]
330 327 res = self._create_reference_data(
331 328 repo, self.db_repo_name, refs_to_create)
332 329 data = {
333 330 'more': False,
334 331 'results': res
335 332 }
336 333 return data
337 334
338 335 def _create_reference_data(self, repo, full_repo_name, refs_to_create):
339 336 format_ref_id = utils.get_format_ref_id(repo)
340 337
341 338 result = []
342 339 for title, refs, ref_type in refs_to_create:
343 340 if refs:
344 341 result.append({
345 342 'text': title,
346 343 'children': self._create_reference_items(
347 344 repo, full_repo_name, refs, ref_type,
348 345 format_ref_id),
349 346 })
350 347 return result
351 348
352 349 def _create_reference_items(self, repo, full_repo_name, refs, ref_type,
353 350 format_ref_id):
354 351 result = []
355 352 is_svn = h.is_svn(repo)
356 353 for ref_name, raw_id in refs.iteritems():
357 354 files_url = self._create_files_url(
358 355 repo, full_repo_name, ref_name, raw_id, is_svn)
359 356 result.append({
360 357 'text': ref_name,
361 358 'id': format_ref_id(ref_name, raw_id),
362 359 'raw_id': raw_id,
363 360 'type': ref_type,
364 361 'files_url': files_url,
365 362 })
366 363 return result
367 364
368 365 def _create_files_url(self, repo, full_repo_name, ref_name, raw_id, is_svn):
369 366 use_commit_id = '/' in ref_name or is_svn
370 367 return h.route_path(
371 368 'repo_files',
372 369 repo_name=full_repo_name,
373 370 f_path=ref_name if is_svn else '',
374 371 commit_id=raw_id if use_commit_id else ref_name,
375 372 _query=dict(at=ref_name))
@@ -1,543 +1,546 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 The base Controller API
23 23 Provides the BaseController class for subclassing. And usage in different
24 24 controllers
25 25 """
26 26
27 27 import logging
28 28 import socket
29 29
30 30 import markupsafe
31 31 import ipaddress
32 32
33 33 from paste.auth.basic import AuthBasicAuthenticator
34 34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
35 35 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
36 36
37 37 import rhodecode
38 38 from rhodecode.authentication.base import VCS_TYPE
39 39 from rhodecode.lib import auth, utils2
40 40 from rhodecode.lib import helpers as h
41 41 from rhodecode.lib.auth import AuthUser, CookieStoreWrapper
42 42 from rhodecode.lib.exceptions import UserCreationError
43 43 from rhodecode.lib.utils import (password_changed, get_enabled_hook_classes)
44 44 from rhodecode.lib.utils2 import (
45 45 str2bool, safe_unicode, AttributeDict, safe_int, md5, aslist, safe_str)
46 46 from rhodecode.model.db import Repository, User, ChangesetComment
47 47 from rhodecode.model.notification import NotificationModel
48 48 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
49 49
50 50 log = logging.getLogger(__name__)
51 51
52 52
53 53 def _filter_proxy(ip):
54 54 """
55 55 Passed in IP addresses in HEADERS can be in a special format of multiple
56 56 ips. Those comma separated IPs are passed from various proxies in the
57 57 chain of request processing. The left-most being the original client.
58 58 We only care about the first IP which came from the org. client.
59 59
60 60 :param ip: ip string from headers
61 61 """
62 62 if ',' in ip:
63 63 _ips = ip.split(',')
64 64 _first_ip = _ips[0].strip()
65 65 log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
66 66 return _first_ip
67 67 return ip
68 68
69 69
70 70 def _filter_port(ip):
71 71 """
72 72 Removes a port from ip, there are 4 main cases to handle here.
73 73 - ipv4 eg. 127.0.0.1
74 74 - ipv6 eg. ::1
75 75 - ipv4+port eg. 127.0.0.1:8080
76 76 - ipv6+port eg. [::1]:8080
77 77
78 78 :param ip:
79 79 """
80 80 def is_ipv6(ip_addr):
81 81 if hasattr(socket, 'inet_pton'):
82 82 try:
83 83 socket.inet_pton(socket.AF_INET6, ip_addr)
84 84 except socket.error:
85 85 return False
86 86 else:
87 87 # fallback to ipaddress
88 88 try:
89 89 ipaddress.IPv6Address(safe_unicode(ip_addr))
90 90 except Exception:
91 91 return False
92 92 return True
93 93
94 94 if ':' not in ip: # must be ipv4 pure ip
95 95 return ip
96 96
97 97 if '[' in ip and ']' in ip: # ipv6 with port
98 98 return ip.split(']')[0][1:].lower()
99 99
100 100 # must be ipv6 or ipv4 with port
101 101 if is_ipv6(ip):
102 102 return ip
103 103 else:
104 104 ip, _port = ip.split(':')[:2] # means ipv4+port
105 105 return ip
106 106
107 107
108 108 def get_ip_addr(environ):
109 109 proxy_key = 'HTTP_X_REAL_IP'
110 110 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
111 111 def_key = 'REMOTE_ADDR'
112 112 _filters = lambda x: _filter_port(_filter_proxy(x))
113 113
114 114 ip = environ.get(proxy_key)
115 115 if ip:
116 116 return _filters(ip)
117 117
118 118 ip = environ.get(proxy_key2)
119 119 if ip:
120 120 return _filters(ip)
121 121
122 122 ip = environ.get(def_key, '0.0.0.0')
123 123 return _filters(ip)
124 124
125 125
126 126 def get_server_ip_addr(environ, log_errors=True):
127 127 hostname = environ.get('SERVER_NAME')
128 128 try:
129 129 return socket.gethostbyname(hostname)
130 130 except Exception as e:
131 131 if log_errors:
132 132 # in some cases this lookup is not possible, and we don't want to
133 133 # make it an exception in logs
134 134 log.exception('Could not retrieve server ip address: %s', e)
135 135 return hostname
136 136
137 137
138 138 def get_server_port(environ):
139 139 return environ.get('SERVER_PORT')
140 140
141 141
142 142 def get_access_path(environ):
143 143 path = environ.get('PATH_INFO')
144 144 org_req = environ.get('pylons.original_request')
145 145 if org_req:
146 146 path = org_req.environ.get('PATH_INFO')
147 147 return path
148 148
149 149
150 150 def get_user_agent(environ):
151 151 return environ.get('HTTP_USER_AGENT')
152 152
153 153
154 154 def vcs_operation_context(
155 155 environ, repo_name, username, action, scm, check_locking=True,
156 156 is_shadow_repo=False):
157 157 """
158 158 Generate the context for a vcs operation, e.g. push or pull.
159 159
160 160 This context is passed over the layers so that hooks triggered by the
161 161 vcs operation know details like the user, the user's IP address etc.
162 162
163 163 :param check_locking: Allows to switch of the computation of the locking
164 164 data. This serves mainly the need of the simplevcs middleware to be
165 165 able to disable this for certain operations.
166 166
167 167 """
168 168 # Tri-state value: False: unlock, None: nothing, True: lock
169 169 make_lock = None
170 170 locked_by = [None, None, None]
171 171 is_anonymous = username == User.DEFAULT_USER
172 172 user = User.get_by_username(username)
173 173 if not is_anonymous and check_locking:
174 174 log.debug('Checking locking on repository "%s"', repo_name)
175 175 repo = Repository.get_by_repo_name(repo_name)
176 176 make_lock, __, locked_by = repo.get_locking_state(
177 177 action, user.user_id)
178 178 user_id = user.user_id
179 179 settings_model = VcsSettingsModel(repo=repo_name)
180 180 ui_settings = settings_model.get_ui_settings()
181 181
182 182 extras = {
183 183 'ip': get_ip_addr(environ),
184 184 'username': username,
185 185 'user_id': user_id,
186 186 'action': action,
187 187 'repository': repo_name,
188 188 'scm': scm,
189 189 'config': rhodecode.CONFIG['__file__'],
190 190 'make_lock': make_lock,
191 191 'locked_by': locked_by,
192 192 'server_url': utils2.get_server_url(environ),
193 193 'user_agent': get_user_agent(environ),
194 194 'hooks': get_enabled_hook_classes(ui_settings),
195 195 'is_shadow_repo': is_shadow_repo,
196 196 }
197 197 return extras
198 198
199 199
200 200 class BasicAuth(AuthBasicAuthenticator):
201 201
202 202 def __init__(self, realm, authfunc, registry, auth_http_code=None,
203 203 initial_call_detection=False, acl_repo_name=None):
204 204 self.realm = realm
205 205 self.initial_call = initial_call_detection
206 206 self.authfunc = authfunc
207 207 self.registry = registry
208 208 self.acl_repo_name = acl_repo_name
209 209 self._rc_auth_http_code = auth_http_code
210 210
211 211 def _get_response_from_code(self, http_code):
212 212 try:
213 213 return get_exception(safe_int(http_code))
214 214 except Exception:
215 215 log.exception('Failed to fetch response for code %s' % http_code)
216 216 return HTTPForbidden
217 217
218 218 def get_rc_realm(self):
219 219 return safe_str(self.registry.rhodecode_settings.get('rhodecode_realm'))
220 220
221 221 def build_authentication(self):
222 222 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
223 223 if self._rc_auth_http_code and not self.initial_call:
224 224 # return alternative HTTP code if alternative http return code
225 225 # is specified in RhodeCode config, but ONLY if it's not the
226 226 # FIRST call
227 227 custom_response_klass = self._get_response_from_code(
228 228 self._rc_auth_http_code)
229 229 return custom_response_klass(headers=head)
230 230 return HTTPUnauthorized(headers=head)
231 231
232 232 def authenticate(self, environ):
233 233 authorization = AUTHORIZATION(environ)
234 234 if not authorization:
235 235 return self.build_authentication()
236 236 (authmeth, auth) = authorization.split(' ', 1)
237 237 if 'basic' != authmeth.lower():
238 238 return self.build_authentication()
239 239 auth = auth.strip().decode('base64')
240 240 _parts = auth.split(':', 1)
241 241 if len(_parts) == 2:
242 242 username, password = _parts
243 243 auth_data = self.authfunc(
244 244 username, password, environ, VCS_TYPE,
245 245 registry=self.registry, acl_repo_name=self.acl_repo_name)
246 246 if auth_data:
247 247 return {'username': username, 'auth_data': auth_data}
248 248 if username and password:
249 249 # we mark that we actually executed authentication once, at
250 250 # that point we can use the alternative auth code
251 251 self.initial_call = False
252 252
253 253 return self.build_authentication()
254 254
255 255 __call__ = authenticate
256 256
257 257
258 258 def calculate_version_hash(config):
259 259 return md5(
260 260 config.get('beaker.session.secret', '') +
261 261 rhodecode.__version__)[:8]
262 262
263 263
264 264 def get_current_lang(request):
265 265 # NOTE(marcink): remove after pyramid move
266 266 try:
267 267 return translation.get_lang()[0]
268 268 except:
269 269 pass
270 270
271 271 return getattr(request, '_LOCALE_', request.locale_name)
272 272
273 273
274 274 def attach_context_attributes(context, request, user_id):
275 275 """
276 276 Attach variables into template context called `c`.
277 277 """
278 278 config = request.registry.settings
279 279
280 280
281 281 rc_config = SettingsModel().get_all_settings(cache=True)
282 282
283 283 context.rhodecode_version = rhodecode.__version__
284 284 context.rhodecode_edition = config.get('rhodecode.edition')
285 285 # unique secret + version does not leak the version but keep consistency
286 286 context.rhodecode_version_hash = calculate_version_hash(config)
287 287
288 288 # Default language set for the incoming request
289 289 context.language = get_current_lang(request)
290 290
291 291 # Visual options
292 292 context.visual = AttributeDict({})
293 293
294 294 # DB stored Visual Items
295 295 context.visual.show_public_icon = str2bool(
296 296 rc_config.get('rhodecode_show_public_icon'))
297 297 context.visual.show_private_icon = str2bool(
298 298 rc_config.get('rhodecode_show_private_icon'))
299 299 context.visual.stylify_metatags = str2bool(
300 300 rc_config.get('rhodecode_stylify_metatags'))
301 301 context.visual.dashboard_items = safe_int(
302 302 rc_config.get('rhodecode_dashboard_items', 100))
303 303 context.visual.admin_grid_items = safe_int(
304 304 rc_config.get('rhodecode_admin_grid_items', 100))
305 305 context.visual.repository_fields = str2bool(
306 306 rc_config.get('rhodecode_repository_fields'))
307 307 context.visual.show_version = str2bool(
308 308 rc_config.get('rhodecode_show_version'))
309 309 context.visual.use_gravatar = str2bool(
310 310 rc_config.get('rhodecode_use_gravatar'))
311 311 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
312 312 context.visual.default_renderer = rc_config.get(
313 313 'rhodecode_markup_renderer', 'rst')
314 314 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
315 315 context.visual.rhodecode_support_url = \
316 316 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
317 317
318 318 context.visual.affected_files_cut_off = 60
319 319
320 320 context.pre_code = rc_config.get('rhodecode_pre_code')
321 321 context.post_code = rc_config.get('rhodecode_post_code')
322 322 context.rhodecode_name = rc_config.get('rhodecode_title')
323 323 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
324 324 # if we have specified default_encoding in the request, it has more
325 325 # priority
326 326 if request.GET.get('default_encoding'):
327 327 context.default_encodings.insert(0, request.GET.get('default_encoding'))
328 328 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
329 329 context.clone_uri_ssh_tmpl = rc_config.get('rhodecode_clone_uri_ssh_tmpl')
330 330
331 331 # INI stored
332 332 context.labs_active = str2bool(
333 333 config.get('labs_settings_active', 'false'))
334 context.ssh_enabled = str2bool(
335 config.get('ssh.generate_authorized_keyfile', 'false'))
336
334 337 context.visual.allow_repo_location_change = str2bool(
335 338 config.get('allow_repo_location_change', True))
336 339 context.visual.allow_custom_hooks_settings = str2bool(
337 340 config.get('allow_custom_hooks_settings', True))
338 341 context.debug_style = str2bool(config.get('debug_style', False))
339 342
340 343 context.rhodecode_instanceid = config.get('instance_id')
341 344
342 345 context.visual.cut_off_limit_diff = safe_int(
343 346 config.get('cut_off_limit_diff'))
344 347 context.visual.cut_off_limit_file = safe_int(
345 348 config.get('cut_off_limit_file'))
346 349
347 350 # AppEnlight
348 351 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
349 352 context.appenlight_api_public_key = config.get(
350 353 'appenlight.api_public_key', '')
351 354 context.appenlight_server_url = config.get('appenlight.server_url', '')
352 355
353 356 # JS template context
354 357 context.template_context = {
355 358 'repo_name': None,
356 359 'repo_type': None,
357 360 'repo_landing_commit': None,
358 361 'rhodecode_user': {
359 362 'username': None,
360 363 'email': None,
361 364 'notification_status': False
362 365 },
363 366 'visual': {
364 367 'default_renderer': None
365 368 },
366 369 'commit_data': {
367 370 'commit_id': None
368 371 },
369 372 'pull_request_data': {'pull_request_id': None},
370 373 'timeago': {
371 374 'refresh_time': 120 * 1000,
372 375 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
373 376 },
374 377 'pyramid_dispatch': {
375 378
376 379 },
377 380 'extra': {'plugins': {}}
378 381 }
379 382 # END CONFIG VARS
380 383
381 384 diffmode = 'sideside'
382 385 if request.GET.get('diffmode'):
383 386 if request.GET['diffmode'] == 'unified':
384 387 diffmode = 'unified'
385 388 elif request.session.get('diffmode'):
386 389 diffmode = request.session['diffmode']
387 390
388 391 context.diffmode = diffmode
389 392
390 393 if request.session.get('diffmode') != diffmode:
391 394 request.session['diffmode'] = diffmode
392 395
393 396 context.csrf_token = auth.get_csrf_token(session=request.session)
394 397 context.backends = rhodecode.BACKENDS.keys()
395 398 context.backends.sort()
396 399 context.unread_notifications = NotificationModel().get_unread_cnt_for_user(user_id)
397 400
398 401 # web case
399 402 if hasattr(request, 'user'):
400 403 context.auth_user = request.user
401 404 context.rhodecode_user = request.user
402 405
403 406 # api case
404 407 if hasattr(request, 'rpc_user'):
405 408 context.auth_user = request.rpc_user
406 409 context.rhodecode_user = request.rpc_user
407 410
408 411 # attach the whole call context to the request
409 412 request.call_context = context
410 413
411 414
412 415 def get_auth_user(request):
413 416 environ = request.environ
414 417 session = request.session
415 418
416 419 ip_addr = get_ip_addr(environ)
417 420 # make sure that we update permissions each time we call controller
418 421 _auth_token = (request.GET.get('auth_token', '') or
419 422 request.GET.get('api_key', ''))
420 423
421 424 if _auth_token:
422 425 # when using API_KEY we assume user exists, and
423 426 # doesn't need auth based on cookies.
424 427 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
425 428 authenticated = False
426 429 else:
427 430 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
428 431 try:
429 432 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
430 433 ip_addr=ip_addr)
431 434 except UserCreationError as e:
432 435 h.flash(e, 'error')
433 436 # container auth or other auth functions that create users
434 437 # on the fly can throw this exception signaling that there's
435 438 # issue with user creation, explanation should be provided
436 439 # in Exception itself. We then create a simple blank
437 440 # AuthUser
438 441 auth_user = AuthUser(ip_addr=ip_addr)
439 442
440 443 # in case someone changes a password for user it triggers session
441 444 # flush and forces a re-login
442 445 if password_changed(auth_user, session):
443 446 session.invalidate()
444 447 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
445 448 auth_user = AuthUser(ip_addr=ip_addr)
446 449
447 450 authenticated = cookie_store.get('is_authenticated')
448 451
449 452 if not auth_user.is_authenticated and auth_user.is_user_object:
450 453 # user is not authenticated and not empty
451 454 auth_user.set_authenticated(authenticated)
452 455
453 456 return auth_user
454 457
455 458
456 459 def h_filter(s):
457 460 """
458 461 Custom filter for Mako templates. Mako by standard uses `markupsafe.escape`
459 462 we wrap this with additional functionality that converts None to empty
460 463 strings
461 464 """
462 465 if s is None:
463 466 return markupsafe.Markup()
464 467 return markupsafe.escape(s)
465 468
466 469
467 470 def add_events_routes(config):
468 471 """
469 472 Adds routing that can be used in events. Because some events are triggered
470 473 outside of pyramid context, we need to bootstrap request with some
471 474 routing registered
472 475 """
473 476
474 477 from rhodecode.apps._base import ADMIN_PREFIX
475 478
476 479 config.add_route(name='home', pattern='/')
477 480
478 481 config.add_route(name='login', pattern=ADMIN_PREFIX + '/login')
479 482 config.add_route(name='logout', pattern=ADMIN_PREFIX + '/logout')
480 483 config.add_route(name='repo_summary', pattern='/{repo_name}')
481 484 config.add_route(name='repo_summary_explicit', pattern='/{repo_name}/summary')
482 485 config.add_route(name='repo_group_home', pattern='/{repo_group_name}')
483 486
484 487 config.add_route(name='pullrequest_show',
485 488 pattern='/{repo_name}/pull-request/{pull_request_id}')
486 489 config.add_route(name='pull_requests_global',
487 490 pattern='/pull-request/{pull_request_id}')
488 491 config.add_route(name='repo_commit',
489 492 pattern='/{repo_name}/changeset/{commit_id}')
490 493
491 494 config.add_route(name='repo_files',
492 495 pattern='/{repo_name}/files/{commit_id}/{f_path}')
493 496
494 497
495 498 def bootstrap_config(request):
496 499 import pyramid.testing
497 500 registry = pyramid.testing.Registry('RcTestRegistry')
498 501
499 502 config = pyramid.testing.setUp(registry=registry, request=request)
500 503
501 504 # allow pyramid lookup in testing
502 505 config.include('pyramid_mako')
503 506 config.include('pyramid_beaker')
504 507 config.include('rhodecode.lib.caches')
505 508
506 509 add_events_routes(config)
507 510
508 511 return config
509 512
510 513
511 514 def bootstrap_request(**kwargs):
512 515 import pyramid.testing
513 516
514 517 class TestRequest(pyramid.testing.DummyRequest):
515 518 application_url = kwargs.pop('application_url', 'http://example.com')
516 519 host = kwargs.pop('host', 'example.com:80')
517 520 domain = kwargs.pop('domain', 'example.com')
518 521
519 522 def translate(self, msg):
520 523 return msg
521 524
522 525 def plularize(self, singular, plural, n):
523 526 return singular
524 527
525 528 def get_partial_renderer(self, tmpl_name):
526 529
527 530 from rhodecode.lib.partial_renderer import get_partial_renderer
528 531 return get_partial_renderer(request=self, tmpl_name=tmpl_name)
529 532
530 533 _call_context = {}
531 534 @property
532 535 def call_context(self):
533 536 return self._call_context
534 537
535 538 class TestDummySession(pyramid.testing.DummySession):
536 539 def save(*arg, **kw):
537 540 pass
538 541
539 542 request = TestRequest(**kwargs)
540 543 request.session = TestDummySession()
541 544
542 545 return request
543 546
@@ -1,214 +1,216 b''
1 1 <%def name="refs_counters(branches, closed_branches, tags, bookmarks)">
2 2 <span class="branchtag tag">
3 3 <a href="${h.route_path('branches_home',repo_name=c.repo_name)}" class="childs">
4 4 <i class="icon-branch"></i>${_ungettext(
5 5 '%(num)s Branch','%(num)s Branches', len(branches)) % {'num': len(branches)}}</a>
6 6 </span>
7 7
8 8 %if closed_branches:
9 9 <span class="branchtag tag">
10 10 <a href="${h.route_path('branches_home',repo_name=c.repo_name)}" class="childs">
11 11 <i class="icon-branch"></i>${_ungettext(
12 12 '%(num)s Closed Branch', '%(num)s Closed Branches', len(closed_branches)) % {'num': len(closed_branches)}}</a>
13 13 </span>
14 14 %endif
15 15
16 16 <span class="tagtag tag">
17 17 <a href="${h.route_path('tags_home',repo_name=c.repo_name)}" class="childs">
18 18 <i class="icon-tag"></i>${_ungettext(
19 19 '%(num)s Tag', '%(num)s Tags', len(tags)) % {'num': len(tags)}}</a>
20 20 </span>
21 21
22 22 %if bookmarks:
23 23 <span class="booktag tag">
24 24 <a href="${h.route_path('bookmarks_home',repo_name=c.repo_name)}" class="childs">
25 25 <i class="icon-bookmark"></i>${_ungettext(
26 26 '%(num)s Bookmark', '%(num)s Bookmarks', len(bookmarks)) % {'num': len(bookmarks)}}</a>
27 27 </span>
28 28 %endif
29 29 </%def>
30 30
31 31 <%def name="summary_detail(breadcrumbs_links, show_downloads=True)">
32 32 <% summary = lambda n:{False:'summary-short'}.get(n) %>
33 33
34 34 <div id="summary-menu-stats" class="summary-detail">
35 35 <div class="summary-detail-header">
36 36 <div class="breadcrumbs files_location">
37 37 <h4>
38 38 ${breadcrumbs_links}
39 39 </h4>
40 40 </div>
41 41 <div id="summary_details_expand" class="btn-collapse" data-toggle="summary-details">
42 42 ${_('Show More')}
43 43 </div>
44 44 </div>
45 45
46 46 <div class="fieldset">
47 47
48 48 <div class="left-clone">
49 49 <select id="clone_option" name="clone_option">
50 50 <option value="http" selected="selected">HTTP</option>
51 51 <option value="http_id">HTTP UID</option>
52 <option value="ssh">SSH</option>
52 % if c.ssh_enabled:
53 <option value="ssh">SSH</option>
54 % endif
53 55 </select>
54 56 </div>
55 57 <div class="right-clone">
56 58 <%
57 59 maybe_disabled = ''
58 60 if h.is_svn_without_proxy(c.rhodecode_db_repo):
59 61 maybe_disabled = 'disabled'
60 62 %>
61 63
62 64 <span id="clone_option_http">
63 65 <input type="text" class="input-monospace clone_url_input" ${maybe_disabled} readonly="readonly" value="${c.clone_repo_url}"/>
64 66 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.clone_repo_url}" title="${_('Copy the clone url')}"></i>
65 67 </span>
66 68
67 69 <span style="display: none;" id="clone_option_http_id">
68 70 <input type="text" class="input-monospace clone_url_input" ${maybe_disabled} readonly="readonly" value="${c.clone_repo_url_id}"/>
69 71 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.clone_repo_url_id}" title="${_('Copy the clone by id url')}"></i>
70 72 </span>
71 73
72 74 <span style="display: none;" id="clone_option_ssh">
73 75 <input type="text" class="input-monospace clone_url_input" ${maybe_disabled} readonly="readonly" value="${c.clone_repo_url_ssh}"/>
74 76 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.clone_repo_url_ssh}" title="${_('Copy the clone by ssh url')}"></i>
75 77 </span>
76 78
77 79 % if maybe_disabled:
78 80 <p class="help-block">${_('SVN Protocol is disabled. To enable it, see the')} <a href="${h.route_url('enterprise_svn_setup')}" target="_blank">${_('documentation here')}</a>.</p>
79 81 % endif
80 82
81 83 </div>
82 84 </div>
83 85
84 86 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
85 87 <div class="left-label">
86 88 ${_('Description')}:
87 89 </div>
88 90 <div class="right-content">
89 91 <div class="input ${summary(c.show_stats)}">
90 92 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
91 93 ${dt.repo_desc(c.rhodecode_db_repo.description_safe, c.visual.stylify_metatags)}
92 94 </div>
93 95 </div>
94 96 </div>
95 97
96 98 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
97 99 <div class="left-label">
98 100 ${_('Information')}:
99 101 </div>
100 102 <div class="right-content">
101 103
102 104 <div class="repo-size">
103 105 <% commit_rev = c.rhodecode_db_repo.changeset_cache.get('revision') %>
104 106
105 107 ## commits
106 108 % if commit_rev == -1:
107 109 ${_ungettext('%(num)s Commit', '%(num)s Commits', 0) % {'num': 0}},
108 110 % else:
109 111 <a href="${h.route_path('repo_changelog', repo_name=c.repo_name)}">
110 112 ${_ungettext('%(num)s Commit', '%(num)s Commits', commit_rev) % {'num': commit_rev}}</a>,
111 113 % endif
112 114
113 115 ## forks
114 116 <a title="${_('Number of Repository Forks')}" href="${h.route_path('repo_forks_show_all', repo_name=c.repo_name)}">
115 117 ${c.repository_forks} ${_ungettext('Fork', 'Forks', c.repository_forks)}</a>,
116 118
117 119 ## repo size
118 120 % if commit_rev == -1:
119 121 <span class="stats-bullet">0 B</span>
120 122 % else:
121 123 <span class="stats-bullet" id="repo_size_container">
122 124 ${_('Calculating Repository Size...')}
123 125 </span>
124 126 % endif
125 127 </div>
126 128
127 129 <div class="commit-info">
128 130 <div class="tags">
129 131 % if c.rhodecode_repo:
130 132 ${refs_counters(
131 133 c.rhodecode_repo.branches,
132 134 c.rhodecode_repo.branches_closed,
133 135 c.rhodecode_repo.tags,
134 136 c.rhodecode_repo.bookmarks)}
135 137 % else:
136 138 ## missing requirements can make c.rhodecode_repo None
137 139 ${refs_counters([], [], [], [])}
138 140 % endif
139 141 </div>
140 142 </div>
141 143
142 144 </div>
143 145 </div>
144 146
145 147 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
146 148 <div class="left-label">
147 149 ${_('Statistics')}:
148 150 </div>
149 151 <div class="right-content">
150 152 <div class="input ${summary(c.show_stats)} statistics">
151 153 % if c.show_stats:
152 154 <div id="lang_stats" class="enabled">
153 155 ${_('Calculating Code Statistics...')}
154 156 </div>
155 157 % else:
156 158 <span class="disabled">
157 159 ${_('Statistics are disabled for this repository')}
158 160 </span>
159 161 % if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
160 162 , ${h.link_to(_('enable statistics'),h.route_path('edit_repo',repo_name=c.repo_name, _anchor='repo_enable_statistics'))}
161 163 % endif
162 164 % endif
163 165 </div>
164 166
165 167 </div>
166 168 </div>
167 169
168 170 % if show_downloads:
169 171 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
170 172 <div class="left-label">
171 173 ${_('Downloads')}:
172 174 </div>
173 175 <div class="right-content">
174 176 <div class="input ${summary(c.show_stats)} downloads">
175 177 % if c.rhodecode_repo and len(c.rhodecode_repo.revisions) == 0:
176 178 <span class="disabled">
177 179 ${_('There are no downloads yet')}
178 180 </span>
179 181 % elif not c.enable_downloads:
180 182 <span class="disabled">
181 183 ${_('Downloads are disabled for this repository')}
182 184 </span>
183 185 % if h.HasPermissionAll('hg.admin')('enable downloads on from summary'):
184 186 , ${h.link_to(_('enable downloads'),h.route_path('edit_repo',repo_name=c.repo_name, _anchor='repo_enable_downloads'))}
185 187 % endif
186 188 % else:
187 189 <span class="enabled">
188 190 <a id="archive_link" class="btn btn-small" href="${h.route_path('repo_archivefile',repo_name=c.rhodecode_db_repo.repo_name,fname='tip.zip')}">
189 191 <i class="icon-archive"></i> tip.zip
190 192 ## replaced by some JS on select
191 193 </a>
192 194 </span>
193 195 ${h.hidden('download_options')}
194 196 % endif
195 197 </div>
196 198 </div>
197 199 </div>
198 200 % endif
199 201
200 202 </div><!--end summary-detail-->
201 203 </%def>
202 204
203 205 <%def name="summary_stats(gravatar_function)">
204 206 <div class="sidebar-right">
205 207 <div class="summary-detail-header">
206 208 <h4 class="item">
207 209 ${_('Owner')}
208 210 </h4>
209 211 </div>
210 212 <div class="sidebar-right-content">
211 213 ${gravatar_function(c.rhodecode_db_repo.user.email, 16)}
212 214 </div>
213 215 </div><!--end sidebar-right-->
214 216 </%def>
General Comments 0
You need to be logged in to leave comments. Login now