diff --git a/docs/web/permissions.md b/docs/web/permissions.md
index 7152c301da..69a2683e1e 100644
--- a/docs/web/permissions.md
+++ b/docs/web/permissions.md
@@ -18,45 +18,40 @@ on the product level.
Table of Contents
=================
-* [The master superuser (root)](#the-master-superuser)
-* [Managing permissions](#managing-permissions)
-* [Permission concepts](#permission-concepts)
- * [Default value](#default-value)
- * [Permission inheritance](#permission-inheritance)
- * [Permission manager](#permission-manager)
-* [Available permissions](#available-permissions)
- * [Server-wide (global) permissions](#global-permissions)
- * [`SUPERUSER`](#superuser)
- * [`PERMISSION_VIEW`](#permission-view)
- * [Product-level permissions](#product-level-permissions)
- * [`PRODUCT_ADMIN`](#product-admin)
- * [`PRODUCT_ACCESS`](#product-access)
- * [`PRODUCT_STORE`](#product-store)
- * [`PRODUCT_VIEW`](#product-view)
+- [Permission subsystem](#permission-subsystem)
+- [Table of Contents](#table-of-contents)
+- [The master superuser (root) ](#the-master-superuser-root-)
+- [Managing permissions ](#managing-permissions-)
+- [Permission concepts ](#permission-concepts-)
+ - [Default value ](#default-value-)
+ - [Permission inheritance ](#permission-inheritance-)
+ - [Permission manager ](#permission-manager-)
+- [Available permissions ](#available-permissions-)
+ - [Server-wide (global) permissions ](#server-wide-global-permissions-)
+ - [`SUPERUSER` ](#superuser-)
+ - [`PERMISSION_VIEW`](#permission_view)
+ - [Product-level permissions ](#product-level-permissions-)
+ - [`PRODUCT_ADMIN` ](#product_admin-)
+ - [`PRODUCT_ACCESS` ](#product_access-)
+ - [`PRODUCT_STORE` ](#product_store-)
+ - [`PRODUCT_VIEW` ](#product_view-)
# The master superuser (root)
-Each CodeChecker server at its first start generates a master superuser
-(*root*) access credential which it prints into its standard output:
+At the first CodeChecker startup it is recommended that
+you set up a single user with `SUPERUSER` permission.
+Then with this user you will be able to configure additional permissions
+for other users in the WEB GUI.
+Let's say you want to give `SUPERUSER` permission to user `admin`.
+Then set `super_user` field in the `server_config.json` configuration file:
```sh
-[WARNING] Server started without 'root.user' present in CONFIG_DIRECTORY!
-A NEW superuser credential was generated for the server. This information IS
-SAVED, thus subsequent server starts WILL use these credentials. You WILL NOT
-get to see the credentials again, so MAKE SURE YOU REMEMBER THIS LOGIN!
------------------------------------------------------------------
-The superuser's username is 'AAAAAA' with the password 'aaaa0000'
------------------------------------------------------------------
+"authentication": {
+ "enabled" : true,
+ "super_user" : "admin",
+...
```
-These credentials can be deleted and new ones can be requested by starting
-CodeChecker server with the `--reset-root` flag. The credentials are always
-**randomly generated**.
-
-If the server has authentication enabled, the *root* user will **always have
-access** despite of the configured authentication backends' decision, and
-will automatically **have the `SUPERUSER` permission**.
-
# Managing permissions
![Global permission manager](images/permissions.png)
@@ -184,4 +179,4 @@ delete existing analysis runs from the server.
|---------|-----------------|-----------------|
| Granted | `PRODUCT_ADMIN` | `PRODUCT_ADMIN`, `PRODUCT_STORE`, `PRODUCT_ACCESS` |
-Users need the `PRODUCT_VIEW` permission to `view` analysis runs without modifying any properties of the runs.
\ No newline at end of file
+Users need the `PRODUCT_VIEW` permission to `view` analysis runs without modifying any properties of the runs.
\ No newline at end of file
diff --git a/docs/web/user_guide.md b/docs/web/user_guide.md
index 846599b76a..c5ca9b9004 100644
--- a/docs/web/user_guide.md
+++ b/docs/web/user_guide.md
@@ -12,8 +12,8 @@
- [Configuring database and server settings location](#configuring-database-and-server-settings-location)
- [Server Configuration (Authentication and Server Limits)](#server-configuration-authentication-and-server-limits)
- [Database Configuration](#database-configuration)
- - [Master superuser and authentication forcing](#master-superuser-and-authentication-forcing)
- - [Enfore secure socket (TLS/SSL)](#enfore-secure-socket-ssl)
+ - [Initial super-user](#initial-super-user)
+ - [Enfore secure socket (TLS/SSL)](#enfore-secure-socket-tlsssl)
- [Managing running servers](#managing-running-servers)
- [Manage server database upgrades](#manage-server-database-upgrades)
- [`store`](#store)
@@ -150,7 +150,7 @@ usage: CodeChecker server [-h] [-w WORKSPACE] [-f CONFIG_DIRECTORY]
[--sqlite SQLITE_FILE | --postgresql]
[--dbaddress DBADDRESS] [--dbport DBPORT]
[--dbusername DBUSERNAME] [--dbname DBNAME]
- [--reset-root] [--force-authentication]
+ [--force-authentication]
[-l | -r | -s | --stop-all]
[--db-status STATUS | --db-upgrade-schema PRODUCT_TO_UPGRADE | --db-force-upgrade]
[--verbose {info,debug,debug_analyzer}]
@@ -309,26 +309,21 @@ project.
**It is recommended to use only the Postgresql databse for production
deployments!**
-#### Master superuser and authentication forcing
-
-```
-root account arguments:
- Servers automatically create a root user to access the server's
- configuration via the clients. This user is created at first start and
- saved in the CONFIG_DIRECTORY, and the credentials are printed to the
- server's standard output. The plaintext credentials are NEVER accessible
- again.
-
- --reset-root Force the server to recreate the master superuser
- (root) account name and password. The previous
- credentials will be invalidated, and the new ones will
- be printed to the standard output.
- --force-authentication
- Force the server to run in authentication requiring
- mode, despite the configuration value in
- 'session_config.json'. This is needed if you need to
- edit the product configuration of a server that would
- not require authentication otherwise.
+#### Initial super-user
+
+You can give a single user SUPER_USER permission
+by setting the `super_user` field in the `authentication`
+section of the `server_config.json`.
+The user which is set here, must be an existing user.
+For example it should be a user
+with dictionary authentication method.
+
+```
+ "authentication": {
+ "enabled" : true,
+ "super_user" : "admin",
+...
+
```
#### Enfore secure socket (TLS/SSL)
diff --git a/web/server/codechecker_server/cmd/server.py b/web/server/codechecker_server/cmd/server.py
index 33bbbd20f1..c7781250b8 100644
--- a/web/server/codechecker_server/cmd/server.py
+++ b/web/server/codechecker_server/cmd/server.py
@@ -212,17 +212,6 @@ def add_arguments_to_parser(parser):
CONFIG_DIRECTORY, and the credentials are printed to the server's standard
output. The plaintext credentials are NEVER accessible again.""")
- root_account.add_argument('--reset-root',
- dest="reset_root",
- action='store_true',
- default=argparse.SUPPRESS,
- required=False,
- help="Force the server to recreate the master "
- "superuser (root) account name and "
- "password. The previous credentials will "
- "be invalidated, and the new ones will be "
- "printed to the standard output.")
-
root_account.add_argument('--force-authentication',
dest="force_auth",
action='store_true',
@@ -932,15 +921,6 @@ def server_init_start(args):
not os.path.isdir(os.path.dirname(args.sqlite)):
os.makedirs(os.path.dirname(args.sqlite))
- if 'reset_root' in args:
- try:
- os.remove(os.path.join(args.config_directory, 'root.user'))
- LOG.info("Master superuser (root) credentials invalidated and "
- "deleted. New ones will be generated...")
- except OSError:
- # File doesn't exist.
- pass
-
if 'force_auth' in args:
LOG.info("'--force-authentication' was passed as a command-line "
"option. The server will ask for users to authenticate!")
diff --git a/web/server/codechecker_server/server.py b/web/server/codechecker_server/server.py
index 40bdf6db4d..36f2713bd2 100644
--- a/web/server/codechecker_server/server.py
+++ b/web/server/codechecker_server/server.py
@@ -14,17 +14,14 @@
import atexit
import datetime
from functools import partial
-from hashlib import sha256
from http.server import HTTPServer, SimpleHTTPRequestHandler
import os
import posixpath
-from random import sample
import shutil
import signal
import socket
import ssl
import sys
-import stat
from typing import List, Optional, Tuple
import urllib
@@ -68,8 +65,6 @@
Configuration as ORMConfiguration
from .database.database import DBSession
from .database.run_db_model import IDENTIFIER as RUN_META, Run, RunLock
-from .tmp import get_tmp_dir_hash
-
LOG = get_logger('server')
@@ -991,43 +986,6 @@ class CCSimpleHttpServerIPv6(CCSimpleHttpServer):
address_family = socket.AF_INET6
-def __make_root_file(root_file):
- """
- Generate a root username and password SHA. This hash is saved to the
- given file path, and is also returned.
- """
-
- LOG.debug("Generating initial superuser (root) credentials...")
-
- username = ''.join(sample("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 6))
- password = get_tmp_dir_hash()[:8]
-
- LOG.info("A NEW superuser credential was generated for the server. "
- "This information IS SAVED, thus subsequent server starts "
- "WILL use these credentials. You WILL NOT get to see "
- "the credentials again, so MAKE SURE YOU REMEMBER THIS "
- "LOGIN!")
-
- # Highlight the message a bit more, as the server owner configuring the
- # server must know this root access initially.
- credential_msg = f"The superuser's username is '{username}' with the " \
- f"password '{password}'"
- LOG.info("-" * len(credential_msg))
- LOG.info(credential_msg)
- LOG.info("-" * len(credential_msg))
-
- sha = sha256((username + ':' + password).encode('utf-8')).hexdigest()
- secret = f"{username}:{sha}"
- with open(root_file, 'w', encoding="utf-8", errors="ignore") as f:
- LOG.debug("Save root SHA256 '%s'", secret)
- f.write(secret)
-
- # This file should be only readable by the process owner, and noone else.
- os.chmod(root_file, stat.S_IRUSR)
-
- return secret
-
-
def start_server(config_directory, package_data, port, config_sql_server,
listen_address, force_auth, skip_db_cleanup: bool,
context, check_env):
@@ -1038,22 +996,17 @@ def start_server(config_directory, package_data, port, config_sql_server,
server_addr = (listen_address, port)
+ # The root user file is DEPRECATED AND IGNORED
root_file = os.path.join(config_directory, 'root.user')
- if not os.path.exists(root_file):
- LOG.warning("Server started without 'root.user' present in "
- "CONFIG_DIRECTORY!")
- root_sha = __make_root_file(root_file)
- else:
- LOG.debug("Root file was found. Loading...")
- try:
- with open(root_file, 'r', encoding="utf-8", errors="ignore") as f:
- root_sha = f.read()
- LOG.debug("Root digest is '%s'", root_sha)
- except IOError:
- LOG.info("Cannot open root file '%s' even though it exists",
- root_file)
- root_sha = __make_root_file(root_file)
-
+ if os.path.exists(root_file):
+ LOG.warning("The 'root.user' file: %s"
+ " is deprecated and ignored. If you want to"
+ " setup an initial user with SUPER_USER permission,"
+ " configure the super_user field in the server_config.json"
+ " as described in the documentation."
+ " To get rid off this warning,"
+ " simply delete the root.user file.",
+ root_file)
# Check whether configuration file exists, create an example if not.
server_cfg_file = os.path.join(config_directory, 'server_config.json')
if not os.path.exists(server_cfg_file):
@@ -1077,7 +1030,6 @@ def start_server(config_directory, package_data, port, config_sql_server,
try:
manager = session_manager.SessionManager(
server_cfg_file,
- root_sha,
force_auth)
except IOError as ioerr:
LOG.debug(ioerr)
@@ -1098,7 +1050,7 @@ def start_server(config_directory, package_data, port, config_sql_server,
"Earlier logs might contain additional detailed "
"reasoning.\n\t* %s", len(fails),
"\n\t* ".join(
- (f"'{ep}' ({reason})" for (ep, reason) in fails)
+ (f"'{ep}' ({reason})" for (ep, reason) in fails)
))
else:
LOG.debug("Skipping db_cleanup, as requested.")
diff --git a/web/server/codechecker_server/session_manager.py b/web/server/codechecker_server/session_manager.py
index 276af909cd..2bcae3cbc9 100644
--- a/web/server/codechecker_server/session_manager.py
+++ b/web/server/codechecker_server/session_manager.py
@@ -9,7 +9,6 @@
Handles the management of authentication sessions on the server's side.
"""
-import hashlib
import json
import os
import re
@@ -161,13 +160,12 @@ class SessionManager:
CodeChecker server.
"""
- def __init__(self, configuration_file, root_sha, force_auth=False):
+ def __init__(self, configuration_file, force_auth=False):
"""
Initialise a new Session Manager on the server.
:param configuration_file: The configuration file to read
authentication backends from.
- :param root_sha: The SHA-256 hash of the root user's authentication.
:param force_auth: If True, the manager will be enabled even if the
configuration file disables authentication.
"""
@@ -199,9 +197,6 @@ def __init__(self, configuration_file, root_sha, force_auth=False):
self.__refresh_time = self.__auth_config['refresh_time'] \
if 'refresh_time' in self.__auth_config else None
- # Save the root SHA into the configuration (but only in memory!)
- self.__auth_config['method_root'] = root_sha
-
self.__regex_groups_enabled = False
# Pre-compile the regular expressions of 'regex_groups'
@@ -334,17 +329,16 @@ def get_realm(self):
"error": self.__auth_config.get('realm_error')
}
+ @property
+ def get_super_user(self):
+ return {
+ "super_user": self.__auth_config.get('super_user'),
+ }
+
@property
def default_superuser_name(self) -> Optional[str]:
""" Get default superuser name. """
- root = self.__auth_config['method_root'].split(":")
-
- # Previously the root file doesn't contain the user name. In this case
- # we will return with no user name.
- if len(root) <= 1:
- return None
-
- return root[0]
+ return self.__auth_config['super_user']
def set_database_connection(self, connection):
"""
@@ -365,8 +359,7 @@ def __handle_validation(self, auth_string):
This validation object contains two keys: username and groups.
"""
- validation = self.__try_auth_root(auth_string) \
- or self.__try_auth_dictionary(auth_string) \
+ validation = self.__try_auth_dictionary(auth_string) \
or self.__try_auth_pam(auth_string) \
or self.__try_auth_ldap(auth_string)
if not validation:
@@ -387,22 +380,6 @@ def __is_method_enabled(self, method):
'method_' + method in self.__auth_config and \
self.__auth_config['method_' + method].get('enabled')
- def __try_auth_root(self, auth_string):
- """
- Try to authenticate the user against the root username:password's hash.
- """
- user_name = SessionManager.get_user_name(auth_string)
- sha = hashlib.sha256(auth_string.encode('utf8')).hexdigest()
-
- if f"{user_name}:{sha}" == self.__auth_config['method_root']:
- return {
- 'username': SessionManager.get_user_name(auth_string),
- 'groups': [],
- 'root': True
- }
-
- return False
-
def __try_auth_token(self, auth_string):
if not self.__database_connection:
return None
@@ -562,7 +539,7 @@ def get_db_auth_session_tokens(self, user_name):
def __is_root_user(self, user_name):
""" Return True if the given user has system permissions. """
- if self.__auth_config['method_root'].split(":")[0] == user_name:
+ if self.__auth_config['super_user'] == user_name:
return True
transaction = None
diff --git a/web/server/vue-cli/e2e/init.workspace.js b/web/server/vue-cli/e2e/init.workspace.js
index 9ca21579f5..c1e43a5453 100644
--- a/web/server/vue-cli/e2e/init.workspace.js
+++ b/web/server/vue-cli/e2e/init.workspace.js
@@ -7,20 +7,19 @@ const WORKSPACE_DIR = path.join(CC_DIR, "workspace");
const SERVER_CONFIG = {
authentication: {
enabled : true,
+ "super_user" : "root",
session_lifetime : 60000,
refresh_time : 60,
logins_until_cleanup : 30,
method_dictionary: {
enabled : true,
- auths : [ "cc:admin" ],
+ auths : [ "cc:admin",
+ "root:S3cr3t" ],
groups : {}
}
}
};
-const ROOT_USER =
- "root:2691b13e4c5eadd0adad38983e611b2caa19caaa3476ccf31cbcadddf65c321c";
-
// Create workspace directory if it does not exists.
if (!fs.existsSync(WORKSPACE_DIR)) {
fs.mkdirSync(WORKSPACE_DIR);
@@ -29,10 +28,4 @@ if (!fs.existsSync(WORKSPACE_DIR)) {
// Create server configuration file and enable authentication.
const serverConfigFile = path.join(WORKSPACE_DIR, "server_config.json");
const data = JSON.stringify(SERVER_CONFIG, null, " ");
-fs.writeFileSync(serverConfigFile, data);
-
-// Generate initial root credentials.
-// - username: root
-// - password: S3cr3t
-const rootUserFile = path.join(WORKSPACE_DIR, "root.user");
-fs.writeFileSync(rootUserFile, ROOT_USER);
+fs.writeFileSync(serverConfigFile, data);
\ No newline at end of file
diff --git a/web/tests/libtest/env.py b/web/tests/libtest/env.py
index 1610db8bef..725fb0d177 100644
--- a/web/tests/libtest/env.py
+++ b/web/tests/libtest/env.py
@@ -10,13 +10,11 @@
"""
-from hashlib import sha256
import os
import json
import tempfile
import shutil
import socket
-import stat
import subprocess
from codechecker_common.util import load_json
@@ -350,11 +348,12 @@ def enable_auth(workspace):
scfg_dict = load_json(server_cfg_file, {})
scfg_dict["authentication"]["enabled"] = True
+ scfg_dict["authentication"]["super_user"] = "root"
scfg_dict["authentication"]["method_dictionary"]["enabled"] = True
scfg_dict["authentication"]["method_dictionary"]["auths"] = \
["cc:test", "john:doe", "admin:admin123", "colon:my:password",
"admin_group_user:admin123", "regex_admin:blah",
- "permission_view_user:pvu"]
+ "permission_view_user:pvu", "root:root"]
scfg_dict["authentication"]["method_dictionary"]["groups"] = \
{"admin_group_user": ["admin_GROUP"]}
scfg_dict["authentication"]["regex_groups"]["enabled"] = True
@@ -363,13 +362,6 @@ def enable_auth(workspace):
encoding="utf-8", errors="ignore") as scfg:
json.dump(scfg_dict, scfg, indent=2, sort_keys=True)
- # Create a root user.
- root_file = os.path.join(workspace, 'root.user')
- with open(root_file, 'w',
- encoding='utf-8', errors='ignore') as rootf:
- rootf.write(f"root:{sha256(b'root:root').hexdigest()}")
- os.chmod(root_file, stat.S_IRUSR | stat.S_IWUSR)
-
def enable_storage_of_analysis_statistics(workspace):
"""