From 1e54d3bc3f68110b717e0d45d961c157b0fc31f5 Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Thu, 13 Nov 2025 14:51:45 +0700 Subject: [PATCH] fix (encounter): add guard for create, delete and cancel --- .../domain/main-entities/encounter/dto.go | 4 ++ .../main-handler/encounter/handler.go | 11 +++++ internal/lib/auth/tycovar.go | 2 +- .../main-use-case/authentication/case.go | 2 +- .../use-case/main-use-case/encounter/case.go | 48 +++++++++++++++---- pkg/use-case-helper/use-case-helper.go | 16 +++++++ 6 files changed, 73 insertions(+), 10 deletions(-) diff --git a/internal/domain/main-entities/encounter/dto.go b/internal/domain/main-entities/encounter/dto.go index d3f2d04d..f43a1de9 100644 --- a/internal/domain/main-entities/encounter/dto.go +++ b/internal/domain/main-entities/encounter/dto.go @@ -104,10 +104,14 @@ type UpdateDto struct { type UpdateStatusDto struct { Id uint16 `json:"id"` StatusCode erc.DataStatusCode `json:"status_code"` + + pa.AuthInfo } type DeleteDto struct { Id uint16 `json:"id"` + + pa.AuthInfo } type MetaDto struct { diff --git a/internal/interface/main-handler/encounter/handler.go b/internal/interface/main-handler/encounter/handler.go index 22d1d8cf..c8fcfa1a 100644 --- a/internal/interface/main-handler/encounter/handler.go +++ b/internal/interface/main-handler/encounter/handler.go @@ -89,9 +89,14 @@ func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { if id <= 0 { return } + authInfo, err := pa.GetAuthInfo(r) + if err != nil { + rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": err.Error()}, nil) + } dto := e.DeleteDto{} dto.Id = uint16(id) + dto.AuthInfo = *authInfo res, err := u.Delete(dto) rw.DataResponse(w, res, err) } @@ -159,11 +164,17 @@ func (obj myBase) Cancel(w http.ResponseWriter, r *http.Request) { return } + authInfo, err := pa.GetAuthInfo(r) + if err != nil { + rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": err.Error()}, nil) + } + dto := e.UpdateStatusDto{ Id: uint16(id), StatusCode: erc.DSCCancel, } + dto.AuthInfo = *authInfo res, err := u.UpdateStatusCode(dto) rw.DataResponse(w, res, err) } diff --git a/internal/lib/auth/tycovar.go b/internal/lib/auth/tycovar.go index 467bd1b6..3567cd61 100644 --- a/internal/lib/auth/tycovar.go +++ b/internal/lib/auth/tycovar.go @@ -11,7 +11,7 @@ type AuthInfo struct { Uuid string User_Id uint User_Name string - User_ContractPosition_code string + User_ContractPosition_Code string Employee_Position_Code *string Employee_Id *uint Doctor_Code *string diff --git a/internal/use-case/main-use-case/authentication/case.go b/internal/use-case/main-use-case/authentication/case.go index 15f18ef8..db83d315 100644 --- a/internal/use-case/main-use-case/authentication/case.go +++ b/internal/use-case/main-use-case/authentication/case.go @@ -205,7 +205,7 @@ func ExtractToken(r *http.Request, tokenType TokenType) (data *pa.AuthInfo, err User_Name: fmt.Sprintf("%v", claims["user_name"]), } - data.User_ContractPosition_code = checkStrClaims(claims, "contractPosition_code") + data.User_ContractPosition_Code = checkStrClaims(claims, "contractPosition_code") data.Employee_Position_Code = checkStrPtrClaims(claims, "employee_position_code") data.Doctor_Code = checkStrPtrClaims(claims, "doctor_code") data.Nurse_Code = checkStrPtrClaims(claims, "nurse_code") diff --git a/internal/use-case/main-use-case/encounter/case.go b/internal/use-case/main-use-case/encounter/case.go index 9381e639..8a006a35 100644 --- a/internal/use-case/main-use-case/encounter/case.go +++ b/internal/use-case/main-use-case/encounter/case.go @@ -16,10 +16,10 @@ import ( erc "simrs-vx/internal/domain/references/common" ere "simrs-vx/internal/domain/references/encounter" + erg "simrs-vx/internal/domain/references/organization" eaeh "simrs-vx/internal/domain/main-entities/adm-employee-hist" edc "simrs-vx/internal/domain/main-entities/death-cause" - eem "simrs-vx/internal/domain/main-entities/employee" e "simrs-vx/internal/domain/main-entities/encounter" eir "simrs-vx/internal/domain/main-entities/internal-reference" erdh "simrs-vx/internal/domain/main-entities/responsible-doctor-hist" @@ -27,7 +27,6 @@ import ( uaeh "simrs-vx/internal/use-case/main-use-case/adm-employee-hist" udc "simrs-vx/internal/use-case/main-use-case/death-cause" - uem "simrs-vx/internal/use-case/main-use-case/employee" uir "simrs-vx/internal/use-case/main-use-case/internal-reference" urdh "simrs-vx/internal/use-case/main-use-case/responsible-doctor-hist" ) @@ -79,6 +78,19 @@ func Create(input e.CreateDto) (*d.Data, error) { } } + // check only user with registration position is allowed to create encounter + if input.AuthInfo.User_ContractPosition_Code != string(erg.EPCReg) { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "auth-forbidden", + Detail: "user position is not allowed, only user with registration position is allowed to create encounter", + Raw: errors.New("authentication failed"), + } + return nil, pl.SetLogError(&event, input) + } else { + input.Adm_Employee_Id = input.AuthInfo.Employee_Id + } + // check if patient is new in the hospital input.NewStatus, err = identifyPatientStatus(input) @@ -90,12 +102,6 @@ func Create(input e.CreateDto) (*d.Data, error) { return err } - if emp, err := uem.ReadDetailData(eem.ReadDetailDto{User_Id: &input.AuthInfo.User_Id}, &event, tx); err != nil { - return err - } else { - input.Adm_Employee_Id = &emp.Id - } - // create encounter if resData, err := CreateData(input, &event, tx); err != nil { return err @@ -322,6 +328,17 @@ func Delete(input e.DeleteDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "delete") + // check only user with registration position is allowed to create encounter + if input.AuthInfo.User_ContractPosition_Code != string(erg.EPCReg) { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "auth-forbidden", + Detail: "user position is not allowed, only user with registration position is allowed to create encounter", + Raw: errors.New("authentication failed"), + } + return nil, pl.SetLogError(&event, input) + } + err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") if data, err = ReadDetailData(rdDto, &event, tx); err != nil { @@ -494,6 +511,21 @@ func UpdateStatusCode(input e.UpdateStatusDto) (*d.Data, error) { // TODO: Prevent cancellation if the billing has been verified // TODO: Only "supervisi pendaftaran" could cancel encounter + roleAllowedToCancel := []string{ + string(erg.EPCReg), + string(erg.EPCNur), + string(erg.EPCDoc), + } + + if !pu.Contains(roleAllowedToCancel, input.AuthInfo.User_ContractPosition_Code) { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "auth-forbidden", + Detail: "user position is not allowed, only user with registration, nurse, or doctor position is allowed to cancel encounter", + Raw: errors.New("authentication failed"), + } + return pl.SetLogError(&event, input) + } // Prevent cancellation if soapi exist encounterId := uint(input.Id) diff --git a/pkg/use-case-helper/use-case-helper.go b/pkg/use-case-helper/use-case-helper.go index a80595bb..07df05ba 100644 --- a/pkg/use-case-helper/use-case-helper.go +++ b/pkg/use-case-helper/use-case-helper.go @@ -150,3 +150,19 @@ func IsDateBeforeNow(t *time.Time) bool { } return t.Before(time.Now()) } + +// Contains reports whether v is present in s. +func Contains[S ~[]E, E comparable](s S, v E) bool { + return index(s, v) >= 0 +} + +// Index returns the index of the first occurrence of v in s, +// or -1 if not present. +func index[S ~[]E, E comparable](s S, v E) int { + for i := range s { + if v == s[i] { + return i + } + } + return -1 +}