mirror of
https://github.com/vmario89/fabaccess-users-toml-validator.git
synced 2025-03-12 06:51:42 +01:00
Initial commit
This commit is contained in:
parent
e434b049af
commit
eef619899d
@ -1,2 +1,7 @@
|
||||
# fabaccess-users-toml-validator
|
||||
A repository containing a script to validate a FabAccess BFFH users.toml file
|
||||
|
||||
This script required at least Python 3.11, because it contains the neccesary tomllib library.
|
||||
|
||||
# Usage
|
||||
`python3 validate.py --db users.toml`
|
||||
|
166
validate.py
Normal file
166
validate.py
Normal file
@ -0,0 +1,166 @@
|
||||
'''
|
||||
This script validates users.toml for several aspects
|
||||
The script requires at least Python 3.11
|
||||
|
||||
Written by Mario Voigt (vmario89) - Stadtfabrikanten e.V. - 2024
|
||||
|
||||
ToDos
|
||||
- enter bffh.dhall path to check roles against users.toml. If our toml contains roles, which bffh does not know, we should also warn!
|
||||
'''
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import tomllib
|
||||
import uuid
|
||||
|
||||
'''
|
||||
cardkeys for FabAccess use Uuid format in Version v4 (see https://docs.rs/uuid/latest/uuid/struct.Uuid.html)
|
||||
allowed formattings:
|
||||
- simple: a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8
|
||||
- hyphenated: a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8
|
||||
- urn: urn:uuid:A1A2A3A4-B1B2-C1C2-D1D2-D3D4D5D6D7D8
|
||||
- braced: {a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8}
|
||||
'''
|
||||
def is_valid_uuid(val):
|
||||
try:
|
||||
_uuid = uuid.UUID(val, version=4)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
def main():
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--db", type=str, help="path of users.toml user database file")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.db is None:
|
||||
print("Error: no users.toml given. Please add with '--db </path/to/users.toml>'")
|
||||
sys.exit(1)
|
||||
|
||||
countUsers = 0
|
||||
countUsersWithoutCardkeyOrPassword = 0
|
||||
uniqueRoles = []
|
||||
countUserWithoutRoles = 0
|
||||
countPassword = 0
|
||||
countPasswordUnencrypted = 0
|
||||
countPasswordEncrypted = 0
|
||||
countCardkey = 0
|
||||
countCardkeyInvalid = 0
|
||||
countUnknownKeys = 0
|
||||
|
||||
countWarnings = 0
|
||||
|
||||
#a definition of valid keys within a user section of FabAccess
|
||||
knownKeys = ['roles', 'passwd', 'cardkey']
|
||||
|
||||
usertoml = args.db
|
||||
|
||||
print("{} Checking database {}\n".format("*"*25, "*"*25))
|
||||
|
||||
file_stats = os.stat(usertoml)
|
||||
#print(file_stats)
|
||||
print("Database size: {} Bytes ({:0.5f} MB)".format(file_stats.st_size, file_stats.st_size / (1024 * 1024)))
|
||||
if file_stats.st_size == 0:
|
||||
print("Error: File size is zero! Database is corrupted!")
|
||||
sys.exit(1)
|
||||
|
||||
print("\n")
|
||||
|
||||
with open(usertoml, "rb") as f:
|
||||
try:
|
||||
data = tomllib.load(f)
|
||||
except Exception as e:
|
||||
if "Cannot declare" in str(e) and "twice" in str(e):
|
||||
print("Error: found at least one duplicate user. Cannot parse database. Please fix and try again. Message: {}".format(str(e)))
|
||||
elif "Invalid value" in str(e):
|
||||
print("Error: Some user contains a key without value (e.g. 'passwd = '). Cannot parse database. Please fix and try again. Message: {}".format(str(e)))
|
||||
elif "Expected '=' after a key" in str(e):
|
||||
print("Error: Found an incorrect key/value mapping. Cannot parse database. Please fix and try again. Message: {}".format(str(e)))
|
||||
else:
|
||||
print(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
for user in data:
|
||||
print("--- {}".format(user))
|
||||
|
||||
for key in data[user].keys():
|
||||
if key not in knownKeys:
|
||||
print("Warning: User '{}' contains unknown key '{}' (will be ignored by BFFH server)".format(user, key))
|
||||
countWarnings += 1
|
||||
countUnknownKeys += 1
|
||||
|
||||
if "roles" in data[user]:
|
||||
roles = data[user]["roles"]
|
||||
if type(roles) != list:
|
||||
print("Warning: roles for user '{}' are not defined as array! BFFH will fail to load".format(user))
|
||||
countWarnings += 1
|
||||
for role in roles:
|
||||
if role not in uniqueRoles:
|
||||
uniqueRoles.append(role)
|
||||
if roles is None: #if role key is defined but empty
|
||||
countUserWithoutRoles += 1
|
||||
else: #if role key is not existent
|
||||
countUserWithoutRoles += 1
|
||||
|
||||
if "passwd" in data[user]:
|
||||
passwd = data[user]["passwd"]
|
||||
countPassword += 1
|
||||
if passwd.startswith("$argon2") is False:
|
||||
print("Warning: Password for user '{}' is not encrypted!".format(user))
|
||||
countWarnings += 1
|
||||
countPasswordUnencrypted += 1
|
||||
else:
|
||||
countPasswordEncrypted += 1
|
||||
|
||||
if "cardkey" in data[user]:
|
||||
cardkey = data[user]["cardkey"]
|
||||
if is_valid_uuid(cardkey) is False:
|
||||
print("Warning: Cardkey for user '{}' contains invalid cardkey (no UUID v4)".format(user))
|
||||
countCardkeyInvalid += 1
|
||||
countWarnings += 1
|
||||
|
||||
countCardkey += 1
|
||||
|
||||
if "passwd" not in data[user] and "cardkey" not in data[user]:
|
||||
countUsersWithoutCardkeyOrPassword += 1
|
||||
|
||||
countUsers += 1
|
||||
print("\n")
|
||||
|
||||
print("\n")
|
||||
|
||||
if countUsers == 0:
|
||||
print("Error: Database does not contain any users!")
|
||||
sys.exit(1)
|
||||
|
||||
print("{} Database statistics {}\n".format("*"*25, "*"*25))
|
||||
print("- Total users: {}".format(countUsers))
|
||||
print("- Total unique roles: {}".format(len(uniqueRoles)))
|
||||
print("- Total passwords: {} (encrypted: {}, unencrypted: {})".format(countPassword, countPasswordEncrypted, countPasswordUnencrypted))
|
||||
print("- Total cardkeys: {}".format(countCardkey))
|
||||
|
||||
print("\n")
|
||||
|
||||
print("{} Important information {}\n".format("*"*25, "*"*25))
|
||||
if countUnknownKeys > 0:
|
||||
print("- {} unknown keys (will be ignored by BFFH server)".format(countUnknownKeys))
|
||||
|
||||
if countUserWithoutRoles > 0:
|
||||
print("- {} users without any roles. They won't be able to do something as client!".format(countUserWithoutRoles))
|
||||
|
||||
if len(uniqueRoles) == 0:
|
||||
print("- Globally, there are no roles assigned for any user. They won't be able to do something as client!")
|
||||
|
||||
if countCardkeyInvalid > 0:
|
||||
print("- {} invalid cardkeys in your database. They won't be able to authenticate at BFFH server by keycard!".format(countCardkeyInvalid))
|
||||
|
||||
if countUsersWithoutCardkeyOrPassword > 0:
|
||||
print("- {} users without both: password and cardkey. They won't be able to login anyhow!".format(countUsersWithoutCardkeyOrPassword))
|
||||
|
||||
if countWarnings > 0:
|
||||
print("- {} warnings in total. You might need to optimize your user database!".format(countWarnings))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
x
Reference in New Issue
Block a user