package main import ( "context" "encoding/hex" "encoding/json" "flag" "log" "net/http" "os" "golang.org/x/oauth2" "github.com/coreos/go-oidc/v3/oidc" "github.com/gin-gonic/gin" ) const ( OAUTH_GITLAB_FILE = ".gitlab-oauth-token" ) var ( gitlabBaseURL = "https://gitlab.cri.epita.fr" gitlabClientID = "" gitlabSecret = "" gitlaboauth2Config oauth2.Config gitlaboidcVerifier *oidc.IDTokenVerifier gitlabToken *oauth2.Token ) func init() { flag.StringVar(&gitlabClientID, "gitlab-clientid", gitlabClientID, "ClientID for GitLab's OIDC") flag.StringVar(&gitlabSecret, "gitlab-secret", gitlabSecret, "Secret for GitLab's OIDC") } func initializeGitLabOIDC(router *gin.Engine, authrouter *gin.RouterGroup, adminrouter *gin.RouterGroup) { adminrouter.GET("/auth/gitlabcri", redirectOAuth_GitLab) router.GET("/callback/gitlabcri/complete", GitLab_OAuth_complete) if _, err := os.Stat(OAUTH_GITLAB_FILE); err == nil { gitlabToken, err = loadOAuth2Token(OAUTH_GITLAB_FILE) if err != nil { log.Println("Unable to load OAuth2 Token:", err.Error()) } } if gitlabClientID != "" && gitlabSecret != "" { provider, err := oidc.NewProvider(context.Background(), gitlabBaseURL) if err != nil { log.Fatal("Unable to setup oidc:", err) } gitlaboauth2Config = oauth2.Config{ ClientID: gitlabClientID, ClientSecret: gitlabSecret, RedirectURL: oidcRedirectURL + baseURL + "/callback/gitlabcri/complete", // Discovery returns the OAuth2 endpoints. Endpoint: provider.Endpoint(), // "openid" is a required scope for OpenID Connect flows. Scopes: []string{"api", "read_repository", "email"}, } oidcConfig := oidc.Config{ ClientID: gitlabClientID, } gitlaboidcVerifier = provider.Verifier(&oidcConfig) authrouter.GET("/api/gitlabcri/repositories", GitLab_getRepositories) } } func loadOAuth2Token(file string) (*oauth2.Token, error) { fd, err := os.Open(file) if err != nil { return nil, err } defer fd.Close() var tok oauth2.Token err = json.NewDecoder(fd).Decode(&tok) return &tok, err } func saveOAuth2Token(file string, tok *oauth2.Token) error { fd, err := os.Create(file) if err != nil { return err } defer fd.Close() return json.NewEncoder(fd).Encode(tok) } func redirectOAuth_GitLab(c *gin.Context) { session := c.MustGet("Session").(*Session) // Save next parameter if len(c.Request.URL.Query().Get("next")) > 0 { session.SetKey("gitlab-oidc-source", c.Request.URL.Query().Get("next")) } c.Redirect(http.StatusFound, gitlaboauth2Config.AuthCodeURL(hex.EncodeToString(session.Id))) } func GitLab_OAuth_complete(c *gin.Context) { idsession, err := hex.DecodeString(c.Request.URL.Query().Get("state")) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) return } session, err := getSession(idsession) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) return } oauth2Token, err := gitlaboauth2Config.Exchange(c.Request.Context(), c.Request.URL.Query().Get("code")) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Failed to exchange token: " + err.Error()}) return } gitlabToken = oauth2Token err = saveOAuth2Token(OAUTH_GITLAB_FILE, oauth2Token) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to save OAuth2 token: " + err.Error()}) return } log.Println("New GitLab OAuth2 session opened") if source, ok := session.GetKey("gitlab-oidc-source"); ok { session.DeleteKey("gitlab-oidc-source") c.Redirect(http.StatusFound, baseURL+source.(string)) } else { c.Redirect(http.StatusFound, baseURL+"/works") } session.Update() } func GitLab_getRepositories(c *gin.Context) { client := gitlaboauth2Config.Client(c.Request.Context(), gitlabToken) req, err := http.NewRequest("GET", gitlabBaseURL+"/api/v4/projects", nil) if err != nil { log.Println("Unable to create NewRequest before GitLab call: ", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when performing the GitLab request."}) return } resp, err := client.Do(req) if err != nil { log.Println("Unable to perform the GitLab request: ", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when performing the GitLab request."}) return } c.DataFromReader(resp.StatusCode, resp.ContentLength, resp.Header.Get("content-type"), resp.Body, nil) }