handlers.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. package sso
  2. /*
  3. handlers.go
  4. This file contains the handlers for the SSO module.
  5. If you are looking for handlers for SSO user management,
  6. please refer to userHandlers.go.
  7. */
  8. import (
  9. "encoding/json"
  10. "net/http"
  11. "strings"
  12. "github.com/gofrs/uuid"
  13. "imuslab.com/zoraxy/mod/utils"
  14. )
  15. // HandleSSOStatus handle the request to get the status of the SSO portal server
  16. func (s *SSOHandler) HandleSSOStatus(w http.ResponseWriter, r *http.Request) {
  17. type SSOStatus struct {
  18. Enabled bool
  19. SSOInterceptEnabled bool
  20. ListeningPort int
  21. AuthURL string
  22. }
  23. status := SSOStatus{
  24. Enabled: s.ssoPortalServer != nil,
  25. //SSOInterceptEnabled: s.ssoInterceptEnabled,
  26. ListeningPort: s.Config.PortalServerPort,
  27. AuthURL: s.Config.AuthURL,
  28. }
  29. js, _ := json.Marshal(status)
  30. utils.SendJSONResponse(w, string(js))
  31. }
  32. // Wrapper for starting and stopping the SSO portal server
  33. // require POST request with key "enable" and value "true" or "false"
  34. func (s *SSOHandler) HandleSSOEnable(w http.ResponseWriter, r *http.Request) {
  35. enable, err := utils.PostBool(r, "enable")
  36. if err != nil {
  37. utils.SendErrorResponse(w, "invalid enable value")
  38. return
  39. }
  40. if enable {
  41. s.HandleStartSSOPortal(w, r)
  42. } else {
  43. s.HandleStopSSOPortal(w, r)
  44. }
  45. }
  46. // HandleStartSSOPortal handle the request to start the SSO portal server
  47. func (s *SSOHandler) HandleStartSSOPortal(w http.ResponseWriter, r *http.Request) {
  48. if s.ssoPortalServer != nil {
  49. //Already enabled. Do restart instead.
  50. err := s.RestartSSOServer()
  51. if err != nil {
  52. utils.SendErrorResponse(w, "failed to start SSO server")
  53. return
  54. }
  55. utils.SendOK(w)
  56. return
  57. }
  58. //Check if the authURL is set correctly. If not, return error
  59. if s.Config.AuthURL == "" {
  60. utils.SendErrorResponse(w, "auth URL not set")
  61. return
  62. }
  63. //Start the SSO portal server in go routine
  64. go s.StartSSOPortal()
  65. //Write current state to database
  66. err := s.Config.Database.Write("sso_conf", "enabled", true)
  67. if err != nil {
  68. utils.SendErrorResponse(w, "failed to update SSO state")
  69. return
  70. }
  71. utils.SendOK(w)
  72. }
  73. // HandleStopSSOPortal handle the request to stop the SSO portal server
  74. func (s *SSOHandler) HandleStopSSOPortal(w http.ResponseWriter, r *http.Request) {
  75. if s.ssoPortalServer == nil {
  76. //Already disabled
  77. utils.SendOK(w)
  78. return
  79. }
  80. err := s.ssoPortalServer.Close()
  81. if err != nil {
  82. s.Log("Failed to stop SSO portal server", err)
  83. utils.SendErrorResponse(w, "failed to stop SSO portal server")
  84. return
  85. }
  86. s.ssoPortalServer = nil
  87. //Write current state to database
  88. err = s.Config.Database.Write("sso_conf", "enabled", false)
  89. if err != nil {
  90. utils.SendErrorResponse(w, "failed to update SSO state")
  91. return
  92. }
  93. utils.SendOK(w)
  94. }
  95. // HandlePortChange handle the request to change the SSO portal server port
  96. func (s *SSOHandler) HandlePortChange(w http.ResponseWriter, r *http.Request) {
  97. if r.Method == http.MethodGet {
  98. //Return the current port
  99. js, _ := json.Marshal(s.Config.PortalServerPort)
  100. utils.SendJSONResponse(w, string(js))
  101. return
  102. }
  103. port, err := utils.PostInt(r, "port")
  104. if err != nil {
  105. utils.SendErrorResponse(w, "invalid port given")
  106. return
  107. }
  108. s.Config.PortalServerPort = port
  109. //Write to the database
  110. err = s.Config.Database.Write("sso_conf", "port", port)
  111. if err != nil {
  112. utils.SendErrorResponse(w, "failed to update port")
  113. return
  114. }
  115. if s.IsRunning() {
  116. //Restart the server if it is running
  117. err = s.RestartSSOServer()
  118. if err != nil {
  119. utils.SendErrorResponse(w, "failed to restart SSO server")
  120. return
  121. }
  122. }
  123. utils.SendOK(w)
  124. }
  125. // HandleSetAuthURL handle the request to change the SSO auth URL
  126. // This is the URL that the SSO portal server will redirect to for authentication
  127. // e.g. auth.yourdomain.com
  128. func (s *SSOHandler) HandleSetAuthURL(w http.ResponseWriter, r *http.Request) {
  129. if r.Method == http.MethodGet {
  130. //Return the current auth URL
  131. js, _ := json.Marshal(s.Config.AuthURL)
  132. utils.SendJSONResponse(w, string(js))
  133. return
  134. }
  135. //Get the auth URL
  136. authURL, err := utils.PostPara(r, "auth_url")
  137. if err != nil {
  138. utils.SendErrorResponse(w, "invalid auth URL given")
  139. return
  140. }
  141. s.Config.AuthURL = authURL
  142. //Write to the database
  143. err = s.Config.Database.Write("sso_conf", "authurl", authURL)
  144. if err != nil {
  145. utils.SendErrorResponse(w, "failed to update auth URL")
  146. return
  147. }
  148. //Clear the cookie store and restart the server
  149. err = s.RestartSSOServer()
  150. if err != nil {
  151. utils.SendErrorResponse(w, "failed to restart SSO server")
  152. return
  153. }
  154. utils.SendOK(w)
  155. }
  156. // HandleRegisterApp handle the request to register a new app to the SSO portal
  157. func (s *SSOHandler) HandleRegisterApp(w http.ResponseWriter, r *http.Request) {
  158. appName, err := utils.PostPara(r, "app_name")
  159. if err != nil {
  160. utils.SendErrorResponse(w, "invalid app name given")
  161. return
  162. }
  163. id, err := utils.PostPara(r, "app_id")
  164. if err != nil {
  165. //If id is not given, use the app name with a random UUID
  166. newID, err := uuid.NewV4()
  167. if err != nil {
  168. utils.SendErrorResponse(w, "failed to generate new app ID")
  169. return
  170. }
  171. id = strings.ReplaceAll(appName, " ", "") + "-" + newID.String()
  172. }
  173. //Check if the given appid is already in use
  174. if _, ok := s.Apps[id]; ok {
  175. utils.SendErrorResponse(w, "app ID already in use")
  176. return
  177. }
  178. /*
  179. Process the app domain
  180. An app can have multiple domains, separated by commas
  181. Usually the app domain is the proxy rule that points to the app
  182. For example, if the app is hosted at app.yourdomain.com, the app domain is app.yourdomain.com
  183. */
  184. appDomain, err := utils.PostPara(r, "app_domain")
  185. if err != nil {
  186. utils.SendErrorResponse(w, "invalid app URL given")
  187. return
  188. }
  189. appURLs := strings.Split(appDomain, ",")
  190. //Remove padding and trailing spaces in each URL
  191. for i := range appURLs {
  192. appURLs[i] = strings.TrimSpace(appURLs[i])
  193. }
  194. //Create a new app entry
  195. thisAppEntry := RegisteredUpstreamApp{
  196. ID: id,
  197. Secret: "",
  198. Domain: appURLs,
  199. Scopes: []string{},
  200. SessionDuration: 3600,
  201. }
  202. js, _ := json.Marshal(thisAppEntry)
  203. //Create a new app in the database
  204. err = s.Config.Database.Write("sso_apps", appName, string(js))
  205. if err != nil {
  206. utils.SendErrorResponse(w, "failed to create new app")
  207. return
  208. }
  209. //Also add the app to runtime config
  210. s.Apps[appName] = thisAppEntry
  211. utils.SendOK(w)
  212. }
  213. // HandleAppRemove handle the request to remove an app from the SSO portal
  214. func (s *SSOHandler) HandleAppRemove(w http.ResponseWriter, r *http.Request) {
  215. appID, err := utils.PostPara(r, "app_id")
  216. if err != nil {
  217. utils.SendErrorResponse(w, "invalid app ID given")
  218. return
  219. }
  220. //Check if the app actually exists
  221. if _, ok := s.Apps[appID]; !ok {
  222. utils.SendErrorResponse(w, "app not found")
  223. return
  224. }
  225. delete(s.Apps, appID)
  226. //Also remove it from the database
  227. err = s.Config.Database.Delete("sso_apps", appID)
  228. if err != nil {
  229. s.Log("Failed to remove app from database", err)
  230. }
  231. }