diff --git a/api/Gopkg.lock b/api/Gopkg.lock index 8a0f34c..b3fcc56 100644 --- a/api/Gopkg.lock +++ b/api/Gopkg.lock @@ -124,11 +124,12 @@ [[projects]] branch = "master" - digest = "1:82e6e4dc5ab71680d89684e4649be630fdeeaf81feb8e88e4a56273a0cd4d966" + digest = "1:341ceeee37101c62dae441691406bf4ecc71bbeb7b424417879fe88d9f88f487" name = "golang.org/x/oauth2" packages = [ ".", "github", + "gitlab", "google", "internal", "jws", @@ -173,6 +174,7 @@ "golang.org/x/net/html", "golang.org/x/oauth2", "golang.org/x/oauth2/github", + "golang.org/x/oauth2/gitlab", "golang.org/x/oauth2/google", ] solver-name = "gps-cdcl" diff --git a/api/config.go b/api/config.go index 78d2c04..29a3208 100644 --- a/api/config.go +++ b/api/config.go @@ -53,6 +53,9 @@ func configParse() error { "TWITTER_KEY": "", "TWITTER_SECRET": "", + + "GITLAB_KEY": "", + "GITLAB_SECRET": "", } for key, value := range defaults { diff --git a/api/oauth.go b/api/oauth.go index 5550cee..d3a80a6 100644 --- a/api/oauth.go +++ b/api/oauth.go @@ -19,5 +19,9 @@ func oauthConfigure() error { return err } + if err := gitlabOauthConfigure(); err != nil { + return err + } + return nil } diff --git a/api/oauth_gitlab.go b/api/oauth_gitlab.go new file mode 100644 index 0000000..ba882c2 --- /dev/null +++ b/api/oauth_gitlab.go @@ -0,0 +1,42 @@ +package main + +import ( + "golang.org/x/oauth2" + "golang.org/x/oauth2/gitlab" + "os" +) + +var gitlabConfig *oauth2.Config + +func gitlabOauthConfigure() error { + gitlabConfig = nil + if os.Getenv("GITLAB_KEY") == "" && os.Getenv("GITLAB_SECRET") == "" { + return nil + } + + if os.Getenv("GITLAB_KEY") == "" { + logger.Errorf("COMMENTO_GITLAB_KEY not configured, but COMMENTO_GITLAB_SECRET is set") + return errorOauthMisconfigured + } + + if os.Getenv("GITLAB_SECRET") == "" { + logger.Errorf("COMMENTO_GITLAB_SECRET not configured, but COMMENTO_GITLAB_KEY is set") + return errorOauthMisconfigured + } + + logger.Infof("loading gitlab OAuth config") + + gitlabConfig = &oauth2.Config{ + RedirectURL: os.Getenv("ORIGIN") + "/api/oauth/gitlab/callback", + ClientID: os.Getenv("GITLAB_KEY"), + ClientSecret: os.Getenv("GITLAB_SECRET"), + Scopes: []string{ + "read_user", + }, + Endpoint: gitlab.Endpoint, + } + + configuredOauths = append(configuredOauths, "gitlab") + + return nil +} diff --git a/api/oauth_gitlab_callback.go b/api/oauth_gitlab_callback.go new file mode 100644 index 0000000..96941f8 --- /dev/null +++ b/api/oauth_gitlab_callback.go @@ -0,0 +1,96 @@ +package main + +import ( + "encoding/json" + "fmt" + "golang.org/x/oauth2" + "io/ioutil" + "net/http" +) + +func gitlabCallbackHandler(w http.ResponseWriter, r *http.Request) { + commenterToken := r.FormValue("state") + code := r.FormValue("code") + + _, err := commenterGetByCommenterToken(commenterToken) + if err != nil && err != errorNoSuchToken { + fmt.Fprintf(w, "Error: %s\n", err.Error()) + return + } + + token, err := gitlabConfig.Exchange(oauth2.NoContext, code) + if err != nil { + fmt.Fprintf(w, "Error: %s", err.Error()) + return + } + + resp, err := http.Get("https://gitlab.com/api/v4/user?access_token=" + token.AccessToken) + if err != nil { + fmt.Fprintf(w, "Error: %s", err.Error()) + return + } + logger.Infof("%v", resp.StatusCode) + defer resp.Body.Close() + + contents, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Fprintf(w, "Error: %s", errorCannotReadResponse.Error()) + return + } + + user := make(map[string]interface{}) + if err := json.Unmarshal(contents, &user); err != nil { + fmt.Fprintf(w, "Error: %s", errorInternal.Error()) + return + } + + if user["email"] == nil { + fmt.Fprintf(w, "Error: no email address returned by Gitlab") + return + } + + email := user["email"].(string) + + if user["name"] == nil { + fmt.Fprintf(w, "Error: no name returned by Gitlab") + return + } + + name := user["name"].(string) + + link := "undefined" + if user["web_url"] != nil { + link = user["web_url"].(string) + } + + photo := "undefined" + if user["avatar_url"] != nil { + photo = user["avatar_url"].(string) + } + + c, err := commenterGetByEmail("gitlab", email) + if err != nil && err != errorNoSuchCommenter { + fmt.Fprintf(w, "Error: %s", err.Error()) + return + } + + var commenterHex string + + // TODO: in case of returning users, update the information we have on record? + if err == errorNoSuchCommenter { + commenterHex, err = commenterNew(email, name, link, photo, "gitlab", "") + if err != nil { + fmt.Fprintf(w, "Error: %s", err.Error()) + return + } + } else { + commenterHex = c.CommenterHex + } + + if err := commenterSessionUpdate(commenterToken, commenterHex); err != nil { + fmt.Fprintf(w, "Error: %s", err.Error()) + return + } + + fmt.Fprintf(w, "") +} diff --git a/api/oauth_gitlab_redirect.go b/api/oauth_gitlab_redirect.go new file mode 100644 index 0000000..3ed794f --- /dev/null +++ b/api/oauth_gitlab_redirect.go @@ -0,0 +1,25 @@ +package main + +import ( + "fmt" + "net/http" +) + +func gitlabRedirectHandler(w http.ResponseWriter, r *http.Request) { + if gitlabConfig == nil { + logger.Errorf("gitlab oauth access attempt without configuration") + fmt.Fprintf(w, "error: this website has not configured gitlab OAuth") + return + } + + commenterToken := r.FormValue("commenterToken") + + _, err := commenterGetByCommenterToken(commenterToken) + if err != nil && err != errorNoSuchToken { + fmt.Fprintf(w, "error: %s\n", err.Error()) + return + } + + url := gitlabConfig.AuthCodeURL(commenterToken) + http.Redirect(w, r, url, http.StatusFound) +} diff --git a/api/router_api.go b/api/router_api.go index 96fa6bb..ae3c1b0 100644 --- a/api/router_api.go +++ b/api/router_api.go @@ -41,6 +41,9 @@ func apiRouterInit(router *mux.Router) error { router.HandleFunc("/api/oauth/twitter/redirect", twitterRedirectHandler).Methods("GET") router.HandleFunc("/api/oauth/twitter/callback", twitterCallbackHandler).Methods("GET") + router.HandleFunc("/api/oauth/gitlab/redirect", gitlabRedirectHandler).Methods("GET") + router.HandleFunc("/api/oauth/gitlab/callback", gitlabCallbackHandler).Methods("GET") + router.HandleFunc("/api/comment/new", commentNewHandler).Methods("POST") router.HandleFunc("/api/comment/list", commentListHandler).Methods("POST") router.HandleFunc("/api/comment/count", commentCountHandler).Methods("POST") diff --git a/frontend/sass/commento-oauth.scss b/frontend/sass/commento-oauth.scss index 7895026..7dc9a5a 100644 --- a/frontend/sass/commento-oauth.scss +++ b/frontend/sass/commento-oauth.scss @@ -32,5 +32,12 @@ font-size: 13px; width: 70px; } + + .commento-gitlab-button { + background: #fc6d26; + text-transform: uppercase; + font-size: 13px; + width: 70px; + } } }