diff --git a/config/server_config.json b/config/server_config.json index 4a08a4c78a..9a3e9d5e71 100644 --- a/config/server_config.json +++ b/config/server_config.json @@ -1,4 +1,5 @@ { + "max_run_count": 200, "authentication": { "enabled" : false, "realm_name" : "CodeChecker Privileged server", diff --git a/docs/server_config.md b/docs/server_config.md new file mode 100644 index 0000000000..22fe2a908a --- /dev/null +++ b/docs/server_config.md @@ -0,0 +1,24 @@ +CodeChecker server configuration +==================================== + +The server's configuration is stored in the server's *workspace* folder, in +`server_config.json`. This file is created, at the first start of the server, +using the package's installed `config/server_config.json` as a template. + +> **NOTICE!** `session_config.json` file has been deprecated. + +Table of Contents +================= +* [Run limitation](#run-limitations) +* [Authentication](#authentication) + +## Run limitation +The `max_run_count` section of the config file controls how many runs can be +stored on the server for a product. + +If this field is not present in the config file or the value of this field is a +negative value, run storage becomes unlimited. + +## Authentication +For authentication configuration options see the +[Authentication](authentication.md) documentation. diff --git a/libcodechecker/server/api/report_server.py b/libcodechecker/server/api/report_server.py index ec5bd40397..b10d5cc394 100644 --- a/libcodechecker/server/api/report_server.py +++ b/libcodechecker/server/api/report_server.py @@ -429,6 +429,7 @@ class ThriftRequestHandler(object): """ def __init__(self, + manager, Session, product, auth_session, @@ -442,6 +443,7 @@ def __init__(self, raise ValueError("Cannot initialize request handler without " "a product to serve.") + self.__manager = manager self.__product = product self.__auth_session = auth_session self.__config_database = config_database @@ -1916,6 +1918,27 @@ def __store_reports(self, session, report_dir, source_root, run_id, def massStoreRun(self, name, tag, version, b64zip, force): self.__require_store() + with DBSession(self.__Session) as session: + run = session.query(Run).filter(Run.name == name).one_or_none() + max_run_count = self.__manager.get_max_run_count() + + # If max_run_count is not set in the config file, it will allow + # the user to upload unlimited runs. + if max_run_count: + run_count = session.query(Run.id).count() + + # If we are not updating a run or the run count is reached the + # limit it will throw an exception. + if not run and run_count >= max_run_count: + remove_run_count = run_count - max_run_count + 1 + raise shared.ttypes.RequestFailed( + shared.ttypes.ErrorCode.GENERAL, + 'You reached the maximum number of allowed runs ' + '({0}/{1})! Please remove at least {2} run(s) before ' + 'you try it again.'.format(run_count, + max_run_count, + remove_run_count)) + with util.TemporaryDirectory() as zip_dir: # Unzip sent data. unzip(b64zip, zip_dir) diff --git a/libcodechecker/server/server.py b/libcodechecker/server/server.py index a6aa00b982..f8865c826b 100644 --- a/libcodechecker/server/server.py +++ b/libcodechecker/server/server.py @@ -404,6 +404,7 @@ def do_POST(self): self.__check_prod_db(product) acc_handler = ReportHandler_v6( + self.server.manager, product.session_factory, product, auth_session, diff --git a/libcodechecker/session_manager.py b/libcodechecker/session_manager.py index a0d04a316b..0ba614bab9 100644 --- a/libcodechecker/session_manager.py +++ b/libcodechecker/session_manager.py @@ -137,8 +137,11 @@ def load_server_cfg(server_cfg_file): valid json file, if loading fails returns an empty dict. """ - check_file_owner_rw(server_cfg_file) - return util.load_json_or_empty(server_cfg_file, {}) + if os.path.exists(server_cfg_file): + check_file_owner_rw(server_cfg_file) + return util.load_json_or_empty(server_cfg_file, {}) + + return {} class SessionManager: @@ -180,6 +183,9 @@ def __init__(self, root_sha, force_auth=False): scfg_dict = {'authentication': {'enabled': False}} scfg_dict.update(load_server_cfg(server_cfg_file)) + self.__max_run_count = scfg_dict["max_run_count"] \ + if "max_run_count" in scfg_dict else None + self.__auth_config = scfg_dict["authentication"] if force_auth: @@ -406,6 +412,13 @@ def is_valid(self, token, access=False): return any(_sess.token == token and _sess.still_valid(access) for _sess in SessionManager.__valid_sessions) + def get_max_run_count(self): + """ + Returns the maximum storable run count. If the value is None it means + we can upload unlimited number of runs. + """ + return self.__max_run_count + def get_session(self, token, access=False): """Gets the privileged session object based based on the token. diff --git a/tests/libtest/env.py b/tests/libtest/env.py index ac52bdbe90..d45b43f9f7 100644 --- a/tests/libtest/env.py +++ b/tests/libtest/env.py @@ -21,6 +21,8 @@ from functional import PKG_ROOT from functional import REPO_ROOT +from libcodechecker import util + def get_free_port(): """ @@ -282,23 +284,14 @@ def enable_auth(workspace): server_cfg_file = os.path.join(workspace, server_config_filename) - with open(server_cfg_file, 'r+') as scfg: - __scfg_original = scfg.read() - scfg.seek(0) - try: - scfg_dict = json.loads(__scfg_original) - except ValueError as verr: - print(verr) - print('Malformed server config json.') - sys.exit(1) - - scfg_dict["authentication"]["enabled"] = True - scfg_dict["authentication"]["method_dictionary"]["enabled"] = True - scfg_dict["authentication"]["method_dictionary"]["auths"] =\ - ["cc:test", "john:doe"] + scfg_dict = util.load_json_or_empty(server_cfg_file, {}) + scfg_dict["authentication"]["enabled"] = True + scfg_dict["authentication"]["method_dictionary"]["enabled"] = True + scfg_dict["authentication"]["method_dictionary"]["auths"] = \ + ["cc:test", "john:doe"] + with open(server_cfg_file, 'w') as scfg: json.dump(scfg_dict, scfg, indent=2, sort_keys=True) - scfg.truncate() # Create a root user. root_file = os.path.join(workspace, 'root.user')