package handlers import ( "fmt" "io" "net/http" "net/url" "strings" "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/auth" "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/proxy" "github.com/go-chi/chi/v5" "go.uber.org/zap" ) // DicomHandler handles DICOM Web requests type DicomHandler struct { client *proxy.Client logger *zap.Logger } // NewDicomHandler creates a new DICOM handler func NewDicomHandler(client *proxy.Client, logger *zap.Logger) *DicomHandler { return &DicomHandler{ client: client, logger: logger, } } // ForwardRequest forwards the request to Google Healthcare API func (h *DicomHandler) ForwardRequest(w http.ResponseWriter, r *http.Request) { // Get claims from context if they exist var claims *auth.CustomClaims claimsValue := r.Context().Value("claims") if claimsValue != nil { claims = claimsValue.(*auth.CustomClaims) } // Get the path after /dicomWeb urlPath := chi.URLParam(r, "*") // If the URL parameter is empty, try to extract it from the URL path if urlPath == "" { // Remove /dicomWeb prefix from the URL prefix := "/dicomWeb" if strings.HasPrefix(r.URL.Path, prefix) { urlPath = r.URL.Path[len(prefix):] } } h.logger.Debug("Forwarding request", zap.String("path", urlPath), zap.String("method", r.Method), zap.String("url", r.URL.String()), ) // Copy query parameters queryParams := r.URL.Query() // Apply role-specific query modifications if claims != nil { switch claims.Role { case "patient": // For patients requesting study list, filter to only show their studies if strings.HasPrefix(urlPath, "/studies") && !strings.Contains(urlPath, "/") { // Check if studies are available in the claim if len(claims.StudyIUIDs) > 0 { // Remove existing StudyInstanceUID param if it exists queryParams.Del("StudyInstanceUID") // For DICOMweb, we can use comma-separated UIDs queryParams.Set("StudyInstanceUID", strings.Join(claims.StudyIUIDs, ",")) h.logger.Debug("Filtering by studies", zap.Strings("studies", claims.StudyIUIDs), ) } } else if strings.HasPrefix(urlPath, "/studies/") { // This is a request for a specific study - check if the patient is authorized // Extract the study ID from the path // Format: /studies/{studyID}/... pathParts := strings.Split(strings.TrimPrefix(urlPath, "/"), "/") if len(pathParts) >= 2 { studyID := pathParts[1] // Check if this study is in the patient's authorized studies authorized := false // Check StudyIUIDs array for _, id := range claims.StudyIUIDs { if id == studyID { authorized = true break } } // If not authorized, return 403 Forbidden if !authorized { h.logger.Warn("Unauthorized study access attempt", zap.String("studyID", studyID), zap.String("patientID", claims.PatientID), zap.Strings("authorizedStudies", claims.StudyIUIDs), ) http.Error(w, "Forbidden: You are not authorized to access this study", http.StatusForbidden) return } h.logger.Debug("Authorized access to specific study", zap.String("studyID", studyID), zap.String("patientID", claims.PatientID), ) } } case "ref_doctor": // For ref_doctor requesting study list, apply filter from token if strings.HasPrefix(urlPath, "/studies") && !strings.Contains(urlPath, "/") && claims.FilterURL != "" { // Parse the filter URL and add its query params filterURL, err := url.Parse(claims.FilterURL) if err == nil { filterQuery := filterURL.Query() for key, values := range filterQuery { for _, value := range values { queryParams.Add(key, value) } } } } case "expertise_doctor": // No restrictions for expertise_doctor } } // Add the query string back to the path encodedQuery := queryParams.Encode() if encodedQuery != "" { if !strings.HasPrefix(urlPath, "/") { urlPath = "/" + urlPath } urlPath = urlPath + "?" + encodedQuery } // Read request body if present var bodyBytes []byte if r.Body != nil && r.ContentLength > 0 { var err error bodyBytes, err = io.ReadAll(r.Body) if err != nil { h.logger.Error("Failed to read request body", zap.Error(err)) http.Error(w, "Failed to read request body", http.StatusInternalServerError) return } } // Get request headers headers := make(map[string]string) for k, v := range r.Header { if len(v) > 0 { headers[k] = v[0] } } // Forward the request to Healthcare API response, err := h.client.ForwardRequest( r.Context(), r.Method, urlPath, headers, bodyBytes, ) if err != nil { h.logger.Error("Request forwarding failed", zap.Error(err)) http.Error(w, fmt.Sprintf("Request failed: %v", err), http.StatusInternalServerError) return } // Set response headers for k, v := range response.Headers { w.Header().Set(k, v) } // Set status and write response body w.WriteHeader(response.StatusCode) w.Write(response.Body) }