178 lines
3.7 KiB
Go
178 lines
3.7 KiB
Go
package services
|
||
|
||
import (
|
||
"bssapp-backend/models"
|
||
"crypto/md5"
|
||
"crypto/sha1"
|
||
"encoding/hex"
|
||
"log"
|
||
"strings"
|
||
|
||
"golang.org/x/crypto/bcrypt"
|
||
)
|
||
|
||
// CheckPasswordWithLegacy
|
||
// - SADECE doğrulama yapar
|
||
// - DB write YAPMAZ
|
||
// - Migration kararını caller verir
|
||
func CheckPasswordWithLegacy(user *models.User, plain string) bool {
|
||
if user == nil {
|
||
return false
|
||
}
|
||
|
||
if plain == "" {
|
||
return false
|
||
}
|
||
|
||
stored := strings.TrimSpace(user.Upass)
|
||
if stored == "" {
|
||
return false
|
||
}
|
||
|
||
// 1️⃣ bcrypt hash mi?
|
||
if isBcryptHash(stored) {
|
||
candidates := make([]string, 0, 10)
|
||
seen := map[string]struct{}{}
|
||
add := func(v string) {
|
||
if v == "" {
|
||
return
|
||
}
|
||
if _, ok := seen[v]; ok {
|
||
return
|
||
}
|
||
seen[v] = struct{}{}
|
||
candidates = append(candidates, v)
|
||
}
|
||
|
||
add(plain)
|
||
trimmed := strings.TrimSpace(plain)
|
||
add(trimmed)
|
||
|
||
bases := append([]string(nil), candidates...)
|
||
for _, base := range bases {
|
||
md5Sum := md5.Sum([]byte(base))
|
||
md5Hex := hex.EncodeToString(md5Sum[:])
|
||
add(md5Hex)
|
||
add(strings.ToUpper(md5Hex))
|
||
|
||
sha1Sum := sha1.Sum([]byte(base))
|
||
sha1Hex := hex.EncodeToString(sha1Sum[:])
|
||
add(sha1Hex)
|
||
add(strings.ToUpper(sha1Hex))
|
||
}
|
||
|
||
var lastErr error
|
||
for _, candidate := range candidates {
|
||
if err := bcrypt.CompareHashAndPassword([]byte(stored), []byte(candidate)); err == nil {
|
||
return true
|
||
} else {
|
||
lastErr = err
|
||
}
|
||
if encoded, ok := encodeLegacySingleByte(candidate); ok {
|
||
if err := bcrypt.CompareHashAndPassword([]byte(stored), encoded); err == nil {
|
||
return true
|
||
} else {
|
||
lastErr = err
|
||
}
|
||
}
|
||
}
|
||
if lastErr != nil {
|
||
log.Printf(
|
||
"LEGACY BCRYPT MISMATCH stored_len=%d candidates=%d last_err=%v",
|
||
len(stored),
|
||
len(candidates),
|
||
lastErr,
|
||
)
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
// 2️⃣ TAM LEGACY — düz metin (eski kayıtlar)
|
||
if stored == plain {
|
||
return true
|
||
}
|
||
trimmed := strings.TrimSpace(plain)
|
||
if trimmed != plain && trimmed != "" && stored == trimmed {
|
||
return true
|
||
}
|
||
|
||
// 3️⃣ Legacy hash variants seen in old dfusr.upass data.
|
||
if isHexDigest(stored, 32) {
|
||
sumRaw := md5.Sum([]byte(plain))
|
||
if strings.EqualFold(stored, hex.EncodeToString(sumRaw[:])) {
|
||
return true
|
||
}
|
||
if trimmed != plain && trimmed != "" {
|
||
sumTrim := md5.Sum([]byte(trimmed))
|
||
if strings.EqualFold(stored, hex.EncodeToString(sumTrim[:])) {
|
||
return true
|
||
}
|
||
}
|
||
}
|
||
|
||
if isHexDigest(stored, 40) {
|
||
sumRaw := sha1.Sum([]byte(plain))
|
||
if strings.EqualFold(stored, hex.EncodeToString(sumRaw[:])) {
|
||
return true
|
||
}
|
||
if trimmed != plain && trimmed != "" {
|
||
sumTrim := sha1.Sum([]byte(trimmed))
|
||
if strings.EqualFold(stored, hex.EncodeToString(sumTrim[:])) {
|
||
return true
|
||
}
|
||
}
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
func isBcryptHash(s string) bool {
|
||
return strings.HasPrefix(s, "$2a$") ||
|
||
strings.HasPrefix(s, "$2b$") ||
|
||
strings.HasPrefix(s, "$2y$")
|
||
}
|
||
|
||
func isHexDigest(s string, expectedLen int) bool {
|
||
if len(s) != expectedLen {
|
||
return false
|
||
}
|
||
for _, r := range s {
|
||
if (r < '0' || r > '9') &&
|
||
(r < 'a' || r > 'f') &&
|
||
(r < 'A' || r > 'F') {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
// encodeLegacySingleByte converts text to a Turkish-compatible single-byte
|
||
// representation (similar to Windows-1254 / ISO-8859-9) for legacy bcrypt data.
|
||
func encodeLegacySingleByte(s string) ([]byte, bool) {
|
||
out := make([]byte, 0, len(s))
|
||
for _, r := range s {
|
||
switch r {
|
||
case 'Ğ':
|
||
out = append(out, 0xD0)
|
||
case 'ğ':
|
||
out = append(out, 0xF0)
|
||
case 'İ':
|
||
out = append(out, 0xDD)
|
||
case 'ı':
|
||
out = append(out, 0xFD)
|
||
case 'Ş':
|
||
out = append(out, 0xDE)
|
||
case 'ş':
|
||
out = append(out, 0xFE)
|
||
default:
|
||
if r >= 0 && r <= 0xFF {
|
||
out = append(out, byte(r))
|
||
} else {
|
||
return nil, false
|
||
}
|
||
}
|
||
}
|
||
return out, true
|
||
}
|