func GetLoginToken(c *gin.Context) { in := &tokenPayload{} err := c.Bind(in) if err != nil { c.AbortWithError(http.StatusBadRequest, err) return } login, err := remote.Auth(c, in.Access, in.Refresh) if err != nil { c.AbortWithError(http.StatusUnauthorized, err) return } user, err := store.GetUserLogin(c, login) if err != nil { c.AbortWithError(http.StatusNotFound, err) return } exp := time.Now().Add(time.Hour * 72).Unix() token := token.New(token.SessToken, user.Login) tokenstr, err := token.SignExpires(user.Hash, exp) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } c.JSON(http.StatusOK, &tokenPayload{ Access: tokenstr, Expires: exp - time.Now().Unix(), }) }
func SetRepo() gin.HandlerFunc { return func(c *gin.Context) { var ( owner = c.Param("owner") name = c.Param("name") ) user := User(c) repo, err := store.GetRepoOwnerName(c, owner, name) if err == nil { c.Set("repo", repo) c.Next() return } // if the user is not nil, check the remote system // to see if the repository actually exists. If yes, // we can prompt the user to add. if user != nil { remote := remote.FromContext(c) repo, err = remote.Repo(user, owner, name) if err != nil { log.Errorf("Cannot find remote repository %s/%s for user %s. %s", owner, name, user.Login, err) } else { log.Debugf("Found remote repository %s/%s for user %s", owner, name, user.Login) } } data := gin.H{ "User": user, "Repo": repo, } // if we found a repository, we should display a page // to the user allowing them to activate. if repo != nil && len(repo.FullName) != 0 { // we should probably move this code to a // separate route, but for now we need to // add a CSRF token. data["Csrf"], _ = token.New( token.CsrfToken, user.Login, ).Sign(user.Hash) c.HTML(http.StatusNotFound, "repo_activate.html", data) } else { c.HTML(http.StatusNotFound, "404.html", data) } c.Abort() } }
func PostToken(c *gin.Context) { user := session.User(c) token := token.New(token.UserToken, user.Login) tokenstr, err := token.Sign(user.Hash) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) } else { c.String(http.StatusOK, tokenstr) } }
func ShowUser(c *gin.Context) { user := session.User(c) token, _ := token.New( token.CsrfToken, user.Login, ).Sign(user.Hash) c.HTML(200, "user.html", gin.H{ "User": user, "Csrf": token, }) }
func ShowRepoEncrypt(c *gin.Context) { user := session.User(c) repo := session.Repo(c) token, _ := token.New( token.CsrfToken, user.Login, ).Sign(user.Hash) c.HTML(200, "repo_secret.html", gin.H{ "User": user, "Repo": repo, "Csrf": token, }) }
func ShowBuild(c *gin.Context) { user := session.User(c) repo := session.Repo(c) num, _ := strconv.Atoi(c.Param("number")) seq, _ := strconv.Atoi(c.Param("job")) if seq == 0 { seq = 1 } build, err := store.GetBuildNumber(c, repo, num) if err != nil { c.AbortWithError(404, err) return } jobs, err := store.GetJobList(c, build) if err != nil { c.AbortWithError(404, err) return } var job *model.Job for _, j := range jobs { if j.Number == seq { job = j break } } httputil.SetCookie(c.Writer, c.Request, "user_last", repo.FullName) var csrf string if user != nil { csrf, _ = token.New( token.CsrfToken, user.Login, ).Sign(user.Hash) } c.HTML(200, "build.html", gin.H{ "User": user, "Repo": repo, "Build": build, "Jobs": jobs, "Job": job, "Csrf": csrf, }) }
// ShowIndex serves the main Drone application page. func ShowIndex(c *gin.Context) { user := session.User(c) var csrf string if user != nil { csrf, _ = token.New( token.CsrfToken, user.Login, ).Sign(user.Hash) } c.HTML(200, "index.html", gin.H{ "user": user, "csrf": csrf, }) }
func ShowRepoConf(c *gin.Context) { user := session.User(c) repo := session.Repo(c) token, _ := token.New( token.CsrfToken, user.Login, ).Sign(user.Hash) c.HTML(200, "repo_config.html", gin.H{ "User": user, "Repo": repo, "Csrf": token, "Link": httputil.GetURL(c.Request), }) }
func DeleteToken(c *gin.Context) { user := session.User(c) user.Hash = base32.StdEncoding.EncodeToString( securecookie.GenerateRandomKey(32), ) if err := store.UpdateUser(c, user); err != nil { c.String(500, "Error revoking tokens. %s", err) return } token := token.New(token.UserToken, user.Login) tokenstr, err := token.Sign(user.Hash) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } c.String(http.StatusOK, tokenstr) }
// Agents is a middleware function that initializes the authorization middleware // for agents to connect to the queue. func Agents(cli *cli.Context) gin.HandlerFunc { secret := cli.String("agent-secret") if secret == "" { logrus.Fatalf("failed to generate token from DRONE_AGENT_SECRET") } t := token.New(token.AgentToken, secret) s, err := t.Sign(secret) if err != nil { logrus.Fatalf("failed to generate token from DRONE_AGENT_SECRET. %s", err) } logrus.Infof("using agent secret %s", secret) logrus.Warnf("agents can connect with token %s", s) return func(c *gin.Context) { c.Set(agentKey, secret) } }
func ShowUsers(c *gin.Context) { user := session.User(c) if !user.Admin { c.AbortWithStatus(http.StatusForbidden) return } users, _ := store.GetUserList(c) token, _ := token.New( token.CsrfToken, user.Login, ).Sign(user.Hash) c.HTML(200, "users.html", gin.H{ "User": user, "Users": users, "Csrf": token, }) }
// Netrc returns a .netrc file that can be used to clone // private repositories from a remote system. func (g *Gitlab) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) { url_, err := url.Parse(g.URL) if err != nil { return nil, err } netrc := &model.Netrc{} netrc.Machine = url_.Host switch g.CloneMode { case "oauth": netrc.Login = "******" netrc.Password = u.Token case "token": t := token.New(token.HookToken, r.FullName) netrc.Login = "******" netrc.Password, err = t.Sign(r.Hash) } return netrc, err }
func start(c *cli.Context) { // debug level if requested by user if c.Bool("debug") { logrus.SetLevel(logrus.DebugLevel) } else { logrus.SetLevel(logrus.WarnLevel) } var accessToken string if c.String("drone-secret") != "" { secretToken := c.String("drone-secret") accessToken, _ = token.New(token.AgentToken, "").Sign(secretToken) } else { accessToken = c.String("drone-token") } logrus.Infof("Connecting to %s with token %s", c.String("drone-server"), accessToken, ) client := client.NewClientToken( c.String("drone-server"), accessToken, ) tls, err := dockerclient.TLSConfigFromCertPath(c.String("docker-cert-path")) if err == nil { tls.InsecureSkipVerify = c.Bool("docker-tls-verify") } docker, err := dockerclient.NewDockerClient(c.String("docker-host"), tls) if err != nil { logrus.Fatal(err) } go func() { for { if err := client.Ping(); err != nil { logrus.Warnf("unable to ping the server. %s", err.Error()) } time.Sleep(c.Duration("ping")) } }() var wg sync.WaitGroup for i := 0; i < c.Int("docker-max-procs"); i++ { wg.Add(1) go func() { r := pipeline{ drone: client, docker: docker, config: config{ platform: c.String("docker-os") + "/" + c.String("docker-arch"), timeout: c.Duration("timeout"), namespace: c.String("namespace"), privileged: c.StringSlice("privileged"), pull: c.BoolT("pull"), logs: int64(c.Int("max-log-size")) * 1000000, }, } for { if err := r.run(); err != nil { dur := c.Duration("backoff") logrus.Warnf("reconnect in %v. %s", dur, err.Error()) time.Sleep(dur) } } }() } handleSignals() wg.Wait() }
func GetLogin(c *gin.Context) { remote := remote.FromContext(c) // when dealing with redirects we may need // to adjust the content type. I cannot, however, // remember why, so need to revisit this line. c.Writer.Header().Del("Content-Type") tmpuser, open, err := remote.Login(c.Writer, c.Request) if err != nil { log.Errorf("cannot authenticate user. %s", err) c.Redirect(303, "/login?error=oauth_error") return } // this will happen when the user is redirected by // the remote provide as part of the oauth dance. if tmpuser == nil { return } // get the user from the database u, err := store.GetUserLogin(c, tmpuser.Login) if err != nil { count, err := store.CountUsers(c) if err != nil { log.Errorf("cannot register %s. %s", tmpuser.Login, err) c.Redirect(303, "/login?error=internal_error") return } // if self-registration is disabled we should // return a notAuthorized error. the only exception // is if no users exist yet in the system we'll proceed. if !open && count != 0 { log.Errorf("cannot register %s. registration closed", tmpuser.Login) c.Redirect(303, "/login?error=access_denied") return } // create the user account u = &model.User{} u.Login = tmpuser.Login u.Token = tmpuser.Token u.Secret = tmpuser.Secret u.Email = tmpuser.Email u.Avatar = tmpuser.Avatar u.Hash = crypto.Rand() // insert the user into the database if err := store.CreateUser(c, u); err != nil { log.Errorf("cannot insert %s. %s", u.Login, err) c.Redirect(303, "/login?error=internal_error") return } // if this is the first user, they // should be an admin. if count == 0 { u.Admin = true } } // update the user meta data and authorization // data and cache in the datastore. u.Token = tmpuser.Token u.Secret = tmpuser.Secret u.Email = tmpuser.Email u.Avatar = tmpuser.Avatar if err := store.UpdateUser(c, u); err != nil { log.Errorf("cannot update %s. %s", u.Login, err) c.Redirect(303, "/login?error=internal_error") return } exp := time.Now().Add(time.Hour * 72).Unix() token := token.New(token.SessToken, u.Login) tokenstr, err := token.SignExpires(u.Hash, exp) if err != nil { log.Errorf("cannot create token for %s. %s", u.Login, err) c.Redirect(303, "/login?error=internal_error") return } httputil.SetCookie(c.Writer, c.Request, "user_sess", tokenstr) redirect := httputil.GetCookie(c.Request, "user_last") if len(redirect) == 0 { redirect = "/" } c.Redirect(303, redirect) }
func ShowNodes(c *gin.Context) { user := session.User(c) nodes, _ := store.GetNodeList(c) token, _ := token.New(token.CsrfToken, user.Login).Sign(user.Hash) c.HTML(http.StatusOK, "nodes.html", gin.H{"User": user, "Nodes": nodes, "Csrf": token}) }
func GetLogin(c *gin.Context) { // when dealing with redirects we may need to adjust the content type. I // cannot, however, remember why, so need to revisit this line. c.Writer.Header().Del("Content-Type") tmpuser, err := remote.Login(c, c.Writer, c.Request) if err != nil { logrus.Errorf("cannot authenticate user. %s", err) c.Redirect(303, "/login?error=oauth_error") return } // this will happen when the user is redirected by the remote provider as // part of the authorization workflow. if tmpuser == nil { return } config := ToConfig(c) // get the user from the database u, err := store.GetUserLogin(c, tmpuser.Login) if err != nil { // if self-registration is disabled we should return a not authorized error if !config.Open && !config.IsAdmin(tmpuser) { logrus.Errorf("cannot register %s. registration closed", tmpuser.Login) c.Redirect(303, "/login?error=access_denied") return } // if self-registration is enabled for whitelisted organizations we need to // check the user's organization membership. if len(config.Orgs) != 0 { teams, terr := remote.Teams(c, tmpuser) if terr != nil || config.IsMember(teams) == false { logrus.Errorf("cannot verify team membership for %s.", u.Login) c.Redirect(303, "/login?error=access_denied") return } } // create the user account u = &model.User{ Login: tmpuser.Login, Token: tmpuser.Token, Secret: tmpuser.Secret, Email: tmpuser.Email, Avatar: tmpuser.Avatar, Hash: base32.StdEncoding.EncodeToString( securecookie.GenerateRandomKey(32), ), } // insert the user into the database if err := store.CreateUser(c, u); err != nil { logrus.Errorf("cannot insert %s. %s", u.Login, err) c.Redirect(303, "/login?error=internal_error") return } } // update the user meta data and authorization data. u.Token = tmpuser.Token u.Secret = tmpuser.Secret u.Email = tmpuser.Email u.Avatar = tmpuser.Avatar // if self-registration is enabled for whitelisted organizations we need to // check the user's organization membership. if len(config.Orgs) != 0 { teams, terr := remote.Teams(c, u) if terr != nil || config.IsMember(teams) == false { logrus.Errorf("cannot verify team membership for %s.", u.Login) c.Redirect(303, "/login?error=access_denied") return } } if err := store.UpdateUser(c, u); err != nil { logrus.Errorf("cannot update %s. %s", u.Login, err) c.Redirect(303, "/login?error=internal_error") return } exp := time.Now().Add(time.Hour * 72).Unix() token := token.New(token.SessToken, u.Login) tokenstr, err := token.SignExpires(u.Hash, exp) if err != nil { logrus.Errorf("cannot create token for %s. %s", u.Login, err) c.Redirect(303, "/login?error=internal_error") return } httputil.SetCookie(c.Writer, c.Request, "user_sess", tokenstr) c.Redirect(303, "/") }
func PostRepo(c *gin.Context) { remote := remote.FromContext(c) user := session.User(c) owner := c.Param("owner") name := c.Param("name") if user == nil { c.AbortWithStatus(403) return } r, err := remote.Repo(user, owner, name) if err != nil { c.String(404, err.Error()) return } m, err := cache.GetPerms(c, user, owner, name) if err != nil { c.String(404, err.Error()) return } if !m.Admin { c.String(403, "Administrative access is required.") return } // error if the repository already exists _, err = store.GetRepoOwnerName(c, owner, name) if err == nil { c.String(409, "Repository already exists.") return } // set the repository owner to the // currently authenticated user. r.UserID = user.ID r.AllowPush = true r.AllowPull = true r.Timeout = 60 // 1 hour default build time r.Hash = base32.StdEncoding.EncodeToString( securecookie.GenerateRandomKey(32), ) // crates the jwt token used to verify the repository t := token.New(token.HookToken, r.FullName) sig, err := t.Sign(r.Hash) if err != nil { c.String(500, err.Error()) return } link := fmt.Sprintf( "%s/hook?access_token=%s", httputil.GetURL(c.Request), sig, ) // activate the repository before we make any // local changes to the database. err = remote.Activate(user, r, link) if err != nil { c.String(500, err.Error()) return } // persist the repository err = store.CreateRepo(c, r) if err != nil { c.String(500, err.Error()) return } c.JSON(200, r) }
func PostRepo(c *gin.Context) { remote := remote.FromContext(c) user := session.User(c) owner := c.Param("owner") name := c.Param("name") if user == nil { c.AbortWithStatus(403) return } r, err := remote.Repo(user, owner, name) if err != nil { c.String(404, err.Error()) return } m, err := remote.Perm(user, owner, name) if err != nil { c.String(404, err.Error()) return } if !m.Admin { c.String(403, "Administrative access is required.") return } // error if the repository already exists _, err = store.GetRepoOwnerName(c, owner, name) if err == nil { c.String(409, "Repository already exists.") return } // set the repository owner to the // currently authenticated user. r.UserID = user.ID r.AllowPush = true r.AllowPull = true r.Timeout = 60 // 1 hour default build time r.Hash = crypto.Rand() // crates the jwt token used to verify the repository t := token.New(token.HookToken, r.FullName) sig, err := t.Sign(r.Hash) if err != nil { c.String(500, err.Error()) return } link := fmt.Sprintf( "%s/hook?access_token=%s", httputil.GetURL(c.Request), sig, ) // generate an RSA key and add to the repo key, err := crypto.GeneratePrivateKey() if err != nil { c.String(500, err.Error()) return } keys := new(model.Key) keys.Public = string(crypto.MarshalPublicKey(&key.PublicKey)) keys.Private = string(crypto.MarshalPrivateKey(key)) // activate the repository before we make any // local changes to the database. err = remote.Activate(user, r, keys, link) if err != nil { c.String(500, err.Error()) return } // persist the repository err = store.CreateRepo(c, r) if err != nil { c.String(500, err.Error()) return } keys.RepoID = r.ID err = store.CreateKey(c, keys) if err != nil { c.String(500, err.Error()) return } c.JSON(200, r) }
func GetLogin(c *gin.Context) { remote := remote.FromContext(c) tmpUser, err := remote.Login(c.Writer, c.Request) if err != nil { log.Errorf("failed to authenticate user. %s", err) c.Redirect(http.StatusSeeOther, "/login?error=oauth_error") return } if tmpUser == nil { return } u, err := store.GetUserLogin(c, tmpUser.Login) if err != nil { count, err := store.CountUsers(c) if err != nil { log.Errorf("cannot register %s. %s", tmpUser.Login, err) c.Redirect(http.StatusSeeOther, "/login?error=internal_error") return } // if self-registration is disabled we should // return a notAuthorized error. the only exception // is if no users exist yet in the system we'll proceed. if count != 0 { log.Errorf("failed to register %s.", tmpUser.Login) c.Redirect(http.StatusSeeOther, "/login?error=access_denied") return } // create the user account u = &model.User{} u.Login = tmpUser.Login u.Hash = base32.StdEncoding.EncodeToString( securecookie.GenerateRandomKey(32), ) // insert the user into the database if err := store.CreateUser(c, u); err != nil { log.Errorf("failed to insert %s. %s", u.Login, err) c.Redirect(http.StatusSeeOther, "/login?error=internal_error") return } // if this is the first user, they // should be an admin. if count == 0 { u.Admin = true } } // update the user meta data and authorization // data and cache in the datastore. u.Token = tmpUser.Token if err := store.UpdateUser(c, u); err != nil { log.Errorf("failed to update %s. %s", u.Login, err) c.Redirect(http.StatusSeeOther, "/login?error=internal_error") return } exp := time.Now().Add(time.Hour * 72).Unix() token := token.New(token.SessToken, u.Login) tokenstr, err := token.SignExpires(u.Hash, exp) if err != nil { log.Errorf("failed to create token for %s. %s", u.Login, err) c.Redirect(http.StatusSeeOther, "/login?error=internal_error") return } httputil.SetCookie(c.Writer, c.Request, "user_sess", tokenstr) redirect := httputil.GetCookie(c.Request, "user_last") if len(redirect) == 0 { redirect = "/" } c.Redirect(http.StatusSeeOther, redirect) }