mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-25 08:42:16 +00:00
feat(relational): implement session repository (#11109)
# Which Problems Are Solved As part of going to a relation storage model, we need to implement the repository for handling sessions and their events. # How the Problems Are Solved - Adds a migration step to create the necessary tables and types in the database. - Adds the repository implementation for sessions. - Implements a projection handler for session events to handle the events written by the old command handlers. - Updates the `Session` type and `SessionRepository` interface: - added `CreatorID` - Adjusted `Delete` method to return amount of affected columns - Removed sub-repository clumns from the main interface - Added `CreatorIDCondition`, `ExpirationCondition`, `ExistsFactor` and `ExistsMetadata` - Removed `SetUserAgent` - Methods of `SessionFactor` and `SessionChallenge` have been made public - fixed enumer on `SessionFactorType` - Adds a `JSON` type to the repository package to easy can JSON columns, resp. jsonb object inside a property of a `JSONArray` object. - Adds a `NoChange` interface, which can be implemented to signal that a `Change` interface implementation is not writing any changes to the statement builder, e.g. `CTEChange` # Additional Changes - Fixes the operators enums. `TextOperations` had wrong comments and `NumberOperations` additionally also had from comparisons. - Fixes downgrade of migration step 008. # Additional Context - closes https://github.com/zitadel/zitadel/issues/10212
This commit is contained in:
+1
-1
@@ -1,3 +1,3 @@
|
||||
DROP TYPE zitadel.project_grant_state;
|
||||
DROP TYPE IF EXISTS zitadel.project_grant_state CASCADE ;
|
||||
DROP TABLE IF EXISTS zitadel.project_grant_roles;
|
||||
DROP TABLE IF EXISTS zitadel.project_grants;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package migration
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 011_session_table/up.sql
|
||||
up011SessionTable string
|
||||
//go:embed 011_session_table/down.sql
|
||||
down011SessionTable string
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerSQLMigration(11, up011SessionTable, down011SessionTable)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
DROP TABLE IF EXISTS zitadel.session_metadata CASCADE;
|
||||
DROP TABLE IF EXISTS zitadel.session_user_agents CASCADE;
|
||||
DROP TYPE IF EXISTS zitadel.session_factor_type CASCADE;
|
||||
DROP TABLE IF EXISTS zitadel.session_factors CASCADE;
|
||||
DROP TABLE IF EXISTS zitadel.sessions CASCADE;
|
||||
DROP FUNCTION IF EXISTS zitadel.update_expiration();
|
||||
@@ -0,0 +1,93 @@
|
||||
CREATE TABLE zitadel.session_user_agents (
|
||||
instance_id TEXT NOT NULL
|
||||
, fingerprint_id TEXT NOT NULL CHECK (fingerprint_id <> '')
|
||||
, ip INET
|
||||
, description TEXT
|
||||
, headers JSONB
|
||||
|
||||
, PRIMARY KEY (instance_id, fingerprint_id)
|
||||
);
|
||||
|
||||
CREATE TABLE zitadel.sessions (
|
||||
instance_id TEXT NOT NULL
|
||||
, id TEXT NOT NULL CHECK (id <> '')
|
||||
, token_id TEXT
|
||||
, user_agent_id TEXT
|
||||
, lifetime INTERVAL
|
||||
, expiration TIMESTAMPTZ
|
||||
, user_id TEXT -- this column is used for referential integrity
|
||||
, creator_id TEXT
|
||||
, created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
|
||||
, updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
|
||||
|
||||
, PRIMARY KEY (instance_id, id)
|
||||
, FOREIGN KEY (instance_id) REFERENCES zitadel.instances(id)
|
||||
-- , FOREIGN KEY (instance_id, user_id) REFERENCES zitadel.users(instance_id, id) ON DELETE CASCADE
|
||||
, FOREIGN KEY (instance_id, user_agent_id) REFERENCES zitadel.session_user_agents(instance_id, fingerprint_id) ON DELETE SET NULL (user_agent_id)
|
||||
);
|
||||
|
||||
CREATE OR REPLACE FUNCTION zitadel.update_expiration()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.expiration := NEW.updated_at + NEW.lifetime;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER set_expiration
|
||||
BEFORE INSERT OR UPDATE OF lifetime ON zitadel.sessions
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION zitadel.update_expiration();
|
||||
|
||||
CREATE TYPE zitadel.session_factor_type AS ENUM (
|
||||
'user',
|
||||
'password',
|
||||
'passkey', -- is also a challenge
|
||||
'identity_provider_intent',
|
||||
'totp',
|
||||
'otp_sms', -- is also a challenge
|
||||
'otp_email', -- is also a challenge
|
||||
'recovery_code'
|
||||
);
|
||||
|
||||
CREATE TABLE zitadel.session_factors (
|
||||
instance_id TEXT NOT NULL
|
||||
, session_id TEXT NOT NULL
|
||||
, type zitadel.session_factor_type NOT NULL
|
||||
, last_challenged_at TIMESTAMPTZ -- this is only set if the type is a challenge
|
||||
, challenged_payload JSONB
|
||||
, last_verified_at TIMESTAMPTZ
|
||||
, verified_payload JSONB
|
||||
|
||||
, PRIMARY KEY (instance_id, session_id, type)
|
||||
, FOREIGN KEY (instance_id, session_id) REFERENCES zitadel.sessions(instance_id, id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE zitadel.session_metadata (
|
||||
instance_id TEXT NOT NULL
|
||||
, session_id TEXT NOT NULL
|
||||
, key TEXT NOT NULL CHECK (key <> '')
|
||||
, value BYTEA NOT NULL
|
||||
|
||||
, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
, updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
|
||||
, PRIMARY KEY (instance_id, session_id, key)
|
||||
|
||||
, CONSTRAINT fk_session_metadata_session FOREIGN KEY (instance_id, session_id) REFERENCES zitadel.sessions (instance_id, id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_session_metadata_key ON zitadel.session_metadata (key);
|
||||
CREATE INDEX idx_session_metadata_value ON zitadel.session_metadata (sha256(value));
|
||||
|
||||
-- TODO(adlerhurst): these indexes can currently not be used by Postgres, because of the type conversion
|
||||
-- the value can be a json but doesn't have to be.
|
||||
-- CREATE INDEX idx_session_metadata_value_number ON zitadel.session_metadata ((value::NUMERIC)) WHERE jsonb_typeof(value) = 'number';
|
||||
-- CREATE INDEX idx_session_metadata_value_string ON zitadel.session_metadata ((value#>>'{}')) WHERE jsonb_typeof(value) = 'string';
|
||||
-- CREATE INDEX idx_session_metadata_value_boolean ON zitadel.session_metadata ((value::BOOLEAN)) WHERE jsonb_typeof(value) = 'boolean';
|
||||
|
||||
CREATE TRIGGER trg_set_updated_at_session_metadata
|
||||
BEFORE INSERT OR UPDATE ON zitadel.session_metadata
|
||||
FOR EACH ROW
|
||||
WHEN (NEW.updated_at IS NULL)
|
||||
EXECUTE FUNCTION zitadel.set_updated_at();
|
||||
Reference in New Issue
Block a user