package http import ( "encoding/json" "fmt" // NEW "sync" "time" "github.com/tuusuario/go-sync-service/internal/config" "github.com/tuusuario/go-sync-service/internal/domain/dto" "github.com/tuusuario/go-sync-service/internal/domain/model" "github.com/tuusuario/go-sync-service/internal/domain/ports" "github.com/tuusuario/go-sync-service/internal/security" ) var ( redisKey = "session:SAP" mutex sync.Mutex ) type SAPSessionProvider struct{} func GetSession(cfg ports.RedisConfigProvider, job dto.CronJob, auth dto.ServiceConfig, dbcore ports.Database, logPrefix string) (*dto.SessionData, error) { mutex.Lock() defer mutex.Unlock() redisKey = "session:" + job.UnidadNegocio.CompanyName + ":" + job.UnidadNegocio.CompanyDB if sess, err := loadSessionFromRedis(cfg, logPrefix); err == nil && time.Now().Before(sess.ExpiresAt) { config.Log.Printf("%v 🔑 Sesión obtenida de Redis %v", logPrefix, redisKey) return sess, nil } parametros := map[string]interface{}{ "company_name": job.UnidadNegocio.CompanyName, "company_db": job.UnidadNegocio.CompanyDB, } credencial, err := dbcore.GetCredencialesFromTemplate(config.GlobalConfig.WhereUnitsBusiness, parametros) if err != nil { config.Log.Printf("%v ❌ Error al obtener credenciales: %v", logPrefix, err) return nil, err } // ============================== // DESCIFRAR PASSWORD (AES-GCM) // ============================== key, err := security.LoadEncryptionKey() if err != nil { return nil, fmt.Errorf("%s %v", logPrefix, err) } // Suponemos que credencial.Password viene como base64(nonce||cipher) generado por EncryptAESGCM plainPass, err := security.DecryptAESGCM(credencial.Password, key) if err != nil { config.Log.Printf("%v ❌ Error al descifrar password: %v", logPrefix, err) return nil, err } credencial.Password = plainPass // No loguees secretos. Si necesitas log, hazlo sin password: config.Log.Debugf("%v Obteniendo credenciales para CompanyDB=%s UserName=%s (password oculto)", logPrefix, credencial.CompanyDB, credencial.UserName) config.Log.Printf("%v 🔑 Realizando login...", logPrefix) mySession := &dto.SessionData{EndPoint: credencial.EndPoint} if auth.Rest == nil { auth.Rest = &dto.RestOptions{} } // 3. Preparar el body del login (usa cred.Password en claro ya descifrado) auth = prepareAuthBody(auth, credencial, logPrefix) config.Log.Debugf(" %v Url: %v + %v", logPrefix, credencial.EndPoint, auth.Path) resp, err := SendRequest(credencial.EndPoint, auth) if err != nil || resp.IsError() { config.Log.Printf("%v ❌ Error al autenticar: %v", logPrefix, err) return nil, err } if auth.GQL { var dataGraphql struct { Token string `json:"token"` RefreshToken string `json:"refresh_token"` } if err := json.Unmarshal(resp.Body(), &dataGraphql); err != nil { config.Log.Printf("%v ❌ Error al parsear sesión graphql: %v", logPrefix, err) return nil, err } mySession.SessionId = dataGraphql.Token mySession.ExpiresAt = time.Now().Add(10 * time.Minute) } else { var dataRest struct { SessionId string `json:"SessionId"` SessionTimeout int `json:"SessionTimeout"` } if err := json.Unmarshal(resp.Body(), &dataRest); err != nil { config.Log.Printf("%v ❌ Error al parsear sesión rest: %v", logPrefix, err) return nil, err } mySession.SessionId = dataRest.SessionId mySession.ExpiresAt = time.Now().Add(time.Duration(dataRest.SessionTimeout) * time.Minute) } config.Log.Printf("%v ✅ Sesión obtenida", logPrefix) saveSessionToRedis(mySession, cfg, logPrefix) return mySession, nil } func loadSessionFromRedis(cfg ports.RedisConfigProvider, logPrefix string) (*dto.SessionData, error) { raw, err := cfg.GetString(redisKey) if err != nil { config.Log.Printf("%v ⚠️ No se pudo obtener sesión de Redis: %v", logPrefix, err) return nil, err } var sess dto.SessionData if err := json.Unmarshal([]byte(raw), &sess); err != nil { config.Log.Printf("%v ⚠️ No se pudo parsear sesión de Redis: %v", logPrefix, err.Error()) return nil, err } return &sess, nil } func saveSessionToRedis(sess *dto.SessionData, cfg ports.RedisConfigProvider, logPrefix string) { data, _ := json.Marshal(sess) ttl := time.Until(sess.ExpiresAt) err := cfg.UpdateParam(redisKey, string(data), ttl) if err != nil { config.Log.Printf("%v ⚠️ No se pudo guardar sesión en Redis: %v", logPrefix, err) } } func prepareAuthBody(auth dto.ServiceConfig, cred *model.CredencialesSAP, logPrefix string) dto.ServiceConfig { if auth.Rest == nil { auth.Rest = &dto.RestOptions{} } if auth.GQL { config.Log.Debugf("%v 🧠 Preparando auth para GraphQL", logPrefix) auth.Rest.Body = map[string]string{ "username": cred.UserName, "password": cred.Password, } } else { config.Log.Debugf("%v🧠 Preparando auth para REST", logPrefix) auth.Rest.Body = map[string]string{ "CompanyDB": cred.CompanyDB, "UserName": cred.UserName, "Password": cred.Password, } } return auth }