##// END OF EJS Templates
fix(2fa): fixed redirect loop in workflow when password reset was done.
fix(2fa): fixed redirect loop in workflow when password reset was done.

File last commit:

r5370:6dc425cb default
r5370:6dc425cb default
Show More
vcs.py
308 lines | 10.1 KiB | text/x-python | PythonLexer
project: added all source files and assets
r1
copyrights: updated for 2023
r5088 # Copyright (C) 2010-2023 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/
import gzip
import shutil
import logging
import tempfile
python3: fixed urlparse import
r4919 import urllib.parse
project: added all source files and assets
r1
vcs: register repo_name as specialized middleware variables instead...
r757 from webob.exc import HTTPNotFound
project: added all source files and assets
r1 import rhodecode
fix(2fa): fixed redirect loop in workflow when password reset was done.
r5370 from rhodecode.apps._base import ADMIN_PREFIX
PATH_INFO: use new method to consistently extract proper PATH_INFO data
r5032 from rhodecode.lib.middleware.utils import get_path_info
project: added all source files and assets
r1 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
from rhodecode.lib.middleware.simplegit import SimpleGit, GIT_PROTO_PAT
from rhodecode.lib.middleware.simplehg import SimpleHg
from rhodecode.lib.middleware.simplesvn import SimpleSvn
fix(git): fixed issues with gzip detection for git protocol
r5229 from rhodecode.lib.str_utils import safe_str
vcs: moved svn proxy settings into vcs related settings...
r754 from rhodecode.model.settings import VcsSettingsModel
project: added all source files and assets
r1
PATH_INFO: use new method to consistently extract proper PATH_INFO data
r5032
project: added all source files and assets
r1 log = logging.getLogger(__name__)
vcs: do an early detection of vcs-type request....
r1297 VCS_TYPE_KEY = '_rc_vcs_type'
VCS_TYPE_SKIP = '_rc_vcs_skip'
project: added all source files and assets
r1
def is_git(environ):
"""
Returns True if requests should be handled by GIT wsgi middleware
"""
PATH_INFO: use new method to consistently extract proper PATH_INFO data
r5032 path_info = get_path_info(environ)
is_git_path = GIT_PROTO_PAT.match(path_info)
project: added all source files and assets
r1 log.debug(
PATH_INFO: use new method to consistently extract proper PATH_INFO data
r5032 'request path: `%s` detected as GIT PROTOCOL %s', path_info,
project: added all source files and assets
r1 is_git_path is not None)
return is_git_path
def is_hg(environ):
"""
Returns True if requests target is mercurial server - header
``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
"""
is_hg_path = False
http_accept = environ.get('HTTP_ACCEPT')
if http_accept and http_accept.startswith('application/mercurial'):
python3: fixed various code issues...
r4973 query = urllib.parse.parse_qs(environ['QUERY_STRING'])
project: added all source files and assets
r1 if 'cmd' in query:
is_hg_path = True
PATH_INFO: use new method to consistently extract proper PATH_INFO data
r5032 path_info = get_path_info(environ)
project: added all source files and assets
r1 log.debug(
PATH_INFO: use new method to consistently extract proper PATH_INFO data
r5032 'request path: `%s` detected as HG PROTOCOL %s', path_info,
project: added all source files and assets
r1 is_hg_path)
return is_hg_path
def is_svn(environ):
"""
Returns True if requests target is Subversion server
"""
svn: extend detection of SVN to PROPFIND/PROPATCH methods....
r2262
project: added all source files and assets
r1 http_dav = environ.get('HTTP_DAV', '')
subversion: Detect requests also based on magic path.
r437 magic_path_segment = rhodecode.CONFIG.get(
'rhodecode_subversion_magic_path', '/!svn')
PATH_INFO: use new method to consistently extract proper PATH_INFO data
r5032 path_info = get_path_info(environ)
feat(svn): improvements to handle SVN protocol 1.4 features...
r5215 req_method = environ['REQUEST_METHOD']
subversion: Detect requests also based on magic path.
r437 is_svn_path = (
'subversion' in http_dav or
PATH_INFO: use new method to consistently extract proper PATH_INFO data
r5032 magic_path_segment in path_info
feat(svn): improvements to handle SVN protocol 1.4 features...
r5215 or req_method in ['PROPFIND', 'PROPPATCH', 'HEAD']
svn: extend detection of SVN to PROPFIND/PROPATCH methods....
r2262 )
project: added all source files and assets
r1 log.debug(
PATH_INFO: use new method to consistently extract proper PATH_INFO data
r5032 'request path: `%s` detected as SVN PROTOCOL %s', path_info,
project: added all source files and assets
r1 is_svn_path)
return is_svn_path
class GunzipMiddleware(object):
"""
WSGI middleware that unzips gzip-encoded requests before
passing on to the underlying application.
"""
def __init__(self, application):
self.app = application
def __call__(self, environ, start_response):
fix(git): fixed issues with gzip detection for git protocol
r5229 accepts_encoding_header = safe_str(environ.get('HTTP_CONTENT_ENCODING', ''))
project: added all source files and assets
r1
fix(git): fixed issues with gzip detection for git protocol
r5229 if 'gzip' in accepts_encoding_header:
project: added all source files and assets
r1 log.debug('gzip detected, now running gunzip wrapper')
wsgi_input = environ['wsgi.input']
if not hasattr(environ['wsgi.input'], 'seek'):
# The gzip implementation in the standard library of Python 2.x
# requires the '.seek()' and '.tell()' methods to be available
# on the input stream. Read the data into a temporary file to
# work around this limitation.
wsgi_input = tempfile.SpooledTemporaryFile(64 * 1024 * 1024)
shutil.copyfileobj(environ['wsgi.input'], wsgi_input)
wsgi_input.seek(0)
environ['wsgi.input'] = gzip.GzipFile(fileobj=wsgi_input, mode='r')
# since we "Ungzipped" the content we say now it's no longer gzip
# content encoding
del environ['HTTP_CONTENT_ENCODING']
# content length has changes ? or i'm not sure
if 'CONTENT_LENGTH' in environ:
del environ['CONTENT_LENGTH']
else:
log.debug('content not gzipped, gzipMiddleware passing '
'request further')
return self.app(environ, start_response)
vcs: do an early detection of vcs-type request....
r1297 def is_vcs_call(environ):
if VCS_TYPE_KEY in environ:
raw_type = environ[VCS_TYPE_KEY]
return raw_type and raw_type != VCS_TYPE_SKIP
return False
def detect_vcs_request(environ, backends):
checks = {
'hg': (is_hg, SimpleHg),
'git': (is_git, SimpleGit),
'svn': (is_svn, SimpleSvn),
}
handler = None
artifacts: skip vcs detection for artifact downloads.
r4475 # List of path views first chunk we don't do any checks
white_list = [
middlewares: all porting for python3
r5082 # favicon often requested by browsers
'favicon.ico',
artifacts: skip vcs detection for artifact downloads.
r4475 # e.g /_file_store/download
middlewares: all porting for python3
r5082 '_file_store++',
ldap: small fixes and improvements over ldap authentication
r5140 # login
"_admin/login",
fix(2fa): fixed redirect loop in workflow when password reset was done.
r5370 # 2fa
f"{ADMIN_PREFIX}/check_2fa",
f"{ADMIN_PREFIX}/setup_2fa",
middlewares: all porting for python3
r5082 # _admin/api is safe too
fix(2fa): fixed redirect loop in workflow when password reset was done.
r5370 f'{ADMIN_PREFIX}/api',
middlewares: all porting for python3
r5082
# _admin/gist is safe too
fix(2fa): fixed redirect loop in workflow when password reset was done.
r5370 f'{ADMIN_PREFIX}/gists++',
middlewares: all porting for python3
r5082
# _admin/my_account is safe too
fix(2fa): fixed redirect loop in workflow when password reset was done.
r5370 f'{ADMIN_PREFIX}/my_account++',
core: optimize some calls to skip license/scm detection on them
r4655
# static files no detection
middlewares: all porting for python3
r5082 '_static++',
# debug-toolbar
'_debug_toolbar++',
core: optimize some calls to skip license/scm detection on them
r4655
ops: added healthcheck view
r4736 # skip ops ping, status
fix(2fa): fixed redirect loop in workflow when password reset was done.
r5370 f'{ADMIN_PREFIX}/ops/ping',
f'{ADMIN_PREFIX}/ops/status',
views: skip vcsdetection for ops view
r4721
core: optimize some calls to skip license/scm detection on them
r4655 # full channelstream connect should be VCS skipped
fix(2fa): fixed redirect loop in workflow when password reset was done.
r5370 f'{ADMIN_PREFIX}/channelstream/connect',
vcs: skip vcs detection on repo creating page
r5131
'++/repo_creating_check'
artifacts: skip vcs detection for artifact downloads.
r4475 ]
PATH_INFO: use new method to consistently extract proper PATH_INFO data
r5032 path_info = get_path_info(environ)
path_url = path_info.lstrip('/')
feat(svn): improvements to handle SVN protocol 1.4 features...
r5215 req_method = environ.get('REQUEST_METHOD')
core: optimize some calls to skip license/scm detection on them
r4655
middlewares: all porting for python3
r5082 for item in white_list:
if item.endswith('++') and path_url.startswith(item[:-2]):
log.debug('path `%s` in whitelist (match:%s), skipping...', path_url, item)
return handler
vcs: skip vcs detection on repo creating page
r5131 if item.startswith('++') and path_url.endswith(item[2:]):
log.debug('path `%s` in whitelist (match:%s), skipping...', path_url, item)
return handler
middlewares: all porting for python3
r5082 if item == path_url:
log.debug('path `%s` in whitelist (match:%s), skipping...', path_url, item)
return handler
core: optimize some calls to skip license/scm detection on them
r4655
vcs: do an early detection of vcs-type request....
r1297 if VCS_TYPE_KEY in environ:
raw_type = environ[VCS_TYPE_KEY]
if raw_type == VCS_TYPE_SKIP:
log.debug('got `skip` marker for vcs detection, skipping...')
return handler
_check, handler = checks.get(raw_type) or [None, None]
if handler:
log.debug('got handler:%s from environ', handler)
if not handler:
feat(svn): improvements to handle SVN protocol 1.4 features...
r5215 log.debug('request start: checking if request for `%s:%s` is of VCS type in order: %s',
req_method, path_url, backends)
vcs: do an early detection of vcs-type request....
r1297 for vcs_type in backends:
vcs_check, _handler = checks[vcs_type]
if vcs_check(environ):
log.debug('vcs handler found %s', _handler)
handler = _handler
break
return handler
project: added all source files and assets
r1 class VCSMiddleware(object):
pylons: remove pylons as dependency...
r2351 def __init__(self, app, registry, config, appenlight_client):
project: added all source files and assets
r1 self.application = app
pylons: remove pylons as dependency...
r2351 self.registry = registry
project: added all source files and assets
r1 self.config = config
self.appenlight_client = appenlight_client
vcs: moved svn proxy settings into vcs related settings...
r754 self.use_gzip = True
vcs: register repo_name as specialized middleware variables instead...
r757 # order in which we check the middlewares, based on vcs.backends config
self.check_middlewares = config['vcs.backends']
vcs: moved svn proxy settings into vcs related settings...
r754
def vcs_config(self, repo_name=None):
"""
returns serialized VcsSettings
"""
simplevcs: fixed logic of extraction of base_path
r2362 try:
return VcsSettingsModel(
repo=repo_name).get_ui_settings_as_config_obj()
except Exception:
pass
vcs: moved svn proxy settings into vcs related settings...
r754
vcs: register repo_name as specialized middleware variables instead...
r757 def wrap_in_gzip_if_enabled(self, app, config):
vcs: moved svn proxy settings into vcs related settings...
r754 if self.use_gzip:
app = GunzipMiddleware(app)
return app
project: added all source files and assets
r1
def _get_handler_app(self, environ):
app = None
vcs: do an early detection of vcs-type request....
r1297 log.debug('VCSMiddleware: detecting vcs type.')
handler = detect_vcs_request(environ, self.check_middlewares)
if handler:
pylons: remove pylons as dependency...
r2351 app = handler(self.config, self.registry)
project: added all source files and assets
r1
return app
def __call__(self, environ, start_response):
vcs: register repo_name as specialized middleware variables instead...
r757 # check if we handle one of interesting protocols, optionally extract
# specific vcsSettings and allow changes of how things are wrapped
project: added all source files and assets
r1 vcs_handler = self._get_handler_app(environ)
if vcs_handler:
vcs: register repo_name as specialized middleware variables instead...
r757 # translate the _REPO_ID into real repo NAME for usage
# in middleware
PATH_INFO: use new method to consistently extract proper PATH_INFO data
r5032
path_info = get_path_info(environ)
environ['PATH_INFO'] = vcs_handler._get_by_id(path_info)
vcs: register repo_name as specialized middleware variables instead...
r757
Martin Bornhold
vcs: Clean up the shadow-repo-expose code and make some nicer comments.
r904 # Set acl, url and vcs repo names.
Martin Bornhold
vcs: Move code for parsing the repository names (url, vcs, acl) from vcs to simplevcs.
r889 vcs_handler.set_repo_names(environ)
vcs: Minimal change to expose the shadow repository...
r887
simplevcs: fixed logic of extraction of base_path
r2362 # register repo config back to the handler
vcs_conf = self.vcs_config(vcs_handler.acl_repo_name)
# maybe damaged/non existent settings. We still want to
# pass that point to validate on is_valid_and_existing_repo
# and return proper HTTP Code back to client
if vcs_conf:
vcs_handler.repo_vcs_config = vcs_conf
vcs: register repo_name as specialized middleware variables instead...
r757 # check for type, presence in database and on filesystem
if not vcs_handler.is_valid_and_existing_repo(
Martin Bornhold
vcs: Move code for parsing the repository names (url, vcs, acl) from vcs to simplevcs.
r889 vcs_handler.acl_repo_name,
pylons: remove pylons as dependency...
r2351 vcs_handler.base_path,
Martin Bornhold
vcs: Move code for parsing the repository names (url, vcs, acl) from vcs to simplevcs.
r889 vcs_handler.SCM):
vcs: register repo_name as specialized middleware variables instead...
r757 return HTTPNotFound()(environ, start_response)
Martin Bornhold
vcs: Move code for parsing the repository names (url, vcs, acl) from vcs to simplevcs.
r889 environ['REPO_NAME'] = vcs_handler.url_repo_name
vcs: register repo_name as specialized middleware variables instead...
r757
Martin Bornhold
vcs: Move code for parsing the repository names (url, vcs, acl) from vcs to simplevcs.
r889 # Wrap handler in middlewares if they are enabled.
vcs: register repo_name as specialized middleware variables instead...
r757 vcs_handler = self.wrap_in_gzip_if_enabled(
vcs_handler, self.config)
vcs_handler, _ = wrap_in_appenlight_if_enabled(
vcs_handler, self.config, self.appenlight_client)
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 return vcs_handler(environ, start_response)
return self.application(environ, start_response)