base.py
218 lines
| 7.9 KiB
| text/x-python
|
PythonLexer
r547 | """The base Controller API | ||
Provides the BaseController class for subclassing. | |||
""" | |||
r1373 | import logging | ||
r1601 | import time | ||
r1813 | import traceback | ||
r1761 | |||
from paste.auth.basic import AuthBasicAuthenticator | |||
r2132 | from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden | ||
from paste.httpheaders import WWW_AUTHENTICATE | |||
r1761 | |||
r1373 | from pylons import config, tmpl_context as c, request, session, url | ||
r547 | from pylons.controllers import WSGIController | ||
r1373 | from pylons.controllers.util import redirect | ||
r547 | from pylons.templating import render_mako as render | ||
r1304 | |||
r1718 | from rhodecode import __version__, BACKENDS | ||
r2109 | from rhodecode.lib.utils2 import str2bool, safe_unicode | ||
r1761 | from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\ | ||
r2030 | HasPermissionAnyMiddleware, CookieStoreWrapper | ||
r1761 | from rhodecode.lib.utils import get_repo_slug, invalidate_cache | ||
r547 | from rhodecode.model import meta | ||
r1718 | |||
r1366 | from rhodecode.model.db import Repository | ||
r1702 | from rhodecode.model.notification import NotificationModel | ||
r1718 | from rhodecode.model.scm import ScmModel | ||
r665 | |||
r1373 | log = logging.getLogger(__name__) | ||
r1307 | |||
r1813 | |||
r2374 | def _get_ip_addr(environ): | ||
proxy_key = 'HTTP_X_REAL_IP' | |||
proxy_key2 = 'HTTP_X_FORWARDED_FOR' | |||
def_key = 'REMOTE_ADDR' | |||
return environ.get(proxy_key2, | |||
environ.get(proxy_key, environ.get(def_key, '0.0.0.0')) | |||
) | |||
r2132 | class BasicAuth(AuthBasicAuthenticator): | ||
def __init__(self, realm, authfunc, auth_http_code=None): | |||
self.realm = realm | |||
self.authfunc = authfunc | |||
self._rc_auth_http_code = auth_http_code | |||
def build_authentication(self): | |||
head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm) | |||
if self._rc_auth_http_code and self._rc_auth_http_code == '403': | |||
# return 403 if alternative http return code is specified in | |||
# RhodeCode config | |||
return HTTPForbidden(headers=head) | |||
return HTTPUnauthorized(headers=head) | |||
r1761 | class BaseVCSController(object): | ||
r1813 | |||
r1761 | def __init__(self, application, config): | ||
self.application = application | |||
self.config = config | |||
# base path of repo locations | |||
self.basepath = self.config['base_path'] | |||
#authenticate this mercurial request using authfunc | |||
r2132 | self.authenticate = BasicAuth('', authfunc, | ||
config.get('auth_ret_code')) | |||
r1761 | self.ipaddr = '0.0.0.0' | ||
r1813 | |||
r1978 | def _handle_request(self, environ, start_response): | ||
raise NotImplementedError() | |||
r1813 | def _get_by_id(self, repo_name): | ||
""" | |||
Get's a special pattern _<ID> from clone url and tries to replace it | |||
with a repository_name for support of _<ID> non changable urls | |||
:param repo_name: | |||
""" | |||
try: | |||
data = repo_name.split('/') | |||
if len(data) >= 2: | |||
by_id = data[1].split('_') | |||
if len(by_id) == 2 and by_id[1].isdigit(): | |||
_repo_name = Repository.get(by_id[1]).repo_name | |||
data[1] = _repo_name | |||
except: | |||
log.debug('Failed to extract repo_name from id %s' % ( | |||
traceback.format_exc() | |||
) | |||
) | |||
return '/'.join(data) | |||
r1761 | def _invalidate_cache(self, repo_name): | ||
""" | |||
Set's cache for this repository for invalidation on next access | |||
r1813 | |||
r1761 | :param repo_name: full repo name, also a cache key | ||
""" | |||
invalidate_cache('get_repo_cached_%s' % repo_name) | |||
r1813 | |||
r1761 | def _check_permission(self, action, user, repo_name): | ||
""" | |||
Checks permissions using action (push/pull) user and repository | |||
name | |||
:param action: push or pull action | |||
:param user: user instance | |||
:param repo_name: repository name | |||
""" | |||
if action == 'push': | |||
if not HasPermissionAnyMiddleware('repository.write', | |||
'repository.admin')(user, | |||
repo_name): | |||
return False | |||
else: | |||
#any other action need at least read permission | |||
if not HasPermissionAnyMiddleware('repository.read', | |||
'repository.write', | |||
'repository.admin')(user, | |||
repo_name): | |||
return False | |||
r1813 | return True | ||
r2184 | def _get_ip_addr(self, environ): | ||
r2374 | return _get_ip_addr(environ) | ||
r2184 | |||
r1761 | def __call__(self, environ, start_response): | ||
start = time.time() | |||
try: | |||
return self._handle_request(environ, start_response) | |||
finally: | |||
r1763 | log = logging.getLogger('rhodecode.' + self.__class__.__name__) | ||
r1761 | log.debug('Request time: %.3fs' % (time.time() - start)) | ||
meta.Session.remove() | |||
r547 | class BaseController(WSGIController): | ||
r659 | |||
r547 | def __before__(self): | ||
r548 | c.rhodecode_version = __version__ | ||
r2016 | c.rhodecode_instanceid = config.get('instance_id') | ||
r890 | c.rhodecode_name = config.get('rhodecode_title') | ||
r1629 | c.use_gravatar = str2bool(config.get('use_gravatar')) | ||
r891 | c.ga_code = config.get('rhodecode_ga_code') | ||
r547 | c.repo_name = get_repo_slug(request) | ||
r659 | c.backends = BACKENDS.keys() | ||
r1702 | c.unread_notifications = NotificationModel()\ | ||
.get_unread_cnt_for_user(c.rhodecode_user.user_id) | |||
r890 | self.cut_off_limit = int(config.get('cut_off_limit')) | ||
r1036 | |||
r1749 | self.sa = meta.Session | ||
r1045 | self.scm_model = ScmModel(self.sa) | ||
r2374 | self.ip_addr = '' | ||
r1366 | |||
r547 | def __call__(self, environ, start_response): | ||
"""Invoke the Controller""" | |||
# WSGIController.__call__ dispatches to the Controller method | |||
# the request is routed to. This routing information is | |||
# available in environ['pylons.routes_dict'] | |||
r1601 | start = time.time() | ||
r547 | try: | ||
r2374 | self.ip_addr = _get_ip_addr(environ) | ||
r1628 | # make sure that we update permissions each time we call controller | ||
r1117 | api_key = request.GET.get('api_key') | ||
r2030 | cookie_store = CookieStoreWrapper(session.get('rhodecode_user')) | ||
r1718 | user_id = cookie_store.get('user_id', None) | ||
|
r1630 | username = get_container_username(environ, config) | |
r1628 | auth_user = AuthUser(user_id, api_key, username) | ||
r1728 | request.user = auth_user | ||
r1628 | self.rhodecode_user = c.rhodecode_user = auth_user | ||
|
r1618 | if not self.rhodecode_user.is_authenticated and \ | |
self.rhodecode_user.user_id is not None: | |||
r2030 | self.rhodecode_user.set_authenticated( | ||
cookie_store.get('is_authenticated') | |||
) | |||
r2027 | log.info('User: %s accessed %s' % ( | ||
auth_user, safe_unicode(environ.get('PATH_INFO'))) | |||
) | |||
r547 | return WSGIController.__call__(self, environ, start_response) | ||
finally: | |||
r2027 | log.info('Request to %s time: %.3fs' % ( | ||
safe_unicode(environ.get('PATH_INFO')), time.time() - start) | |||
) | |||
r547 | meta.Session.remove() | ||
r1045 | |||
class BaseRepoController(BaseController): | |||
""" | |||
r1628 | Base class for controllers responsible for loading all needed data for | ||
repository loaded items are | |||
r1203 | |||
r1628 | c.rhodecode_repo: instance of scm repository | ||
c.rhodecode_db_repo: instance of db | |||
c.repository_followers: number of followers | |||
c.repository_forks: number of forks | |||
r1045 | """ | ||
def __before__(self): | |||
super(BaseRepoController, self).__before__() | |||
if c.repo_name: | |||
r2440 | dbr = c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name) | ||
r1373 | c.rhodecode_repo = c.rhodecode_db_repo.scm_instance | ||
if c.rhodecode_repo is None: | |||
log.error('%s this repository is present in database but it ' | |||
'cannot be created as an scm instance', c.repo_name) | |||
r1282 | |||
r1373 | redirect(url('home')) | ||
r1304 | |||
r2440 | # 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) |