PlantDB Authentication & Authorization OverviewLink
This guide gives a developer‑oriented walkthrough of the authentication subsystem that lives under
plantdb.commons.auth.
It covers the key modules and shows how they inter‑operate in a typical request cycle.
1. Core building blocksLink
| Module | Responsibility | Key classes / functions |
|---|---|---|
models.py |
Pure data definitions | Permission, Role, User, Group |
manager.py |
CRUD on persisted users/groups (JSON files) | UserManager, GroupManager |
session.py |
Token / session lifecycle | SessionManager, JWTSessionManager, SingleSessionManager |
rbac.py |
Permission resolution / access checks | RBACManager |
All modules share the same
plantdb.commons.log.get_loggerlogger for consistent debugging output.
2. User & Group Persistence (manager.py)Link
2.1 UserManagerLink
- Storage -
users.json(JSON array). File is atomically updated via a temporary file +os.replace. - Password handling - Argon2 (
argon2.PasswordHasher) - never store plain text. - Lock‑out -
max_login_attemptsandlockout_durationconfigure how many failed attempts trigger a temporary lock. - Guest & Admin - On first run a
guestuser is created automatically; anadminaccount is also provisioned (debug‑mode password"admin"- replace in production). - Public API
from plantdb.commons.auth.manager import UserManager
from plantdb.commons.auth.models import Role
um = UserManager(users_file="users.json")
um.create("alice", "Alice Smith", "secret", roles={Role.ADMIN})
assert um.validate("alice", "secret") # login
um.update_password("alice", "secret", "newpass")
2.2 GroupManagerLink
- Storage -
groups.json(JSON object keyed by group name). - Group model -
Group(name, users, description, created_at, created_by). - Operations
from plantdb.commons.auth.manager import GroupManager
gm = GroupManager(groups_file="groups.json")
gm.create_group("admins", creator="alice", users={"alice"})
gm.add_user_to_group("admins", "bob")
gm.get_user_groups("bob") # returns list of Group
3. Session Management (session.py)Link
3.1 SessionManagerLink
- Handles plain token sessions (random string, stored in memory).
- Enforces
max_concurrent_sessions. - Keeps a
sessionsdict mapping token → metadata (username, timestamps,expires_at).
3.2 JWTSessionManagerLink
- Issues JWTs signed with HS512 (
secret_key). - Payload contains:
iss,sub(username),aud,exp,iat,jti.
- On
create_sessionthe JWT is issued and a session record is stored underjtifor concurrent‑limit checks. validate_sessiondecodes & verifies JWT, updateslast_accessed.refresh_sessioninvalidates old token and creates a new one.
Tip: When integrating with a REST API, set the
Authorization: Bearer <jwt>header and calljwt_mgr.validate_session(token)to obtain the user’s identity.
4. Role‑Based Access Control (rbac.py)Link
RBACManageraggregatesUserManagerandGroupManagerto resolve permissions.- Permission lookup - combines user roles, group memberships, and per‑scan metadata.
- Scan access - methods like
can_access_scan,can_modify_scan_ownerconsider owner, sharing groups, and the caller’s role. - Guest user - automatically granted
Role.READERand read‑only permissions.
from plantdb.commons.auth.rbac import RBACManager
from plantdb.commons.auth.models import Permission
rbac_mgr = RBACManager()
user = rbac_mgr.users.get_user("alice")
can_read = rbac_mgr.can_access_scan(user, {}, Permission.READ)
5. Typical authentication flowLink
from requests.exceptions import HTTPError
from plantdb.commons.auth.models import Permission
from plantdb.commons.auth.manager import UserManager
from plantdb.commons.auth.rbac import RBACManager
from plantdb.commons.auth.session import JWTSessionManager
# 1 User logs in
um = UserManager()
if um.validate("username", "password"):
# 2 Issue a JWT
jwt_mgr = JWTSessionManager(secret_key="super_secret")
token = jwt_mgr.create_session("username")
# 3 Protect an endpoint
user_info = jwt_mgr.validate_session(token) # returns dict with 'username', 'jti', etc.
if not user_info:
raise HTTPError(401)
# 4 Authorization check
rbac_mgr = RBACManager()
if not rbac_mgr.can_access_scan(user_info['username'], {}, Permission.READ):
raise HTTPError(403)
# 5 Proceed with the request
6. Key considerations for productionLink
| Topic | Recommendation |
|---|---|
| Secret management | Store JWTSessionManager.secret_key in an environment variable or a secret vault. |
| HTTPS | JWTs should be transmitted over TLS to prevent eavesdropping. |
| Refresh strategy | Use short JWT lifetimes (e.g., 15 min) and refresh_session for seamless user experience. |
| Audit log | Capture login attempts, lock‑out events, and failed permissions for compliance. |
| Group sync | If groups are dynamic (e.g., from LDAP), implement a sync job that updates groups.json. |
| Password policy | Enforce complexity and rotation; Argon2 already provides strong hashing. |
| Scalability | The current file‑based persistence is suitable for prototyping; switch to a database (PostgreSQL, MongoDB) when scaling. |
7. Extending the systemLink
- New roles - Add a value to the
Roleenum and extendRole.permissionsaccordingly. - Custom permissions - Create a new enum entry in
Permissionand grant it via a role or directly to a user. - External auth provider - Replace
UserManager.validatewith a call to an external SSO service; keep the JWT logic unchanged.
8. Quick sanity checkLink
from plantdb.commons.auth.models import Permission
from plantdb.commons.auth.manager import UserManager
from plantdb.commons.auth.rbac import RBACManager
from plantdb.commons.auth.session import JWTSessionManager
# Verify that a fresh JWT is accepted
um = UserManager(users_file="users.json")
if not um.exists("alice"):
um.create("alice", "Alice", "mypwd")
jwt_mgr = JWTSessionManager(secret_key="demo")
token = jwt_mgr.create_session("alice")
assert jwt_mgr.validate_session(token) is not None
# Verify RBAC
rbac = RBACManager()
alice = rbac.users.get_user("alice")
scan = {"owner": "bob", "sharing": ["admins"]} # Example metadata
assert rbac.can_access_scan(alice, scan, Permission.READ) # Depends on role/group
Running this snippet should never raise an exception - it demonstrates that the authentication, session, and RBAC layers are wired together correctly.
9. Final wordsLink
The PlantDB auth stack is intentionally lightweight:
- file‑based persistence → no heavy DB dependencies,
- Argon2 hashing → strong security,
- JWT → stateless, scalable tokens.
Feel free to tweak any of the defaults (session timeout, lock‑out policy, password policy) to fit your deployment.