##// END OF EJS Templates
fix(tests): on jenkins ensure re-creation of the path
fix(tests): on jenkins ensure re-creation of the path

File last commit:

r5608:6d33e504 default
r5609:cccd61c3 default
Show More
simplevcs.py
659 lines | 26.4 KiB | text/x-python | PythonLexer
core: updated copyright to 2024
r5608 # Copyright (C) 2014-2024 RhodeCode GmbH
project: added all source files and assets
r1 #
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# 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 Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
"""
SimpleVCS middleware for handling protocol request (push/clone etc.)
It's implemented with basic auth function
"""
import os
auth: use cache_ttl from a plugin to also cache permissions....
r2154 import re
project: added all source files and assets
r1 import logging
import importlib
from functools import wraps
auth: use cache_ttl from a plugin to also cache permissions....
r2154 import time
project: added all source files and assets
r1 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
svn: enable hooks and integration framework execution....
r2677
tests: fixed test suite for celery adoption
r5607 from pyramid.httpexceptions import HTTPNotFound, HTTPForbidden, HTTPNotAcceptable, HTTPInternalServerError
vcs: also register a global config for vcs_handler....
r2404 from zope.cachedescriptors.property import Lazy as LazyProperty
project: added all source files and assets
r1
import rhodecode
caches: rewrite of auth/permission caches to dogpile.
r2845 from rhodecode.authentication.base import authenticate, VCS_TYPE, loadplugin
caches: new cache context managers....
r2932 from rhodecode.lib import rc_cache
fix(svn): svn events fixes and change the way how we handle the events
r5459 from rhodecode.lib.svn_txn_utils import store_txn_id_data
project: added all source files and assets
r1 from rhodecode.lib.auth import AuthUser, HasPermissionAnyMiddleware
tests: fixed test suite for celery adoption
r5607 from rhodecode.lib.base import BasicAuth, get_ip_addr, get_user_agent, vcs_operation_context
from rhodecode.lib.exceptions import UserCreationError, NotAllowedToCreateUserError
from rhodecode.lib.hook_daemon.utils import prepare_callback_daemon
project: added all source files and assets
r1 from rhodecode.lib.middleware import appenlight
core: removed pyro4 from Enterprise code. Fixes #5198
r1409 from rhodecode.lib.middleware.utils import scm_app_http
fix(svn): svn events fixes and change the way how we handle the events
r5459 from rhodecode.lib.str_utils import safe_bytes, safe_int
auth: use cache_ttl from a plugin to also cache permissions....
r2154 from rhodecode.lib.utils import is_valid_repo, SLUG_RE
core: multiple fixes to unicode vs str usage...
r5065 from rhodecode.lib.utils2 import safe_str, fix_PATH, str2bool
Martin Bornhold
config: Use hooks protocol and direc calls seting from vcs settings module.
r590 from rhodecode.lib.vcs.conf import settings as vcs_settings
vcs: moved svn proxy settings into vcs related settings...
r754 from rhodecode.lib.vcs.backends import base
svn: enable hooks and integration framework execution....
r2677
project: added all source files and assets
r1 from rhodecode.model import meta
Martin Bornhold
vcs: Fix missing import.
r894 from rhodecode.model.db import User, Repository, PullRequest
project: added all source files and assets
r1 from rhodecode.model.scm import ScmModel
Martin Bornhold
vcs: Fix missing import.
r894 from rhodecode.model.pull_request import PullRequestModel
pylons: remove pylons as dependency...
r2351 from rhodecode.model.settings import SettingsModel, VcsSettingsModel
vcs: moved svn proxy settings into vcs related settings...
r754
project: added all source files and assets
r1 log = logging.getLogger(__name__)
def initialize_generator(factory):
"""
Initializes the returned generator by draining its first element.
This can be used to give a generator an initializer, which is the code
up to the first yield statement. This decorator enforces that the first
produced element has the value ``"__init__"`` to make its special
purpose very explicit in the using code.
"""
@wraps(factory)
def wrapper(*args, **kwargs):
gen = factory(*args, **kwargs)
try:
python3: fixed usage of .next() and .func_name
r4936 init = next(gen)
project: added all source files and assets
r1 except StopIteration:
tests: fixed test suite for celery adoption
r5607 raise ValueError("Generator must yield at least one element.")
project: added all source files and assets
r1 if init != "__init__":
raise ValueError('First yielded element must be "__init__".')
return gen
tests: fixed test suite for celery adoption
r5607
project: added all source files and assets
r1 return wrapper
class SimpleVCS(object):
"""Common functionality for SCM HTTP handlers."""
tests: fixed test suite for celery adoption
r5607 SCM = "unknown"
project: added all source files and assets
r1
vcs: Minimal change to expose the shadow repository...
r887 acl_repo_name = None
url_repo_name = None
vcs_repo_name = None
simplevcs: store rc_extras reference inside the apps itself so it can be used during...
r2389 rc_extras = {}
vcs: Minimal change to expose the shadow repository...
r887
Martin Bornhold
vcs: Move shadow repo regex up to class level.
r902 # We have to handle requests to shadow repositories different than requests
# to normal repositories. Therefore we have to distinguish them. To do this
# we use this regex which will match only on URLs pointing to shadow
# repositories.
shadow_repo_re = re.compile(
tests: fixed test suite for celery adoption
r5607 "(?P<groups>(?:{slug_pat}/)*)" # repo groups
"(?P<target>{slug_pat})/" # target repo
"pull-request/(?P<pr_id>\\d+)/" # pull request
"repository$".format(slug_pat=SLUG_RE.pattern) # shadow repo
)
Martin Bornhold
vcs: Move shadow repo regex up to class level.
r902
pylons: remove pylons as dependency...
r2351 def __init__(self, config, registry):
Martin Bornhold
vcs: Pass registry to vcs for user authentication....
r591 self.registry = registry
project: added all source files and assets
r1 self.config = config
vcs: register repo_name as specialized middleware variables instead...
r757 # re-populated by specialized middleware
vcs: moved svn proxy settings into vcs related settings...
r754 self.repo_vcs_config = base.Config()
pylons: remove pylons as dependency...
r2351
Authentication: cache plugins for auth and their settings in the auth_registry....
r4220 rc_settings = SettingsModel().get_all_settings(cache=True, from_request=False)
tests: fixed test suite for celery adoption
r5607 realm = rc_settings.get("rhodecode_realm") or "RhodeCode AUTH"
Authentication: cache plugins for auth and their settings in the auth_registry....
r4220
project: added all source files and assets
r1 # authenticate this VCS request using authfunc
tests: fixed test suite for celery adoption
r5607 auth_ret_code_detection = str2bool(self.config.get("auth_ret_code_detection", False))
Martin Bornhold
vcs: Pass registry to vcs for user authentication....
r591 self.authenticate = BasicAuth(
tests: fixed test suite for celery adoption
r5607 "", authenticate, registry, config.get("auth_ret_code"), auth_ret_code_detection, rc_realm=realm
)
self.ip_addr = "0.0.0.0"
project: added all source files and assets
r1
vcs: also register a global config for vcs_handler....
r2404 @LazyProperty
def global_vcs_config(self):
try:
return VcsSettingsModel().get_ui_settings_as_config_obj()
except Exception:
return base.Config()
pylons: remove pylons as dependency...
r2351 @property
def base_path(self):
tests: fixed test suite for celery adoption
r5607 settings_path = self.config.get("repo_store.path")
vcs: also register a global config for vcs_handler....
r2404
simplevcs: fixed logic of extraction of base_path
r2362 if not settings_path:
tests: fixed test suite for celery adoption
r5607 raise ValueError("FATAL: repo_store.path is empty")
simplevcs: fixed logic of extraction of base_path
r2362 return settings_path
pylons: remove pylons as dependency...
r2351
Martin Bornhold
vcs: Move code for parsing the repository names (url, vcs, acl) from vcs to simplevcs.
r889 def set_repo_names(self, environ):
"""
Martin Bornhold
vcs: Get shadow repository file system path by using already existing functions....
r892 This will populate the attributes acl_repo_name, url_repo_name,
Martin Bornhold
vcs: Clean up the shadow-repo-expose code and make some nicer comments.
r904 vcs_repo_name and is_shadow_repo. In case of requests to normal (non
shadow) repositories all names are equal. In case of requests to a
shadow repository the acl-name points to the target repo of the pull
request and the vcs-name points to the shadow repo file system path.
The url-name is always the URL used by the vcs client program.
Martin Bornhold
vcs: Move code for parsing the repository names (url, vcs, acl) from vcs to simplevcs.
r889
Martin Bornhold
vcs: Clean up the shadow-repo-expose code and make some nicer comments.
r904 Example in case of a shadow repo:
acl_repo_name = RepoGroup/MyRepo
url_repo_name = RepoGroup/MyRepo/pull-request/3/repository
vcs_repo_name = /repo/base/path/RepoGroup/.__shadow_MyRepo_pr-3'
"""
# First we set the repo name from URL for all attributes. This is the
# default if handling normal (non shadow) repo requests.
self.url_repo_name = self._get_repository_name(environ)
self.acl_repo_name = self.vcs_repo_name = self.url_repo_name
self.is_shadow_repo = False
# Check if this is a request to a shadow repository.
Martin Bornhold
vcs: Move shadow repo regex up to class level.
r902 match = self.shadow_repo_re.match(self.url_repo_name)
Martin Bornhold
vcs: Move code for parsing the repository names (url, vcs, acl) from vcs to simplevcs.
r889 if match:
match_dict = match.groupdict()
Martin Bornhold
vcs: Get shadow repository file system path by using already existing functions....
r892
Martin Bornhold
vcs: Clean up the shadow-repo-expose code and make some nicer comments.
r904 # Build acl repo name from regex match.
tests: fixed test suite for celery adoption
r5607 acl_repo_name = safe_str(
"{groups}{target}".format(groups=match_dict["groups"] or "", target=match_dict["target"])
)
Martin Bornhold
vcs: Clean up the shadow-repo-expose code and make some nicer comments.
r904
# Retrieve pull request instance by ID from regex match.
tests: fixed test suite for celery adoption
r5607 pull_request = PullRequest.get(match_dict["pr_id"])
Martin Bornhold
vcs: Get shadow repository file system path by using already existing functions....
r892
Martin Bornhold
vcs: Clean up the shadow-repo-expose code and make some nicer comments.
r904 # Only proceed if we got a pull request and if acl repo name from
# URL equals the target repo name of the pull request.
shadow-repos: skip init of full repo to generate shadow repo path.
r3931 if pull_request and (acl_repo_name == pull_request.target_repo.repo_name):
Martin Bornhold
vcs: Clean up the shadow-repo-expose code and make some nicer comments.
r904 # Get file system path to shadow repository.
workspace_id = PullRequestModel()._workspace_id(pull_request)
shadow-repos: skip init of full repo to generate shadow repo path.
r3931 vcs_repo_name = pull_request.target_repo.get_shadow_repository_path(workspace_id)
Martin Bornhold
vcs: Clean up the shadow-repo-expose code and make some nicer comments.
r904
# Store names for later usage.
self.vcs_repo_name = vcs_repo_name
self.acl_repo_name = acl_repo_name
self.is_shadow_repo = True
tests: fixed test suite for celery adoption
r5607 log.debug(
"Setting all VCS repository names: %s",
{
"acl_repo_name": self.acl_repo_name,
"url_repo_name": self.url_repo_name,
"vcs_repo_name": self.vcs_repo_name,
},
)
Martin Bornhold
vcs: Move code for parsing the repository names (url, vcs, acl) from vcs to simplevcs.
r889
project: added all source files and assets
r1 @property
def scm_app(self):
tests: fixed test suite for celery adoption
r5607 custom_implementation = self.config["vcs.scm_app_implementation"]
if custom_implementation == "http":
log.debug("Using HTTP implementation of scm app.")
Martin Bornhold
http: Allow http, pyro and custom implementations for scm app.
r962 scm_app_impl = scm_app_http
else:
tests: fixed test suite for celery adoption
r5607 log.debug('Using custom implementation of scm_app: "{}"'.format(custom_implementation))
project: added all source files and assets
r1 scm_app_impl = importlib.import_module(custom_implementation)
return scm_app_impl
def _get_by_id(self, repo_name):
"""
Gets a special pattern _<ID> from clone url and tries to replace it
vcs: register repo_name as specialized middleware variables instead...
r757 with a repository_name for support of _<ID> non changeable urls
project: added all source files and assets
r1 """
tests: fixed test suite for celery adoption
r5607 data = repo_name.split("/")
project: added all source files and assets
r1 if len(data) >= 2:
from rhodecode.model.repo import RepoModel
tests: fixed test suite for celery adoption
r5607
project: added all source files and assets
r1 by_id_match = RepoModel().get_repo_by_id(repo_name)
if by_id_match:
data[1] = by_id_match.repo_name
core-app: use a consistent pep-3333 compatible PATH_INFO extraction
r5031 # Because PEP-3333-WSGI uses bytes-tunneled-in-latin-1 as PATH_INFO
# and we use this data
tests: fixed test suite for celery adoption
r5607 maybe_new_path = "/".join(data)
return safe_bytes(maybe_new_path).decode("latin1")
project: added all source files and assets
r1
def _invalidate_cache(self, repo_name):
"""
Set's cache for this repository for invalidation on next access
:param repo_name: full repo name, also a cache key
"""
ScmModel().mark_for_invalidation(repo_name)
def is_valid_and_existing_repo(self, repo_name, base_path, scm_type):
db_repo = Repository.get_by_repo_name(repo_name)
if not db_repo:
tests: fixed test suite for celery adoption
r5607 log.debug("Repository `%s` not found inside the database.", repo_name)
project: added all source files and assets
r1 return False
if db_repo.repo_type != scm_type:
log.warning(
tests: fixed test suite for celery adoption
r5607 "Repository `%s` have incorrect scm_type, expected %s got %s", repo_name, db_repo.repo_type, scm_type
)
project: added all source files and assets
r1 return False
simplevcs: allow passing config into repo detection logic....
r2519 config = db_repo._config
tests: fixed test suite for celery adoption
r5607 config.set("extensions", "largefiles", "")
return is_valid_repo(repo_name, base_path, explicit_scm=scm_type, expect_scm=scm_type, config=config)
project: added all source files and assets
r1
def valid_and_active_user(self, user):
"""
Checks if that user is not empty, and if it's actually object it checks
if he's active.
:param user: user object or None
:return: boolean
"""
if user is None:
return False
elif user.active:
return True
return False
vcs: report 404 for shadow repos that are not existing anymore. Before we got 500 exception in this case.
r2069 @property
def is_shadow_repo_dir(self):
return os.path.isdir(self.vcs_repo_name)
tests: fixed test suite for celery adoption
r5607 def _check_permission(
self, action, user, auth_user, repo_name, ip_addr=None, plugin_id="", plugin_cache_active=False, cache_ttl=0
):
project: added all source files and assets
r1 """
Checks permissions using action (push/pull) user and repository
auth: use cache_ttl from a plugin to also cache permissions....
r2154 name. If plugin_cache and ttl is set it will use the plugin which
authenticated the user to store the cached permissions result for N
amount of seconds as in cache_ttl
project: added all source files and assets
r1
:param action: push or pull action
:param user: user instance
:param repo_name: repository name
"""
auth: use cache_ttl from a plugin to also cache permissions....
r2154
tests: fixed test suite for celery adoption
r5607 log.debug("AUTH_CACHE_TTL for permissions `%s` active: %s (TTL: %s)", plugin_id, plugin_cache_active, cache_ttl)
auth: use cache_ttl from a plugin to also cache permissions....
r2154
caches: rewrite of auth/permission caches to dogpile.
r2845 user_id = user.user_id
tests: fixed test suite for celery adoption
r5607 cache_namespace_uid = f"cache_user_auth.{rc_cache.PERMISSIONS_CACHE_VER}.{user_id}"
region = rc_cache.get_or_create_region("cache_perms", cache_namespace_uid)
project: added all source files and assets
r1
tests: fixed test suite for celery adoption
r5607 @region.conditional_cache_on_arguments(
namespace=cache_namespace_uid, expiration_time=cache_ttl, condition=plugin_cache_active
)
def compute_perm_vcs(cache_name, plugin_id, action, user_id, repo_name, ip_addr):
log.debug("auth: calculating permission access now for vcs operation: %s", action)
auth: use cache_ttl from a plugin to also cache permissions....
r2154 # check IP
inherit = user.inherit_default_permissions
tests: fixed test suite for celery adoption
r5607 ip_allowed = AuthUser.check_ip_allowed(user_id, ip_addr, inherit_from_default=inherit)
auth: use cache_ttl from a plugin to also cache permissions....
r2154 if ip_allowed:
tests: fixed test suite for celery adoption
r5607 log.info("Access for IP:%s allowed", ip_addr)
auth: use cache_ttl from a plugin to also cache permissions....
r2154 else:
project: added all source files and assets
r1 return False
tests: fixed test suite for celery adoption
r5607 if action == "push":
perms = ("repository.write", "repository.admin")
branch-permissions: handle vcs operations and branch permissions....
r2979 if not HasPermissionAnyMiddleware(*perms)(auth_user, repo_name):
auth: use cache_ttl from a plugin to also cache permissions....
r2154 return False
else:
# any other action need at least read permission
tests: fixed test suite for celery adoption
r5607 perms = ("repository.read", "repository.write", "repository.admin")
branch-permissions: handle vcs operations and branch permissions....
r2979 if not HasPermissionAnyMiddleware(*perms)(auth_user, repo_name):
auth: use cache_ttl from a plugin to also cache permissions....
r2154 return False
return True
caches: rewrite of auth/permission caches to dogpile.
r2845 start = time.time()
tests: fixed test suite for celery adoption
r5607 log.debug("Running plugin `%s` permissions check", plugin_id)
caches: rewrite of auth/permission caches to dogpile.
r2845
# for environ based auth, password can be empty, but then the validation is
# on the server that fills in the env data needed for authentication
tests: fixed test suite for celery adoption
r5607 perm_result = compute_perm_vcs("vcs_permissions", plugin_id, action, user.user_id, repo_name, ip_addr)
project: added all source files and assets
r1
auth: use cache_ttl from a plugin to also cache permissions....
r2154 auth_time = time.time() - start
tests: fixed test suite for celery adoption
r5607 log.debug(
"Permissions for plugin `%s` completed in %.4fs, " "expiration time of fetched cache %.1fs.",
plugin_id,
auth_time,
cache_ttl,
)
auth: use cache_ttl from a plugin to also cache permissions....
r2154
return perm_result
project: added all source files and assets
r1
dan
git-lfs: fixed bug #5399 git-lfs application failed to generate HTTPS urls properly.
r3781 def _get_http_scheme(self, environ):
try:
tests: fixed test suite for celery adoption
r5607 return environ["wsgi.url_scheme"]
dan
git-lfs: fixed bug #5399 git-lfs application failed to generate HTTPS urls properly.
r3781 except Exception:
tests: fixed test suite for celery adoption
r5607 log.exception("Failed to read http scheme")
return "http"
dan
git-lfs: fixed bug #5399 git-lfs application failed to generate HTTPS urls properly.
r3781
auth, performance: use cache_ttl for anonymous access taken from the rhodecode plugin....
r2425 def _get_default_cache_ttl(self):
# take AUTH_CACHE_TTL from the `rhodecode` auth plugin
tests: fixed test suite for celery adoption
r5607 plugin = loadplugin("egg:rhodecode-enterprise-ce#rhodecode")
auth, performance: use cache_ttl for anonymous access taken from the rhodecode plugin....
r2425 plugin_settings = plugin.get_settings()
tests: fixed test suite for celery adoption
r5607 plugin_cache_active, cache_ttl = plugin.get_ttl_cache(plugin_settings) or (False, 0)
auth, performance: use cache_ttl for anonymous access taken from the rhodecode plugin....
r2425 return plugin_cache_active, cache_ttl
project: added all source files and assets
r1 def __call__(self, environ, start_response):
try:
return self._handle_request(environ, start_response)
except Exception:
log.exception("Exception while handling request")
appenlight.track_exception(environ)
return HTTPInternalServerError()(environ, start_response)
finally:
meta.Session.remove()
def _handle_request(self, environ, start_response):
Martin Bornhold
vcs: Use correct repo name attribute and remove workaround.
r898 if not self.url_repo_name:
tests: fixed test suite for celery adoption
r5607 log.warning("Repository name is empty: %s", self.url_repo_name)
vcs: register repo_name as specialized middleware variables instead...
r757 # failed to get repo name, we fail now
return HTTPNotFound()(environ, start_response)
tests: fixed test suite for celery adoption
r5607 log.debug("Extracted repo name is %s", self.url_repo_name)
vcs: register repo_name as specialized middleware variables instead...
r757
project: added all source files and assets
r1 ip_addr = get_ip_addr(environ)
audit-logs: log user agent for push/pull.
r1711 user_agent = get_user_agent(environ)
project: added all source files and assets
r1 username = None
# skip passing error to error controller
tests: fixed test suite for celery adoption
r5607 environ["pylons.status_code_redirect"] = True
project: added all source files and assets
r1
# ======================================================================
# GET ACTION PULL or PUSH
# ======================================================================
action = self._get_action(environ)
# ======================================================================
Martin Bornhold
vcs: Only allow 'pull' actions on shadow repositories....
r891 # Check if this is a request to a shadow repository of a pull request.
# In this case only pull action is allowed.
# ======================================================================
tests: fixed test suite for celery adoption
r5607 if self.is_shadow_repo and action != "pull":
reason = "Only pull action is allowed for shadow repositories."
log.debug("User not allowed to proceed, %s", reason)
Martin Bornhold
vcs: Only allow 'pull' actions on shadow repositories....
r891 return HTTPNotAcceptable(reason)(environ, start_response)
vcs: report 404 for shadow repos that are not existing anymore. Before we got 500 exception in this case.
r2069 # Check if the shadow repo actually exists, in case someone refers
# to it, and it has been deleted because of successful merge.
if self.is_shadow_repo and not self.is_shadow_repo_dir:
tests: fixed test suite for celery adoption
r5607 log.debug("Shadow repo detected, and shadow repo dir `%s` is missing", self.is_shadow_repo_dir)
vcs: report 404 for shadow repos that are not existing anymore. Before we got 500 exception in this case.
r2069 return HTTPNotFound()(environ, start_response)
Martin Bornhold
vcs: Only allow 'pull' actions on shadow repositories....
r891 # ======================================================================
project: added all source files and assets
r1 # CHECK ANONYMOUS PERMISSION
# ======================================================================
branch-permissions: handle vcs operations and branch permissions....
r2979 detect_force_push = False
check_branch_perms = False
tests: fixed test suite for celery adoption
r5607 if action in ["pull", "push"]:
branch-permissions: handle vcs operations and branch permissions....
r2979 user_obj = anonymous_user = User.get_default_user()
auth_user = user_obj.AuthUser()
project: added all source files and assets
r1 username = anonymous_user.username
if anonymous_user.active:
auth, performance: use cache_ttl for anonymous access taken from the rhodecode plugin....
r2425 plugin_cache_active, cache_ttl = self._get_default_cache_ttl()
project: added all source files and assets
r1 # ONLY check permissions if the user is activated
anonymous_perm = self._check_permission(
tests: fixed test suite for celery adoption
r5607 action,
anonymous_user,
auth_user,
self.acl_repo_name,
ip_addr,
plugin_id="anonymous_access",
http: improved log for ssl required
r2593 plugin_cache_active=plugin_cache_active,
cache_ttl=cache_ttl,
auth, performance: use cache_ttl for anonymous access taken from the rhodecode plugin....
r2425 )
project: added all source files and assets
r1 else:
anonymous_perm = False
if not anonymous_user.active or not anonymous_perm:
if not anonymous_user.active:
tests: fixed test suite for celery adoption
r5607 log.debug("Anonymous access is disabled, running " "authentication")
project: added all source files and assets
r1
if not anonymous_perm:
tests: fixed test suite for celery adoption
r5607 log.debug(
"Not enough credentials to access repo: `%s` " "repository as anonymous user",
self.acl_repo_name,
)
core: multiple fixes to unicode vs str usage...
r5065
project: added all source files and assets
r1 username = None
# ==============================================================
# DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
# NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
# ==============================================================
# try to auth based on environ, container auth methods
tests: fixed test suite for celery adoption
r5607 log.debug("Running PRE-AUTH for container|headers based authentication")
core: multiple fixes to unicode vs str usage...
r5065
# headers auth, by just reading special headers and bypass the auth with user/passwd
Martin Bornhold
vcs: Pass registry to vcs for user authentication....
r591 pre_auth = authenticate(
tests: fixed test suite for celery adoption
r5607 "", "", environ, VCS_TYPE, registry=self.registry, acl_repo_name=self.acl_repo_name
)
core: multiple fixes to unicode vs str usage...
r5065
tests: fixed test suite for celery adoption
r5607 if pre_auth and pre_auth.get("username"):
username = pre_auth["username"]
log.debug("PRE-AUTH got `%s` as username", username)
auth: use cache_ttl from a plugin to also cache permissions....
r2154 if pre_auth:
tests: fixed test suite for celery adoption
r5607 log.debug("PRE-AUTH successful from %s", pre_auth.get("auth_data", {}).get("_plugin"))
project: added all source files and assets
r1
# If not authenticated by the container, running basic auth
authentication: enabled authentication with auth_token and repository scope....
r1510 # before inject the calling repo_name for special scope checks
self.authenticate.acl_repo_name = self.acl_repo_name
auth: use cache_ttl from a plugin to also cache permissions....
r2154
plugin_cache_active, cache_ttl = False, 0
plugin = None
core: multiple fixes to unicode vs str usage...
r5065
# regular auth chain
project: added all source files and assets
r1 if not username:
vcs: reduce sql queries used during pull/push operations.
r2140 self.authenticate.realm = self.authenticate.get_rc_realm()
project: added all source files and assets
r1
try:
auth: use cache_ttl from a plugin to also cache permissions....
r2154 auth_result = self.authenticate(environ)
project: added all source files and assets
r1 except (UserCreationError, NotAllowedToCreateUserError) as e:
log.error(e)
reason = safe_str(e)
return HTTPNotAcceptable(reason)(environ, start_response)
auth: use cache_ttl from a plugin to also cache permissions....
r2154 if isinstance(auth_result, dict):
tests: fixed test suite for celery adoption
r5607 AUTH_TYPE.update(environ, "basic")
REMOTE_USER.update(environ, auth_result["username"])
username = auth_result["username"]
plugin = auth_result.get("auth_data", {}).get("_plugin")
log.info("MAIN-AUTH successful for user `%s` from %s plugin", username, plugin)
auth: use cache_ttl from a plugin to also cache permissions....
r2154
tests: fixed test suite for celery adoption
r5607 plugin_cache_active, cache_ttl = auth_result.get("auth_data", {}).get("_ttl_cache") or (
False,
0,
)
project: added all source files and assets
r1 else:
vcs: handle excessive slashes in from of the repo name path, fixes #5522
r3328 return auth_result.wsgi_application(environ, start_response)
auth: use cache_ttl from a plugin to also cache permissions....
r2154
project: added all source files and assets
r1 # ==============================================================
# CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
# ==============================================================
user = User.get_by_username(username)
if not self.valid_and_active_user(user):
return HTTPForbidden()(environ, start_response)
username = user.username
security: update lastactivity when on audit logs....
r2930 user_id = user.user_id
project: added all source files and assets
r1
# check user attributes for password change flag
user_obj = user
branch-permissions: handle vcs operations and branch permissions....
r2979 auth_user = user_obj.AuthUser()
tests: fixed test suite for celery adoption
r5607 if (
user_obj
and user_obj.username != User.DEFAULT_USER
and user_obj.user_data.get("force_password_change")
):
reason = "password change required"
log.debug("User not allowed to authenticate, %s", reason)
project: added all source files and assets
r1 return HTTPNotAcceptable(reason)(environ, start_response)
# check permissions for this repository
vcs: register repo_name as specialized middleware variables instead...
r757 perm = self._check_permission(
tests: fixed test suite for celery adoption
r5607 action, user, auth_user, self.acl_repo_name, ip_addr, plugin, plugin_cache_active, cache_ttl
)
project: added all source files and assets
r1 if not perm:
return HTTPForbidden()(environ, start_response)
tests: fixed test suite for celery adoption
r5607 environ["rc_auth_user_id"] = str(user_id)
project: added all source files and assets
r1
tests: fixed test suite for celery adoption
r5607 if action == "push":
branch-permissions: handle vcs operations and branch permissions....
r2979 perms = auth_user.get_branch_permissions(self.acl_repo_name)
if perms:
check_branch_perms = True
detect_force_push = True
project: added all source files and assets
r1 # extras are injected into UI object and later available
auth: use cache_ttl from a plugin to also cache permissions....
r2154 # in hooks executed by RhodeCode
tests: fixed test suite for celery adoption
r5607 check_locking = _should_check_locking(environ.get("QUERY_STRING"))
branch-permissions: handle vcs operations and branch permissions....
r2979
project: added all source files and assets
r1 extras = vcs_operation_context(
tests: fixed test suite for celery adoption
r5607 environ,
repo_name=self.acl_repo_name,
username=username,
action=action,
scm=self.SCM,
check_locking=check_locking,
is_shadow_repo=self.is_shadow_repo,
check_branch_perms=check_branch_perms,
detect_force_push=detect_force_push,
Martin Bornhold
vcs: Add flag to indicate if repository is a shadow repository.
r899 )
project: added all source files and assets
r1
# ======================================================================
# REQUEST HANDLING
# ======================================================================
tests: fixed test suite for celery adoption
r5607 repo_path = os.path.join(safe_str(self.base_path), safe_str(self.vcs_repo_name))
log.debug("Repository path is %s", repo_path)
project: added all source files and assets
r1
fix_PATH()
log.info(
audit-logs: log user agent for push/pull.
r1711 '%s action on %s repo "%s" by "%s" from %s %s',
tests: fixed test suite for celery adoption
r5607 action,
self.SCM,
safe_str(self.url_repo_name),
safe_str(username),
ip_addr,
user_agent,
)
Martin Bornhold
vcs: Add a flag to the environ to skip the error handling for responses from VCSMiddleware
r600
tests: fixed test suite for celery adoption
r5607 return self._generate_vcs_response(environ, start_response, repo_path, extras, action)
project: added all source files and assets
r1
fix(svn): svn events fixes and change the way how we handle the events
r5459 def _get_txn_id(self, environ):
tests: fixed test suite for celery adoption
r5607 for k in ["RAW_URI", "HTTP_DESTINATION"]:
fix(svn): svn events fixes and change the way how we handle the events
r5459 url = environ.get(k)
if not url:
continue
# regex to search for svn-txn-id
tests: fixed test suite for celery adoption
r5607 pattern = r"/!svn/txr/([^/]+)/"
fix(svn): svn events fixes and change the way how we handle the events
r5459
# Search for the pattern in the URL
match = re.search(pattern, url)
# Check if a match is found and extract the captured group
if match:
txn_id = match.group(1)
return txn_id
project: added all source files and assets
r1 @initialize_generator
tests: fixed test suite for celery adoption
r5607 def _generate_vcs_response(self, environ, start_response, repo_path, extras, action):
project: added all source files and assets
r1 """
Returns a generator for the response content.
This method is implemented as a generator, so that it can trigger
the cache validation after all content sent back to the client. It
also handles the locking exceptions which will be triggered when
the first chunk is produced by the underlying WSGI application.
"""
tests: fixed test suite for celery adoption
r5607 svn_txn_id = ""
if action == "push":
fix(svn): svn events fixes and change the way how we handle the events
r5459 svn_txn_id = self._get_txn_id(environ)
project: added all source files and assets
r1
tests: fixed test suite for celery adoption
r5607 callback_daemon, extras = self._prepare_callback_daemon(extras, environ, action, txn_id=svn_txn_id)
fix(svn): svn events fixes and change the way how we handle the events
r5459
if svn_txn_id:
txn_id_data = extras.copy()
tests: fixed test suite for celery adoption
r5607 txn_id_data.update({"req_method": environ["REQUEST_METHOD"]})
fix(svn): svn events fixes and change the way how we handle the events
r5459
full_repo_path = repo_path
store_txn_id_data(full_repo_path, svn_txn_id, txn_id_data)
tests: fixed test suite for celery adoption
r5607 log.debug("HOOKS extras is %s", extras)
svn: enable hooks and integration framework execution....
r2677
dan
git-lfs: fixed bug #5399 git-lfs application failed to generate HTTPS urls properly.
r3781 http_scheme = self._get_http_scheme(environ)
config = self._create_config(extras, self.acl_repo_name, scheme=http_scheme)
svn: enable hooks and integration framework execution....
r2677 app = self._create_wsgi_app(repo_path, self.url_repo_name, config)
with callback_daemon:
app.rc_extras = extras
project: added all source files and assets
r1
svn: enable hooks and integration framework execution....
r2677 try:
response = app(environ, start_response)
finally:
# This statement works together with the decorator
# "initialize_generator" above. The decorator ensures that
# we hit the first yield statement before the generator is
# returned back to the WSGI server. This is needed to
# ensure that the call to "app" above triggers the
# needed callback to "start_response" before the
# generator is actually used.
yield "__init__"
project: added all source files and assets
r1
svn: enable hooks and integration framework execution....
r2677 # iter content
for chunk in response:
project: added all source files and assets
r1 yield chunk
svn: enable hooks and integration framework execution....
r2677
dan
db: move Session.remove to outer wsgi layer and also add it...
r669 try:
svn: enable hooks and integration framework execution....
r2677 # invalidate cache on push
tests: fixed test suite for celery adoption
r5607 if action == "push":
Martin Bornhold
vcs: Explicit use one of the repo names (acl, url, vcs)
r905 self._invalidate_cache(self.url_repo_name)
dan
db: move Session.remove to outer wsgi layer and also add it...
r669 finally:
meta.Session.remove()
project: added all source files and assets
r1
def _get_repository_name(self, environ):
"""Get repository name out of the environmnent
:param environ: WSGI environment
"""
raise NotImplementedError()
def _get_action(self, environ):
"""Map request commands into a pull or push command.
:param environ: WSGI environment
"""
raise NotImplementedError()
def _create_wsgi_app(self, repo_path, repo_name, config):
"""Return the WSGI app that will finally handle the request."""
raise NotImplementedError()
tests: fixed test suite for celery adoption
r5607 def _create_config(self, extras, repo_name, scheme="http"):
core: removed pyro4 from Enterprise code. Fixes #5198
r1409 """Create a safe config representation."""
project: added all source files and assets
r1 raise NotImplementedError()
svn: enable hooks and integration framework execution....
r2677 def _should_use_callback_daemon(self, extras, environ, action):
tests: fixed test suite for celery adoption
r5607 if extras.get("is_shadow_repo"):
commits: disable shadow repo callback daemon
r3932 # we don't want to execute hooks, and callback daemon for shadow repos
return False
svn: enable hooks and integration framework execution....
r2677 return True
def _prepare_callback_daemon(self, extras, environ, action, txn_id=None):
feat(celery-hooks): added all needed changes to support new celery backend, removed DummyHooksCallbackDaemon, updated tests. Fixes: RCCE-55
r5298 protocol = vcs_settings.HOOKS_PROTOCOL
fix(svn): svn events fixes and change the way how we handle the events
r5459
svn: enable hooks and integration framework execution....
r2677 if not self._should_use_callback_daemon(extras, environ, action):
# disable callback daemon for actions that don't require it
tests: fixed test suite for celery adoption
r5607 protocol = "local"
svn: enable hooks and integration framework execution....
r2677
tests: fixed test suite for celery adoption
r5607 return prepare_callback_daemon(extras, protocol=protocol, txn_id=txn_id)
project: added all source files and assets
r1
def _should_check_locking(query_string):
# this is kind of hacky, but due to how mercurial handles client-server
# server see all operation on commit; bookmarks, phases and
# obsolescence marker in different transaction, we don't want to check
# locking on those
tests: fixed test suite for celery adoption
r5607 return query_string not in ["cmd=listkeys"]