oauth2.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. package oauth2
  2. import (
  3. "encoding/json"
  4. "io/ioutil"
  5. "log"
  6. "net/http"
  7. "strconv"
  8. "time"
  9. "golang.org/x/oauth2"
  10. "golang.org/x/oauth2/google"
  11. auth "imuslab.com/arozos/mod/auth"
  12. syncdb "imuslab.com/arozos/mod/auth/oauth2/syncdb"
  13. reg "imuslab.com/arozos/mod/auth/register"
  14. db "imuslab.com/arozos/mod/database"
  15. )
  16. type OauthHandler struct {
  17. googleOauthConfig *oauth2.Config
  18. syncDb *syncdb.SyncDB
  19. oauthStateString string
  20. DefaultUserGroup string
  21. ag *auth.AuthAgent
  22. reg *reg.RegisterHandler
  23. coredb *db.Database
  24. config *Config
  25. }
  26. type GoogleField struct {
  27. ID string `json:"id"`
  28. Email string `json:"email"`
  29. VerifiedEmail bool `json:"verified_email"`
  30. Name string `json:"name"`
  31. GivenName string `json:"given_name"`
  32. FamilyName string `json:"family_name"`
  33. Picture string `json:"picture"`
  34. Locale string `json:"locale"`
  35. }
  36. type Config struct {
  37. Enabled bool `json:"enabled"`
  38. IDP string `json:"idp"`
  39. RedirectURL string `json:"redirect_url"`
  40. ClientID string `json:"client_id"`
  41. ClientSecret string `json:"client_secret"`
  42. DefaultUserGroup string `json:"default_user_group"`
  43. }
  44. //NewOauthHandler xxx
  45. func NewOauthHandler(authAgent *auth.AuthAgent, register *reg.RegisterHandler, coreDb *db.Database) *OauthHandler {
  46. err := coreDb.NewTable("oauth")
  47. if err != nil {
  48. log.Println("Failed to create oauth database. Terminating.")
  49. panic(err)
  50. }
  51. NewlyCreatedOauthHandler := OauthHandler{
  52. googleOauthConfig: &oauth2.Config{
  53. RedirectURL: readSingleConfig("redirecturl", coreDb) + "/system/auth/oauth/authorize",
  54. ClientID: readSingleConfig("clientid", coreDb),
  55. ClientSecret: readSingleConfig("clientsecret", coreDb),
  56. Scopes: []string{"https://www.googleapis.com/auth/userinfo.profile",
  57. "https://www.googleapis.com/auth/userinfo.email"},
  58. Endpoint: google.Endpoint,
  59. },
  60. DefaultUserGroup: readSingleConfig("defaultusergroup", coreDb),
  61. ag: authAgent,
  62. syncDb: syncdb.NewSyncDB(),
  63. reg: register,
  64. coredb: coreDb,
  65. }
  66. return &NewlyCreatedOauthHandler
  67. }
  68. //HandleOauthLogin xxx
  69. func (oh *OauthHandler) HandleLogin(w http.ResponseWriter, r *http.Request) {
  70. //add cookies
  71. redirect, e := r.URL.Query()["redirect"]
  72. uuid := ""
  73. if !e || len(redirect[0]) < 1 {
  74. uuid = oh.syncDb.Store("/")
  75. } else {
  76. uuid = oh.syncDb.Store(redirect[0])
  77. }
  78. oh.addCookie(w, "uuid_login", uuid, 30*time.Minute)
  79. //handle redirect
  80. url := oh.googleOauthConfig.AuthCodeURL(uuid)
  81. http.Redirect(w, r, url, http.StatusTemporaryRedirect)
  82. }
  83. //OauthAuthorize xxx
  84. func (oh *OauthHandler) HandleAuthorize(w http.ResponseWriter, r *http.Request) {
  85. //read the uuid(aka the state parameter)
  86. uuid, err := r.Cookie("uuid_login")
  87. if err != nil {
  88. sendTextResponse(w, "Invalid redirect URI.")
  89. }
  90. state := r.FormValue("state")
  91. if state != uuid.Value {
  92. sendTextResponse(w, "Invalid oauth state.")
  93. return
  94. }
  95. code := r.FormValue("code")
  96. token, err := oh.googleOauthConfig.Exchange(oauth2.NoContext, code)
  97. if err != nil {
  98. sendTextResponse(w, "Code exchange failed.")
  99. return
  100. }
  101. response, err := http.Get("https://www.googleapis.com/oauth2/v2/userinfo?access_token=" + token.AccessToken)
  102. defer response.Body.Close()
  103. contents, err := ioutil.ReadAll(response.Body)
  104. var data GoogleField
  105. json.Unmarshal([]byte(contents), &data)
  106. if !oh.ag.UserExists(data.Email) {
  107. //register user if not already exists
  108. //random pwd to prevent ppl bypassing the OAuth handler
  109. if oh.reg.AllowRegistry {
  110. http.Redirect(w, r, "/public/register/register.system?user="+data.Email, 302)
  111. } else {
  112. sendTextResponse(w, "You are not allowed to register in this system.&nbsp;<a href=\"/\">Back</a>")
  113. }
  114. } else {
  115. log.Println(data.Email + " logged in via OAuth.")
  116. oh.ag.LoginUserByRequest(w, r, data.Email, true)
  117. //clear the cooke
  118. oh.addCookie(w, "uuid_login", "-invaild-", -1)
  119. //read the value from db and delete it from db
  120. url := oh.syncDb.Read(uuid.Value)
  121. oh.syncDb.Delete(uuid.Value)
  122. //redirect to the desired page
  123. http.Redirect(w, r, url, 302)
  124. }
  125. }
  126. func (oh *OauthHandler) CheckOAuth(w http.ResponseWriter, r *http.Request) {
  127. enabled := oh.readSingleConfig("enabled")
  128. if enabled == "" {
  129. enabled = "false"
  130. }
  131. sendJSONResponse(w, enabled)
  132. }
  133. //https://golangcode.com/add-a-http-cookie/
  134. func (oh *OauthHandler) addCookie(w http.ResponseWriter, name, value string, ttl time.Duration) {
  135. expire := time.Now().Add(ttl)
  136. cookie := http.Cookie{
  137. Name: name,
  138. Value: value,
  139. Expires: expire,
  140. }
  141. http.SetCookie(w, &cookie)
  142. }
  143. func (oh *OauthHandler) ReadConfig(w http.ResponseWriter, r *http.Request) {
  144. enabled, _ := strconv.ParseBool(oh.readSingleConfig("enabled"))
  145. idp := oh.readSingleConfig("idp")
  146. redirecturl := oh.readSingleConfig("redirecturl")
  147. clientid := oh.readSingleConfig("clientid")
  148. clientsecret := oh.readSingleConfig("clientsecret")
  149. defaultusergroup := oh.readSingleConfig("defaultusergroup")
  150. config, err := json.Marshal(Config{
  151. Enabled: enabled,
  152. IDP: idp,
  153. RedirectURL: redirecturl,
  154. ClientID: clientid,
  155. ClientSecret: clientsecret,
  156. DefaultUserGroup: defaultusergroup,
  157. })
  158. if err != nil {
  159. empty, _ := json.Marshal(Config{})
  160. sendJSONResponse(w, string(empty))
  161. }
  162. sendJSONResponse(w, string(config))
  163. }
  164. func (oh *OauthHandler) WriteConfig(w http.ResponseWriter, r *http.Request) {
  165. enabled, err := mv(r, "enabled", true)
  166. if err != nil {
  167. sendTextResponse(w, "enabled field can't be empty'")
  168. }
  169. idp, err := mv(r, "idp", true)
  170. if err != nil {
  171. sendTextResponse(w, "idp field can't be empty'")
  172. }
  173. redirecturl, err := mv(r, "redirecturl", true)
  174. if err != nil {
  175. sendTextResponse(w, "redirecturl field can't be empty'")
  176. }
  177. clientid, err := mv(r, "clientid", true)
  178. if err != nil {
  179. sendTextResponse(w, "clientid field can't be empty'")
  180. }
  181. clientsecret, err := mv(r, "clientsecret", true)
  182. if err != nil {
  183. sendTextResponse(w, "clientsecret field can't be empty'")
  184. }
  185. defaultusergroup, err := mv(r, "defaultusergroup", true)
  186. if err != nil {
  187. sendTextResponse(w, "defaultusergroup field can't be empty'")
  188. }
  189. oh.coredb.Write("oauth", "enabled", enabled)
  190. oh.coredb.Write("oauth", "idp", idp)
  191. oh.coredb.Write("oauth", "redirecturl", redirecturl)
  192. oh.coredb.Write("oauth", "clientid", clientid)
  193. oh.coredb.Write("oauth", "clientsecret", clientsecret)
  194. oh.coredb.Write("oauth", "defaultusergroup", defaultusergroup)
  195. //update the information inside the oauth class
  196. oh.googleOauthConfig = &oauth2.Config{
  197. RedirectURL: oh.readSingleConfig("redirecturl") + "/system/auth/oauth/authorize",
  198. ClientID: oh.readSingleConfig("clientid"),
  199. ClientSecret: oh.readSingleConfig("clientsecret"),
  200. Scopes: []string{"https://www.googleapis.com/auth/userinfo.profile",
  201. "https://www.googleapis.com/auth/userinfo.email"},
  202. Endpoint: google.Endpoint,
  203. }
  204. sendOK(w)
  205. }
  206. func (oh *OauthHandler) readSingleConfig(key string) string {
  207. var value string
  208. err := oh.coredb.Read("oauth", key, &value)
  209. if err != nil {
  210. value = ""
  211. }
  212. return value
  213. }
  214. func readSingleConfig(key string, coredb *db.Database) string {
  215. var value string
  216. err := coredb.Read("oauth", key, &value)
  217. if err != nil {
  218. value = ""
  219. }
  220. return value
  221. }