feat: own shared user API tokens
This commit is contained in:
36
updates/202610040001_create_api_tokens.go
Normal file
36
updates/202610040001_create_api_tokens.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package updates
|
||||
|
||||
import (
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var apiTokenMigrations = []*gormigrate.Migration{
|
||||
{
|
||||
ID: "202610040001_create_user_api_tokens",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.Exec(`CREATE TABLE user_api_tokens (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NULL,
|
||||
token_hash VARCHAR(64) NOT NULL UNIQUE,
|
||||
scopes TEXT NULL,
|
||||
collection_ids TEXT NULL,
|
||||
expires_at TIMESTAMPTZ NULL,
|
||||
revoked_at TIMESTAMPTZ NULL,
|
||||
last_used_at TIMESTAMPTZ NULL,
|
||||
last_used_ip VARCHAR(64) NULL,
|
||||
oauth_client_id TEXT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX user_api_tokens_user_id_index ON user_api_tokens (user_id);
|
||||
CREATE INDEX user_api_tokens_oauth_client_id_index ON user_api_tokens (oauth_client_id)`).Error
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return tx.Exec("DROP TABLE IF EXISTS user_api_tokens").Error
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() { Register(apiTokenMigrations...) }
|
||||
42
updates/api_tokens_test.go
Normal file
42
updates/api_tokens_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package updates
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.golem15.com/golem15/sm-user-plugin/models"
|
||||
"git.golem15.com/golem15/summercms/modules/lagoon"
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
)
|
||||
|
||||
func TestAPITokenMigration(t *testing.T) {
|
||||
sqlDB := dedicatedDB(t, "user_api_tokens")
|
||||
db, err := lagoon.Use(t.Context(), sqlDB)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m := gormigrate.New(db, &gormigrate.Options{TableName: "summer_migrations_golem15_user", UseTransaction: true}, All())
|
||||
if err := m.Migrate(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !db.Migrator().HasTable((&models.APIToken{}).TableName()) {
|
||||
t.Fatal("user_api_tokens missing")
|
||||
}
|
||||
for _, column := range []string{"id", "user_id", "name", "token_hash", "scopes", "collection_ids", "expires_at", "revoked_at", "last_used_at", "last_used_ip", "oauth_client_id", "created_at", "updated_at"} {
|
||||
if !db.Migrator().HasColumn(&models.APIToken{}, column) {
|
||||
t.Fatalf("user_api_tokens.%s missing", column)
|
||||
}
|
||||
}
|
||||
var oauthClientType string
|
||||
if err := db.Raw(`SELECT data_type FROM information_schema.columns WHERE table_name = 'user_api_tokens' AND column_name = 'oauth_client_id'`).Scan(&oauthClientType).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if oauthClientType != "text" {
|
||||
t.Fatalf("oauth_client_id type = %q, want text for legacy OAuth client IDs", oauthClientType)
|
||||
}
|
||||
if err := m.RollbackLast(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if db.Migrator().HasTable(&models.APIToken{}) {
|
||||
t.Fatal("user_api_tokens survived rollback")
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,7 @@ func TestCreateOrganisationsMigration(t *testing.T) {
|
||||
"202609220006_create_user_throttle",
|
||||
"202609220007_create_jwt_blacklist",
|
||||
"202610020001_create_user_groups",
|
||||
"202610040001_create_user_api_tokens",
|
||||
}
|
||||
if len(ids) != len(want) {
|
||||
t.Fatalf("history = %v", ids)
|
||||
@@ -55,8 +56,8 @@ func TestCreateOrganisationsMigration(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// user groups, jwt blacklist, throttle, extend-users, then organisations.
|
||||
for range 5 {
|
||||
// API tokens, user groups, jwt blacklist, throttle, extend-users, then organisations.
|
||||
for range 6 {
|
||||
if err := m.RollbackLast(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -54,8 +54,10 @@ func TestUserGroupsMigration(t *testing.T) {
|
||||
t.Fatalf("users_groups primary key = %q", pk)
|
||||
}
|
||||
|
||||
if err := m.RollbackLast(); err != nil {
|
||||
t.Fatal(err)
|
||||
for range 2 { // API tokens, then user groups.
|
||||
if err := m.RollbackLast(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if gdb.Migrator().HasTable("user_groups") || gdb.Migrator().HasTable("users_groups") {
|
||||
t.Fatal("user groups tables survived rollback")
|
||||
|
||||
@@ -47,8 +47,8 @@ func TestExtendUsersMigration(t *testing.T) {
|
||||
if !row.HasSelfSetPassword || row.IsActivated || row.MarketingConsent || row.IsOnboarded {
|
||||
t.Fatalf("defaults = %+v", row)
|
||||
}
|
||||
// user groups, jwt blacklist, throttle, then extend-users.
|
||||
for range 4 {
|
||||
// API tokens, user groups, jwt blacklist, throttle, then extend-users.
|
||||
for range 5 {
|
||||
if err := m.RollbackLast(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -87,8 +87,8 @@ func TestCreateUserThrottleMigration(t *testing.T) {
|
||||
t.Fatalf("missing column %s", col)
|
||||
}
|
||||
}
|
||||
// user groups, jwt blacklist, then throttle.
|
||||
for range 3 {
|
||||
// API tokens, user groups, jwt blacklist, then throttle.
|
||||
for range 4 {
|
||||
if err := m.RollbackLast(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -119,8 +119,8 @@ func TestCreateJwtBlacklistMigration(t *testing.T) {
|
||||
t.Fatalf("missing column %s", col)
|
||||
}
|
||||
}
|
||||
// user groups, then jwt blacklist.
|
||||
for range 2 {
|
||||
// API tokens, user groups, then jwt blacklist.
|
||||
for range 3 {
|
||||
if err := m.RollbackLast(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user