# Copyright (C) 2011-2024 RhodeCode GmbH # # 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 . # # 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 logging import itertools from rhodecode.api import ( jsonrpc_method, JSONRPCError, JSONRPCForbidden, find_methods) from rhodecode.api.utils import ( Optional, OAttr, has_superadmin_permission, get_user_or_error) from rhodecode.lib.utils import get_rhodecode_repo_store_path from rhodecode.lib import system_info from rhodecode.lib import user_sessions from rhodecode.lib import exc_tracking from rhodecode.lib.ext_json import json from rhodecode.lib.utils2 import safe_int from rhodecode.model.db import UserIpMap from rhodecode.model.scm import ScmModel log = logging.getLogger(__name__) @jsonrpc_method() def get_server_info(request, apiuser): """ Returns the |RCE| server information. This includes the running version of |RCE| and all installed packages. This command takes the following options: :param apiuser: This is filled automatically from the |authtoken|. :type apiuser: AuthUser Example output: .. code-block:: bash id : result : { 'modules': [,...] 'py_version': , 'platform': , 'rhodecode_version': } error : null """ if not has_superadmin_permission(apiuser): raise JSONRPCForbidden() server_info = ScmModel().get_server_info(request.environ) # rhodecode-index requires those server_info['index_storage'] = server_info['search']['value']['location'] server_info['storage'] = server_info['storage']['value']['path'] return server_info @jsonrpc_method() def get_repo_store(request, apiuser): """ Returns the |RCE| repository storage information. :param apiuser: This is filled automatically from the |authtoken|. :type apiuser: AuthUser Example output: .. code-block:: bash id : result : { 'modules': [,...] 'py_version': , 'platform': , 'rhodecode_version': } error : null """ if not has_superadmin_permission(apiuser): raise JSONRPCForbidden() path = get_rhodecode_repo_store_path() return {"path": path} @jsonrpc_method() def get_ip(request, apiuser, userid=Optional(OAttr('apiuser'))): """ Displays the IP Address as seen from the |RCE| server. * This command displays the IP Address, as well as all the defined IP addresses for the specified user. If the ``userid`` is not set, the data returned is for the user calling the method. This command can only be run using an |authtoken| with admin rights to the specified repository. This command takes the following options: :param apiuser: This is filled automatically from |authtoken|. :type apiuser: AuthUser :param userid: Sets the userid for which associated IP Address data is returned. :type userid: Optional(str or int) Example output: .. code-block:: bash id : result : { "server_ip_addr": "", "user_ips": [ { "ip_addr": "", "ip_range": ["", ""], }, ... ] } """ if not has_superadmin_permission(apiuser): raise JSONRPCForbidden() userid = Optional.extract(userid, evaluate_locals=locals()) userid = getattr(userid, 'user_id', userid) user = get_user_or_error(userid) ips = UserIpMap.query().filter(UserIpMap.user == user).all() return { 'server_ip_addr': request.rpc_ip_addr, 'user_ips': ips } @jsonrpc_method() def rescan_repos(request, apiuser): """ Triggers a rescan of the specified repositories. It returns list of added repositories, and errors during scan. This command can only be run using an |authtoken| with admin rights to the specified repository. This command takes the following options: :param apiuser: This is filled automatically from the |authtoken|. :type apiuser: AuthUser Example output: .. code-block:: bash id : result : { 'added': [,...] 'errors': [,...] } error : null Example error output: .. code-block:: bash id : result : null error : { 'Error occurred during rescan repositories action' } """ from rhodecode.lib.utils import repo2db_mapper # re-import for testing patches if not has_superadmin_permission(apiuser): raise JSONRPCForbidden() try: added, errors = repo2db_mapper(ScmModel().repo_scan(), force_hooks_rebuild=True) return {'added': added, 'errors': errors} except Exception: log.exception('Failed to run repo rescan') raise JSONRPCError( 'Error occurred during rescan repositories action' ) @jsonrpc_method() def cleanup_repos(request, apiuser): """ Triggers a cleanup of non-existing repositories or repository groups in filesystem. This command can only be run using an |authtoken| with admin rights to the specified repository. This command takes the following options: :param apiuser: This is filled automatically from the |authtoken|. :type apiuser: AuthUser Example output: .. code-block:: bash id : result : { 'removed': [,...] 'errors': [,...] } error : null Example error output: .. code-block:: bash id : result : null error : { 'Error occurred during repo storage cleanup action' } """ from rhodecode.lib.utils import repo2db_cleanup # re-import for testing patches if not has_superadmin_permission(apiuser): raise JSONRPCForbidden() try: removed, errors = repo2db_cleanup() return {'removed': removed, 'errors': errors} except Exception: log.exception('Failed to run repo storage cleanup') raise JSONRPCError( 'Error occurred during repo storage cleanup action' ) @jsonrpc_method() def cleanup_sessions(request, apiuser, older_then=Optional(60)): """ Triggers a session cleanup action. If the ``older_then`` option is set, only sessions that hasn't been accessed in the given number of days will be removed. This command can only be run using an |authtoken| with admin rights to the specified repository. This command takes the following options: :param apiuser: This is filled automatically from the |authtoken|. :type apiuser: AuthUser :param older_then: Deletes session that hasn't been accessed in given number of days. :type older_then: Optional(int) Example output: .. code-block:: bash id : result: { "backend": "", "sessions_removed": } error : null Example error output: .. code-block:: bash id : result : null error : { 'Error occurred during session cleanup' } """ if not has_superadmin_permission(apiuser): raise JSONRPCForbidden() older_then = safe_int(Optional.extract(older_then)) or 60 older_than_seconds = 60 * 60 * 24 * older_then config = system_info.rhodecode_config().get_value()['value']['config'] session_model = user_sessions.get_session_handler( config.get('beaker.session.type', 'memory'))(config) backend = session_model.SESSION_TYPE try: cleaned = session_model.clean_sessions( older_than_seconds=older_than_seconds) return {'sessions_removed': cleaned, 'backend': backend} except user_sessions.CleanupCommand as msg: return {'cleanup_command': str(msg), 'backend': backend} except Exception as e: log.exception('Failed session cleanup') raise JSONRPCError( 'Error occurred during session cleanup' ) @jsonrpc_method() def get_method(request, apiuser, pattern=Optional('*')): """ Returns list of all available API methods. By default match pattern os "*" but any other pattern can be specified. eg *comment* will return all methods with comment inside them. If just single method is matched returned data will also include method specification This command can only be run using an |authtoken| with admin rights to the specified repository. This command takes the following options: :param apiuser: This is filled automatically from the |authtoken|. :type apiuser: AuthUser :param pattern: pattern to match method names against :type pattern: Optional("*") Example output: .. code-block:: bash id : "result": [ "changeset_comment", "comment_pull_request", "comment_commit" ] error : null .. code-block:: bash id : "result": [ "comment_commit", { "apiuser": "", "comment_type": "", "commit_id": "", "message": "", "repoid": "", "request": "", "resolves_comment_id": "", "status": "", "userid": ">" } ] error : null """ from rhodecode.config import patches inspect = patches.inspect_getargspec() if not has_superadmin_permission(apiuser): raise JSONRPCForbidden() pattern = Optional.extract(pattern) matches = find_methods(request.registry.jsonrpc_methods, pattern) args_desc = [] matches_keys = list(matches.keys()) if len(matches_keys) == 1: func = matches[matches_keys[0]] argspec = inspect.getargspec(func) arglist = argspec[0] defaults = list(map(repr, argspec[3] or [])) default_empty = '' # kw arguments required by this method func_kwargs = dict(itertools.zip_longest( reversed(arglist), reversed(defaults), fillvalue=default_empty)) args_desc.append(func_kwargs) return matches_keys + args_desc @jsonrpc_method() def store_exception(request, apiuser, exc_data_json, prefix=Optional('rhodecode')): """ Stores sent exception inside the built-in exception tracker in |RCE| server. This command can only be run using an |authtoken| with admin rights to the specified repository. This command takes the following options: :param apiuser: This is filled automatically from the |authtoken|. :type apiuser: AuthUser :param exc_data_json: JSON data with exception e.g {"exc_traceback": "Value `1` is not allowed", "exc_type_name": "ValueError"} :type exc_data_json: JSON data :param prefix: prefix for error type, e.g 'rhodecode', 'vcsserver', 'rhodecode-tools' :type prefix: Optional("rhodecode") Example output: .. code-block:: bash id : "result": { "exc_id": 139718459226384, "exc_url": "http://localhost:8080/_admin/settings/exceptions/139718459226384" } error : null """ if not has_superadmin_permission(apiuser): raise JSONRPCForbidden() prefix = Optional.extract(prefix) exc_id = exc_tracking.generate_id() try: exc_data = json.loads(exc_data_json) except Exception: log.error('Failed to parse JSON: %r', exc_data_json) raise JSONRPCError('Failed to parse JSON data from exc_data_json field. ' 'Please make sure it contains a valid JSON.') try: exc_traceback = exc_data['exc_traceback'] exc_type_name = exc_data['exc_type_name'] exc_value = '' except KeyError as err: raise JSONRPCError( f'Missing exc_traceback, or exc_type_name ' f'in exc_data_json field. Missing: {err}') class ExcType: __name__ = exc_type_name exc_info = (ExcType(), exc_value, exc_traceback) exc_tracking._store_exception( exc_id=exc_id, exc_info=exc_info, prefix=prefix) exc_url = request.route_url( 'admin_settings_exception_tracker_show', exception_id=exc_id) return {'exc_id': exc_id, 'exc_url': exc_url}