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 Logger *logger.Logger } type SSOHandler struct { cookieStore *sessions.CookieStore ssoPortalServer *http.Server ssoPortalMux *http.ServeMux Oauth2Server *OAuth2Server Config *SSOConfig } // 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, } //Create a table for the new sso user management system err := config.Database.NewTable("sso_users") if err != nil { return nil, err } //Create the SSO Handler thisHandler := SSOHandler{ cookieStore: cookieStore, Config: config, } //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 } // ServeForwardAuth handle the SSO request by forwarding auth to the authelia server // return false if the request is not authorized and shall not be proceed // Note that only accounts that have SSO enabled will be handled by this handler func (h *SSOHandler) ServeForwardAuth(w http.ResponseWriter, r *http.Request) bool { //Get the current uri for appending to the auth subdomain originalRequestURL := r.RequestURI //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, h.Config.AuthURL+"?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, h.Config.AuthURL+"?m=expired&t="+originalRequestURL, http.StatusFound) return false } //Check if the current request subdomain is allowed userName := session.Values["username"].(string) user, err := h.SSO_GetUser(userName) if err != nil { //User might have been removed from SSO. Redirect to auth subdomain http.Redirect(w, r, h.Config.AuthURL, 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) }