mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-03 06:07:20 +02:00

- Added struct for oauth - Added interception handler for Zoraxy SSO - Added user structure for SSO
159 lines
4.5 KiB
Go
159 lines
4.5 KiB
Go
package sso
|
|
|
|
import (
|
|
"embed"
|
|
"net/http"
|
|
|
|
"github.com/gorilla/sessions"
|
|
"imuslab.com/zoraxy/mod/database"
|
|
"imuslab.com/zoraxy/mod/info/logger"
|
|
)
|
|
|
|
/*
|
|
sso.go
|
|
|
|
This file contains the main SSO handler and the SSO configuration
|
|
structure. It also contains the main SSO handler functions.
|
|
|
|
SSO web interface are stored in the static folder, which is embedded
|
|
into the binary.
|
|
*/
|
|
|
|
//go:embed static/*
|
|
var staticFiles embed.FS //Static files for the SSO portal
|
|
|
|
type SSOConfig struct {
|
|
SystemUUID string //System UUID, should be passed in from main scope
|
|
AuthURL string //Authentication subdomain URL, e.g. auth.example.com
|
|
PortalServerPort int //SSO portal server port
|
|
Database *database.Database //System master key-value database
|
|
Logger *logger.Logger
|
|
}
|
|
|
|
// SSOHandler is the main SSO handler structure
|
|
type SSOHandler struct {
|
|
cookieStore *sessions.CookieStore
|
|
ssoPortalServer *http.Server
|
|
ssoPortalMux *http.ServeMux
|
|
Oauth2Server *OAuth2Server
|
|
Config *SSOConfig
|
|
Apps map[string]RegisteredUpstreamApp
|
|
}
|
|
|
|
// Create a new Zoraxy SSO handler
|
|
func NewSSOHandler(config *SSOConfig) (*SSOHandler, error) {
|
|
//Create a cookie store for the SSO handler
|
|
cookieStore := sessions.NewCookieStore([]byte(config.SystemUUID))
|
|
cookieStore.Options = &sessions.Options{
|
|
Path: "",
|
|
Domain: "",
|
|
MaxAge: 0,
|
|
Secure: false,
|
|
HttpOnly: false,
|
|
SameSite: 0,
|
|
}
|
|
|
|
config.Database.NewTable("sso_users") //For storing user information
|
|
config.Database.NewTable("sso_conf") //For storing SSO configuration
|
|
config.Database.NewTable("sso_apps") //For storing registered apps
|
|
|
|
//Create the SSO Handler
|
|
thisHandler := SSOHandler{
|
|
cookieStore: cookieStore,
|
|
Config: config,
|
|
}
|
|
|
|
//Read the app info from database
|
|
thisHandler.Apps = make(map[string]RegisteredUpstreamApp)
|
|
|
|
//Create an oauth2 server
|
|
oauth2Server, err := NewOAuth2Server(config, &thisHandler)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
//Register endpoints
|
|
thisHandler.Oauth2Server = oauth2Server
|
|
thisHandler.InitSSOPortal(config.PortalServerPort)
|
|
|
|
return &thisHandler, nil
|
|
}
|
|
|
|
func (h *SSOHandler) RestorePreviousRunningState() {
|
|
//Load the previous SSO state
|
|
ssoEnabled := false
|
|
ssoPort := 5488
|
|
ssoAuthURL := ""
|
|
h.Config.Database.Read("sso_conf", "enabled", &ssoEnabled)
|
|
h.Config.Database.Read("sso_conf", "port", &ssoPort)
|
|
h.Config.Database.Read("sso_conf", "authurl", &ssoAuthURL)
|
|
|
|
if ssoAuthURL == "" {
|
|
//Cannot enable SSO without auth URL
|
|
ssoEnabled = false
|
|
}
|
|
|
|
h.Config.PortalServerPort = ssoPort
|
|
h.Config.AuthURL = ssoAuthURL
|
|
|
|
if ssoEnabled {
|
|
go h.StartSSOPortal()
|
|
}
|
|
}
|
|
|
|
// ServeForwardAuth handle the SSO request in interception mode
|
|
// Suppose to be called in dynamicproxy.
|
|
// Return true if the request is allowed to pass, false if the request is blocked and shall not be further processed
|
|
func (h *SSOHandler) ServeForwardAuth(w http.ResponseWriter, r *http.Request) bool {
|
|
//Get the current uri for appending to the auth subdomain
|
|
originalRequestURL := r.RequestURI
|
|
|
|
redirectAuthURL := h.Config.AuthURL
|
|
if redirectAuthURL == "" || !h.IsRunning() {
|
|
//Redirect not set or auth server is offlined
|
|
w.Write([]byte("SSO auth URL not set or SSO server offline."))
|
|
//TODO: Use better looking template if exists
|
|
return false
|
|
}
|
|
|
|
//Check if the user have the cookie "Zoraxy-SSO" set
|
|
session, err := h.cookieStore.Get(r, "Zoraxy-SSO")
|
|
if err != nil {
|
|
//Redirect to auth subdomain
|
|
http.Redirect(w, r, redirectAuthURL+"/sso/login?m=new&t="+originalRequestURL, http.StatusFound)
|
|
return false
|
|
}
|
|
|
|
//Check if the user is logged in
|
|
if session.Values["username"] != true {
|
|
//Redirect to auth subdomain
|
|
http.Redirect(w, r, redirectAuthURL+"/sso/login?m=expired&t="+originalRequestURL, http.StatusFound)
|
|
return false
|
|
}
|
|
|
|
//Check if the current request subdomain is allowed
|
|
userName := session.Values["username"].(string)
|
|
user, err := h.GetSSOUser(userName)
|
|
if err != nil {
|
|
//User might have been removed from SSO. Redirect to auth subdomain
|
|
http.Redirect(w, r, redirectAuthURL, http.StatusFound)
|
|
return false
|
|
}
|
|
|
|
//Check if the user have access to the current subdomain
|
|
if !user.Subdomains[r.Host].AllowAccess {
|
|
//User is not allowed to access the current subdomain. Sent 403
|
|
http.Error(w, "Forbidden", http.StatusForbidden)
|
|
//TODO: Use better looking template if exists
|
|
return false
|
|
}
|
|
|
|
//User is logged in, continue to the next handler
|
|
return true
|
|
}
|
|
|
|
// Log a message with the SSO module tag
|
|
func (h *SSOHandler) Log(message string, err error) {
|
|
h.Config.Logger.PrintAndLog("SSO", message, err)
|
|
}
|