package authentication import ( "fmt" "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" l "github.com/karincake/lepet" pl "simrs-vx/pkg/logger" 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" esp "simrs-vx/internal/domain/main-entities/specialist-position" essp "simrs-vx/internal/domain/main-entities/subspecialist-position" eup "simrs-vx/internal/domain/main-entities/unit-position" eu "simrs-vx/internal/domain/main-entities/user" erg "simrs-vx/internal/domain/references/organization" 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" uup "simrs-vx/internal/use-case/main-use-case/unit-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) } } return result, nil } func getInstallationPosition(employeeId uint, event *pl.Event) ([]string, error) { var result []string // get data unit_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 getUnitPosition(employeeId uint, event *pl.Event) ([]string, error) { var result []string // get data unit_position based on employee_id data, _, err := uup.ReadListData(eup.ReadListDto{FilterDto: eup.FilterDto{Employee_Id: &employeeId}}, event) if err != nil { return nil, err } if len(data) > 0 { for _, dp := range data { result = append(result, "unit-"+*dp.Unit_Code+"-"+dp.Code) } } return result, nil } func getSpecialistPosition(employeeId uint, event *pl.Event) ([]string, error) { var result []string // get data unit_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 unit_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.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 } 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 } // unit position unitPositions, err := getUnitPosition(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, unitPositions...) 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 }