Files
IRHM 2e53997f9e (refactor) Move db from service function parameters to service structs
Now that we have everything encapsulated in Services, we can move the db parameters of each function into the service struct itself which:
1. Simplified calling and reduces us passing around the db in random funcs that just pass it to a service func
2. probably other stuff
3. looks nicer now
2025-11-07 00:46:07 +00:00

92 lines
3.2 KiB
Go

// auth_proxy contains the logic for Trusted Header Authentication/SSO.
//
// This code is inherently dangerous since we are implicitly trusting
// a header for auth, so this should only be configured if you are
// certain your watcharr instance is only available behind your proxy.
package auth
import (
"errors"
"log/slog"
"github.com/sbondCo/Watcharr/config"
"github.com/sbondCo/Watcharr/database/entity"
"gorm.io/gorm"
)
type TrustedHeaderAuthLogoutDetailsResponse struct {
LogoutUrl string `json:"logoutUrl,omitempty"`
}
type TrustedHeaderService struct {
db *gorm.DB
cfg *config.ServerConfig
authService *Service
}
func NewTrustedHeaderService(db *gorm.DB, cfg *config.ServerConfig, authService *Service) *TrustedHeaderService {
return &TrustedHeaderService{
db,
cfg,
authService,
}
}
// Is trusted header auth configured on this server?
func (s *TrustedHeaderService) TrustedHeaderAuthIsEnabled() bool {
return s.cfg.HEADER_AUTH.Enabled && s.cfg.HEADER_AUTH.HeaderName != ""
}
func (s *TrustedHeaderService) SetTrustedHeaderAuthSetting(has config.TrustedHeaderAuthSetting) error {
slog.Debug("setTrustedHeaderAuthSetting: Attempting to update to new provided value", "new_value", has)
s.cfg.HEADER_AUTH = has
err := s.cfg.Write()
if err != nil {
slog.Error("setTrustedHeaderAuthSetting: Failed to write updated config!", "error", err)
return errors.New("failed to write config")
}
return nil
}
// Gets proxy logout details.
// Details are accessible to any user for the logout flow.
// If proxy configured should be checked before using this.
func (s *TrustedHeaderService) GetTrustedHeaderAuthLogoutDetails() *TrustedHeaderAuthLogoutDetailsResponse {
return &TrustedHeaderAuthLogoutDetailsResponse{
LogoutUrl: s.cfg.HEADER_AUTH.LogoutUrl,
}
}
// Login via header sso
func (s *TrustedHeaderService) LoginTrustedHeaderAuth(user *entity.User) (AuthResponse, error) {
slog.Debug("loginTrustedHeaderAuth: A user is logging in", "username_from_header", user.Username)
dbUser := new(entity.User)
res := s.db.Where("username = ? AND type = ?", user.Username, entity.PROXY_USER).Take(&dbUser)
if res.Error != nil {
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
slog.Info("loginTrustedHeaderAuth: Creating new User from authentication header", "username_from_header", user.Username)
// Record not found, so we should create the user (if configured to do so)
// dbUser will be empty, so we can just reuse it for this purpose.
dbUser.Username = user.Username
dbUser.Type = entity.PROXY_USER
dbUser.Country = &s.cfg.DEFAULT_COUNTRY
res = s.db.Create(&dbUser)
if res.Error != nil {
slog.Error("loginTrustedHeaderAuth: Failed to create new user in db", "error", res.Error)
return AuthResponse{}, errors.New("failed to create new user")
}
} else {
slog.Error("loginTrustedHeaderAuth: An error occurred when looking up user in db", "error", res.Error)
return AuthResponse{}, errors.New("error locating user in db")
}
}
token, err := s.authService.signJWT(dbUser)
if err != nil {
slog.Error("loginTrustedHeaderAuth: Failed to sign new jwt", "error", err)
return AuthResponse{}, errors.New("failed to get auth token")
}
return AuthResponse{Token: token}, nil
}