forked from baron/baron-sso
동의 내역 관리를 위한 ClientConsent 모델 및 Repository 구현
This commit is contained in:
@@ -57,6 +57,7 @@ require (
|
|||||||
github.com/lestrrat-go/iter v1.0.2 // indirect
|
github.com/lestrrat-go/iter v1.0.2 // indirect
|
||||||
github.com/lestrrat-go/jwx/v2 v2.1.6 // indirect
|
github.com/lestrrat-go/jwx/v2 v2.1.6 // indirect
|
||||||
github.com/lestrrat-go/option v1.0.1 // indirect
|
github.com/lestrrat-go/option v1.0.1 // indirect
|
||||||
|
github.com/lib/pq v1.11.1 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||||
|
|||||||
@@ -110,6 +110,8 @@ github.com/lestrrat-go/jwx/v2 v2.1.6 h1:hxM1gfDILk/l5ylers6BX/Eq1m/pnxe9NBwW6lVf
|
|||||||
github.com/lestrrat-go/jwx/v2 v2.1.6/go.mod h1:Y722kU5r/8mV7fYDifjug0r8FK8mZdw0K0GpJw/l8pU=
|
github.com/lestrrat-go/jwx/v2 v2.1.6/go.mod h1:Y722kU5r/8mV7fYDifjug0r8FK8mZdw0K0GpJw/l8pU=
|
||||||
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
|
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
|
||||||
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||||
|
github.com/lib/pq v1.11.1 h1:wuChtj2hfsGmmx3nf1m7xC2XpK6OtelS2shMY+bGMtI=
|
||||||
|
github.com/lib/pq v1.11.1/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ func migrateSchemas(db *gorm.DB) error {
|
|||||||
&domain.ApiKey{},
|
&domain.ApiKey{},
|
||||||
&domain.IdentityProviderConfig{},
|
&domain.IdentityProviderConfig{},
|
||||||
&domain.ClientSecret{},
|
&domain.ClientSecret{},
|
||||||
|
&domain.ClientConsent{},
|
||||||
// &domain.RelyingParty{}, // Removed: SSOT is Hydra + Keto
|
// &domain.RelyingParty{}, // Removed: SSOT is Hydra + Keto
|
||||||
// &domain.UserConsent{}, // TODO: Uncomment when model is ready
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
33
backend/internal/domain/client_consent.go
Normal file
33
backend/internal/domain/client_consent.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/lib/pq"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClientConsent struct {
|
||||||
|
ID string `gorm:"primaryKey;type:uuid;default:gen_random_uuid()" json:"id"`
|
||||||
|
ClientID string `gorm:"index;uniqueIndex:idx_client_subject;not null" json:"clientId"`
|
||||||
|
Subject string `gorm:"index;uniqueIndex:idx_client_subject;not null" json:"subject"` // User UUID
|
||||||
|
GrantedScopes pq.StringArray `gorm:"type:text[];not null" json:"grantedScopes"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
|
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientConsentWithTenantInfo is a struct to hold joined data for API responses
|
||||||
|
type ClientConsentWithTenantInfo struct {
|
||||||
|
ClientConsent
|
||||||
|
TenantID string `gorm:"column:tenant_id" json:"tenantId"`
|
||||||
|
TenantName string `gorm:"column:tenant_name" json:"tenantName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConsent) BeforeCreate(tx *gorm.DB) (err error) {
|
||||||
|
if c.ID == "" {
|
||||||
|
c.ID = uuid.New().String()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
92
backend/internal/repository/client_consent_repository.go
Normal file
92
backend/internal/repository/client_consent_repository.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"baron-sso-backend/internal/domain"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClientConsentRepository interface {
|
||||||
|
Upsert(ctx context.Context, consent *domain.ClientConsent) error
|
||||||
|
Delete(ctx context.Context, subject, clientID string) error
|
||||||
|
List(ctx context.Context, clientID string, limit, offset int) ([]domain.ClientConsentWithTenantInfo, int64, error)
|
||||||
|
ListByTenant(ctx context.Context, clientID, tenantID string, limit, offset int) ([]domain.ClientConsentWithTenantInfo, int64, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type clientConsentRepo struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientConsentRepository(db *gorm.DB) ClientConsentRepository {
|
||||||
|
return &clientConsentRepo{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *clientConsentRepo) Upsert(ctx context.Context, consent *domain.ClientConsent) error {
|
||||||
|
return r.db.WithContext(ctx).
|
||||||
|
Where("client_id = ? AND subject = ?", consent.ClientID, consent.Subject).
|
||||||
|
Assign(map[string]interface{}{
|
||||||
|
"granted_scopes": consent.GrantedScopes,
|
||||||
|
"updated_at": gorm.Expr("NOW()"),
|
||||||
|
}).
|
||||||
|
FirstOrCreate(consent).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *clientConsentRepo) Delete(ctx context.Context, subject, clientID string) error {
|
||||||
|
return r.db.WithContext(ctx).
|
||||||
|
Where("subject = ? AND client_id = ?", subject, clientID).
|
||||||
|
Delete(&domain.ClientConsent{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *clientConsentRepo) List(ctx context.Context, clientID string, limit, offset int) ([]domain.ClientConsentWithTenantInfo, int64, error) {
|
||||||
|
var consents []domain.ClientConsentWithTenantInfo
|
||||||
|
var total int64
|
||||||
|
|
||||||
|
// Base query for counting
|
||||||
|
countQuery := r.db.WithContext(ctx).Model(&domain.ClientConsent{}).Where("client_id = ?", clientID)
|
||||||
|
if err := countQuery.Count(&total).Error; err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query for fetching data
|
||||||
|
query := r.db.WithContext(ctx).
|
||||||
|
Model(&domain.ClientConsent{}).
|
||||||
|
Select("client_consents.*, users.tenant_id, tenants.name as tenant_name").
|
||||||
|
Joins("LEFT JOIN users ON users.id::text = client_consents.subject").
|
||||||
|
Joins("LEFT JOIN tenants ON tenants.id = users.tenant_id").
|
||||||
|
Where("client_consents.client_id = ?", clientID)
|
||||||
|
|
||||||
|
err := query.Limit(limit).Offset(offset).Order("client_consents.updated_at DESC").Scan(&consents).Error
|
||||||
|
return consents, total, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *clientConsentRepo) ListByTenant(ctx context.Context, clientID, tenantID string, limit, offset int) ([]domain.ClientConsentWithTenantInfo, int64, error) {
|
||||||
|
var consents []domain.ClientConsentWithTenantInfo
|
||||||
|
var total int64
|
||||||
|
|
||||||
|
// Base query for counting
|
||||||
|
countQuery := r.db.WithContext(ctx).
|
||||||
|
Model(&domain.ClientConsent{}).
|
||||||
|
Joins("JOIN users ON users.id::text = client_consents.subject").
|
||||||
|
Where("client_consents.client_id = ? AND users.tenant_id = ?", clientID, tenantID)
|
||||||
|
|
||||||
|
if err := countQuery.Count(&total).Error; err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query for fetching data
|
||||||
|
query := r.db.WithContext(ctx).
|
||||||
|
Model(&domain.ClientConsent{}).
|
||||||
|
Select("client_consents.*, users.tenant_id, tenants.name as tenant_name").
|
||||||
|
Joins("JOIN users ON users.id::text = client_consents.subject").
|
||||||
|
Joins("JOIN tenants ON tenants.id = users.tenant_id").
|
||||||
|
Where("client_consents.client_id = ? AND users.tenant_id = ?", clientID, tenantID)
|
||||||
|
|
||||||
|
err := query.
|
||||||
|
Limit(limit).
|
||||||
|
Offset(offset).
|
||||||
|
Order("client_consents.updated_at DESC").
|
||||||
|
Scan(&consents).Error
|
||||||
|
|
||||||
|
return consents, total, err
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user