Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server config #1249

Merged
merged 2 commits into from
Jan 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/session_config.json → config/server_config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"max_run_count": 200,
"authentication": {
"enabled" : false,
"realm_name" : "CodeChecker Privileged server",
Expand Down
4 changes: 2 additions & 2 deletions docs/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ Table of Contents
## <a name="server-side-configuration"></a> Server-side configuration

The server's configuration is stored in the server's *workspace* folder, in
`session_config.json`. This file is created, at the first start of the server,
using the package's installed `config/session_config.json` as a template.
`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.

The `authentication` section of the config file controls how authentication
is handled.
Expand Down
24 changes: 24 additions & 0 deletions docs/server_config.md
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion libcodechecker/libauth/cc_ldap.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"""

LDAP authentication module for CodeChecker.
Authenticate user based on the session_config.json LDAP part.
Authenticate user based on the server_config.json LDAP part.

In the configuration `null` means it is not configured.

Expand Down
2 changes: 1 addition & 1 deletion libcodechecker/libhandlers/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ def add_arguments_to_parser(parser):
help="Force the server to run in "
"authentication requiring mode, despite "
"the configuration value in "
"'session_config.json'. This is needed "
"'server_config.json'. This is needed "
"if you need to edit the product "
"configuration of a server that would not "
"require authentication otherwise.")
Expand Down
23 changes: 23 additions & 0 deletions libcodechecker/server/api/report_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ class ThriftRequestHandler(object):
"""

def __init__(self,
manager,
Session,
product,
auth_session,
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions libcodechecker/server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
74 changes: 41 additions & 33 deletions libcodechecker/session_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import portalocker

from libcodechecker.logger import get_logger
from libcodechecker import util

unsupported_methods = []

Expand Down Expand Up @@ -130,29 +131,17 @@ def check_file_owner_rw(file_to_check):
return True


def load_session_cfg(session_cfg_file):
def load_server_cfg(server_cfg_file):
"""
Tries to load the session config file which should be a
valid json file, if loading fails returns an empty dict.
"""

scfg_dict = {}
try:
with open(session_cfg_file, 'r') as scfg:
scfg_dict = json.loads(scfg.read())
check_file_owner_rw(session_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, {})

except IOError:
LOG.debug('Failed to open user authentication file: ' +
session_cfg_file)
raise
except ValueError as verr:
LOG.warning(verr)
LOG.warning('Not valid user authentication file: ' +
session_cfg_file)
raise

return scfg_dict
return {}


class SessionManager:
Expand All @@ -165,21 +154,37 @@ def __init__(self, root_sha, force_auth=False):
LOG.debug('Loading session config')

# Check whether workspace's configuration exists.
session_cfg_file = os.path.join(SessionManager.CodeChecker_Workspace,
"session_config.json")

if not os.path.exists(session_cfg_file):
LOG.info("CodeChecker server's authentication example "
"configuration file created at " + session_cfg_file)
shutil.copyfile(os.path.join(os.environ['CC_PACKAGE_ROOT'],
"config", "session_config.json"),
session_cfg_file)
server_cfg_file = os.path.join(SessionManager.CodeChecker_Workspace,
"server_config.json")

if not os.path.exists(server_cfg_file):
# For backward compatibility reason if the session_config.json file
# exists we rename it to server_config.json.
session_cfg_file = os.path.join(
SessionManager.CodeChecker_Workspace,
"session_config.json")
example_cfg_file = os.path.join(os.environ['CC_PACKAGE_ROOT'],
"config", "server_config.json")
if os.path.exists(session_cfg_file):
LOG.info("Renaming {0} to {1}. Please check the example "
"configuration file ({2}) or the user guide for "
"more information.".format(session_cfg_file,
server_cfg_file,
example_cfg_file))
os.rename(session_cfg_file, server_cfg_file)
else:
LOG.info("CodeChecker server's example configuration file "
"created at " + server_cfg_file)
shutil.copyfile(example_cfg_file, server_cfg_file)

LOG.debug(session_cfg_file)
LOG.debug(server_cfg_file)

# Create the default settings and then load the file from the disk.
scfg_dict = {'authentication': {'enabled': False}}
scfg_dict.update(load_session_cfg(session_cfg_file))
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"]

Expand Down Expand Up @@ -407,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.
Expand Down Expand Up @@ -444,11 +456,7 @@ def __init__(self):
".codechecker.passwords.json")
LOG.debug(session_cfg_file)

try:
scfg_dict = load_session_cfg(session_cfg_file)
except (IOError, ValueError):
# On the client's side, continue execution with defaults.
scfg_dict = {}
scfg_dict = load_server_cfg(session_cfg_file)

if not scfg_dict.get("credentials"):
scfg_dict["credentials"] = {}
Expand Down
21 changes: 21 additions & 0 deletions libcodechecker/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import datetime
import hashlib
import json
import os
import re
import shutil
Expand Down Expand Up @@ -433,3 +434,23 @@ def get_new_line_col_without_whitespace(line_content, old_col):

return ''.join(line_content.split()), \
old_col - line_strip_len


def load_json_or_empty(path, default=None, kind=None):
"""
Load the contents of the given file as a JSON and return it's value,
or default if the file can't be loaded.
"""

ret = default
try:
with open(path, 'r') as handle:
ret = json.loads(handle.read())
except IOError:
LOG.warning("Failed to open {0} file: {1}"
.format(kind if kind else 'json', path))
except ValueError:
LOG.warning("'{1}' is not a valid {0} file."
.format(kind if kind else 'json', path))

return ret
33 changes: 13 additions & 20 deletions tests/libtest/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
from functional import PKG_ROOT
from functional import REPO_ROOT

from libcodechecker import util


def get_free_port():
"""
Expand Down Expand Up @@ -267,38 +269,29 @@ def enable_auth(workspace):
an auth-enabled server.

Running the tests only work if the initial value (in package
session_config.json) is FALSE for authentication.enabled.
server_config.json) is FALSE for authentication.enabled.
"""

session_config_filename = "session_config.json"
server_config_filename = "server_config.json"

cc_package = codechecker_package()
original_auth_cfg = os.path.join(cc_package,
'config',
session_config_filename)
server_config_filename)

shutil.copy(original_auth_cfg, workspace)

session_cfg_file = os.path.join(workspace,
session_config_filename)

with open(session_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 session config json.')
sys.exit(1)
server_cfg_file = os.path.join(workspace,
server_config_filename)

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')
Expand Down