User identity: tiny-pacs-identity
tiny-pacs-identity authenticates incoming associations. User accounts
live in the database; when an association arrives, the request is checked
against the calling device’s identity policy using the DICOM User Identity
negotiation sub-item (PS3.7 D.3.3.7).
The extension contributes two components:
Users— keeps user accounts (salted password hashes, an active flag and last-login bookkeeping) in the database;UserIdentityAuth— enforces the identity policies on incoming associations.
It depends on tiny-pacs-admin: the identity policy is part of the
device record (see Administration: tiny-pacs-admin), so installing
tiny-pacs-identity pulls the device registry in automatically.
Installation
pip install tiny-pacs-identity
or with the convenience extra:
pip install tiny_pacs[identity]
The effect is identical. From PyPI the extras pull the published extension distributions; when installing the core from the repository they resolve against the bundled extension packages — see Installation for both routes.
Installing the extension never changes server behaviour: both components stay disabled until enabled in the configuration.
Bootstrap: create users before enforcing identity
Users are managed offline with the users CLI subcommands against the
configured database — there is no implicit “first user” magic. Create the
accounts before starting a server configuration that requires
identity, otherwise every association needing that user is rejected:
tiny-pacs users add alice
Password:
Repeat password:
Added user alice
Passwords are prompted with getpass and are never accepted as command
line arguments. The other commands:
tiny-pacs users list
tiny-pacs users passwd alice
tiny-pacs users remove alice
tiny-pacs users set-active alice --inactive
tiny-pacs users set-active alice --active
Deactivating a user locks the account out immediately without deleting
it; users list shows the accounts (never password hashes). Every
subcommand accepts the shared -c/--config flags and needs the same
persistent database configuration as the other admin commands — for
SQLite set db_name and mode: rwc on the Database component.
Configuration
Enable both components and tune the fallback policies:
components:
Users:
on: true
UserIdentityAuth:
on: true
default_policy: none # known devices without a policy
unknown_device_policy: none # none | username | password | reject
Policy resolution order
When an association arrives, the effective policy is resolved in this order:
the calling device’s own
identitypolicy, when the device is known and carries one (set per device, see below);default_policy, when the device is known but has no policy of its own (e.g. a YAML device of the built-inDevicescomponent that omits the field);unknown_device_policy, when no device registry knows the calling AE title — orrejectrefuses such associations outright.
A “require identity everywhere” deployment is expressed without any
extra switch: default_policy: password plus
unknown_device_policy: reject.
Per-device policy
The policy is part of the device record, configured either in YAML:
components:
Devices:
on: true
devices:
SOME_MODALITY:
aet: SOME_MODALITY
address: 10.0.0.5
port: 104
identity: password
or in the database through the admin CLI:
tiny-pacs devices add MRI_01 --address 10.0.0.20 --identity password
tiny-pacs devices update MRI_01 --identity username
Enforcement matrix
The effective policy crossed with what the association presents:
Policy |
No identity |
Type 1 (username) |
Type 2 (user + password) |
Types 3–5 (Kerberos, SAML, JWT) |
|---|---|---|---|---|
|
accept |
accepted if the user exists and is active [1] |
accepted if the credentials are valid [1] |
rejected |
|
reject |
accepted if the user exists and is active |
accepted if the password is valid |
rejected |
|
reject |
reject |
accepted if the password is valid |
rejected |
|
reject |
reject |
reject |
rejected |
A rejected association receives an A-ASSOCIATE-RJ and — because the authentication runs before the device registries — is never auto-added as a device either. Kerberos, SAML and JWT identities (types 3–5) are not supported in this version and are rejected under every policy.
Auto-add defaults
When DeviceStore auto-add is enabled, unknown devices that pass
authentication are registered with its default_identity policy, which
then governs their next association. That default and the auth
component’s unknown_device_policy therefore describe the same
devices and should be configured consistently; UserIdentityAuth logs
a warning at startup when they disagree. With auto-add disabled,
unknown_device_policy alone governs.
components:
DeviceStore:
on: true
default_identity: username # policy of auto-added devices
Devices:
on: true
auto_add: false
UserIdentityAuth:
on: true
unknown_device_policy: username
Outgoing identity
tiny_pacs can also act as a requestor presenting identity: a device’s
username and password fields are forwarded to the association
layer for outgoing connections (C-MOVE sub-operations, Storage
Commitment, or a plain DICOMClient).
Security
Warning
User Identity negotiation transmits credentials inside A-ASSOCIATE.
Enable the existing TLS support (ae.tls) whenever non-none
identity policies are used:
ae:
tls:
certificate: /etc/tiny_pacs/server.pem
key: /etc/tiny_pacs/server.key
Additional guarantees:
user passwords are stored salted and hashed (
scrypt, with aPBKDF2fallback); verification compares in constant time;rejected associations never reveal through the response whether the user or the password was wrong;
the CLI prompts for passwords and never logs or accepts them as arguments.
Reference implementation
Like Administration: tiny-pacs-admin, tiny-pacs-identity doubles as a
reference implementation of the extension contract described in
Writing a third-party extension: components published through tiny_pacs.components
(with their own tables and Migrations) and a
CLI subcommand published through tiny_pacs.cli.