- golem15_user_backend_links (1:1, both sides unique) and one-shot hashed
golem15_user_backend_link_requests (golem15.user.backend_links.request_ttl, 10m)
- classes.BackendLinks: CreateLinkRequest, ConfirmLinkRequest (row lock,
JWT iat not before the request), SetLink, RemoveLink, LinkedBackendUser;
published as *classes.BackendLinks and pact.MCPLinkedBackendResolver
- GET /_user/api/v1/backend-link/{code} and POST /backend-link/confirm (jwt.auth)
- My account section frontend_account with link and unlink
- BackendLinkedEvent, BackendUnlinkedEvent and pact.MCPSubjectChanged after commit;
deleting or deactivating a frontend user removes its link
- golem15.user.frontend_url (falls back to app.url)
61 lines
2.4 KiB
Go
61 lines
2.4 KiB
Go
package updates
|
|
|
|
import (
|
|
"github.com/go-gormigrate/gormigrate/v2"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// backendLinkMigrations create the verified frontend-to-backend account link
|
|
// (one frontend user to one administrator, both sides unique) and the
|
|
// one-shot link requests an administrator starts on My account. A request
|
|
// stores only the SHA-256 of its code. Deleting either account removes its
|
|
// link and requests; deleting the administrator who set a link keeps the
|
|
// link and clears linked_by.
|
|
//
|
|
// The foreign keys to backend_users are added when that table exists, which
|
|
// it always does when lagoon.Migrate runs the plugin (the admin's own
|
|
// migrations run first); a run of the plugin's migrations alone, as some
|
|
// tests do, creates the tables without them.
|
|
var backendLinkMigrations = []*gormigrate.Migration{
|
|
{
|
|
ID: "202610220001_create_golem15_user_backend_links",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.Exec(`CREATE TABLE golem15_user_backend_links (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
user_id INTEGER NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE,
|
|
backend_user_id INTEGER NOT NULL UNIQUE,
|
|
linked_by INTEGER NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE TABLE golem15_user_backend_link_requests (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
code_hash CHAR(64) NOT NULL UNIQUE,
|
|
backend_user_id INTEGER NOT NULL,
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
used_at TIMESTAMPTZ NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX golem15_user_backend_link_requests_backend_user_id_index ON golem15_user_backend_link_requests (backend_user_id);
|
|
DO $$
|
|
BEGIN
|
|
IF to_regclass('backend_users') IS NOT NULL THEN
|
|
ALTER TABLE golem15_user_backend_links
|
|
ADD CONSTRAINT golem15_user_backend_links_backend_user_id_foreign
|
|
FOREIGN KEY (backend_user_id) REFERENCES backend_users(id) ON DELETE CASCADE,
|
|
ADD CONSTRAINT golem15_user_backend_links_linked_by_foreign
|
|
FOREIGN KEY (linked_by) REFERENCES backend_users(id) ON DELETE SET NULL;
|
|
ALTER TABLE golem15_user_backend_link_requests
|
|
ADD CONSTRAINT golem15_user_backend_link_requests_backend_user_id_foreign
|
|
FOREIGN KEY (backend_user_id) REFERENCES backend_users(id) ON DELETE CASCADE;
|
|
END IF;
|
|
END
|
|
$$`).Error
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Exec("DROP TABLE IF EXISTS golem15_user_backend_link_requests; DROP TABLE IF EXISTS golem15_user_backend_links").Error
|
|
},
|
|
},
|
|
}
|
|
|
|
func init() { Register(backendLinkMigrations...) }
|