Skip to content

Commit 866f379

Browse files
dkruppbruntib
authored andcommitted
Removing the root user creation
Instead the user can assign the super user role to a user
1 parent a48e4ae commit 866f379

File tree

7 files changed

+73
-189
lines changed

7 files changed

+73
-189
lines changed

docs/web/permissions.md

+28-33
Original file line numberDiff line numberDiff line change
@@ -18,45 +18,40 @@ on the product level.
1818

1919
Table of Contents
2020
=================
21-
* [The master superuser (root)](#the-master-superuser)
22-
* [Managing permissions](#managing-permissions)
23-
* [Permission concepts](#permission-concepts)
24-
* [Default value](#default-value)
25-
* [Permission inheritance](#permission-inheritance)
26-
* [Permission manager](#permission-manager)
27-
* [Available permissions](#available-permissions)
28-
* [Server-wide (global) permissions](#global-permissions)
29-
* [`SUPERUSER`](#superuser)
30-
* [`PERMISSION_VIEW`](#permission-view)
31-
* [Product-level permissions](#product-level-permissions)
32-
* [`PRODUCT_ADMIN`](#product-admin)
33-
* [`PRODUCT_ACCESS`](#product-access)
34-
* [`PRODUCT_STORE`](#product-store)
35-
* [`PRODUCT_VIEW`](#product-view)
21+
- [Permission subsystem](#permission-subsystem)
22+
- [Table of Contents](#table-of-contents)
23+
- [The master superuser (root) ](#the-master-superuser-root-)
24+
- [Managing permissions ](#managing-permissions-)
25+
- [Permission concepts ](#permission-concepts-)
26+
- [Default value ](#default-value-)
27+
- [Permission inheritance ](#permission-inheritance-)
28+
- [Permission manager ](#permission-manager-)
29+
- [Available permissions ](#available-permissions-)
30+
- [Server-wide (global) permissions ](#server-wide-global-permissions-)
31+
- [`SUPERUSER` ](#superuser-)
32+
- [`PERMISSION_VIEW`](#permission_view)
33+
- [Product-level permissions ](#product-level-permissions-)
34+
- [`PRODUCT_ADMIN` ](#product_admin-)
35+
- [`PRODUCT_ACCESS` ](#product_access-)
36+
- [`PRODUCT_STORE` ](#product_store-)
37+
- [`PRODUCT_VIEW` ](#product_view-)
3638

3739
# The master superuser (root) <a name="the-master-superuser"></a>
3840

39-
Each CodeChecker server at its first start generates a master superuser
40-
(*root*) access credential which it prints into its standard output:
4141

42+
At the first CodeChecker startup it is recommended that
43+
you set up a single user with `SUPERUSER` permission.
44+
Then with this user you will be able to configure additional permissions
45+
for other users in the WEB GUI.
46+
Let's say you want to give `SUPERUSER` permission to user `admin`.
47+
Then set `super_user` field in the `server_config.json` configuration file:
4248
```sh
43-
[WARNING] Server started without 'root.user' present in CONFIG_DIRECTORY!
44-
A NEW superuser credential was generated for the server. This information IS
45-
SAVED, thus subsequent server starts WILL use these credentials. You WILL NOT
46-
get to see the credentials again, so MAKE SURE YOU REMEMBER THIS LOGIN!
47-
-----------------------------------------------------------------
48-
The superuser's username is 'AAAAAA' with the password 'aaaa0000'
49-
-----------------------------------------------------------------
49+
"authentication": {
50+
"enabled" : true,
51+
"super_user" : "admin",
52+
...
5053
```
5154
52-
These credentials can be deleted and new ones can be requested by starting
53-
CodeChecker server with the `--reset-root` flag. The credentials are always
54-
**randomly generated**.
55-
56-
If the server has authentication enabled, the *root* user will **always have
57-
access** despite of the configured authentication backends' decision, and
58-
will automatically **have the `SUPERUSER` permission**.
59-
6055
# Managing permissions <a name="managing-permissions"></a>
6156
6257
![Global permission manager](images/permissions.png)
@@ -184,4 +179,4 @@ delete existing analysis runs from the server.
184179
|---------|-----------------|-----------------|
185180
| Granted | `PRODUCT_ADMIN` | `PRODUCT_ADMIN`, `PRODUCT_STORE`, `PRODUCT_ACCESS` |
186181
187-
Users need the `PRODUCT_VIEW` permission to `view` analysis runs without modifying any properties of the runs.
182+
Users need the `PRODUCT_VIEW` permission to `view` analysis runs without modifying any properties of the runs.

docs/web/user_guide.md

+18-23
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
- [Configuring database and server settings location](#configuring-database-and-server-settings-location)
1313
- [Server Configuration (Authentication and Server Limits)](#server-configuration-authentication-and-server-limits)
1414
- [Database Configuration](#database-configuration)
15-
- [Master superuser and authentication forcing](#master-superuser-and-authentication-forcing)
16-
- [Enfore secure socket (TLS/SSL)](#enfore-secure-socket-ssl)
15+
- [Initial super-user](#initial-super-user)
16+
- [Enfore secure socket (TLS/SSL)](#enfore-secure-socket-tlsssl)
1717
- [Managing running servers](#managing-running-servers)
1818
- [Manage server database upgrades](#manage-server-database-upgrades)
1919
- [`store`](#store)
@@ -150,7 +150,7 @@ usage: CodeChecker server [-h] [-w WORKSPACE] [-f CONFIG_DIRECTORY]
150150
[--sqlite SQLITE_FILE | --postgresql]
151151
[--dbaddress DBADDRESS] [--dbport DBPORT]
152152
[--dbusername DBUSERNAME] [--dbname DBNAME]
153-
[--reset-root] [--force-authentication]
153+
[--force-authentication]
154154
[-l | -r | -s | --stop-all]
155155
[--db-status STATUS | --db-upgrade-schema PRODUCT_TO_UPGRADE | --db-force-upgrade]
156156
[--verbose {info,debug,debug_analyzer}]
@@ -309,26 +309,21 @@ project.
309309
**It is recommended to use only the Postgresql databse for production
310310
deployments!**
311311

312-
#### Master superuser and authentication forcing
313-
314-
```
315-
root account arguments:
316-
Servers automatically create a root user to access the server's
317-
configuration via the clients. This user is created at first start and
318-
saved in the CONFIG_DIRECTORY, and the credentials are printed to the
319-
server's standard output. The plaintext credentials are NEVER accessible
320-
again.
321-
322-
--reset-root Force the server to recreate the master superuser
323-
(root) account name and password. The previous
324-
credentials will be invalidated, and the new ones will
325-
be printed to the standard output.
326-
--force-authentication
327-
Force the server to run in authentication requiring
328-
mode, despite the configuration value in
329-
'session_config.json'. This is needed if you need to
330-
edit the product configuration of a server that would
331-
not require authentication otherwise.
312+
#### Initial super-user
313+
314+
You can give a single user SUPER_USER permission
315+
by setting the `super_user` field in the `authentication`
316+
section of the `server_config.json`.
317+
The user which is set here, must be an existing user.
318+
For example it should be a user
319+
with dictionary authentication method.
320+
321+
```
322+
"authentication": {
323+
"enabled" : true,
324+
"super_user" : "admin",
325+
...
326+
332327
```
333328

334329
#### Enfore secure socket (TLS/SSL)

web/server/codechecker_server/cmd/server.py

-20
Original file line numberDiff line numberDiff line change
@@ -212,17 +212,6 @@ def add_arguments_to_parser(parser):
212212
CONFIG_DIRECTORY, and the credentials are printed to the server's standard
213213
output. The plaintext credentials are NEVER accessible again.""")
214214

215-
root_account.add_argument('--reset-root',
216-
dest="reset_root",
217-
action='store_true',
218-
default=argparse.SUPPRESS,
219-
required=False,
220-
help="Force the server to recreate the master "
221-
"superuser (root) account name and "
222-
"password. The previous credentials will "
223-
"be invalidated, and the new ones will be "
224-
"printed to the standard output.")
225-
226215
root_account.add_argument('--force-authentication',
227216
dest="force_auth",
228217
action='store_true',
@@ -932,15 +921,6 @@ def server_init_start(args):
932921
not os.path.isdir(os.path.dirname(args.sqlite)):
933922
os.makedirs(os.path.dirname(args.sqlite))
934923

935-
if 'reset_root' in args:
936-
try:
937-
os.remove(os.path.join(args.config_directory, 'root.user'))
938-
LOG.info("Master superuser (root) credentials invalidated and "
939-
"deleted. New ones will be generated...")
940-
except OSError:
941-
# File doesn't exist.
942-
pass
943-
944924
if 'force_auth' in args:
945925
LOG.info("'--force-authentication' was passed as a command-line "
946926
"option. The server will ask for users to authenticate!")

web/server/codechecker_server/server.py

+11-59
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,14 @@
1414
import atexit
1515
import datetime
1616
from functools import partial
17-
from hashlib import sha256
1817
from http.server import HTTPServer, SimpleHTTPRequestHandler
1918
import os
2019
import posixpath
21-
from random import sample
2220
import shutil
2321
import signal
2422
import socket
2523
import ssl
2624
import sys
27-
import stat
2825
from typing import List, Optional, Tuple
2926
import urllib
3027

@@ -68,8 +65,6 @@
6865
Configuration as ORMConfiguration
6966
from .database.database import DBSession
7067
from .database.run_db_model import IDENTIFIER as RUN_META, Run, RunLock
71-
from .tmp import get_tmp_dir_hash
72-
7368

7469
LOG = get_logger('server')
7570

@@ -991,43 +986,6 @@ class CCSimpleHttpServerIPv6(CCSimpleHttpServer):
991986
address_family = socket.AF_INET6
992987

993988

994-
def __make_root_file(root_file):
995-
"""
996-
Generate a root username and password SHA. This hash is saved to the
997-
given file path, and is also returned.
998-
"""
999-
1000-
LOG.debug("Generating initial superuser (root) credentials...")
1001-
1002-
username = ''.join(sample("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 6))
1003-
password = get_tmp_dir_hash()[:8]
1004-
1005-
LOG.info("A NEW superuser credential was generated for the server. "
1006-
"This information IS SAVED, thus subsequent server starts "
1007-
"WILL use these credentials. You WILL NOT get to see "
1008-
"the credentials again, so MAKE SURE YOU REMEMBER THIS "
1009-
"LOGIN!")
1010-
1011-
# Highlight the message a bit more, as the server owner configuring the
1012-
# server must know this root access initially.
1013-
credential_msg = f"The superuser's username is '{username}' with the " \
1014-
f"password '{password}'"
1015-
LOG.info("-" * len(credential_msg))
1016-
LOG.info(credential_msg)
1017-
LOG.info("-" * len(credential_msg))
1018-
1019-
sha = sha256((username + ':' + password).encode('utf-8')).hexdigest()
1020-
secret = f"{username}:{sha}"
1021-
with open(root_file, 'w', encoding="utf-8", errors="ignore") as f:
1022-
LOG.debug("Save root SHA256 '%s'", secret)
1023-
f.write(secret)
1024-
1025-
# This file should be only readable by the process owner, and noone else.
1026-
os.chmod(root_file, stat.S_IRUSR)
1027-
1028-
return secret
1029-
1030-
1031989
def start_server(config_directory, package_data, port, config_sql_server,
1032990
listen_address, force_auth, skip_db_cleanup: bool,
1033991
context, check_env):
@@ -1038,22 +996,17 @@ def start_server(config_directory, package_data, port, config_sql_server,
1038996

1039997
server_addr = (listen_address, port)
1040998

999+
# The root user file is DEPRECATED AND IGNORED
10411000
root_file = os.path.join(config_directory, 'root.user')
1042-
if not os.path.exists(root_file):
1043-
LOG.warning("Server started without 'root.user' present in "
1044-
"CONFIG_DIRECTORY!")
1045-
root_sha = __make_root_file(root_file)
1046-
else:
1047-
LOG.debug("Root file was found. Loading...")
1048-
try:
1049-
with open(root_file, 'r', encoding="utf-8", errors="ignore") as f:
1050-
root_sha = f.read()
1051-
LOG.debug("Root digest is '%s'", root_sha)
1052-
except IOError:
1053-
LOG.info("Cannot open root file '%s' even though it exists",
1054-
root_file)
1055-
root_sha = __make_root_file(root_file)
1056-
1001+
if os.path.exists(root_file):
1002+
LOG.warning("The 'root.user' file: %s"
1003+
" is deprecated and ignored. If you want to"
1004+
" setup an initial user with SUPER_USER permission,"
1005+
" configure the super_user field in the server_config.json"
1006+
" as described in the documentation."
1007+
" To get rid off this warning,"
1008+
" simply delete the root.user file.",
1009+
root_file)
10571010
# Check whether configuration file exists, create an example if not.
10581011
server_cfg_file = os.path.join(config_directory, 'server_config.json')
10591012
if not os.path.exists(server_cfg_file):
@@ -1077,7 +1030,6 @@ def start_server(config_directory, package_data, port, config_sql_server,
10771030
try:
10781031
manager = session_manager.SessionManager(
10791032
server_cfg_file,
1080-
root_sha,
10811033
force_auth)
10821034
except IOError as ioerr:
10831035
LOG.debug(ioerr)
@@ -1098,7 +1050,7 @@ def start_server(config_directory, package_data, port, config_sql_server,
10981050
"Earlier logs might contain additional detailed "
10991051
"reasoning.\n\t* %s", len(fails),
11001052
"\n\t* ".join(
1101-
(f"'{ep}' ({reason})" for (ep, reason) in fails)
1053+
(f"'{ep}' ({reason})" for (ep, reason) in fails)
11021054
))
11031055
else:
11041056
LOG.debug("Skipping db_cleanup, as requested.")

web/server/codechecker_server/session_manager.py

+10-33
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
Handles the management of authentication sessions on the server's side.
1010
"""
1111

12-
import hashlib
1312
import json
1413
import os
1514
import re
@@ -161,13 +160,12 @@ class SessionManager:
161160
CodeChecker server.
162161
"""
163162

164-
def __init__(self, configuration_file, root_sha, force_auth=False):
163+
def __init__(self, configuration_file, force_auth=False):
165164
"""
166165
Initialise a new Session Manager on the server.
167166
168167
:param configuration_file: The configuration file to read
169168
authentication backends from.
170-
:param root_sha: The SHA-256 hash of the root user's authentication.
171169
:param force_auth: If True, the manager will be enabled even if the
172170
configuration file disables authentication.
173171
"""
@@ -199,9 +197,6 @@ def __init__(self, configuration_file, root_sha, force_auth=False):
199197
self.__refresh_time = self.__auth_config['refresh_time'] \
200198
if 'refresh_time' in self.__auth_config else None
201199

202-
# Save the root SHA into the configuration (but only in memory!)
203-
self.__auth_config['method_root'] = root_sha
204-
205200
self.__regex_groups_enabled = False
206201

207202
# Pre-compile the regular expressions of 'regex_groups'
@@ -334,17 +329,16 @@ def get_realm(self):
334329
"error": self.__auth_config.get('realm_error')
335330
}
336331

332+
@property
333+
def get_super_user(self):
334+
return {
335+
"super_user": self.__auth_config.get('super_user'),
336+
}
337+
337338
@property
338339
def default_superuser_name(self) -> Optional[str]:
339340
""" Get default superuser name. """
340-
root = self.__auth_config['method_root'].split(":")
341-
342-
# Previously the root file doesn't contain the user name. In this case
343-
# we will return with no user name.
344-
if len(root) <= 1:
345-
return None
346-
347-
return root[0]
341+
return self.__auth_config['super_user']
348342

349343
def set_database_connection(self, connection):
350344
"""
@@ -365,8 +359,7 @@ def __handle_validation(self, auth_string):
365359
366360
This validation object contains two keys: username and groups.
367361
"""
368-
validation = self.__try_auth_root(auth_string) \
369-
or self.__try_auth_dictionary(auth_string) \
362+
validation = self.__try_auth_dictionary(auth_string) \
370363
or self.__try_auth_pam(auth_string) \
371364
or self.__try_auth_ldap(auth_string)
372365
if not validation:
@@ -387,22 +380,6 @@ def __is_method_enabled(self, method):
387380
'method_' + method in self.__auth_config and \
388381
self.__auth_config['method_' + method].get('enabled')
389382

390-
def __try_auth_root(self, auth_string):
391-
"""
392-
Try to authenticate the user against the root username:password's hash.
393-
"""
394-
user_name = SessionManager.get_user_name(auth_string)
395-
sha = hashlib.sha256(auth_string.encode('utf8')).hexdigest()
396-
397-
if f"{user_name}:{sha}" == self.__auth_config['method_root']:
398-
return {
399-
'username': SessionManager.get_user_name(auth_string),
400-
'groups': [],
401-
'root': True
402-
}
403-
404-
return False
405-
406383
def __try_auth_token(self, auth_string):
407384
if not self.__database_connection:
408385
return None
@@ -562,7 +539,7 @@ def get_db_auth_session_tokens(self, user_name):
562539

563540
def __is_root_user(self, user_name):
564541
""" Return True if the given user has system permissions. """
565-
if self.__auth_config['method_root'].split(":")[0] == user_name:
542+
if self.__auth_config['super_user'] == user_name:
566543
return True
567544

568545
transaction = None

0 commit comments

Comments
 (0)