forked from baron/baron-sso
- Added support for fixed UUIDs during bulk registration (Search-first + ExternalID mapping) - Implemented idempotency and visibility restoration for soft-deleted users - Enhanced bulk upload UI to show 'New/Updated/Unchanged' status and modified fields - Added logic to reclaim identifiers (login_id) from colliding records - Added frontend E2E and backend unit tests for UUID integrity and conflict handling - Fixed i18n, formatting, and mock tests to satisfy code-check - Applied 'go fix' for 'omitzero' tags and general Go standards
139 lines
4.8 KiB
Go
139 lines
4.8 KiB
Go
package repository
|
|
|
|
import (
|
|
"baron-sso-backend/internal/domain"
|
|
"context"
|
|
"errors"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type ClientConsentRepository interface {
|
|
Upsert(ctx context.Context, consent *domain.ClientConsent) error
|
|
Delete(ctx context.Context, subject, clientID string) error
|
|
DeleteByClient(ctx context.Context, 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)
|
|
ListBySubject(ctx context.Context, subject string) ([]domain.ClientConsent, error)
|
|
ListSubjectsByClient(ctx context.Context, clientID string) ([]string, error)
|
|
Find(ctx context.Context, clientID, subject string) (*domain.ClientConsent, error)
|
|
}
|
|
|
|
type clientConsentRepo struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func NewClientConsentRepository(db *gorm.DB) ClientConsentRepository {
|
|
return &clientConsentRepo{db: db}
|
|
}
|
|
|
|
func (r *clientConsentRepo) Find(ctx context.Context, clientID, subject string) (*domain.ClientConsent, error) {
|
|
var consent domain.ClientConsent
|
|
err := r.db.WithContext(ctx).
|
|
Where("client_id = ? AND subject = ?", clientID, subject).
|
|
First(&consent).Error
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
return &consent, nil
|
|
}
|
|
|
|
func (r *clientConsentRepo) Upsert(ctx context.Context, consent *domain.ClientConsent) error {
|
|
return r.db.WithContext(ctx).Unscoped().
|
|
Where("client_id = ? AND subject = ?", consent.ClientID, consent.Subject).
|
|
Assign(map[string]any{
|
|
"granted_scopes": consent.GrantedScopes,
|
|
"updated_at": gorm.Expr("NOW()"),
|
|
"deleted_at": nil,
|
|
}).
|
|
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) DeleteByClient(ctx context.Context, clientID string) error {
|
|
return r.db.WithContext(ctx).
|
|
Where("client_id = ?", 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).Unscoped().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).Unscoped().
|
|
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).Unscoped().
|
|
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).Unscoped().
|
|
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
|
|
}
|
|
|
|
func (r *clientConsentRepo) ListBySubject(ctx context.Context, subject string) ([]domain.ClientConsent, error) {
|
|
var consents []domain.ClientConsent
|
|
err := r.db.WithContext(ctx).Unscoped().
|
|
Where("subject = ?", subject).
|
|
Order("updated_at DESC").
|
|
Find(&consents).Error
|
|
return consents, err
|
|
}
|
|
|
|
func (r *clientConsentRepo) ListSubjectsByClient(ctx context.Context, clientID string) ([]string, error) {
|
|
var subjects []string
|
|
err := r.db.WithContext(ctx).Unscoped().
|
|
Model(&domain.ClientConsent{}).
|
|
Distinct("subject").
|
|
Where("client_id = ?", clientID).
|
|
Order("subject ASC").
|
|
Pluck("subject", &subjects).Error
|
|
return subjects, err
|
|
}
|