##// END OF EJS Templates
i18n: updated translation for Polish...
i18n: updated translation for Polish Currently translated at 56.5% (614 of 1087 strings)

File last commit:

r8082:6c381371 default
r8092:7fef5132 default
Show More
base.py
649 lines | 26.6 KiB | text/x-python | PythonLexer
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 # -*- coding: utf-8 -*-
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
kallithea.lib.base
~~~~~~~~~~~~~~~~~~
The base Controller API
Provides the BaseController class for subclassing. And usage in different
controllers
Bradley M. Kuhn
RhodeCode GmbH is not the sole author of this work
r4211 This file was forked by the Kallithea project in July 2014.
Original author and date, and relevant copyright and licensing information is below:
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 :created_on: Oct 06, 2010
:author: marcink
Bradley M. Kuhn
RhodeCode GmbH is not the sole author of this work
r4211 :copyright: (c) 2013 RhodeCode GmbH, and others.
Bradley M. Kuhn
Correct licensing information in individual files....
r4208 :license: GPLv3, see LICENSE.md for more details.
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 """
Mads Kiilerich
py3: replace base64 encoding with base64 module
r7905 import base64
Søren Løvborg
log_in_user: extract user session setup from LoginController...
r5256 import datetime
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 import logging
import traceback
Thomas De Schampheleire
lib: move jsonify from utils to base...
r6409 import warnings
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
Mads Kiilerich
scripts: initial run of import cleanup using isort
r7718 import decorator
import paste.auth.basic
Mads Kiilerich
compare: handle revset revision errors more gracefully
r4364 import paste.httpexceptions
import paste.httpheaders
Mads Kiilerich
scripts: initial run of import cleanup using isort
r7718 import webob.exc
from tg import TGController, config, render_template, request, response, session
from tg import tmpl_context as c
Mads Kiilerich
tg: minimize future diff by some mocking and replacing some pylons imports with tg...
r6508 from tg.i18n import ugettext as _
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
Mads Kiilerich
scripts: initial run of import cleanup using isort
r7718 from kallithea import BACKENDS, __version__
Thomas De Schampheleire
Turbogears2 migration: replace pylons.url by kallithea.config.routing.url...
r6182 from kallithea.config.routing import url
Mads Kiilerich
lib: clean up ext_json and how it is used - avoid monkey patching...
r7987 from kallithea.lib import auth_modules, ext_json
Søren Løvborg
AuthUser: refactor AuthUser cookie/session serialization...
r5265 from kallithea.lib.auth import AuthUser, HasPermissionAnyMiddleware
Mads Kiilerich
scripts: initial run of import cleanup using isort
r7718 from kallithea.lib.exceptions import UserCreationError
Mads Kiilerich
middleware: unify Mercurial and Git _handle_request in the VCS base class...
r7630 from kallithea.lib.utils import get_repo_slug, is_valid_repo
Mads Kiilerich
py3: rename all existing safe_unicode to safe_str
r8078 from kallithea.lib.utils2 import AttributeDict, ascii_bytes, safe_int, safe_str, set_hook_environment, str2bool
Mads Kiilerich
scripts: initial run of import cleanup using isort
r7718 from kallithea.lib.vcs.exceptions import ChangesetDoesNotExistError, EmptyRepositoryError, RepositoryError
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 from kallithea.model import meta
Mads Kiilerich
scripts: initial run of import cleanup using isort
r7718 from kallithea.model.db import PullRequest, Repository, Setting, User
from kallithea.model.scm import ScmModel
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
log = logging.getLogger(__name__)
Alessandro Molina
backend: replace Pylons with TurboGears2...
r6522 def render(template_path):
return render_template({'url': url}, 'mako', template_path)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 def _filter_proxy(ip):
"""
HEADERS can have multiple ips inside the left-most being the original
client, and each successive proxy that passed the request adding the IP
address where it received the request from.
:param ip:
"""
if ',' in ip:
_ips = ip.split(',')
_first_ip = _ips[0].strip()
Mads Kiilerich
cleanup: pass log strings unformatted - avoid unnecessary % formatting when not logging
r5375 log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 return _first_ip
return ip
def _get_ip_addr(environ):
proxy_key = 'HTTP_X_REAL_IP'
proxy_key2 = 'HTTP_X_FORWARDED_FOR'
def_key = 'REMOTE_ADDR'
ip = environ.get(proxy_key)
if ip:
return _filter_proxy(ip)
ip = environ.get(proxy_key2)
if ip:
return _filter_proxy(ip)
ip = environ.get(def_key, '0.0.0.0')
return _filter_proxy(ip)
Mads Kiilerich
lib: refactor _get_access_path as get_path_info...
r7948 def get_path_info(environ):
Mads Kiilerich
py3: fix non-ASCII URLs - decode unicode correctly before passing them to controllers as unicode strings...
r8082 """Return PATH_INFO from environ ... using tg.original_request if available.
In Python 3 WSGI, PATH_INFO is a unicode str, but kind of contains encoded
bytes. The code points are guaranteed to only use the lower 8 bit bits, and
encoding the string with the 1:1 encoding latin1 will give the
corresponding byte string ... which then can be decoded to proper unicode.
Mads Kiilerich
lib: refactor _get_access_path as get_path_info...
r7948 """
Alessandro Molina
backend: replace Pylons with TurboGears2...
r6522 org_req = environ.get('tg.original_request')
Mads Kiilerich
middleware: minor cleanup and alignment between VCSs to clarify how things work
r7629 if org_req is not None:
environ = org_req.environ
Mads Kiilerich
py3: fix non-ASCII URLs - decode unicode correctly before passing them to controllers as unicode strings...
r8082 return safe_str(environ['PATH_INFO'].encode('latin1'))
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
Mads Kiilerich
auth: move IP check to AuthUser.make - it is more about accepting authentication than about permissions after authentication
r7603 def log_in_user(user, remember, is_external_auth, ip_addr):
Søren Løvborg
log_in_user: extract user session setup from LoginController...
r5256 """
Log a `User` in and update session and cookies. If `remember` is True,
the session cookie is set to expire in a year; otherwise, it expires at
the end of the browser session.
Søren Løvborg
BaseController: enable container authentication on all pages...
r5264
Returns populated `AuthUser` object.
Søren Løvborg
log_in_user: extract user session setup from LoginController...
r5256 """
Mads Kiilerich
auth: introduce AuthUser.make factory which can return None if user can't be authenticated
r7602 # It should not be possible to explicitly log in as the default user.
assert not user.is_default_user, user
Mads Kiilerich
auth: move IP check to AuthUser.make - it is more about accepting authentication than about permissions after authentication
r7603 auth_user = AuthUser.make(dbuser=user, is_external_auth=is_external_auth, ip_addr=ip_addr)
Mads Kiilerich
auth: introduce AuthUser.make factory which can return None if user can't be authenticated
r7602 if auth_user is None:
return None
Søren Løvborg
log_in_user: extract user session setup from LoginController...
r5256 user.update_lastlogin()
meta.Session().commit()
# Start new session to prevent session fixation attacks.
session.invalidate()
Søren Løvborg
AuthUser: refactor AuthUser cookie/session serialization...
r5265 session['authuser'] = cookie = auth_user.to_cookie()
Søren Løvborg
log_in_user: extract user session setup from LoginController...
r5256
Mads Kiilerich
docs: improve documentation of beaker session configuration...
r5400 # If they want to be remembered, update the cookie.
# NOTE: Assumes that beaker defaults to browser session cookie.
Søren Løvborg
log_in_user: extract user session setup from LoginController...
r5256 if remember:
t = datetime.datetime.now() + datetime.timedelta(days=365)
session._set_cookie_expires(t)
session.save()
log.info('user %s is now authenticated and stored in '
Søren Løvborg
AuthUser: refactor AuthUser cookie/session serialization...
r5265 'session, session attrs %s', user.username, cookie)
Søren Løvborg
log_in_user: extract user session setup from LoginController...
r5256
# dumps session attrs back to cookie
session._update_cookie_out()
Søren Løvborg
BaseController: enable container authentication on all pages...
r5264 return auth_user
Søren Løvborg
log_in_user: extract user session setup from LoginController...
r5256
Mads Kiilerich
compare: handle revset revision errors more gracefully
r4364 class BasicAuth(paste.auth.basic.AuthBasicAuthenticator):
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
def __init__(self, realm, authfunc, auth_http_code=None):
self.realm = realm
self.authfunc = authfunc
self._rc_auth_http_code = auth_http_code
domruf
auth: consume request body before responding 401 or 403 during authentication...
r6851 def build_authentication(self, environ):
Mads Kiilerich
compare: handle revset revision errors more gracefully
r4364 head = paste.httpheaders.WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
domruf
auth: consume request body before responding 401 or 403 during authentication...
r6851 # Consume the whole body before sending a response
try:
request_body_size = int(environ.get('CONTENT_LENGTH', 0))
except (ValueError):
request_body_size = 0
environ['wsgi.input'].read(request_body_size)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 if self._rc_auth_http_code and self._rc_auth_http_code == '403':
# return 403 if alternative http return code is specified in
Bradley M. Kuhn
General renaming to Kallithea
r4212 # Kallithea config
Mads Kiilerich
compare: handle revset revision errors more gracefully
r4364 return paste.httpexceptions.HTTPForbidden(headers=head)
return paste.httpexceptions.HTTPUnauthorized(headers=head)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
def authenticate(self, environ):
Mads Kiilerich
compare: handle revset revision errors more gracefully
r4364 authorization = paste.httpheaders.AUTHORIZATION(environ)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 if not authorization:
domruf
auth: consume request body before responding 401 or 403 during authentication...
r6851 return self.build_authentication(environ)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 (authmeth, auth) = authorization.split(' ', 1)
if 'basic' != authmeth.lower():
domruf
auth: consume request body before responding 401 or 403 during authentication...
r6851 return self.build_authentication(environ)
Mads Kiilerich
py3: add safe_str where we really need it to get a str - probably from bytes
r8079 auth = safe_str(base64.b64decode(auth.strip()))
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 _parts = auth.split(':', 1)
if len(_parts) == 2:
username, password = _parts
Mads Kiilerich
auth: various minor cleanup of general auth functionality
r5337 if self.authfunc(username, password, environ) is not None:
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 return username
domruf
auth: consume request body before responding 401 or 403 during authentication...
r6851 return self.build_authentication(environ)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
__call__ = authenticate
class BaseVCSController(object):
Søren Løvborg
vcs: dedup auth code between Hg and Git middleware...
r6452 """Base controller for handling Mercurial/Git protocol requests
(coming from a VCS client, and not a browser).
"""
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
Mads Kiilerich
middleware: introduce BaseVCSController scm_alias - prepare for sharing shared code
r7627 scm_alias = None # 'hg' / 'git'
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 def __init__(self, application, config):
self.application = application
self.config = config
# base path of repo locations
self.basepath = self.config['base_path']
Mads Kiilerich
auth: various minor cleanup of general auth functionality
r5337 # authenticate this VCS request using the authentication modules
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 self.authenticate = BasicAuth('', auth_modules.authenticate,
config.get('auth_ret_code'))
Mads Kiilerich
middleware: introduce BaseVCSController parse_request retrieving repo_name and use that for VCS dispatch...
r7624 @classmethod
def parse_request(cls, environ):
"""If request is parsed as a request for this VCS, return a namespace with the parsed request.
If the request is unknown, return None.
"""
raise NotImplementedError()
Mads Kiilerich
middleware: minor cleanup and alignment between VCSs to clarify how things work
r7629 def _authorize(self, environ, action, repo_name, ip_addr):
Søren Løvborg
vcs: dedup auth code between Hg and Git middleware...
r6452 """Authenticate and authorize user.
Since we're dealing with a VCS client and not a browser, we only
support HTTP basic authentication, either directly via raw header
inspection, or by using container authentication to delegate the
authentication to the web server.
Returns (user, None) on successful authentication and authorization.
Returns (None, wsgi_app) to send the wsgi_app response to the client.
"""
Mads Kiilerich
auth: introduce AuthUser.make factory which can return None if user can't be authenticated
r7602 # Use anonymous access if allowed for action on repo.
Søren Løvborg
vcs: restructure authorization check...
r6453 default_user = User.get_default_user(cache=True)
Mads Kiilerich
auth: move IP check to AuthUser.make - it is more about accepting authentication than about permissions after authentication
r7603 default_authuser = AuthUser.make(dbuser=default_user, ip_addr=ip_addr)
Mads Kiilerich
auth: introduce AuthUser.make factory which can return None if user can't be authenticated
r7602 if default_authuser is None:
log.debug('No anonymous access at all') # move on to proper user auth
Søren Løvborg
vcs: dedup auth code between Hg and Git middleware...
r6452 else:
Mads Kiilerich
auth: move IP check to AuthUser.make - it is more about accepting authentication than about permissions after authentication
r7603 if self._check_permission(action, default_authuser, repo_name):
Mads Kiilerich
auth: introduce AuthUser.make factory which can return None if user can't be authenticated
r7602 return default_authuser, None
log.debug('Not authorized to access this repository as anonymous user')
Søren Løvborg
vcs: dedup auth code between Hg and Git middleware...
r6452
Søren Løvborg
vcs: restructure authorization check...
r6453 username = None
#==============================================================
# DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
# NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
#==============================================================
Søren Løvborg
vcs: dedup auth code between Hg and Git middleware...
r6452
Søren Løvborg
vcs: restructure authorization check...
r6453 # try to auth based on environ, container auth methods
log.debug('Running PRE-AUTH for container based authentication')
pre_auth = auth_modules.authenticate('', '', environ)
if pre_auth is not None and pre_auth.get('username'):
username = pre_auth['username']
log.debug('PRE-AUTH got %s as username', username)
Søren Løvborg
vcs: dedup auth code between Hg and Git middleware...
r6452
Søren Løvborg
vcs: restructure authorization check...
r6453 # If not authenticated by the container, running basic auth
if not username:
Mads Kiilerich
py3: drop the last uses of safe_str - they are no longer relevant when we don't have a separate unicode type
r8076 self.authenticate.realm = self.config['realm']
Søren Løvborg
vcs: restructure authorization check...
r6453 result = self.authenticate(environ)
if isinstance(result, str):
paste.httpheaders.AUTH_TYPE.update(environ, 'basic')
paste.httpheaders.REMOTE_USER.update(environ, result)
username = result
else:
return None, result.wsgi_application
Søren Løvborg
vcs: dedup auth code between Hg and Git middleware...
r6452
Søren Løvborg
vcs: restructure authorization check...
r6453 #==============================================================
# CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
#==============================================================
try:
user = User.get_by_username_or_email(username)
except Exception:
log.error(traceback.format_exc())
return None, webob.exc.HTTPInternalServerError()
Søren Løvborg
vcs: dedup auth code between Hg and Git middleware...
r6452
Mads Kiilerich
auth: move IP check to AuthUser.make - it is more about accepting authentication than about permissions after authentication
r7603 authuser = AuthUser.make(dbuser=user, ip_addr=ip_addr)
Mads Kiilerich
auth: introduce AuthUser.make factory which can return None if user can't be authenticated
r7602 if authuser is None:
return None, webob.exc.HTTPForbidden()
Mads Kiilerich
auth: move IP check to AuthUser.make - it is more about accepting authentication than about permissions after authentication
r7603 if not self._check_permission(action, authuser, repo_name):
Søren Løvborg
vcs: restructure authorization check...
r6453 return None, webob.exc.HTTPForbidden()
Søren Løvborg
vcs: dedup auth code between Hg and Git middleware...
r6452
return user, None
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
def _handle_request(self, environ, start_response):
raise NotImplementedError()
Mads Kiilerich
auth: move IP check to AuthUser.make - it is more about accepting authentication than about permissions after authentication
r7603 def _check_permission(self, action, authuser, repo_name):
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 """
Checks permissions using action (push/pull) user and repository
name
Mads Kiilerich
hg: make __get_action command parsing simpler and safer
r7317 :param action: 'push' or 'pull' action
Søren Løvborg
AuthUser: make get_perms method private...
r5251 :param user: `User` instance
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 :param repo_name: repository name
"""
if action == 'push':
if not HasPermissionAnyMiddleware('repository.write',
Mads Kiilerich
auth: introduce AuthUser.make factory which can return None if user can't be authenticated
r7602 'repository.admin')(authuser,
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 repo_name):
return False
else:
#any other action need at least read permission
if not HasPermissionAnyMiddleware('repository.read',
'repository.write',
Mads Kiilerich
auth: introduce AuthUser.make factory which can return None if user can't be authenticated
r7602 'repository.admin')(authuser,
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 repo_name):
return False
return True
def _get_ip_addr(self, environ):
return _get_ip_addr(environ)
def __call__(self, environ, start_response):
try:
Mads Kiilerich
middleware: unify Mercurial and Git _handle_request in the VCS base class...
r7630 # try parsing a request for this VCS - if it fails, call the wrapped app
Mads Kiilerich
middleware: introduce BaseVCSController parse_request retrieving repo_name and use that for VCS dispatch...
r7624 parsed_request = self.parse_request(environ)
if parsed_request is None:
return self.application(environ, start_response)
Mads Kiilerich
middleware: unify Mercurial and Git _handle_request in the VCS base class...
r7630
# skip passing error to error controller
environ['pylons.status_code_redirect'] = True
# quick check if repo exists...
if not is_valid_repo(parsed_request.repo_name, self.basepath, self.scm_alias):
raise webob.exc.HTTPNotFound()
if parsed_request.action is None:
# Note: the client doesn't get the helpful error message
raise webob.exc.HTTPBadRequest('Unable to detect pull/push action for %r! Are you using a nonstandard command or client?' % parsed_request.repo_name)
#======================================================================
# CHECK PERMISSIONS
#======================================================================
ip_addr = self._get_ip_addr(environ)
user, response_app = self._authorize(environ, parsed_request.action, parsed_request.repo_name, ip_addr)
if response_app is not None:
return response_app(environ, start_response)
#======================================================================
# REQUEST HANDLING
#======================================================================
Mads Kiilerich
hooks: move _handle_rc_scm_extras to utils2 as set_hook_environment and get_hook_environment...
r7634 set_hook_environment(user.username, ip_addr,
Mads Kiilerich
middleware: use shared code for setting hook context (KALLITHEA_EXTRAS environment variable)
r7633 parsed_request.repo_name, self.scm_alias, parsed_request.action)
Mads Kiilerich
middleware: unify Mercurial and Git _handle_request in the VCS base class...
r7630
try:
log.info('%s action on %s repo "%s" by "%s" from %s',
Mads Kiilerich
py3: drop the last uses of safe_str - they are no longer relevant when we don't have a separate unicode type
r8076 parsed_request.action, self.scm_alias, parsed_request.repo_name, user.username, ip_addr)
Mads Kiilerich
middleware: unify Mercurial and Git _handle_request in the VCS base class...
r7630 app = self._make_app(parsed_request)
return app(environ, start_response)
except Exception:
log.error(traceback.format_exc())
raise webob.exc.HTTPInternalServerError()
Mads Kiilerich
middleware: introduce more generic VCS webob.exc.HTTPException exception handling
r7625 except webob.exc.HTTPException as e:
return e(environ, start_response)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
Alessandro Molina
backend: replace Pylons with TurboGears2...
r6522 class BaseController(TGController):
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
Thomas De Schampheleire
controllers: rename __before__ to _before in preparation of TurboGears2...
r6513 def _before(self, *args, **kwargs):
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 """
Alessandro Molina
backend: replace Pylons with TurboGears2...
r6522 _before is called before controller methods and after __call__
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 """
Mads Kiilerich
auth: move CSRF checks from the optional LoginRequired to the more basic BaseController._before...
r7609 if request.needs_csrf_check:
# CSRF protection: Whenever a request has ambient authority (whether
# through a session cookie or its origin IP address), it must include
# the correct token, unless the HTTP method is GET or HEAD (and thus
# guaranteed to be side effect free. In practice, the only situation
# where we allow side effects without ambient authority is when the
# authority comes from an API key; and that is handled above.
Mads Kiilerich
helpers: always access secure_form through helpers
r7708 from kallithea.lib import helpers as h
Mads Kiilerich
helpers: rename internal names of authentication_token to clarify that secure_form is about session CSRF secrets - not authentication
r7709 token = request.POST.get(h.session_csrf_secret_name)
if not token or token != h.session_csrf_secret_token():
Mads Kiilerich
auth: move CSRF checks from the optional LoginRequired to the more basic BaseController._before...
r7609 log.error('CSRF check failed')
raise webob.exc.HTTPForbidden()
Bradley M. Kuhn
Rename rhodecode_version to kallithea_version
r4200 c.kallithea_version = __version__
Bradley M. Kuhn
Rename database classes (but not table names)
r4203 rc_config = Setting.get_app_settings()
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
# Visual options
c.visual = AttributeDict({})
## DB stored
Bradley M. Kuhn
Drop rhodecode_ prefix for known setting names that are stored in kallithea_settings without prefix
r4218 c.visual.show_public_icon = str2bool(rc_config.get('show_public_icon'))
c.visual.show_private_icon = str2bool(rc_config.get('show_private_icon'))
domruf
less: use .label and .label-* style instead of custom .*tag...
r7032 c.visual.stylify_metalabels = str2bool(rc_config.get('stylify_metalabels'))
Mads Kiilerich
repositories: drop the word "dashboard" - it is just a list of repositories and groups
r6274 c.visual.page_size = safe_int(rc_config.get('dashboard_items', 100))
Bradley M. Kuhn
Drop rhodecode_ prefix for known setting names that are stored in kallithea_settings without prefix
r4218 c.visual.admin_grid_items = safe_int(rc_config.get('admin_grid_items', 100))
c.visual.repository_fields = str2bool(rc_config.get('repository_fields'))
c.visual.show_version = str2bool(rc_config.get('show_version'))
c.visual.use_gravatar = str2bool(rc_config.get('use_gravatar'))
c.visual.gravatar_url = rc_config.get('gravatar_url')
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
Bradley M. Kuhn
Drop rhodecode_ prefix for known setting names that are stored in kallithea_settings without prefix
r4218 c.ga_code = rc_config.get('ga_code')
domruf
support other analytic systems then just Google Analytics...
r4462 # TODO: replace undocumented backwards compatibility hack with db upgrade and rename ga_code
if c.ga_code and '<' not in c.ga_code:
c.ga_code = '''<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '%s']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>''' % c.ga_code
Bradley M. Kuhn
Drop rhodecode_ prefix for known setting names that are stored in kallithea_settings without prefix
r4218 c.site_name = rc_config.get('title')
Mads Kiilerich
clone_url: always pass a clone_uri_tmpl, with Repository.DEFAULT_CLONE_URI as last resort...
r7667 c.clone_uri_tmpl = rc_config.get('clone_uri_tmpl') or Repository.DEFAULT_CLONE_URI
domruf
ssh: show ssh URL on summary page...
r7688 c.clone_ssh_tmpl = rc_config.get('clone_ssh_tmpl') or Repository.DEFAULT_CLONE_SSH
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
## INI stored
c.visual.allow_repo_location_change = str2bool(config.get('allow_repo_location_change', True))
c.visual.allow_custom_hooks_settings = str2bool(config.get('allow_custom_hooks_settings', True))
Thomas De Schampheleire
ssh: introduce ini setting 'ssh_enabled', disabled by default...
r7677 c.ssh_enabled = str2bool(config.get('ssh_enabled', False))
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
Bradley M. Kuhn
Rename rhodecode_instanceid to instance_id
r4199 c.instance_id = config.get('instance_id')
Bradley M. Kuhn
Correct support url and naming
r4194 c.issues_url = config.get('bugtracker', url('issues_url'))
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 # END CONFIG VARS
c.repo_name = get_repo_slug(request) # can be empty
Mads Kiilerich
cleanup: minor refactorings and simplification of dict usage...
r7906 c.backends = list(BACKENDS)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
self.cut_off_limit = safe_int(config.get('cut_off_limit'))
Mads Kiilerich
pull requests: add new "my" page, separate from "my account" and prominently shown in the page headers
r4281
Mads Kiilerich
controllers: avoid setting request state in controller instances - set it in the thread global request variable...
r6412 c.my_pr_count = PullRequest.query(reviewer_id=request.authuser.user_id, include_closed=False).count()
Mads Kiilerich
pull requests: add new "my" page, separate from "my account" and prominently shown in the page headers
r4281
Søren Løvborg
model: remove BaseModel class...
r6483 self.scm_model = ScmModel()
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
Mads Kiilerich
BaseController: refactor, extract _determine_auth_user function...
r5192 @staticmethod
Mads Kiilerich
auth: simplify API key auth - move it out of _determine_auth_user...
r7608 def _determine_auth_user(session_authuser, ip_addr):
Mads Kiilerich
BaseController: refactor, extract _determine_auth_user function...
r5192 """
Søren Løvborg
auth: add support for "Bearer" auth scheme (API key variant)...
r6347 Create an `AuthUser` object given the API key/bearer token
(if any) and the value of the authuser session cookie.
Mads Kiilerich
auth: move IP check to AuthUser.make - it is more about accepting authentication than about permissions after authentication
r7603 Returns None if no valid user is found (like not active or no access for IP).
Mads Kiilerich
BaseController: refactor, extract _determine_auth_user function...
r5192 """
Søren Løvborg
BaseController: refactor API key authentication...
r5254 # Authenticate by session cookie
Søren Løvborg
AuthUser: refactor AuthUser cookie/session serialization...
r5265 # In ancient login sessions, 'authuser' may not be a dict.
# In that case, the user will have to log in again.
Søren Løvborg
auth: note that we never emit authuser "cookies" for the default user...
r5548 # v0.3 and earlier included an 'is_authenticated' key; if present,
# this must be True.
if isinstance(session_authuser, dict) and session_authuser.get('is_authenticated', True):
Mads Kiilerich
auth: move IP check to AuthUser.make - it is more about accepting authentication than about permissions after authentication
r7603 return AuthUser.from_cookie(session_authuser, ip_addr=ip_addr)
Søren Løvborg
BaseController: refactor session cookie authentication...
r5255
Søren Løvborg
BaseController: enable container authentication on all pages...
r5264 # Authenticate by auth_container plugin (if enabled)
if any(
domruf
auth: refactor auth plugin importing...
r6517 plugin.is_container_auth
for plugin in auth_modules.get_auth_plugins()
Søren Løvborg
BaseController: enable container authentication on all pages...
r5264 ):
try:
Mads Kiilerich
auth: consistently use the name 'user_data' for the dict with data for the authenticated user
r5338 user_info = auth_modules.authenticate('', '', request.environ)
Søren Løvborg
BaseController: enable container authentication on all pages...
r5264 except UserCreationError as e:
from kallithea.lib import helpers as h
h.flash(e, 'error', logf=log.error)
else:
Mads Kiilerich
auth: consistently use the name 'user_data' for the dict with data for the authenticated user
r5338 if user_info is not None:
username = user_info['username']
Søren Løvborg
BaseController: enable container authentication on all pages...
r5264 user = User.get_by_username(username, case_insensitive=True)
Mads Kiilerich
auth: move IP check to AuthUser.make - it is more about accepting authentication than about permissions after authentication
r7603 return log_in_user(user, remember=False, is_external_auth=True, ip_addr=ip_addr)
Søren Løvborg
BaseController: enable container authentication on all pages...
r5264
Mads Kiilerich
auth: remove AuthUser __init__ magic for fallback to default user instead of the requested user...
r7601 # User is default user (if active) or anonymous
default_user = User.get_default_user(cache=True)
Mads Kiilerich
auth: move IP check to AuthUser.make - it is more about accepting authentication than about permissions after authentication
r7603 authuser = AuthUser.make(dbuser=default_user, ip_addr=ip_addr)
Mads Kiilerich
auth: introduce AuthUser.make factory which can return None if user can't be authenticated
r7602 if authuser is None: # fall back to anonymous
authuser = AuthUser(dbuser=default_user) # TODO: somehow use .make?
return authuser
Mads Kiilerich
BaseController: refactor, extract _determine_auth_user function...
r5192
Søren Løvborg
auth: perform basic HTTP security checks already in BaseController...
r6344 @staticmethod
def _basic_security_checks():
"""Perform basic security/sanity checks before processing the request."""
# Only allow the following HTTP request methods.
if request.method not in ['GET', 'HEAD', 'POST']:
raise webob.exc.HTTPMethodNotAllowed()
# Also verify the _method override - no longer allowed.
if request.params.get('_method') is None:
pass # no override, no problem
else:
raise webob.exc.HTTPMethodNotAllowed()
Mads Kiilerich
BaseController: refactor, extract _determine_auth_user function...
r5192
Søren Løvborg
auth: perform basic HTTP security checks already in BaseController...
r6344 # Make sure CSRF token never appears in the URL. If so, invalidate it.
Mads Kiilerich
helpers: always access secure_form through helpers
r7708 from kallithea.lib import helpers as h
Mads Kiilerich
helpers: rename internal names of authentication_token to clarify that secure_form is about session CSRF secrets - not authentication
r7709 if h.session_csrf_secret_name in request.GET:
Søren Løvborg
auth: perform basic HTTP security checks already in BaseController...
r6344 log.error('CSRF key leak detected')
Mads Kiilerich
helpers: rename internal names of authentication_token to clarify that secure_form is about session CSRF secrets - not authentication
r7709 session.pop(h.session_csrf_secret_name, None)
Søren Løvborg
auth: perform basic HTTP security checks already in BaseController...
r6344 session.save()
h.flash(_('CSRF token leak has been detected - all form tokens have been expired'),
category='error')
# WebOb already ignores request payload parameters for anything other
# than POST/PUT, but double-check since other Kallithea code relies on
# this assumption.
if request.method not in ['POST', 'PUT'] and request.POST:
log.error('%r request with payload parameters; WebOb should have stopped this', request.method)
raise webob.exc.HTTPBadRequest()
Alessandro Molina
backend: replace Pylons with TurboGears2...
r6522 def __call__(self, environ, context):
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 try:
Mads Kiilerich
auth: move IP check to AuthUser.make - it is more about accepting authentication than about permissions after authentication
r7603 ip_addr = _get_ip_addr(environ)
Søren Løvborg
auth: perform basic HTTP security checks already in BaseController...
r6344 self._basic_security_checks()
Mads Kiilerich
auth: simplify API key auth - move it out of _determine_auth_user...
r7608 api_key = request.GET.get('api_key')
Søren Løvborg
auth: add support for "Bearer" auth scheme (API key variant)...
r6347 try:
# Request.authorization may raise ValueError on invalid input
type, params = request.authorization
except (ValueError, TypeError):
pass
else:
if type.lower() == 'bearer':
Mads Kiilerich
auth: simplify API key auth - move it out of _determine_auth_user...
r7608 api_key = params # bearer token is an api key too
if api_key is None:
authuser = self._determine_auth_user(
session.get('authuser'),
ip_addr=ip_addr,
)
Mads Kiilerich
auth: move CSRF checks from the optional LoginRequired to the more basic BaseController._before...
r7609 needs_csrf_check = request.method not in ['GET', 'HEAD']
Søren Løvborg
auth: add support for "Bearer" auth scheme (API key variant)...
r6347
Mads Kiilerich
auth: simplify API key auth - move it out of _determine_auth_user...
r7608 else:
dbuser = User.get_by_api_key(api_key)
if dbuser is None:
log.info('No db user found for authentication with API key ****%s from %s',
api_key[-4:], ip_addr)
Mads Kiilerich
auth: drop authenticating_api_key from AuthUser...
r7610 authuser = AuthUser.make(dbuser=dbuser, is_external_auth=True, ip_addr=ip_addr)
Mads Kiilerich
auth: move CSRF checks from the optional LoginRequired to the more basic BaseController._before...
r7609 needs_csrf_check = False # API key provides CSRF protection
Mads Kiilerich
auth: simplify API key auth - move it out of _determine_auth_user...
r7608
Mads Kiilerich
auth: introduce AuthUser.make factory which can return None if user can't be authenticated
r7602 if authuser is None:
log.info('No valid user found')
raise webob.exc.HTTPForbidden()
Mads Kiilerich
auth: make sure request.authuser *always* has been checked for check_ip_allowed - there is thus no need to check it later
r7406
Mads Kiilerich
auth: simplify API key auth - move it out of _determine_auth_user...
r7608 # set globals for auth user
Mads Kiilerich
auth: make sure request.authuser *always* has been checked for check_ip_allowed - there is thus no need to check it later
r7406 request.authuser = authuser
Mads Kiilerich
auth: move IP check to AuthUser.make - it is more about accepting authentication than about permissions after authentication
r7603 request.ip_addr = ip_addr
Mads Kiilerich
auth: move CSRF checks from the optional LoginRequired to the more basic BaseController._before...
r7609 request.needs_csrf_check = needs_csrf_check
Mads Kiilerich
auth: make sure request.authuser *always* has been checked for check_ip_allowed - there is thus no need to check it later
r7406
Mads Kiilerich
BaseController: refactor, extract _determine_auth_user function...
r5192 log.info('IP: %s User: %s accessed %s',
Mads Kiilerich
controllers: avoid setting request state in controller instances - set it in the thread global request variable...
r6412 request.ip_addr, request.authuser,
Mads Kiilerich
lib: refactor _get_access_path as get_path_info...
r7948 get_path_info(environ),
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 )
Alessandro Molina
backend: replace Pylons with TurboGears2...
r6522 return super(BaseController, self).__call__(environ, context)
Søren Løvborg
auth: perform basic HTTP security checks already in BaseController...
r6344 except webob.exc.HTTPException as e:
Alessandro Molina
backend: replace Pylons with TurboGears2...
r6522 return e
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
class BaseRepoController(BaseController):
"""
Base class for controllers responsible for loading all needed data for
repository loaded items are
Bradley M. Kuhn
Rename rhodecode_repo to db_repo_scm_instance
r4196 c.db_repo_scm_instance: instance of scm repository
Bradley M. Kuhn
Rename rhodecode_db_repo to db_repo - it stores db repo abstractions
r4195 c.db_repo: instance of db
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 c.repository_followers: number of followers
c.repository_forks: number of forks
c.repository_following: weather the current user is following the current repo
"""
Thomas De Schampheleire
controllers: rename __before__ to _before in preparation of TurboGears2...
r6513 def _before(self, *args, **kwargs):
super(BaseRepoController, self)._before(*args, **kwargs)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 if c.repo_name: # extracted from routes
_dbr = Repository.get_by_repo_name(c.repo_name)
if not _dbr:
return
Mads Kiilerich
cleanup: pass log strings unformatted - avoid unnecessary % formatting when not logging
r5375 log.debug('Found repository in database %s with state `%s`',
Mads Kiilerich
py3: remove safe_unicode in places where it no longer is needed because all strings (except bytes) already *are* unicode strings...
r8075 _dbr, _dbr.repo_state)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 route = getattr(request.environ.get('routes.route'), 'name', '')
# allow to delete repos that are somehow damages in filesystem
if route in ['delete_repo']:
return
if _dbr.repo_state in [Repository.STATE_PENDING]:
if route in ['repo_creating_home']:
return
check_url = url('repo_creating_home', repo_name=c.repo_name)
Søren Løvborg
cleanup: replace redirect with WebOb exceptions...
r5543 raise webob.exc.HTTPFound(location=check_url)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
Bradley M. Kuhn
Rename rhodecode_db_repo to db_repo - it stores db repo abstractions
r4195 dbr = c.db_repo = _dbr
Bradley M. Kuhn
Rename rhodecode_repo to db_repo_scm_instance
r4196 c.db_repo_scm_instance = c.db_repo.scm_instance
if c.db_repo_scm_instance is None:
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 log.error('%s this repository is present in database but it '
'cannot be created as an scm instance', c.repo_name)
Mads Kiilerich
redirect: don't redirect on error - it is better to have clear error messages and stable and meaningful URLs
r4363 from kallithea.lib import helpers as h
Thomas De Schampheleire
cleanup: remove unnecessary (and potentially problematic) use of 'literal'...
r7467 h.flash(_('Repository not found in the filesystem'),
Mads Kiilerich
redirect: don't redirect on error - it is better to have clear error messages and stable and meaningful URLs
r4363 category='error')
Thomas De Schampheleire
lib: BaseRepoController: use webob.exc.HTTPNotFound if possible...
r7415 raise webob.exc.HTTPNotFound()
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
# some globals counter for menu
c.repository_followers = self.scm_model.get_followers(dbr)
c.repository_forks = self.scm_model.get_forks(dbr)
c.repository_pull_requests = self.scm_model.get_pull_requests(dbr)
c.repository_following = self.scm_model.is_following_repo(
Mads Kiilerich
controllers: avoid setting request state in controller instances - set it in the thread global request variable...
r6412 c.repo_name, request.authuser.user_id)
Mads Kiilerich
compare: handle revset revision errors more gracefully
r4364
@staticmethod
Mads Kiilerich
pull requests: handle pull requests to empty repos (Issue #27)
r4506 def _get_ref_rev(repo, ref_type, ref_name, returnempty=False):
Mads Kiilerich
compare: handle revset revision errors more gracefully
r4364 """
Safe way to get changeset. If error occurs show error.
"""
from kallithea.lib import helpers as h
try:
return repo.scm_instance.get_ref_revision(ref_type, ref_name)
except EmptyRepositoryError as e:
Mads Kiilerich
pull requests: handle pull requests to empty repos (Issue #27)
r4506 if returnempty:
return repo.scm_instance.EMPTY_CHANGESET
Thomas De Schampheleire
cleanup: remove unnecessary (and potentially problematic) use of 'literal'...
r7467 h.flash(_('There are no changesets yet'), category='error')
Mads Kiilerich
compare: handle revset revision errors more gracefully
r4364 raise webob.exc.HTTPNotFound()
except ChangesetDoesNotExistError as e:
Thomas De Schampheleire
cleanup: remove unnecessary (and potentially problematic) use of 'literal'...
r7467 h.flash(_('Changeset for %s %s not found in %s') %
(ref_type, ref_name, repo.repo_name),
Mads Kiilerich
compare: handle revset revision errors more gracefully
r4364 category='error')
raise webob.exc.HTTPNotFound()
except RepositoryError as e:
log.error(traceback.format_exc())
Mads Kiilerich
lib: handle both HTML, unsafe strings, and exceptions passed to helpers.flash()...
r7949 h.flash(e, category='error')
Mads Kiilerich
compare: handle revset revision errors more gracefully
r4364 raise webob.exc.HTTPBadRequest()
Mads Kiilerich
vcs: invalidate repo caches _after_ a push finishes...
r5414
Thomas De Schampheleire
lib: move jsonify from utils to base...
r6409 @decorator.decorator
def jsonify(func, *args, **kwargs):
"""Action decorator that formats output for JSON
Mads Kiilerich
vcs: invalidate repo caches _after_ a push finishes...
r5414
Thomas De Schampheleire
lib: move jsonify from utils to base...
r6409 Given a function that will return content, this decorator will turn
the result into JSON, with a content-type of 'application/json' and
output it.
"""
response.headers['Content-Type'] = 'application/json; charset=utf-8'
data = func(*args, **kwargs)
if isinstance(data, (list, tuple)):
# A JSON list response is syntactically valid JavaScript and can be
# loaded and executed as JavaScript by a malicious third-party site
# using <script>, which can lead to cross-site data leaks.
# JSON responses should therefore be scalars or objects (i.e. Python
# dicts), because a JSON object is a syntax error if intepreted as JS.
msg = "JSON responses with Array envelopes are susceptible to " \
"cross-site data leak attacks, see " \
"https://web.archive.org/web/20120519231904/http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
warnings.warn(msg, Warning, 2)
log.warning(msg)
log.debug("Returning JSON wrapped action output")
Mads Kiilerich
lib: clean up ext_json and how it is used - avoid monkey patching...
r7987 return ascii_bytes(ext_json.dumps(data))
Thomas De Schampheleire
ssh: introduce ini setting 'ssh_enabled', disabled by default...
r7677
@decorator.decorator
def IfSshEnabled(func, *args, **kwargs):
"""Decorator for functions that can only be called if SSH access is enabled.
If SSH access is disabled in the configuration file, HTTPNotFound is raised.
"""
if not c.ssh_enabled:
from kallithea.lib import helpers as h
h.flash(_("SSH access is disabled."), category='warning')
raise webob.exc.HTTPNotFound()
return func(*args, **kwargs)