diff --git a/svc/routes/first_password_change.go b/svc/routes/first_password_change.go index fe34279..abe6ad6 100644 --- a/svc/routes/first_password_change.go +++ b/svc/routes/first_password_change.go @@ -29,7 +29,7 @@ func FirstPasswordChangeHandler(db *sql.DB) http.HandlerFunc { r.Method, r.URL.Path, ) - http.Error(w, "unauthorized: token missing or invalid", http.StatusUnauthorized) + http.Error(w, "yetkisiz: token eksik veya geçersiz", http.StatusUnauthorized) return } @@ -39,14 +39,14 @@ func FirstPasswordChangeHandler(db *sql.DB) http.HandlerFunc { } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - http.Error(w, "invalid payload", http.StatusBadRequest) + http.Error(w, "geçersiz istek gövdesi", http.StatusBadRequest) return } req.CurrentPassword = strings.TrimSpace(req.CurrentPassword) req.NewPassword = strings.TrimSpace(req.NewPassword) if req.CurrentPassword == "" || req.NewPassword == "" { - http.Error(w, "password fields required", http.StatusUnprocessableEntity) + http.Error(w, "şifre alanları zorunludur", http.StatusUnprocessableEntity) return } @@ -61,7 +61,7 @@ func FirstPasswordChangeHandler(db *sql.DB) http.HandlerFunc { claims.ID, mkErr, ) - http.Error(w, "user lookup failed", http.StatusInternalServerError) + http.Error(w, "kullanıcı sorgulama hatası", http.StatusInternalServerError) return } @@ -79,20 +79,30 @@ func FirstPasswordChangeHandler(db *sql.DB) http.HandlerFunc { claims.ID, claims.Username, ) - http.Error(w, "mevcut sifre hatali", http.StatusUnauthorized) + http.Error(w, "mevcut şifre hatalı", http.StatusUnauthorized) return } } else { var err error legacyUser, err = legacyRepo.GetLegacyUserForLogin(claims.Username) - if err != nil || legacyUser == nil || !legacyUser.IsActive || int64(legacyUser.ID) != claims.ID { + if err != nil || legacyUser == nil || !legacyUser.IsActive { log.Printf( "FIRST_PASSWORD_CHANGE 401 reason=legacy_user_not_found user_id=%d username=%s err=%v", claims.ID, claims.Username, err, ) - http.Error(w, "unauthorized: user not found", http.StatusUnauthorized) + http.Error(w, "yetkisiz: kullanıcı bulunamadı", http.StatusUnauthorized) + return + } + if !hasMkUser && int64(legacyUser.ID) != claims.ID { + log.Printf( + "FIRST_PASSWORD_CHANGE 401 reason=legacy_id_mismatch user_id=%d legacy_id=%d username=%s", + claims.ID, + legacyUser.ID, + claims.Username, + ) + http.Error(w, "yetkisiz: kullanıcı bulunamadı", http.StatusUnauthorized) return } @@ -102,7 +112,7 @@ func FirstPasswordChangeHandler(db *sql.DB) http.HandlerFunc { claims.ID, claims.Username, ) - http.Error(w, "mevcut sifre hatali", http.StatusUnauthorized) + http.Error(w, "mevcut şifre hatalı", http.StatusUnauthorized) return } } @@ -117,13 +127,13 @@ func FirstPasswordChangeHandler(db *sql.DB) http.HandlerFunc { bcrypt.DefaultCost, ) if err != nil { - http.Error(w, "password hash error", http.StatusInternalServerError) + http.Error(w, "şifre hash hatası", http.StatusInternalServerError) return } tx, err := db.Begin() if err != nil { - http.Error(w, "transaction error", http.StatusInternalServerError) + http.Error(w, "işlem başlatılamadı", http.StatusInternalServerError) return } defer tx.Rollback() @@ -146,7 +156,7 @@ func FirstPasswordChangeHandler(db *sql.DB) http.HandlerFunc { claims.ID, err, ) - http.Error(w, "password update failed", http.StatusInternalServerError) + http.Error(w, "şifre güncellenemedi", http.StatusInternalServerError) return } @@ -156,21 +166,31 @@ func FirstPasswordChangeHandler(db *sql.DB) http.HandlerFunc { "FIRST_PASSWORD_CHANGE 500 reason=password_update_no_rows user_id=%d", claims.ID, ) - http.Error(w, "password update failed", http.StatusInternalServerError) + http.Error(w, "şifre güncellenemedi", http.StatusInternalServerError) return } } else { if legacyUser == nil { // Defensive fallback, should not happen. legacyUser, err = legacyRepo.GetLegacyUserForLogin(claims.Username) - if err != nil || legacyUser == nil || int64(legacyUser.ID) != claims.ID { + if err != nil || legacyUser == nil { log.Printf( "FIRST_PASSWORD_CHANGE 500 reason=legacy_reload_failed user_id=%d username=%s err=%v", claims.ID, claims.Username, err, ) - http.Error(w, "legacy user reload failed", http.StatusInternalServerError) + http.Error(w, "legacy kullanıcı yeniden yüklenemedi", http.StatusInternalServerError) + return + } + if !hasMkUser && int64(legacyUser.ID) != claims.ID { + log.Printf( + "FIRST_PASSWORD_CHANGE 500 reason=legacy_reload_id_mismatch user_id=%d legacy_id=%d username=%s", + claims.ID, + legacyUser.ID, + claims.Username, + ) + http.Error(w, "legacy kullanıcı yeniden yüklenemedi", http.StatusInternalServerError) return } } @@ -222,7 +242,7 @@ func FirstPasswordChangeHandler(db *sql.DB) http.HandlerFunc { claims.Username, err, ) - http.Error(w, "legacy migration failed", http.StatusInternalServerError) + http.Error(w, "legacy geçişi başarısız", http.StatusInternalServerError) return } @@ -235,7 +255,7 @@ func FirstPasswordChangeHandler(db *sql.DB) http.HandlerFunc { claims.ID, err, ) - http.Error(w, "commit failed", http.StatusInternalServerError) + http.Error(w, "işlem tamamlanamadı", http.StatusInternalServerError) return } @@ -262,7 +282,7 @@ func FirstPasswordChangeHandler(db *sql.DB) http.HandlerFunc { false, ) if err != nil { - http.Error(w, "token generation failed", http.StatusInternalServerError) + http.Error(w, "token üretilemedi", http.StatusInternalServerError) return } diff --git a/svc/routes/login.go b/svc/routes/login.go index dacf33e..0da448b 100644 --- a/svc/routes/login.go +++ b/svc/routes/login.go @@ -30,6 +30,76 @@ type LoginRequest struct { Password string `json:"password"` } +func ensureLegacyUserReadyForSession(db *sql.DB, legacyUser *models.User) (int64, error) { + desiredID := int64(legacyUser.ID) + + _, err := db.Exec(` + INSERT INTO mk_dfusr ( + id, + code, + username, + email, + full_name, + mobile, + address, + is_active, + password_hash, + force_password_change, + created_at, + updated_at, + last_updated_date + ) + VALUES ( + $1,$2,$3,$4,$5,$6,$7,$8,'',true,NOW(),NOW(),NOW() + ) + ON CONFLICT (id) + DO UPDATE SET + code = EXCLUDED.code, + username = EXCLUDED.username, + email = EXCLUDED.email, + full_name = COALESCE(NULLIF(EXCLUDED.full_name, ''), mk_dfusr.full_name), + mobile = COALESCE(NULLIF(EXCLUDED.mobile, ''), mk_dfusr.mobile), + address = COALESCE(NULLIF(EXCLUDED.address, ''), mk_dfusr.address), + is_active = EXCLUDED.is_active, + force_password_change = true, + updated_at = NOW(), + last_updated_date = NOW() + `, + desiredID, + strings.TrimSpace(legacyUser.Username), + strings.TrimSpace(legacyUser.Username), + strings.TrimSpace(legacyUser.Email), + strings.TrimSpace(legacyUser.FullName), + strings.TrimSpace(legacyUser.Mobile), + strings.TrimSpace(legacyUser.Address), + legacyUser.IsActive, + ) + if err == nil { + return desiredID, nil + } + + mkRepo := repository.NewMkUserRepository(db) + existing, lookupErr := mkRepo.GetByUsername(legacyUser.Username) + if lookupErr != nil { + return 0, err + } + + _, updErr := db.Exec(` + UPDATE mk_dfusr + SET + is_active = $1, + force_password_change = true, + updated_at = NOW(), + last_updated_date = NOW() + WHERE id = $2 + `, legacyUser.IsActive, existing.ID) + if updErr != nil { + return 0, updErr + } + + return existing.ID, nil +} + func LoginHandler(db *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { @@ -137,8 +207,15 @@ func LoginHandler(db *sql.DB) http.HandlerFunc { // 3️⃣ LEGACY SESSION (PENDING MIGRATION) // - mk_dfusr migration is completed in /api/password/change // ================================================== + mkID, err := ensureLegacyUserReadyForSession(db, legacyUser) + if err != nil { + log.Printf("LEGACY LOGIN MIGRATION BIND FAILED username=%s err=%v", login, err) + http.Error(w, "Giriş yapılamadı", http.StatusInternalServerError) + return + } + mkUser = &models.MkUser{ - ID: int64(legacyUser.ID), + ID: mkID, Username: legacyUser.Username, Email: legacyUser.Email, IsActive: legacyUser.IsActive, @@ -150,7 +227,7 @@ func LoginHandler(db *sql.DB) http.HandlerFunc { auditlog.Write(auditlog.ActivityLog{ ActionType: "LEGACY_USER_LOGIN_PENDING_MIGRATION", ActionCategory: "security", - Description: "legacy login ok, first password change required", + Description: "legacy giriş başarılı, ilk şifre değişikliği gerekli", IsSuccess: true, })