mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-10-03 08:19:04 +02:00
fix: datatype mismatch between postgres and sqlite causes import to fail
This commit is contained in:
@@ -282,6 +282,61 @@ func (s *TestService) SeedDatabase(baseURL string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
farFuture := datatype.DateTime(time.Date(2099, 1, 1, 0, 0, 0, 0, time.UTC))
|
||||||
|
oauth2Session := oidc.OAuth2Session{
|
||||||
|
Base: model.Base{
|
||||||
|
ID: "551ab785-c830-47d3-8a07-60c9f3bb4859",
|
||||||
|
},
|
||||||
|
Kind: "access_token",
|
||||||
|
Key: "cross-database-test-session",
|
||||||
|
RequestID: "cross-database-test-request",
|
||||||
|
AccessTokenSignature: "",
|
||||||
|
Active: true,
|
||||||
|
RequestData: `{"request":"value"}`,
|
||||||
|
ExpiresAt: &farFuture,
|
||||||
|
}
|
||||||
|
if err := tx.Create(&oauth2Session).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Table("oauth2_jtis").Create(map[string]any{
|
||||||
|
"id": "bd0c8bf2-66ec-487a-9dd5-7d9d78d73543",
|
||||||
|
"created_at": datatype.DateTime(time.Now()),
|
||||||
|
"jti": "cross-database-test-jti",
|
||||||
|
"expires_at": farFuture,
|
||||||
|
}).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
interactionSession := oidc.InteractionSession{
|
||||||
|
Base: model.Base{
|
||||||
|
ID: "aaf5dd23-cd1f-4748-a2aa-baa6af94d800",
|
||||||
|
},
|
||||||
|
Scopes: datatype.StringList{"openid"},
|
||||||
|
ClientID: oidcClients[0].ID,
|
||||||
|
UserID: new(users[0].ID),
|
||||||
|
ConsentRequired: true,
|
||||||
|
RequestedAt: farFuture,
|
||||||
|
Parameters: oidc.InteractionSessionParameters{
|
||||||
|
"client_id": oidcClients[0].ID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := tx.Create(&interactionSession).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
reauthenticationToken := webauthn.ReauthenticationToken{
|
||||||
|
Base: model.Base{
|
||||||
|
ID: "71839ace-d978-4e6f-8fb1-b8648a21031b",
|
||||||
|
},
|
||||||
|
Token: "cross-database-reauthentication-token",
|
||||||
|
ExpiresAt: farFuture,
|
||||||
|
UserID: users[0].ID,
|
||||||
|
}
|
||||||
|
if err := tx.Create(&reauthenticationToken).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
accessToken := model.OneTimeAccessToken{
|
accessToken := model.OneTimeAccessToken{
|
||||||
Token: "one-time-token",
|
Token: "one-time-token",
|
||||||
ExpiresAt: datatype.DateTime(time.Now().Add(1 * time.Hour)),
|
ExpiresAt: datatype.DateTime(time.Now().Add(1 * time.Hour)),
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
-- No-op on PostgreSQL
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
-- No-op on PostgreSQL because its OAuth storage types already match the export format
|
||||||
+151
@@ -0,0 +1,151 @@
|
|||||||
|
PRAGMA foreign_keys = OFF;
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE TABLE reauthentication_tokens_old (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
created_at DATETIME NOT NULL,
|
||||||
|
token TEXT NOT NULL UNIQUE,
|
||||||
|
expires_at INTEGER NOT NULL,
|
||||||
|
user_id TEXT NOT NULL REFERENCES users ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO reauthentication_tokens_old (
|
||||||
|
id,
|
||||||
|
created_at,
|
||||||
|
token,
|
||||||
|
expires_at,
|
||||||
|
user_id
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
created_at,
|
||||||
|
token,
|
||||||
|
expires_at,
|
||||||
|
user_id
|
||||||
|
FROM reauthentication_tokens;
|
||||||
|
|
||||||
|
DROP TABLE reauthentication_tokens;
|
||||||
|
ALTER TABLE reauthentication_tokens_old RENAME TO reauthentication_tokens;
|
||||||
|
|
||||||
|
CREATE INDEX idx_reauthentication_tokens_token ON reauthentication_tokens (token);
|
||||||
|
CREATE INDEX idx_reauthentication_tokens_expires_at ON reauthentication_tokens (expires_at);
|
||||||
|
|
||||||
|
CREATE TABLE oauth2_sessions_old (
|
||||||
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
created_at INTEGER NOT NULL,
|
||||||
|
kind TEXT NOT NULL,
|
||||||
|
key TEXT NOT NULL,
|
||||||
|
request_id TEXT NOT NULL,
|
||||||
|
access_token_signature TEXT NOT NULL DEFAULT '',
|
||||||
|
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
request_data TEXT NOT NULL,
|
||||||
|
expires_at INTEGER
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO oauth2_sessions_old (
|
||||||
|
id,
|
||||||
|
created_at,
|
||||||
|
kind,
|
||||||
|
key,
|
||||||
|
request_id,
|
||||||
|
access_token_signature,
|
||||||
|
active,
|
||||||
|
request_data,
|
||||||
|
expires_at
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
created_at,
|
||||||
|
kind,
|
||||||
|
key,
|
||||||
|
request_id,
|
||||||
|
access_token_signature,
|
||||||
|
active,
|
||||||
|
CAST(request_data AS TEXT),
|
||||||
|
expires_at
|
||||||
|
FROM oauth2_sessions;
|
||||||
|
|
||||||
|
DROP TABLE oauth2_sessions;
|
||||||
|
ALTER TABLE oauth2_sessions_old RENAME TO oauth2_sessions;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX idx_oauth2_sessions_kind_key ON oauth2_sessions (kind, key);
|
||||||
|
CREATE INDEX idx_oauth2_sessions_kind_request ON oauth2_sessions (kind, request_id);
|
||||||
|
CREATE INDEX idx_oauth2_sessions_expires_at ON oauth2_sessions (expires_at);
|
||||||
|
|
||||||
|
CREATE TABLE oauth2_jtis_old (
|
||||||
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
created_at INTEGER NOT NULL,
|
||||||
|
jti TEXT NOT NULL UNIQUE,
|
||||||
|
expires_at INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO oauth2_jtis_old (
|
||||||
|
id,
|
||||||
|
created_at,
|
||||||
|
jti,
|
||||||
|
expires_at
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
created_at,
|
||||||
|
jti,
|
||||||
|
expires_at
|
||||||
|
FROM oauth2_jtis;
|
||||||
|
|
||||||
|
DROP TABLE oauth2_jtis;
|
||||||
|
ALTER TABLE oauth2_jtis_old RENAME TO oauth2_jtis;
|
||||||
|
|
||||||
|
CREATE INDEX idx_oauth2_jtis_expires_at ON oauth2_jtis (expires_at);
|
||||||
|
|
||||||
|
CREATE TABLE interaction_sessions_old (
|
||||||
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
created_at INTEGER NOT NULL,
|
||||||
|
consent_required BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
reauthentication_required BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
authentication_required BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
account_selection_required BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
scopes TEXT NOT NULL DEFAULT '[]',
|
||||||
|
client_id TEXT NOT NULL REFERENCES oidc_clients(id) ON DELETE CASCADE,
|
||||||
|
user_id TEXT REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
requested_at INTEGER NOT NULL,
|
||||||
|
reauthenticated_at INTEGER,
|
||||||
|
parameters TEXT NOT NULL DEFAULT '{}'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO interaction_sessions_old (
|
||||||
|
id,
|
||||||
|
created_at,
|
||||||
|
consent_required,
|
||||||
|
reauthentication_required,
|
||||||
|
authentication_required,
|
||||||
|
account_selection_required,
|
||||||
|
scopes,
|
||||||
|
client_id,
|
||||||
|
user_id,
|
||||||
|
requested_at,
|
||||||
|
reauthenticated_at,
|
||||||
|
parameters
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
created_at,
|
||||||
|
consent_required,
|
||||||
|
reauthentication_required,
|
||||||
|
authentication_required,
|
||||||
|
account_selection_required,
|
||||||
|
CAST(scopes AS TEXT),
|
||||||
|
client_id,
|
||||||
|
user_id,
|
||||||
|
requested_at,
|
||||||
|
reauthenticated_at,
|
||||||
|
CAST(parameters AS TEXT)
|
||||||
|
FROM interaction_sessions;
|
||||||
|
|
||||||
|
DROP TABLE interaction_sessions;
|
||||||
|
ALTER TABLE interaction_sessions_old RENAME TO interaction_sessions;
|
||||||
|
|
||||||
|
CREATE INDEX idx_interaction_sessions_client_id ON interaction_sessions (client_id);
|
||||||
|
CREATE INDEX idx_interaction_sessions_user_id ON interaction_sessions (user_id);
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
PRAGMA foreign_keys = ON;
|
||||||
+152
@@ -0,0 +1,152 @@
|
|||||||
|
PRAGMA foreign_keys = OFF;
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
-- Align JSON and timestamp column types with the export format used for PostgreSQL
|
||||||
|
CREATE TABLE reauthentication_tokens_new (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
created_at DATETIME NOT NULL,
|
||||||
|
token TEXT NOT NULL UNIQUE,
|
||||||
|
expires_at DATETIME NOT NULL,
|
||||||
|
user_id TEXT NOT NULL REFERENCES users ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO reauthentication_tokens_new (
|
||||||
|
id,
|
||||||
|
created_at,
|
||||||
|
token,
|
||||||
|
expires_at,
|
||||||
|
user_id
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
created_at,
|
||||||
|
token,
|
||||||
|
expires_at,
|
||||||
|
user_id
|
||||||
|
FROM reauthentication_tokens;
|
||||||
|
|
||||||
|
DROP TABLE reauthentication_tokens;
|
||||||
|
ALTER TABLE reauthentication_tokens_new RENAME TO reauthentication_tokens;
|
||||||
|
|
||||||
|
CREATE INDEX idx_reauthentication_tokens_token ON reauthentication_tokens (token);
|
||||||
|
CREATE INDEX idx_reauthentication_tokens_expires_at ON reauthentication_tokens (expires_at);
|
||||||
|
|
||||||
|
CREATE TABLE oauth2_sessions_new (
|
||||||
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
created_at DATETIME NOT NULL,
|
||||||
|
kind TEXT NOT NULL,
|
||||||
|
key TEXT NOT NULL,
|
||||||
|
request_id TEXT NOT NULL,
|
||||||
|
access_token_signature TEXT NOT NULL DEFAULT '',
|
||||||
|
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
request_data BLOB NOT NULL,
|
||||||
|
expires_at DATETIME
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO oauth2_sessions_new (
|
||||||
|
id,
|
||||||
|
created_at,
|
||||||
|
kind,
|
||||||
|
key,
|
||||||
|
request_id,
|
||||||
|
access_token_signature,
|
||||||
|
active,
|
||||||
|
request_data,
|
||||||
|
expires_at
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
created_at,
|
||||||
|
kind,
|
||||||
|
key,
|
||||||
|
request_id,
|
||||||
|
access_token_signature,
|
||||||
|
active,
|
||||||
|
CAST(request_data AS BLOB),
|
||||||
|
expires_at
|
||||||
|
FROM oauth2_sessions;
|
||||||
|
|
||||||
|
DROP TABLE oauth2_sessions;
|
||||||
|
ALTER TABLE oauth2_sessions_new RENAME TO oauth2_sessions;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX idx_oauth2_sessions_kind_key ON oauth2_sessions (kind, key);
|
||||||
|
CREATE INDEX idx_oauth2_sessions_kind_request ON oauth2_sessions (kind, request_id);
|
||||||
|
CREATE INDEX idx_oauth2_sessions_expires_at ON oauth2_sessions (expires_at);
|
||||||
|
|
||||||
|
CREATE TABLE oauth2_jtis_new (
|
||||||
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
created_at DATETIME NOT NULL,
|
||||||
|
jti TEXT NOT NULL UNIQUE,
|
||||||
|
expires_at DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO oauth2_jtis_new (
|
||||||
|
id,
|
||||||
|
created_at,
|
||||||
|
jti,
|
||||||
|
expires_at
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
created_at,
|
||||||
|
jti,
|
||||||
|
expires_at
|
||||||
|
FROM oauth2_jtis;
|
||||||
|
|
||||||
|
DROP TABLE oauth2_jtis;
|
||||||
|
ALTER TABLE oauth2_jtis_new RENAME TO oauth2_jtis;
|
||||||
|
|
||||||
|
CREATE INDEX idx_oauth2_jtis_expires_at ON oauth2_jtis (expires_at);
|
||||||
|
|
||||||
|
CREATE TABLE interaction_sessions_new (
|
||||||
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
created_at DATETIME NOT NULL,
|
||||||
|
consent_required BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
reauthentication_required BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
authentication_required BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
account_selection_required BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
scopes BLOB NOT NULL DEFAULT X'5B5D',
|
||||||
|
client_id TEXT NOT NULL REFERENCES oidc_clients(id) ON DELETE CASCADE,
|
||||||
|
user_id TEXT REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
requested_at DATETIME NOT NULL,
|
||||||
|
reauthenticated_at DATETIME,
|
||||||
|
parameters BLOB NOT NULL DEFAULT X'7B7D'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO interaction_sessions_new (
|
||||||
|
id,
|
||||||
|
created_at,
|
||||||
|
consent_required,
|
||||||
|
reauthentication_required,
|
||||||
|
authentication_required,
|
||||||
|
account_selection_required,
|
||||||
|
scopes,
|
||||||
|
client_id,
|
||||||
|
user_id,
|
||||||
|
requested_at,
|
||||||
|
reauthenticated_at,
|
||||||
|
parameters
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
created_at,
|
||||||
|
consent_required,
|
||||||
|
reauthentication_required,
|
||||||
|
authentication_required,
|
||||||
|
account_selection_required,
|
||||||
|
CAST(scopes AS BLOB),
|
||||||
|
client_id,
|
||||||
|
user_id,
|
||||||
|
requested_at,
|
||||||
|
reauthenticated_at,
|
||||||
|
CAST(parameters AS BLOB)
|
||||||
|
FROM interaction_sessions;
|
||||||
|
|
||||||
|
DROP TABLE interaction_sessions;
|
||||||
|
ALTER TABLE interaction_sessions_new RENAME TO interaction_sessions;
|
||||||
|
|
||||||
|
CREATE INDEX idx_interaction_sessions_client_id ON interaction_sessions (client_id);
|
||||||
|
CREATE INDEX idx_interaction_sessions_user_id ON interaction_sessions (user_id);
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
PRAGMA foreign_keys = ON;
|
||||||
@@ -1,7 +1,15 @@
|
|||||||
{
|
{
|
||||||
"provider": "sqlite",
|
"provider": "sqlite",
|
||||||
"version": 20260708130000,
|
"version": 20260722120000,
|
||||||
"tableOrder": ["users", "user_groups", "oidc_clients", "signup_tokens", "apis", "api_permissions", "oidc_clients_allowed_api_permissions"],
|
"tableOrder": [
|
||||||
|
"users",
|
||||||
|
"user_groups",
|
||||||
|
"oidc_clients",
|
||||||
|
"signup_tokens",
|
||||||
|
"apis",
|
||||||
|
"api_permissions",
|
||||||
|
"oidc_clients_allowed_api_permissions"
|
||||||
|
],
|
||||||
"tables": {
|
"tables": {
|
||||||
"apis": [
|
"apis": [
|
||||||
{
|
{
|
||||||
@@ -264,6 +272,52 @@
|
|||||||
"user_id": "f4b89dc2-62fb-46bf-9f5f-c34f4eafe93e"
|
"user_id": "f4b89dc2-62fb-46bf-9f5f-c34f4eafe93e"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"oauth2_jtis": [
|
||||||
|
{
|
||||||
|
"id": "bd0c8bf2-66ec-487a-9dd5-7d9d78d73543",
|
||||||
|
"created_at": "2026-07-22T12:00:00Z",
|
||||||
|
"jti": "cross-database-test-jti",
|
||||||
|
"expires_at": "2099-01-01T00:00:00Z"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"oauth2_sessions": [
|
||||||
|
{
|
||||||
|
"id": "551ab785-c830-47d3-8a07-60c9f3bb4859",
|
||||||
|
"created_at": "2026-07-22T12:00:00Z",
|
||||||
|
"kind": "access_token",
|
||||||
|
"key": "cross-database-test-session",
|
||||||
|
"request_id": "cross-database-test-request",
|
||||||
|
"access_token_signature": "",
|
||||||
|
"active": true,
|
||||||
|
"request_data": "eyJyZXF1ZXN0IjoidmFsdWUifQ==",
|
||||||
|
"expires_at": "2099-01-01T00:00:00Z"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interaction_sessions": [
|
||||||
|
{
|
||||||
|
"id": "aaf5dd23-cd1f-4748-a2aa-baa6af94d800",
|
||||||
|
"created_at": "2026-07-22T12:00:00Z",
|
||||||
|
"consent_required": true,
|
||||||
|
"reauthentication_required": false,
|
||||||
|
"authentication_required": false,
|
||||||
|
"account_selection_required": false,
|
||||||
|
"scopes": "WyJvcGVuaWQiXQ==",
|
||||||
|
"client_id": "3654a746-35d4-4321-ac61-0bdcff2b4055",
|
||||||
|
"user_id": "f4b89dc2-62fb-46bf-9f5f-c34f4eafe93e",
|
||||||
|
"requested_at": "2099-01-01T00:00:00Z",
|
||||||
|
"reauthenticated_at": null,
|
||||||
|
"parameters": "eyJjbGllbnRfaWQiOiIzNjU0YTc0Ni0zNWQ0LTQzMjEtYWM2MS0wYmRjZmYyYjQwNTUifQ=="
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"reauthentication_tokens": [
|
||||||
|
{
|
||||||
|
"id": "71839ace-d978-4e6f-8fb1-b8648a21031b",
|
||||||
|
"created_at": "2026-07-22T12:00:00Z",
|
||||||
|
"token": "cross-database-reauthentication-token",
|
||||||
|
"expires_at": "2099-01-01T00:00:00Z",
|
||||||
|
"user_id": "f4b89dc2-62fb-46bf-9f5f-c34f4eafe93e"
|
||||||
|
}
|
||||||
|
],
|
||||||
"signup_tokens": [
|
"signup_tokens": [
|
||||||
{
|
{
|
||||||
"created_at": "2025-11-25T12:39:02Z",
|
"created_at": "2025-11-25T12:39:02Z",
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ test('Export via stdout', async ({ baseURL }) => {
|
|||||||
compareExports(exampleExportPath, stdoutExtractPath);
|
compareExports(exampleExportPath, stdoutExtractPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Import', async () => {
|
test('Import SQLite export', async () => {
|
||||||
// Reset the backend without seeding
|
// Reset the backend without seeding
|
||||||
await cleanupBackend({ skipSeed: true });
|
await cleanupBackend({ skipSeed: true });
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ test('Import', async () => {
|
|||||||
compareExports(exampleExportPath, exportExtracted);
|
compareExports(exampleExportPath, exportExtracted);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Import via stdin', async () => {
|
test('Import SQLite export via stdin', async () => {
|
||||||
await cleanupBackend({ skipSeed: true });
|
await cleanupBackend({ skipSeed: true });
|
||||||
|
|
||||||
const exampleExportArchivePath = path.join(tmpDir, 'example-export-stdin.zip');
|
const exampleExportArchivePath = path.join(tmpDir, 'example-export-stdin.zip');
|
||||||
|
|||||||
Reference in New Issue
Block a user