package authentication import ( "fmt" pa "simrs-vx/internal/lib/auth" "strconv" "time" "github.com/golang-jwt/jwt" "github.com/google/uuid" dg "github.com/karincake/apem/db-gorm-pg" ms "github.com/karincake/apem/ms-redis" d "github.com/karincake/dodol" gh "github.com/karincake/getuk" l "github.com/karincake/lepet" pl "simrs-vx/pkg/logger" erg "simrs-vx/internal/domain/references/organization" edp "simrs-vx/internal/domain/main-entities/division-position" ed "simrs-vx/internal/domain/main-entities/doctor" ee "simrs-vx/internal/domain/main-entities/employee" eip "simrs-vx/internal/domain/main-entities/installation-position" "simrs-vx/internal/domain/main-entities/intern" em "simrs-vx/internal/domain/main-entities/midwife" en "simrs-vx/internal/domain/main-entities/nurse" ep "simrs-vx/internal/domain/main-entities/pharmacist" er "simrs-vx/internal/domain/main-entities/registrator" esp "simrs-vx/internal/domain/main-entities/specialist-position" essp "simrs-vx/internal/domain/main-entities/subspecialist-position" eu "simrs-vx/internal/domain/main-entities/user" udp "simrs-vx/internal/use-case/main-use-case/division-position" uip "simrs-vx/internal/use-case/main-use-case/installation-position" usp "simrs-vx/internal/use-case/main-use-case/specialist-position" ussp "simrs-vx/internal/use-case/main-use-case/subspecialist-position" ) // just return the error code func getAndCheck(input, condition any, includes any) (eCode string) { qry := dg.I.Where(condition) // WARNING THIS PRELOAD FAILS if includes != nil { if val := includes.(string); val != "" { qry = qry.Preload(val) } else if vals := includes.([]string); len(vals) > 0 { for _, val := range vals { qry = qry.Preload(val) } } } result := qry.First(&input) if result.Error != nil { return "fetch-fail" } else if result.RowsAffected == 0 { return "auth-login-incorrect" } return "" } func getDivisionPosition(employee_id uint, event *pl.Event) ([]string, error) { var result []string // get data division_position based on employee_id data, _, err := udp.ReadListData(edp.ReadListDto{FilterDto: edp.FilterDto{Employee_Id: &employee_id}}, event) if err != nil { return nil, err } if len(data) > 0 { for _, dp := range data { result = append(result, "div|"+*dp.Division_Code+"|"+dp.Code) } } return result, nil } func getInstallationPosition(employeeId uint, event *pl.Event) ([]string, error) { var result []string // get data specialist_position based on employee_id data, _, err := uip.ReadListData(eip.ReadListDto{ FilterDto: eip.FilterDto{Employee_Id: &employeeId}, Includes: "installation"}, event) if err != nil { return nil, err } if len(data) > 0 { for _, dp := range data { result = append(result, "inst|"+*dp.Installation_Code+"|"+dp.Code) } } return result, nil } func getSpecialistPosition(employeeId uint, event *pl.Event) ([]string, error) { var result []string // get data specialist_position based on employee_id data, _, err := usp.ReadListData(esp.ReadListDto{FilterDto: esp.FilterDto{Employee_Id: &employeeId}}, event) if err != nil { return nil, err } if len(data) > 0 { for _, dp := range data { result = append(result, "spec|"+*dp.Specialist_Code+"|"+dp.Code) } } return result, nil } func getSubspecialistPosition(employeeId uint, event *pl.Event) ([]string, error) { var result []string // get data specialist_position based on employee_id data, _, err := ussp.ReadListData(essp.ReadListDto{ FilterDto: essp.FilterDto{Employee_Id: &employeeId}, Includes: "subspecialist"}, event) if err != nil { return nil, err } if len(data) > 0 { for _, dp := range data { result = append(result, "subspec|"+dp.Subspecialist.Code+"|"+dp.Code) } } return result, nil } func checkStrClaims(claim map[string]interface{}, key string) string { if v, exist := claim[key]; exist && v != nil { return v.(string) } return "" } func checkStrPtrClaims(claim map[string]interface{}, key string) *string { if v, exist := claim[key]; exist && v != nil { val := v.(string) return &val } return nil } func checkUntPtrClaims(claim map[string]interface{}, key string) *uint { if v, exist := claim[key]; exist && v != nil { val := uint(v.(float64)) return &val } return nil } func populateRoles(user *eu.User, input eu.LoginDto, atClaims jwt.MapClaims, outputData d.II, event pl.Event) error { id, err := uuid.NewRandom() if err != nil { panic(fmt.Sprintf(l.I.Msg("uuid-gen-fail"), err)) } if input.Duration == 0 { input.Duration = 24 * 60 } duration := time.Minute * time.Duration(input.Duration) aUuid := id.String() atExpires := time.Now().Add(duration).Unix() atClaims["uuid"] = aUuid atClaims["exp"] = atExpires atClaims["user_id"] = user.Id atClaims["user_name"] = user.Name atClaims["user_contractPosition_code"] = user.ContractPosition_Code outputData["user_id"] = user.Id outputData["user_name"] = user.Name outputData["user_contractPosition_code"] = user.ContractPosition_Code roles := []string{} switch user.ContractPosition_Code { case erg.CSCEmp: // employee employee := ee.Employee{} dg.I.Where("\"User_Id\" = ?", user.Id).First(&employee) if employee.Id == 0 { return d.FieldErrors{"authentication": d.FieldError{Code: "auth-noEmployee", Message: pl.GenMessage("auth-noEmployee")}} } atClaims["employee_id"] = employee.Id outputData["employee_id"] = employee.Id roles = append(roles, "emp|"+string(*employee.Position_Code)) // employee position if employee.Id > 0 && employee.Position_Code != nil { atClaims["employee_position_code"] = *employee.Position_Code switch *employee.Position_Code { case erg.EPCDoc: doctor := ed.Doctor{} dg.I.Where("\"Employee_Id\" = ?", employee.Id).First(&doctor) if doctor.Id == 0 { return d.FieldErrors{"authentication": d.FieldError{Code: "auth-noDoctor", Message: pl.GenMessage("auth-noDoctor")}} } atClaims["doctor_code"] = doctor.Code outputData["doctor_code"] = doctor.Code // specialist if doctor.Specialist_Code != nil { atClaims["specialist_code"] = doctor.Specialist_Code outputData["specialist_code"] = doctor.Specialist_Code } if doctor.Specialist_Code != nil { atClaims["specialist_code"] = doctor.Specialist_Code outputData["specialist_code"] = doctor.Specialist_Code } if doctor.Subspecialist_Code != nil { atClaims["subspecialist_code"] = doctor.Subspecialist_Code outputData["subspecialist_code"] = doctor.Subspecialist_Code } case erg.EPCNur: empData := en.Nurse{} dg.I.Where("\"Employee_Id\" = ?", employee.Id).First(&empData) if empData.Id == 0 { return d.FieldErrors{"authentication": d.FieldError{Code: "auth-noNurse", Message: pl.GenMessage("auth-noNurse")}} } atClaims["nurse_code"] = empData.Code outputData["nurse_code"] = empData.Code case erg.EPCMwi: empData := em.Midwife{} dg.I.Where("\"Employee_Id\" = ?", employee.Id).First(&empData) if empData.Id == 0 { return d.FieldErrors{"authentication": d.FieldError{Code: "auth-noMidwife", Message: pl.GenMessage("auth-noMidwife")}} } atClaims["midwife_code"] = empData.Code outputData["midwife_code"] = empData.Code case erg.EPCPha: empData := ep.Pharmacist{} dg.I.Where("\"Employee_Id\" = ?", employee.Id).First(&empData) if empData.Id == 0 { return d.FieldErrors{"authentication": d.FieldError{Code: "auth-noPharmacist", Message: pl.GenMessage("auth-noPharmacist")}} } atClaims["pharmacist_code"] = empData.Code outputData["pharmacist_code"] = empData.Code case erg.EPCReg: empData := er.Registrator{} dg.I.Where("\"Employee_Id\" = ?", employee.Id).First(&empData) if empData.Id == 0 { return d.FieldErrors{"authentication": d.FieldError{Code: "auth-noRegistrator", Message: pl.GenMessage("auth-noRegistrator")}} } atClaims["registrator_id"] = empData.Id outputData["registrator_id"] = empData.Id atClaims["installation_code"] = empData.Installation_Code outputData["installation_code"] = empData.Installation_Code } errorGetPosition := d.FieldErrors{"authentication": d.FieldError{Code: "auth-getData-failed", Message: pl.GenMessage("auth-getData-failed")}} // division position divisionPositions, err := getDivisionPosition(employee.Id, &event) if err != nil { return errorGetPosition } // installation position installationPositions, err := getInstallationPosition(employee.Id, &event) if err != nil { return errorGetPosition } // specialist position specialistPositions, err := getSpecialistPosition(employee.Id, &event) if err != nil { return errorGetPosition } // subspecialist position subspecialistPositions, err := getSubspecialistPosition(employee.Id, &event) if err != nil { return errorGetPosition } roles = append(roles, divisionPositions...) roles = append(roles, installationPositions...) roles = append(roles, specialistPositions...) roles = append(roles, specialistPositions...) roles = append(roles, subspecialistPositions...) // atClaims["division_positions"] = divsionPositions // outputData["division_positions"] = divsionPositions } case erg.CSCInt: intern := intern.Intern{} dg.I.Where("\"User_Id\" = ?", user.Id).First(&intern) roles = append(roles, "int-"+string(*intern.Position_Code)) case erg.CSCSys: roles = append(roles, "system") } atClaims["roles"] = roles outputData["roles"] = roles // Generate jwt atSecretKey := authCfg.AtSecretKey at := jwt.NewWithClaims(jwt.SigningMethodHS256, atClaims) ats, err := at.SignedString([]byte(atSecretKey)) if err != nil { return d.FieldErrors{"user": d.FieldError{Code: "token-sign-err", Message: pl.GenMessage("token-sign-err")}} } outputData["accessToken"] = ats // Save to redis exp, _ := atClaims["exp"].(int64) now := time.Now() atx := time.Unix(exp, 0) //converting Unix to UTC(to Time object) err = ms.I.Set(atClaims["uuid"].(string), strconv.Itoa(int(user.Id)), atx.Sub(now)).Err() if err != nil { panic(fmt.Sprintf(l.I.Msg("redis-store-fail"), err.Error())) } return nil } func getAuthByUserName(username string) (auth *pa.AuthInfo, err error) { // get employee emp := ee.Employee{} if err = dg.I. Joins(`JOIN "User" u ON u."Id" = "Employee"."User_Id"`). Where(`u."Name" = ?`, username). Scopes(gh.Preload("User")). First(&emp).Error; err != nil { return nil, err } auth = &pa.AuthInfo{ User_Id: *emp.User_Id, User_Name: emp.User.Name, User_ContractPosition_Code: string(emp.User.ContractPosition_Code), Employee_Position_Code: strPtr(string(*emp.Position_Code)), Employee_Id: &emp.Id, Sync: true, } // set map table tableMap := map[erg.EmployeePositionCode]string{ erg.EPCDoc: "Doctor", erg.EPCNur: "Nurse", erg.EPCMwi: "Midwife", erg.EPCNut: "Nutritionist", erg.EPCLab: "Laborant", erg.EPCPha: "Pharmachist", } table := tableMap[*emp.Position_Code] if table == "" { return } type CodeResult struct { Code string } var result CodeResult err = dg.I.Table(table). Select("Code"). Where("\"Employee_Id\" = ?", emp.Id). First(&result).Error if err != nil { return nil, err } // set map for code setterMap := map[erg.EmployeePositionCode]func(string){ erg.EPCDoc: func(v string) { auth.Doctor_Code = &v }, erg.EPCNur: func(v string) { auth.Nurse_Code = &v }, erg.EPCMwi: func(v string) { auth.Midwife_Code = &v }, erg.EPCNut: func(v string) { auth.Nutritionist_Code = &v }, erg.EPCLab: func(v string) { auth.Laborant_Code = &v }, erg.EPCPha: func(v string) { auth.Pharmachist_Code = &v }, } setter := setterMap[*emp.Position_Code] setter(result.Code) return } func strPtr(s string) *string { return &s }