sso.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. package sso
  2. import (
  3. "embed"
  4. "net/http"
  5. "github.com/gorilla/sessions"
  6. "imuslab.com/zoraxy/mod/database"
  7. "imuslab.com/zoraxy/mod/info/logger"
  8. )
  9. /*
  10. sso.go
  11. This file contains the main SSO handler and the SSO configuration
  12. structure. It also contains the main SSO handler functions.
  13. SSO web interface are stored in the static folder, which is embedded
  14. into the binary.
  15. */
  16. //go:embed static/*
  17. var staticFiles embed.FS //Static files for the SSO portal
  18. type SSOConfig struct {
  19. SystemUUID string //System UUID, should be passed in from main scope
  20. AuthURL string //Authentication subdomain URL, e.g. auth.example.com
  21. PortalServerPort int //SSO portal server port
  22. Database *database.Database //System master key-value database
  23. Logger *logger.Logger
  24. }
  25. // SSOHandler is the main SSO handler structure
  26. type SSOHandler struct {
  27. cookieStore *sessions.CookieStore
  28. ssoPortalServer *http.Server
  29. ssoPortalMux *http.ServeMux
  30. Oauth2Server *OAuth2Server
  31. Config *SSOConfig
  32. Apps map[string]RegisteredUpstreamApp
  33. }
  34. // Create a new Zoraxy SSO handler
  35. func NewSSOHandler(config *SSOConfig) (*SSOHandler, error) {
  36. //Create a cookie store for the SSO handler
  37. cookieStore := sessions.NewCookieStore([]byte(config.SystemUUID))
  38. cookieStore.Options = &sessions.Options{
  39. Path: "",
  40. Domain: "",
  41. MaxAge: 0,
  42. Secure: false,
  43. HttpOnly: false,
  44. SameSite: 0,
  45. }
  46. config.Database.NewTable("sso_users") //For storing user information
  47. config.Database.NewTable("sso_conf") //For storing SSO configuration
  48. config.Database.NewTable("sso_apps") //For storing registered apps
  49. //Create the SSO Handler
  50. thisHandler := SSOHandler{
  51. cookieStore: cookieStore,
  52. Config: config,
  53. }
  54. //Read the app info from database
  55. thisHandler.Apps = make(map[string]RegisteredUpstreamApp)
  56. //Create an oauth2 server
  57. oauth2Server, err := NewOAuth2Server(config, &thisHandler)
  58. if err != nil {
  59. return nil, err
  60. }
  61. //Register endpoints
  62. thisHandler.Oauth2Server = oauth2Server
  63. thisHandler.InitSSOPortal(config.PortalServerPort)
  64. return &thisHandler, nil
  65. }
  66. func (h *SSOHandler) RestorePreviousRunningState() {
  67. //Load the previous SSO state
  68. ssoEnabled := false
  69. ssoPort := 5488
  70. ssoAuthURL := ""
  71. h.Config.Database.Read("sso_conf", "enabled", &ssoEnabled)
  72. h.Config.Database.Read("sso_conf", "port", &ssoPort)
  73. h.Config.Database.Read("sso_conf", "authurl", &ssoAuthURL)
  74. if ssoAuthURL == "" {
  75. //Cannot enable SSO without auth URL
  76. ssoEnabled = false
  77. }
  78. h.Config.PortalServerPort = ssoPort
  79. h.Config.AuthURL = ssoAuthURL
  80. if ssoEnabled {
  81. go h.StartSSOPortal()
  82. }
  83. }
  84. // ServeForwardAuth handle the SSO request in interception mode
  85. // Suppose to be called in dynamicproxy.
  86. // Return true if the request is allowed to pass, false if the request is blocked and shall not be further processed
  87. func (h *SSOHandler) ServeForwardAuth(w http.ResponseWriter, r *http.Request) bool {
  88. //Get the current uri for appending to the auth subdomain
  89. originalRequestURL := r.RequestURI
  90. redirectAuthURL := h.Config.AuthURL
  91. if redirectAuthURL == "" || !h.IsRunning() {
  92. //Redirect not set or auth server is offlined
  93. w.Write([]byte("SSO auth URL not set or SSO server offline."))
  94. //TODO: Use better looking template if exists
  95. return false
  96. }
  97. //Check if the user have the cookie "Zoraxy-SSO" set
  98. session, err := h.cookieStore.Get(r, "Zoraxy-SSO")
  99. if err != nil {
  100. //Redirect to auth subdomain
  101. http.Redirect(w, r, redirectAuthURL+"/sso/login?m=new&t="+originalRequestURL, http.StatusFound)
  102. return false
  103. }
  104. //Check if the user is logged in
  105. if session.Values["username"] != true {
  106. //Redirect to auth subdomain
  107. http.Redirect(w, r, redirectAuthURL+"/sso/login?m=expired&t="+originalRequestURL, http.StatusFound)
  108. return false
  109. }
  110. //Check if the current request subdomain is allowed
  111. userName := session.Values["username"].(string)
  112. user, err := h.GetSSOUser(userName)
  113. if err != nil {
  114. //User might have been removed from SSO. Redirect to auth subdomain
  115. http.Redirect(w, r, redirectAuthURL, http.StatusFound)
  116. return false
  117. }
  118. //Check if the user have access to the current subdomain
  119. if !user.Subdomains[r.Host].AllowAccess {
  120. //User is not allowed to access the current subdomain. Sent 403
  121. http.Error(w, "Forbidden", http.StatusForbidden)
  122. //TODO: Use better looking template if exists
  123. return false
  124. }
  125. //User is logged in, continue to the next handler
  126. return true
  127. }
  128. // Log a message with the SSO module tag
  129. func (h *SSOHandler) Log(message string, err error) {
  130. h.Config.Logger.PrintAndLog("SSO", message, err)
  131. }