17.3.1. The database model

class privacyidea.models.Admin(**kwargs)[source]

The administrators for managing the system. To manage the administrators use the command pi-manage.

In addition, certain realms can be defined to be administrative realms.

Parameters:
  • username (basestring) – The username of the admin

  • password (basestring) – The password of the admin (stored using PBKDF2, salt and pepper)

  • email (basestring) – The email address of the admin (not used at the moment)

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.Audit(**kwargs)[source]

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.AuthCache(username, realm, resolver, authentication, first_auth=None, last_auth=None)[source]
class privacyidea.models.AuthenticationLog(**kwargs)[source]

Append-only log of authentication events: every authenticated HTTP request produces a row, and normally exactly one (a push_wait request writes two, since it triggers the challenge and awaits the answer within the one request). Several rows may share a transaction_id to correlate the multiple requests of one logical authentication attempt (e.g. a challenge trigger and its later response) at query time. Rows of one logical attempt - including a multi-challenge flow where answering one challenge triggers another - share an attempt_id; ordering an attempt’s rows by (timestamp, id) reconstructs the full chain, and each row’s own transaction_id still links back to the challenge table.

timestamp leads that order rather than id, and carries microsecond precision for it, because id is a plain autoincrement: it is the insertion order of a single writer only. A multi-master cluster (Galera with wsrep_auto_increment_control) gives each node its own offset and applies remote writes asynchronously, so a row committed later on one node can carry a lower id than one committed earlier on another - which would let a counted failure vanish or a counter reset be missed. timestamp is generated by the application, so it orders across writer nodes; id stays the tiebreaker and is exact for the rows of one flush. The residual limit is the clock offset between privacyIDEA instances: events that fall within it are ordered by id, i.e. arbitrarily - which is no worse than they are distinguishable.

The client of a request is recorded as what was decided and how it was decided:

  • source_ip is the effective client IP - the one authorization is evaluated against and the only one the conditional-access engine counts and enforces on. It is unchanged by the provenance columns below.

  • peer_ip is the TCP peer the request actually arrived from (request.remote_addr), which is the same address unless a proxy mapping moved it. It is indexed, because “what came from this machine” is the second question a forensic query asks.

  • source_ip_source names where source_ip was taken from, as a ClientIpSource value. REMOTE_ADDR_UNMAPPED is deliberately distinct from REMOTE_ADDR: the peer was used despite an OverrideAuthorizationClient being configured.

  • ip_chain is the whole path that was considered, recorded even when no override is configured and the X-Forwarded-For header is therefore ignored - recording what a request claimed is not trusting it. Everything past peer_ip is client-supplied and must never gate a decision.

  • client_label is the client_id parameter or the User-Agent, and client_label_source says which.

NULL in any of the four provenance columns means the row predates them, and nothing may be inferred from it - in particular a NULL source_ip_source must never be rendered as “direct connection”.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

property aware_timestamp: datetime

Return timestamp as a timezone-aware UTC datetime.

The column itself is stored as a naive datetime because timezone-aware DateTime columns are not portable across all supported databases (they are ignored or handled differently per backend). We therefore store UTC and re-attach the timezone on read.

to_dict(include_outcomes: bool = False, include_reasons: bool = False) → dict[source]

Serialize the entry for the API response, with the timestamp as an ISO-8601 UTC string.

include_outcomes adds the conditional-access history of this request as conditional_access_outcomes. It is off by default and only the paginated listing turns it on, because the relationship is lazy="raise": reading it on an entry that was not loaded with its outcomes must fail loudly rather than emit a query per entry.

include_reasons adds the classified reasons as the reasons list, in the order they were recorded, under the same rule and for the same reason: that relationship is lazy="raise" too.

class privacyidea.models.AuthenticationLogReason(**kwargs)[source]

Why an authentication-log row came out the way it did: one row per AuthEventReason the request produced, in the order the vocabulary declares them (see order_request_reasons()) as their ids ascend.

A request rarely fails for exactly one reason - a user with three tokens can have one revoked, one past its failcount and one that simply got the wrong OTP - so the reasons are a list, and no reason is picked over another: each is recorded and each is filterable on its own. Kept in a table of its own rather than as a column on the parent for the reason the parent’s column existed at all: “every NO_USABLE_TOKEN caused by the failcounter” has to be a plain indexed predicate, and neither a separator-joined string (LIKE scans, a length limit) nor a JSON array (predicates differ per backend) offers one. What is specific to one request - the deciding policy’s name, which serial failed for which reason - still stays in the parent’s other_info.

Like ConditionalAccessOutcome, a row belongs to its authentication_log row (auth_log_id, a real foreign key that cascades), is only ever read together with it - via selectinload, never a JOIN, which would multiply the parent row and break both LIMIT and the pagination count - and repeats nothing the parent already holds.

Deliberately without MethodsMixin, for the reason the outcome model is: the mixin’s save()/delete() commit db.session, while these rows are written on the conditional-access session so that a failure to record them cannot roll back the request’s own work.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.BlockList(**kwargs)[source]

A blocked source identity (currently a source IP), written by the BLOCK_IP conditional-access action and consulted by the authentication pre-check on the next inbound request — exactly the live-state pattern of UserLockState, but keyed by the request’s source IP rather than by the user.

The IP is the natural primary key. block_expires_at is the load-bearing field: a row exists only while the IP is blocked, and a row whose block_expires_at lies in the future means the IP is currently blocked, while a NULL value means a permanent block (only an admin reset, which deletes the row, clears it). Timestamps are naive UTC, see utc_now().

Like UserLockState the row records the block itself, not which policy produced it; that is the conditional-access history (ConditionalAccessOutcome). It does record block_cause, i.e. whether a policy or an administrator imposed the block now in force.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.CAConnector(name, catype)[source]

The table “caconnector” contains the names and types of the defined CA connectors. Each connector has a different configuration, that is stored in the table “caconnectorconfig”.

class privacyidea.models.CAConnectorConfig(caconnector_id=None, Key=None, Value=None, caconnector=None, Type='', Description='')[source]

Each CAConnector can have multiple configuration entries. Each CA Connector type can have different required config values. Therefore, the configuration is stored in simple key/value pairs. If the type of config entry is set to “password” the value of this config entry is stored encrypted.

The config entries are referenced by the id of the resolver.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.Challenge(**kwds)[source]

Table for handling of the generic challenges.

property data

Return the decrypted challenge data as a JSON string, or empty string.

property encrypted_data: str

Return the stored ciphertext of data, without decrypting it.

For a caller that has to persist the very same value somewhere else - the Redis cache stores this challenge instead of the row - so that it does not have to decrypt and re-encrypt what is already encrypted here.

get(timestamp=False)[source]

return a dictionary of all vars in the challenge class

Parameters:

timestamp (bool) – if true, the timestamp will be returned in a readable format like “2014-11-29 21:56:43.057293”

Returns:

dict of vars

get_data() → dict[source]

Get the decrypted challenge data as a dict.

Returns:

The challenge data as a dict. Returns {} if no data is stored.

Return type:

dict

get_otp_status()[source]

This returns how many OTPs were already received for this challenge. and if a valid OTP was received.

Returns:

tuple of count and True/False

Return type:

tuple

is_open() → bool[source]

Returns true if the challenge can still be answered: it is not expired, has not been answered yet, and has not been refused (declined or cancelled). This is the guard the answering endpoints use before mutating a challenge.

Returns:

True if the challenge is still open

Return type:

bool

is_valid()[source]

Returns true, if the expiration time has not passed, yet.

Returns:

True if valid

Return type:

bool

set_data(data: dict | None)[source]

Set the internal data of the challenge. The data is encrypted before being stored in the database since it may contain OTP values.

Parameters:

data – The challenge data. Must be a dict (or None/empty).

class privacyidea.models.Client(**kwargs)[source]

The clients table holds API clients that authenticate against privacyIDEA with an API key (sent in the X-API-Key header). Each client represents an external integration such as a Windows credential provider, a Keycloak plugin or an Entra ID connector.

An API key has the form <prefix>_<key_id>_<secret>. The key_id is a non-secret, unique identifier that is stored in plaintext and used to look up the client; only the secret half is hashed into key_hash. This way the plaintext key is never stored, the lookup is a direct indexed match on key_id (no scanning), and the publicly shown key_id reveals nothing about the secret.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.ClientApplication(**kwargs)[source]

This table stores the clients, which sent an authentication request to privacyIDEA. This table is filled automatically by authentication requests.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.ClientStatus(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]

The states a client may be in. Only ACTIVE clients can authenticate; SUSPENDED is a reversible off-switch. Permanent removal is a delete, and a compromised key is handled by rotation - so there is deliberately no separate “revoked” state.

Inherits from str so members compare equal to their plain string value (e.g. ClientStatus.ACTIVE == "active") and serialise to it in JSON.

class privacyidea.models.ConditionalAccessOutcome(**kwargs)[source]

History of what conditional access did: one row per action the engine executed for one request, plus dry-run rows recording what a dry-run policy would have done.

This is the queryable counterpart of the live state in user_lock_state and block_list, which show the restriction currently in force and then forget it. Only this table can answer “when was this user locked, by which policy, and for how long”.

Each row belongs to the authentication_log row of the request that caused it (auth_log_id, mandatory) and is only ever read together with it. The table therefore holds only what it alone knows: the subject (resolver, uid, realm, username, source IP) and when it happened are on the parent row and are not repeated here. Using the parent’s timestamp is exact enough by construction - the engine evaluates a request against a single reference instant, so every outcome of one request shares it, a few milliseconds after the row itself was written.

“Read together with” is a batched second SELECT (selectinload), not a JOIN: the log listing pages the parent rows and then fetches WHERE auth_log_id IN (<the page's ids>). A join would multiply each parent row by its outcomes and break both LIMIT and the pagination count.

Nothing on the authentication path ever reads this table - the engine counts over authentication_log and writes outcomes without reading them back - so the relationship that exposes them is declared lazy="raise" and only the paginated log query opts in.

The policy is identified by name only, denormalized, with no id and no foreign key: an outcome must survive the deletion of the policy that produced it, and a stored policy_id could not be acted on afterwards. Ids are not reused on PostgreSQL or Oracle (a sequence), but SQLite hands out max(rowid)+1 and MySQL/MariaDB recompute the AUTO_INCREMENT counter as max(id)+1 on restart (MySQL persists it only from 8.0), so a deleted policy’s id can turn up on a different policy - and finding the logged id on a policy with another name is indistinguishable from a rename, which is the one case an id was worth keeping for. Matching on the name an admin chose can at worst hit a policy deliberately recreated under that name; matching on a recycled surrogate key attributes history to a policy that never produced it.

Whether the policy still exists is therefore a lookup by name (unique on conditional_access_policies). auth_log_id, in contrast, is a real foreign key and cascades: the history of a request is deleted with the request.

There is deliberately no stage id: update_conditional_access_policy() replaces a policy’s stages as a whole, so every edit gives them fresh ids and a stored one would dangle. The stage is identified by its natural key instead: a stage is unique per policy by its threshold (uq_ca_stage_policy_threshold) and that survives edits, and the threshold is what a human reads anyway.

Everything a triggered stage knows is therefore mandatory - policy_name, threshold, event_count - because the engine always has all three when it records an outcome. Only two columns are nullable, each meaning something: stage_name (the admin never named the stage) and info (the action had nothing of its own to record).

The string columns use case_sensitive_unicode() for the same reason the authentication log does: matching must behave identically on every backend. MySQL/MariaDB’s server-default collation is typically case-insensitive (*_ci) while SQLite, PostgreSQL and Oracle compare case-sensitively, so an unpinned column would make action_type == "lock_user" match on one database and not on another.

Deliberately without MethodsMixin, unlike its sibling models: the mixin’s save() and delete() commit db.session, and an outcome must be written on the conditional-access session so that a failure to record history cannot roll back the request’s own work. Rows are created by record_outcomes() and removed with their parent by the authentication log’s delete paths; not offering the two methods is cheaper than documenting that they are wrong.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

to_dict() → dict[source]

Serialize the outcome for the API response.

Every column is emitted, including the ones the WebUI does not display (info, event_count): what to show is the view’s decision, and a client querying the history needs the rest. There is no timestamp of its own - the entry this is nested under carries it.

class privacyidea.models.ConditionalAccessPolicy(**kwargs)[source]

Container for a set of conditional-access lock rules.

A policy defines which failure counter(s) to track (e.g. MFA_FAIL, PASSWORD_FAIL) within a sliding time window. The tracked types live in the related ConditionalAccessPolicyCounterType rows; counter_types_to_track is the list-of-strings view over them used throughout the code and tests (assignable as a plain list). Their events are counted together (a single combined count over all listed types) against the stage thresholds, and reset_on_success decides whether a completed login clears what has been counted so far. Admins can define multiple policies (e.g. “Admin Policy” vs “Default User Policy”); policies are evaluated by ascending priority (a lower number means higher precedence, matching privacyIDEA’s policy engine). The priority is unique across policies so the evaluation order - and thus which policy wins an allow/deny decision - is always unambiguous.

The actual thresholds and reactions live in the related ConditionalAccessPolicyStage and ConditionalAccessStageAction rows, and the related ConditionalAccessPolicyCondition rows restrict to whom the policy applies at all.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.ConditionalAccessPolicyCondition(**kwargs)[source]

One restriction on which requests a ConditionalAccessPolicy applies to, e.g. “only for users of the realms sales and support” or “not for admins”.

This is the applicability axis, orthogonal to the counting one: conditions decide whether a policy is evaluated for a request, while the counter types, the count mode and the stage thresholds decide what trips it. Keeping them separate is what lets the two compose - a policy can restrict itself to a realm and still require a threshold of failures.

All of a policy’s conditions must match (AND); a policy with no conditions applies to everyone, which is why adding this table leaves existing policies behaving exactly as before.

The row is deliberately generic so a new kind of condition is a new condition_type in the registry rather than a schema change:

  • condition_type names what to read from the request context (e.g. USER_REALM, USER_ROLE). A policy carries at most one condition of each type, enforced by the unique constraint: two conditions on the same value could only narrow to a contradiction, since they are ANDed.

  • operator names how to compare, and determines the shape of value: a list for the set-membership operators (IN / NOT_IN), a scalar for future comparison operators, NULL for future zero-arity ones.

  • value is JSON rather than a normalized child table because nothing filters on it in SQL - conditions are evaluated in Python against the request context - so normalizing would only add a join.

A condition type with an open key space (an HTTP header name, a userinfo attribute) would need a key column alongside condition_type, and the unique constraint widened to include it. None of the types defined today is keyed, so the column is deliberately absent rather than carried empty; adding it later is a column plus a constraint rebuild.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.ConditionalAccessPolicyCounterType(**kwargs)[source]

One failure counter type tracked by a ConditionalAccessPolicy, normalized out of the former counter_types_to_track JSON column.

A policy has one row here per tracked AuthEventType value. Keeping the types in their own indexed table lets the authentication hot path select just the policies that track the current event type with a single equality filter, instead of loading every enabled policy and filtering the JSON list in Python (which grew the per-request DB work with the total policy count).

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.ConditionalAccessPolicyStage(**kwargs)[source]

A failure threshold within a ConditionalAccessPolicy. Each policy has N stages (e.g. 5, 10 and 15 failures).

Post-response, each of a stage’s actions fires when the failure count reaches the stage’s failure_threshold (see ConditionalAccessStageAction.retrigger_above_threshold for the per-action fire-once vs re-trigger choice); escalation is expressed as separate stages per threshold. Stages are evaluated by descending failure_threshold, so the most severe matching stage is the one that acts (and, on the pre-auth path, the one that supplies the DENY verdict). The threshold is unique per policy, so that order is total and needs nothing else configured.

A threshold counts failures and therefore starts at 1; the CRUD layer allows 0 only on a stage whose every action is DENY, the unconditional lockdown idiom.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.ConditionalAccessStageAction(**kwargs)[source]

What to do when a ConditionalAccessPolicyStage is triggered. One stage can have multiple actions (e.g. lock the user and email the admin).

action_value is the action-specific payload, stored as JSON: the restriction duration in seconds for LOCK_USER/BLOCK_IP, the SMTP settings object (smtp_identifier, subject, body, …) for EMAIL_ADMIN/EMAIL_USER, and nothing at all for the PERMANENT_* restrictions and the DENY decision. The authoritative per-action contract - what the engine reads, and what the write path therefore rejects - is _ACTION_VALUE_VALIDATORS.

retrigger_above_threshold controls how this action fires as the failure count crosses its stage’s threshold. False: fire once, when the count equals the threshold exactly. True: keep firing while the count stays at or above the threshold and below the next stage’s, so escalation ends it for good (the classic re-triggering lock; de-dup still throttles repeats within one incident). Because it is per action, one stage can e.g. email once at its threshold while re-triggering the user lock. The CRUD layer picks an action-aware default when the client omits it (the DENY decision defaults to True, the lock/email/block effects to False); see _validate_stages() and _action_fires().

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.Config(Key, Value, Type='', Description='')[source]

The config table holds all the system configuration in key value pairs. Additional configuration for realms, resolvers and machine resolvers is stored in specific tables.

class privacyidea.models.CustomUserAttribute(user_id, resolver, realm_id, Key, Value, Type=None)[source]

The table “customuserattribute” is used to store additional, custom attributes for users.

A user is identified by the user_id, the resolver_id and the realm_id.

The additional attributes are stored in Key and Value. The Type can hold extra information like e.g. an encrypted value / password.

Note: Since the users are external, i.e. no objects in this database,

there is no logic reference on a database level. Since users could be deleted from user stores without privacyIDEA realizing that, this table could pile up with remnants of attributes.

Create a new customuserattribute for a user tuple

class privacyidea.models.EventCounter(name, value=0, node='')[source]

This table stores counters of the event handler “Counter”.

Note that an event counter name does not correspond to just one, but rather several table rows, because we store event counters for each privacyIDEA node separately. This is intended to improve the performance of replicated setups, because each privacyIDEA node then only writes to its own “private” table row. This way, we avoid locking issues that would occur if all nodes write to the same table row.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.EventHandler(name, event, handlermodule, action, condition='', ordering=0, options=None, id=None, conditions=None, active=True, position='post', abort_on_error=False)[source]

This model holds the list of defined events and actions of this eventhandler. A handler module can be bound to an event with the corresponding condition and action.

get()[source]

Return the serialized eventhandler object including the options

Returns:

complete dict

Rytpe:

dict

class privacyidea.models.EventHandlerCondition(eventhandler_id, Key, Value, comparator='equal')[source]

Each EventHandler entry can have additional conditions according to the handler module

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.EventHandlerOption(eventhandler_id, Key, Value, Type='', Description='')[source]

Each EventHandler entry can have additional options according to the handler module.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.InternalUserAttribute(user_id, resolver, realm_id, Key, Value=None, node=None)[source]

Generic key-value store for internal per-user state written by privacyIDEA itself (not by admins or resolvers).

Use this table for things like cached FIDO2 user IDs or the last-used token type per client. The customuserattribute table is reserved for admin/resolver-facing attributes that may be referenced from policy conditions; do not put internal state there.

Values are stored as JSON, so a single row can hold a structured dict instead of synthesizing keys with prefixes.

class privacyidea.models.MachineResolver(name, rtype)[source]

This model holds the definition to the machinestore. Machines could be located in flat files, LDAP directory or in puppet services or other…

The usual MachineResolver just holds a name and a type and a reference to its config

class privacyidea.models.MachineResolverConfig(resolver_id=None, Key=None, Value=None, resolver=None, Type='', Description='')[source]

Each Machine Resolver can have multiple configuration entries. The config entries are referenced by the id of the machine resolver

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.MachineToken(**kwds)[source]

The MachineToken assigns a Token and an application type to a machine. The Machine is represented as the tuple of machineresolver.id and the machine_id. The machine_id is defined by the machineresolver.

This can be an n:m mapping.

class privacyidea.models.MachineTokenOptions(machinetoken_id, key, value)[source]

This class holds an Option for the token assigned to a certain client machine. Each Token-Clientmachine-Combination can have several options.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.MetricAggregate(**kwargs)[source]

Rolling counter / histogram bucket for a metric in a 5-minute window.

For counters only count is used. For histograms count, sum_value, max_value and the bucket_le_* columns hold the sample distribution. Buckets follow the prom-style _bucket{le="..."} convention: each column stores the count of observations whose value is <= that boundary (inclusive, cumulative).

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.MonitoringStats(timestamp, key, value)[source]

This is the table that stores measured, arbitrary statistic points in time. This could be used to store time series but also to store current values, by simply fetching the last value from the database.

class privacyidea.models.NodeName(**kwargs)[source]

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.PasswordReset(**kwds)[source]

Table for handling password resets. This table stores the recoverycodes sent to a given user The application should save the HASH of the recovery code. Just like the password for the Admins the application shall salt and pepper the hash of the recoverycode. A database admin will not be able to inject a rogue recovery code. A user can get several recoverycodes. A recovery code has a validity period Optional: The email to which the recoverycode was sent, can be stored.

class privacyidea.models.PeriodicTask(name, active, interval, node_list, taskmodule, ordering, options=None, id=None, retry_if_failed=True)[source]

This class stores tasks that should be run periodically.

Parameters:
  • name – Unique name of the periodic task as Unicode

  • active – a boolean

  • retry_if_failed

  • interval – a Unicode specifying the periodicity of the task

  • node_list – a list of unicodes, denoting the node names that should execute that task. If we update an existing PeriodicTask entry, PeriodicTaskLastRun entries referring to nodes that are not present in node_list anymore will be deleted.

  • taskmodule – a Unicode

  • ordering – an integer. Lower tasks are executed first.

  • options – a dictionary of options, mapping Unicode keys to values. Values will be converted to Unicode. If we update an existing PeriodicTask entry, all options that have been set previously but are not present in options will be deleted.

  • id – the ID of an existing entry, if any

property aware_last_update

Return self.last_update with attached UTC tzinfo

get()[source]

Return the serialized periodic task object including the options and last runs. The last runs are returned as timezone-aware UTC datetimes.

Returns:

complete dict

class privacyidea.models.PeriodicTaskLastRun(periodictask_id, node, timestamp)[source]

Each PeriodicTask entry stores, for each node, the timestamp of the last successful run.

Parameters:
  • periodictask_id – ID of the periodic task we are referring to

  • node – Node name as unicode

  • timestamp – Time of the last run as a datetime. A timezone must not be set! We require the time to be given in UTC.

property aware_timestamp

Return self.timestamp with attached UTC tzinfo

class privacyidea.models.PeriodicTaskOption(periodictask_id, key, value)[source]

Each PeriodicTask entry can have additional options according to the task module.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.Policy(name, active=True, scope='', action='', realm='', adminrealm='', adminuser='', resolver='', user='', client='', time='', pinode='', priority=1, check_all_resolvers=False, conditions=None, user_case_insensitive=False, user_agents=None)[source]

The policy table contains the policy definitions.

The Policies control the behaviour in the scopes
  • enrollment

  • authentication

  • authorization

  • administration

  • user actions

  • webui

get(key=None)[source]

Either returns the complete policy entry or a single value :param key: return the value for this key :type key: string :return: complete dict or single value :rytpe: dict or value

get_conditions_tuples()[source]
Returns:

a list of 5-tuples (section, key, comparator, value, active).

get_policy_description()[source]
class privacyidea.models.PolicyCondition(section, Key, comparator, Value, active=True, handle_missing_data=None)[source]
as_tuple()[source]
Returns:

the condition as a tuple (section, key, comparator, value, active, handle_missing_data)

class privacyidea.models.PolicyDescription(object_id, name='', object_type='', description='')[source]

The description table is used to store the description of policy

class privacyidea.models.PrivacyIDEAServer(**kwargs)[source]

This table can store remote privacyIDEA server definitions

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.RADIUSServer(**kwargs)[source]

This table can store configurations of RADIUS servers. https://github.com/privacyidea/privacyidea/issues/321

It saves * a unique name * a description * an IP address a * a Port * a secret * timeout in seconds (default 5) * retries (default 3)

These RADIUS server definitions can be used in RADIUS tokens or in a radius passthru policy.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.Realm(**kwds)[source]

The realm table contains the defined realms. User Resolvers can be grouped to realms. This very table contains just contains the names of the realms. The linking to resolvers is stored in the table “resolverrealm”.

class privacyidea.models.RememberedDevice(**kwds)[source]

A persistent (“remember this device”) authentication device bound to a specific API client.

The device implements a rotating-token scheme: the client stores a cookie of the form series_id:counter. On every use the counter is incremented both in the cookie and in this row. If a request presents the correct series_id but a stale counter, the token has been replayed (the cookie was stolen), the whole series is deleted and authentication fails.

The cookie never contains the API key; the binding to the client is stored server-side in client_id and checked against g.client_id.

The remembered user is bound by the resolver-stable identity (resolver, user_id, realm_id) - not by the login name. The login is a mutable, reusable label in an external user store: binding to it would drop the remembered device on a rename and, worse, could recognise a different account that later reuses a freed login. user_id is therefore the resolver’s immutable id (as in TokenOwner), and realm_id is a foreign key so a deleted realm cascades its devices away.

The composite (client_id, resolver, user_id, realm_id) index serves the two lookups done on every remember-device request: get_valid_device() (cookie validation) and count_user_devices() (the per-user device cap at issuance), both of which filter on all four columns at once. At utf8mb4 this composite is 1908 bytes, under MySQL’s 3072-byte index-key limit, so - unlike usersetting’s subject_hash - no hashed column is needed here. The individual single-column indexes stay: they are still required by revoke_devices(), the client-independent bulk revoke that filters by realm/user without a client_id.

The composite (client_id, created_at) index serves the paginated, newest-first listing in get_client_devices(): (client_id, resolver, user_id, realm_id) above cannot help that query order rows by created_at, so without this a client with a very large number of devices would need every one of its rows collected and sorted before a single page could be returned.

class privacyidea.models.Resolver(name, rtype)[source]

The table “resolver” contains the names and types of the defined User Resolvers. As each Resolver can have different required config values the configuration of the resolvers is stored in the table “resolverconfig”.

class privacyidea.models.ResolverConfig(resolver_id=None, Key=None, Value=None, resolver=None, Type='', Description='')[source]

Each Resolver can have multiple configuration entries. Each Resolver type can have different required config values. Therefore, the configuration is stored in simple key/value pairs. If the type of a config entry is set to “password” the value of this config entry is stored encrypted.

The config entries are referenced by the id of the resolver.

class privacyidea.models.ResolverRealm(resolver_id=None, realm_id=None, resolver_name=None, realm_name=None, priority=None, node_uuid=None, node_name=None)[source]

This table stores which Resolver is located in which realm This is a N:M relation

class privacyidea.models.SMSGateway(identifier, providermodule, description=None)[source]

This table stores the SMS Gateway definitions. See https://github.com/privacyidea/privacyidea/wiki/concept:-Delivery-Gateway

It saves the * unique name * a description * the SMS provider module

All options and parameters are saved in other tables.

delete()[source]

When deleting an SMS Gateway we also delete all the options.

class privacyidea.models.SMSGatewayOption(gateway_id, Key, Value, Type=None, Encrypted=False)[source]

This table stores the options, parameters and headers for an SMS Gateway definition.

Create a new gateway_option for the gateway_id

class privacyidea.models.SMTPServer(**kwargs)[source]

This table can store configurations for SMTP servers. Each entry represents an SMTP server. EMail Token, SMS SMTP Gateways or Notifications like PIN handlers are supposed to use a reference to a server definition. Each Machine Resolver can have multiple configuration entries. The config entries are referenced by the id of the machine resolver

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

get()[source]
Returns:

the configuration as a dictionary

class privacyidea.models.Serviceid(**kwds)[source]

The serviceid table contains the defined service IDs. These service ID describe services like “webservers” or “dbservers” which e.g. request SSH keys from the privacyIDEA system.

class privacyidea.models.Subscription(**kwargs)[source]

This table stores the imported subscription files.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.Token(serial, tokentype='', isactive=True, otplen=6, otpkey='', **kwargs)[source]

The “Token” table contains the basic token data.

It contains data like
  • serial number

  • secret key

  • PINs

  • …

The table privacyidea.models.TokenOwner contains the owner information of the specified token. The table privacyidea.models.TokenInfo contains additional information that is specific to the tokentype.

get(key=None, fallback=None, save=False)[source]

simulate the dict behaviour to make challenge processing easier, as this will have to deal as well with ‘dict only challenges’

Parameters:
  • key – the attribute name - in case of key is not provided, a dict of all class attributes are returned

  • fallback – if the attribute is not found, the fallback is returned

  • save – in case of all attributes and save==True, the timestamp is converted to a string representation

get_hashed_pin(pin)[source]

Calculate a hash from a pin Fix for working with MS SQL servers MS SQL servers sometimes return a ‘<space>’ when the column is empty: ‘’

Parameters:

pin (str) – the pin to hash

Returns:

hashed pin with current pin_seed

Return type:

str

get_info()[source]
Returns:

The token info as dictionary

get_realms()[source]

return a list of the assigned realms

Returns:

the realms of the token

Return type:

list

get_user_pin()[source]

return the user_pin :rtype : the PIN as a secretObject

set_hashed_pin(pin)[source]

Set the pin of the token in hashed format

Parameters:

pin (str) – the pin to hash

Returns:

the hashed pin

Return type:

str

set_pin(pin, hashed=True)[source]

Set the OTP pin in a hashed way

set_so_pin(security_officer_pin)[source]

For smartcards this sets the security officer pin of the token

:rtype : None

update_otpkey(otpkey)[source]

in case of a new hOtpKey we have to do some more things

update_type(typ)[source]

in case the previous has been different type we must reset the counters But be aware, ray, this could also be upper and lower case mixing…

class privacyidea.models.TokenContainer(serial, container_type='Generic', tokens=None, description='', states=None)[source]

The “Tokencontainer” table contains the containers and their associated tokens.

class privacyidea.models.TokenContainerInfo(container_id, key, value, type=None, description=None)[source]

The table “tokencontainerinfo” is used to store additional, long information that is specific to the containertype.

Create a new tokencontainerinfo for a given token_id

class privacyidea.models.TokenContainerOwner(container_id=None, container_serial=None, resolver=None, user_id=None, realm_id=None, realm_name=None)[source]

Create a new TokenContainerOwner assignment.

class privacyidea.models.TokenContainerRealm(**kwargs)[source]

This table stores to which realms a container is assigned.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.TokenContainerStates(container_id=None, state='active')[source]
class privacyidea.models.TokenContainerTemplate(name, container_type='generic', options='', default=False)[source]
class privacyidea.models.TokenContainerToken(**kwargs)[source]

Association table to link tokens to containers.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.TokenCredentialIdHash(credential_id_hash, token_id)[source]
class privacyidea.models.TokenInfo(token_id, Key, Value, Type=None, Description=None)[source]

The table “tokeninfo” is used to store additional, long information that is specific to the tokentype. E.g. the tokentype “TOTP” has additional entries in the tokeninfo table for “timeStep” and “timeWindow”, which are stored in the column “Key” and “Value”.

The tokeninfo is reference by the foreign key to the “token” table.

Create a new tokeninfo for a given token_id

class privacyidea.models.TokenOwner(token_id=None, serial=None, user_id=None, resolver=None, realm_id=None, realmname=None)[source]

This tables stores the owner of a token. A token can be assigned to several users.

Create a new token assignment to a user.

Parameters:
  • token_id – The database ID of the token

  • serial – The alternate serial number of the token

  • resolver – The identifying name of the resolver

  • realm_id – The database ID of the realm

  • realmname – The alternate name of realm

class privacyidea.models.TokenRealm(realm_id=0, token_id=0, realmname=None)[source]

This table stores to which realms a token is assigned. A token is in the realm of the user it is assigned to. But a token can also be put into many additional realms.

class privacyidea.models.TokenTokengroup(tokengroup_id=0, token_id=0, tokengroupname=None)[source]

This table stores the assignment of tokens to tokengroups. A token can be assigned to several different token groups.

Create a new TokenTokengroup assignment :param tokengroup_id: The id of the token group :param tokengroupname: the name of the tokengroup :param token_id: The id of the token

class privacyidea.models.Tokengroup(**kwds)[source]

The tokengroup table contains the definition of available token groups. A token can then be assigned to several of these tokengroups.

class privacyidea.models.UserCache(username, used_login, resolver, user_id, timestamp)[source]

This class stores the cached information for a user to improve lookup performance.

class privacyidea.models.UserLockState(**kwargs)[source]

The current lock status of a single user, keyed by the same (resolver, uid, realm) tuple used in AuthenticationLog.

There is deliberately no failure counter stored here: failure counts are derived on demand by querying authentication_log over a policy’s time window. That keeps the data flexible (per-policy windows, easy reset, automatic decay) and avoids stale counters on user objects.

lock_expires_at is the load-bearing field: a row exists only while the user is locked, and a row whose lock_expires_at lies in the future (or is NULL for a permanent lock) means the user is currently locked; an admin lifts a lock by deleting the row (timestamps are naive UTC, see utc_now()).

A local database admin is locked here too, and has none of those three values - only a login name. Their row carries the login name as the uid with an empty resolver and realm, which cannot collide with a user’s: a user row is only ever written for a principal that has all three. user_role says which of the two a row is, so nothing has to infer it from what the row lacks; see LockSubject, which is what builds both shapes.

The row records the lock itself, not which policy produced it: what a stage did, and to whom, is the conditional-access history (ConditionalAccessOutcome). It does record lock_cause, i.e. whether a policy or an administrator imposed the lock now in force - a manual lock has no authentication request, so it can have no history row of its own.

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class privacyidea.models.UserSetting(subject_type, username='', user_id='', resolver='', realm_id=None, settings=None, node=None)[source]

Per-principal frontend settings (one row per principal, the whole settings document stored as a single JSON value).

The backend does not interpret these settings; it stores and serves them for the WebUI. A “principal” is whoever logged in, which is not always a resolver user:

  • subject_type='local_admin' — an internal admin from the admin table. Identified by username alone; user_id, resolver and realm_id are empty/NULL.

  • subject_type='user' — a resolver user (this also covers realm-admins). Identified by the (user_id, resolver, realm_id) tuple, the same stable identity used by tokenowner; username is kept only as a convenience for logging/lookups.

Only principals that deviate from the defaults get a row, so the table stays small even with a large user base.

The realm_id foreign key is ON DELETE CASCADE: deleting a realm removes its users’ settings, since those users are gone with it. Rows orphaned by user or resolver removal (which the FK does not cover) are pruned by the pi-tokenjanitor user-settings CLI command.

privacyidea.models.cleanup_challenges(serial)[source]

Delete all challenges, that have expired.

Returns:

None

privacyidea.models.get_machineresolver_id(resolvername)[source]

Return the database ID of the machine resolver :param resolvername: :return:

privacyidea.models.get_machinetoken_ids(machine_id, resolver_name, serial, application)[source]

Returns a list of the ID in the machinetoken table

Parameters:
  • machine_id (basestring) – The resolverdependent machine_id

  • resolver_name (basestring) – The name of the resolver

  • serial (basestring) – the serial number of the token

  • application (basestring) – The application type

Returns:

A list of IDs of the machinetoken entry

Return type:

list of int

privacyidea.models.save_config_timestamp(invalidate_config=True)[source]

Save the current timestamp to the database, and optionally invalidate the current request-local config object. :param invalidate_config: defaults to True