1
0
forked from baron/baron-sso

multi IDP 모델 적용 scaffolding

This commit is contained in:
Lectom C Han
2026-01-23 16:27:30 +09:00
parent a2e9f87d33
commit 98ba4c5b30
11 changed files with 453 additions and 3 deletions

View File

@@ -0,0 +1,62 @@
package validator
import (
"baron-sso-backend/internal/domain"
"fmt"
"reflect"
"strings"
)
// ValidateIDPCompatibility checks if the provided IDP supports all required fields defined in the BrokerUser model.
func ValidateIDPCompatibility(brokerModel interface{}, idp domain.IdentityProvider) error {
metadata, err := idp.GetMetadata()
if err != nil {
return fmt.Errorf("failed to fetch metadata from IDP %s: %w", idp.Name(), err)
}
supportedMap := make(map[string]bool)
for _, f := range metadata.SupportedFields {
supportedMap[f] = true
}
t := reflect.TypeOf(brokerModel)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
// Check "required" tag
isRequired := field.Tag.Get("required") == "true"
jsonTag := field.Tag.Get("json")
fieldName := strings.Split(jsonTag, ",")[0]
// Skip if fieldName is empty or if it's the Attributes map (handled separately)
if fieldName == "" {
continue
}
if fieldName != "attributes" {
if isRequired && !supportedMap[fieldName] {
return fmt.Errorf("IDP %s does not support required field: %s", idp.Name(), fieldName)
}
}
// Check "required_keys" tag for map types (Custom Attributes)
if fieldName == "attributes" {
reqKeys := field.Tag.Get("required_keys")
if reqKeys != "" {
keys := strings.Split(reqKeys, ",")
for _, key := range keys {
key = strings.TrimSpace(key)
if !supportedMap[key] {
return fmt.Errorf("IDP %s does not support required custom attribute: %s", idp.Name(), key)
}
}
}
}
}
return nil
}

View File

@@ -0,0 +1,78 @@
package validator
import (
"baron-sso-backend/internal/domain"
"testing"
)
// MockProvider는 IdentityProvider 인터페이스를 구현하는 테스트용 구조체입니다.
type MockProvider struct {
Supported []string
}
func (m *MockProvider) Name() string {
return "MockIDP"
}
func (m *MockProvider) GetMetadata() (*domain.IDPMetadata, error) {
return &domain.IDPMetadata{
SupportedFields: m.Supported,
}, nil
}
func TestValidateIDPCompatibility(t *testing.T) {
// BrokerUser 모델은 다음과 같이 정의되어 있다고 가정합니다 (idp_models.go 참조):
// ID (required), Email (required), Name, PhoneNumber
// Attributes (required_keys: "grade", "department")
tests := []struct {
name string
supported []string
wantErr bool
}{
{
name: "성공: 모든 필수 필드 지원",
supported: []string{"id", "email", "name", "grade", "department"},
wantErr: false,
},
{
name: "성공: 선택 필드(name) 누락이어도 성공",
supported: []string{"id", "email", "grade", "department"},
wantErr: false,
},
{
name: "실패: 필수 필드(id) 누락",
supported: []string{"email", "grade", "department"},
wantErr: true,
},
{
name: "실패: 필수 필드(email) 누락",
supported: []string{"id", "grade", "department"},
wantErr: true,
},
{
name: "실패: 커스텀 속성(grade) 누락",
supported: []string{"id", "email", "department"},
wantErr: true,
},
{
name: "실패: 커스텀 속성(department) 누락",
supported: []string{"id", "email", "grade"},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 테스트용 IDP Provider 생성
mockIDP := &MockProvider{Supported: tt.supported}
// 검증 수행
err := ValidateIDPCompatibility(domain.BrokerUser{}, mockIDP)
if (err != nil) != tt.wantErr {
t.Errorf("ValidateIDPCompatibility() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}