1
0
forked from baron/baron-sso
Files
baron-sso/backend/internal/handler/password_policy_test.go
2026-02-03 18:10:31 +09:00

99 lines
2.2 KiB
Go

package handler
import (
"baron-sso-backend/internal/domain"
"crypto/rand"
"encoding/binary"
"testing"
"unicode"
)
// 정책을 받아 필수 요구사항을 모두 포함하는 비밀번호를 생성한다.
func generatePasswordFromPolicy(policy *domain.PasswordPolicy) string {
minLen := policy.MinLength
if minLen < 8 {
minLen = 12 // 안전한 기본값
}
pwd := make([]rune, 0, minLen)
if policy.Lowercase {
pwd = append(pwd, 'a')
}
if policy.Uppercase {
pwd = append(pwd, 'B')
}
if policy.Number {
pwd = append(pwd, '3')
}
if policy.NonAlphanumeric {
pwd = append(pwd, '!')
}
charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
for len(pwd) < minLen {
pwd = append(pwd, rune(charset[randomInt(len(charset))]))
}
// 섞어서 예측 가능성을 낮춘다.
for i := range pwd {
j := randomInt(len(pwd))
pwd[i], pwd[j] = pwd[j], pwd[i]
}
return string(pwd)
}
func randomInt(n int) int {
if n <= 0 {
return 0
}
var b [8]byte
if _, err := rand.Read(b[:]); err != nil {
return 0
}
return int(binary.BigEndian.Uint64(b[:]) % uint64(n))
}
func TestGeneratePasswordUsesNonAlphanumericRequirement(t *testing.T) {
policy := &domain.PasswordPolicy{
MinLength: 8,
Lowercase: true,
Uppercase: true,
Number: true,
NonAlphanumeric: true,
}
pwd := generatePasswordFromPolicy(policy)
if len(pwd) < policy.MinLength {
t.Fatalf("비밀번호 길이가 정책 최소 길이 미만: got %d, want >= %d", len(pwd), policy.MinLength)
}
var hasLower, hasUpper, hasNumber, hasSymbol bool
for _, r := range pwd {
switch {
case unicode.IsLower(r):
hasLower = true
case unicode.IsUpper(r):
hasUpper = true
case unicode.IsNumber(r):
hasNumber = true
case !unicode.IsLetter(r) && !unicode.IsNumber(r):
hasSymbol = true
}
}
if policy.Lowercase && !hasLower {
t.Fatalf("소문자 요구사항 미충족: %q", pwd)
}
if policy.Uppercase && !hasUpper {
t.Fatalf("대문자 요구사항 미충족: %q", pwd)
}
if policy.Number && !hasNumber {
t.Fatalf("숫자 요구사항 미충족: %q", pwd)
}
if policy.NonAlphanumeric && !hasSymbol {
t.Fatalf("비영문자 요구사항 미충족: %q", pwd)
}
}