1
0
forked from baron/baron-sso

Implement tenant import and RP auto login policies

This commit is contained in:
2026-04-30 15:45:34 +09:00
parent 24807eab0f
commit f7e4d43b16
76 changed files with 5307 additions and 441 deletions

View File

@@ -63,7 +63,7 @@ func TestMain(m *testing.M) {
}
// Auto-migrate
err = db.AutoMigrate(&domain.Tenant{}, &domain.TenantDomain{}, &domain.User{}, &domain.ClientConsent{})
err = db.AutoMigrate(&domain.Tenant{}, &domain.TenantDomain{}, &domain.User{}, &domain.ClientConsent{}, &domain.RPUserMetadata{})
if err != nil {
log.Fatalf("failed to migrate database: %s", err)
}

View File

@@ -0,0 +1,40 @@
package repository
import (
"baron-sso-backend/internal/domain"
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type RPUserMetadataRepository interface {
Get(ctx context.Context, clientID, userID string) (*domain.RPUserMetadata, error)
Upsert(ctx context.Context, metadata *domain.RPUserMetadata) error
}
type rpUserMetadataRepository struct {
db *gorm.DB
}
func NewRPUserMetadataRepository(db *gorm.DB) RPUserMetadataRepository {
return &rpUserMetadataRepository{db: db}
}
func (r *rpUserMetadataRepository) Get(ctx context.Context, clientID, userID string) (*domain.RPUserMetadata, error) {
var metadata domain.RPUserMetadata
if err := r.db.WithContext(ctx).First(&metadata, "client_id = ? AND user_id = ?", clientID, userID).Error; err != nil {
return nil, err
}
return &metadata, nil
}
func (r *rpUserMetadataRepository) Upsert(ctx context.Context, metadata *domain.RPUserMetadata) error {
return r.db.WithContext(ctx).Clauses(clause.OnConflict{
Columns: []clause.Column{
{Name: "client_id"},
{Name: "user_id"},
},
DoUpdates: clause.AssignmentColumns([]string{"metadata", "updated_at"}),
}).Create(metadata).Error
}

View File

@@ -3,6 +3,7 @@ package repository
import (
"baron-sso-backend/internal/domain"
"context"
"errors"
"strings"
"time"
@@ -88,6 +89,20 @@ func (r *tenantRepository) FindByIDs(ctx context.Context, ids []string) ([]domai
}
func (r *tenantRepository) AddDomain(ctx context.Context, tenantID string, domainName string, verified bool) error {
var existing domain.TenantDomain
err := r.db.WithContext(ctx).Unscoped().
Where("tenant_id = ? AND domain = ?", tenantID, domainName).
First(&existing).Error
if err == nil {
return r.db.WithContext(ctx).Unscoped().Model(&existing).Updates(map[string]any{
"verified": verified,
"deleted_at": nil,
}).Error
}
if !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
td := domain.TenantDomain{
TenantID: tenantID,
Domain: domainName,

View File

@@ -60,6 +60,49 @@ func TestTenantRepository(t *testing.T) {
assert.Equal(t, "test-domain.com", found.Domains[0].Domain)
})
t.Run("AddDomain allows same domain on multiple tenants", func(t *testing.T) {
first := &domain.Tenant{
Name: "Saman Existing",
Slug: "saman-existing",
Type: domain.TenantTypeCompany,
}
second := &domain.Tenant{
Name: "Saman Current",
Slug: "saman-current",
Type: domain.TenantTypeCompany,
}
assert.NoError(t, repo.Create(ctx, first))
assert.NoError(t, repo.Create(ctx, second))
assert.NoError(t, repo.AddDomain(ctx, first.ID, "samaneng.com", true))
assert.NoError(t, repo.AddDomain(ctx, second.ID, "samaneng.com", true))
var rows []domain.TenantDomain
err := testDB.Where("domain = ?", "samaneng.com").Find(&rows).Error
assert.NoError(t, err)
assert.Len(t, rows, 2)
})
t.Run("AddDomain restores deleted tenant domain", func(t *testing.T) {
tenant := &domain.Tenant{
Name: "Domain Restore",
Slug: "domain-restore",
Type: domain.TenantTypeCompany,
}
assert.NoError(t, repo.Create(ctx, tenant))
assert.NoError(t, repo.AddDomain(ctx, tenant.ID, "restore.samaneng.com", true))
assert.NoError(t, testDB.Where("tenant_id = ? AND domain = ?", tenant.ID, "restore.samaneng.com").Delete(&domain.TenantDomain{}).Error)
assert.NoError(t, repo.AddDomain(ctx, tenant.ID, "restore.samaneng.com", true))
var rows []domain.TenantDomain
err := testDB.Where("tenant_id = ? AND domain = ?", tenant.ID, "restore.samaneng.com").Find(&rows).Error
assert.NoError(t, err)
if assert.Len(t, rows, 1) {
assert.True(t, rows[0].Verified)
}
})
t.Run("Update", func(t *testing.T) {
tenant := &domain.Tenant{
Name: "Before Update",