179 lines
5.2 KiB
Go
179 lines
5.2 KiB
Go
package service
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
"devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/models"
|
|
"devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/auth"
|
|
)
|
|
|
|
var (
|
|
ErrInvalidCredentials = errors.New("invalid credentials")
|
|
ErrUserNotFound = errors.New("user not found")
|
|
)
|
|
|
|
// AuthService handles authentication operations
|
|
type AuthService struct {
|
|
jwtManager *auth.JWTManager
|
|
// When you implement database connection, add a db client here
|
|
// db *sqlx.DB
|
|
}
|
|
|
|
// NewAuthService creates a new authentication service
|
|
func NewAuthService(jwtManager *auth.JWTManager) *AuthService {
|
|
return &AuthService{
|
|
jwtManager: jwtManager,
|
|
}
|
|
}
|
|
|
|
// Login authenticates a user and generates tokens
|
|
func (s *AuthService) Login(email, password string) (*models.LoginResponse, error) {
|
|
// Find user in mock data
|
|
user := models.FindUserByCredentials(email, password)
|
|
if user == nil {
|
|
return nil, ErrInvalidCredentials
|
|
}
|
|
|
|
// Create token claims based on user role
|
|
additionalClaims := make(map[string]string)
|
|
var redirectURL string
|
|
|
|
switch user.Role {
|
|
case "patient":
|
|
// Get patient data
|
|
patientData := models.FindPatientDataByUserID(user.ID)
|
|
if patientData == nil {
|
|
return nil, ErrUserNotFound
|
|
}
|
|
|
|
// Set patient-specific claims
|
|
additionalClaims["patient_id"] = patientData.PatientID
|
|
additionalClaims["patient_name"] = patientData.PatientName
|
|
additionalClaims["accession_number"] = patientData.AccessionNumber
|
|
additionalClaims["study_iuid"] = patientData.StudyIUID
|
|
additionalClaims["home_url"] = fmt.Sprintf("viewer?StudyInstanceUIDs=%s", patientData.StudyIUID)
|
|
additionalClaims["study_list"] = "disabled"
|
|
|
|
redirectURL = fmt.Sprintf("/viewer?StudyInstanceUIDs=%s", patientData.StudyIUID)
|
|
|
|
case "ref_doctor":
|
|
// Set referring doctor claims
|
|
encodedName := url.QueryEscape(user.Name)
|
|
filterURL := fmt.Sprintf("studies?limit=101&offset=0&fuzzymatching=false&includefield=00081030,00080060,00080090&00080090=%s", encodedName)
|
|
|
|
additionalClaims["home_url"] = "/"
|
|
additionalClaims["study_list"] = "enabled"
|
|
additionalClaims["filter_url"] = filterURL
|
|
|
|
redirectURL = "/"
|
|
|
|
case "expertise_doctor":
|
|
// Expertise doctors have full access
|
|
additionalClaims["home_url"] = "/"
|
|
additionalClaims["study_list"] = "enabled"
|
|
|
|
redirectURL = "/"
|
|
}
|
|
|
|
// Generate tokens
|
|
accessToken, err := s.jwtManager.GenerateAccessToken(user.ID, user.Email, user.Role, user.Name, additionalClaims)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
refreshToken, err := s.jwtManager.GenerateRefreshToken(user.ID, user.Email, user.Role, user.Name, additionalClaims)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &models.LoginResponse{
|
|
AccessToken: accessToken,
|
|
RefreshToken: refreshToken,
|
|
User: user,
|
|
RedirectURL: redirectURL,
|
|
}, nil
|
|
}
|
|
|
|
// RefreshToken generates a new access token using a refresh token
|
|
func (s *AuthService) RefreshToken(refreshToken string) (string, error) {
|
|
// Validate the refresh token
|
|
claims, err := s.jwtManager.ValidateToken(refreshToken)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// Check if token is a refresh token
|
|
if claims.TokenType != "refresh" {
|
|
return "", errors.New("invalid token type")
|
|
}
|
|
|
|
// Build additionalClaims from the refresh token
|
|
additionalClaims := make(map[string]string)
|
|
if claims.PatientID != "" {
|
|
additionalClaims["patient_id"] = claims.PatientID
|
|
}
|
|
if claims.PatientName != "" {
|
|
additionalClaims["patient_name"] = claims.PatientName
|
|
}
|
|
if claims.AccessionNumber != "" {
|
|
additionalClaims["accession_number"] = claims.AccessionNumber
|
|
}
|
|
if claims.StudyIUID != "" {
|
|
additionalClaims["study_iuid"] = claims.StudyIUID
|
|
}
|
|
if claims.HomeURL != "" {
|
|
additionalClaims["home_url"] = claims.HomeURL
|
|
}
|
|
if claims.StudyList != "" {
|
|
additionalClaims["study_list"] = claims.StudyList
|
|
}
|
|
if claims.FilterURL != "" {
|
|
additionalClaims["filter_url"] = claims.FilterURL
|
|
}
|
|
|
|
// Generate a new access token with the same claims
|
|
accessToken, err := s.jwtManager.GenerateAccessToken(claims.UserID, claims.Email, claims.Role, claims.UserName, additionalClaims)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return accessToken, nil
|
|
}
|
|
|
|
// ValidateToken validates a token and returns the claims
|
|
func (s *AuthService) ValidateToken(token string) (*auth.CustomClaims, error) {
|
|
return s.jwtManager.ValidateToken(token)
|
|
}
|
|
|
|
// HashPassword hashes a password using bcrypt
|
|
func HashPassword(password string) (string, error) {
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(hashedPassword), nil
|
|
}
|
|
|
|
// CheckPassword compares a password with a hash
|
|
func CheckPassword(password, hash string) error {
|
|
return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
|
}
|
|
|
|
// Below functions would be implemented when connecting to a real database
|
|
|
|
// storeRefreshToken stores a refresh token in the database
|
|
func (s *AuthService) storeRefreshToken(userID, token string) error {
|
|
// TODO: In a real implementation, this would insert a record in the database
|
|
return nil
|
|
}
|
|
|
|
// revokeRefreshToken marks a refresh token as revoked
|
|
func (s *AuthService) revokeRefreshToken(token string) error {
|
|
// TODO: In a real implementation, this would update a record in the database
|
|
return nil
|
|
}
|