1
0
mirror of https://github.com/woodpecker-ci/woodpecker.git synced 2024-12-06 08:16:19 +02:00
woodpecker/controller/repos.go

350 lines
7.8 KiB
Go
Raw Normal View History

2015-04-09 00:43:59 +02:00
package server
import (
2015-09-08 00:33:55 +02:00
"bytes"
"encoding/base64"
2015-04-09 00:43:59 +02:00
"fmt"
2015-09-07 21:13:27 +02:00
"io/ioutil"
2015-04-09 00:43:59 +02:00
2015-05-22 20:37:40 +02:00
"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin"
"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin/binding"
2015-09-08 03:10:55 +02:00
"github.com/drone/drone/Godeps/_workspace/src/gopkg.in/yaml.v2"
2015-04-09 00:43:59 +02:00
"github.com/drone/drone/pkg/remote"
"github.com/drone/drone/pkg/token"
2015-05-17 22:51:42 +02:00
common "github.com/drone/drone/pkg/types"
"github.com/drone/drone/pkg/utils/httputil"
"github.com/drone/drone/pkg/utils/sshutil"
"github.com/drone/drone/pkg/yaml/secure"
2015-04-09 00:43:59 +02:00
)
// repoResp is a data structure used for sending
// repository data to the client, augmented with
// additional repository meta-data.
type repoResp struct {
*common.Repo
Perms *common.Perm `json:"permissions,omitempty"`
Keypair *common.Keypair `json:"keypair,omitempty"`
Params map[string]string `json:"params,omitempty"`
Starred bool `json:"starred,omitempty"`
2015-04-09 00:43:59 +02:00
}
// repoReq is a data structure used for receiving
// repository data from the client to modify the
// attributes of an existing repository.
//
// note that attributes are pointers so that we can
// accept null values, effectively patching an existing
// repository object with only the supplied fields.
type repoReq struct {
Trusted *bool `json:"trusted"`
Timeout *int64 `json:"timeout"`
Hooks struct {
PullReqeust *bool `json:"pull_request"`
Push *bool `json:"push"`
}
2015-04-09 00:43:59 +02:00
// optional private parameters can only be
// supplied by the repository admin.
Params *map[string]string `json:"params"`
}
// GetRepo accepts a request to retrieve a commit
// from the datastore for the given repository, branch and
// commit hash.
//
// GET /api/repos/:owner/:name
//
func GetRepo(c *gin.Context) {
store := ToDatastore(c)
repo := ToRepo(c)
user := ToUser(c)
perm := ToPerm(c)
data := repoResp{repo, perm, nil, nil, false}
2015-04-09 00:43:59 +02:00
// if the user is authenticated, we should display
// if she is watching the current repository.
if user == nil {
c.JSON(200, data)
return
2015-04-09 00:43:59 +02:00
}
// if the user is an administrator of the project
// we should display the private parameter data
// and keypair data.
if perm.Push {
data.Params = repo.Params
2015-06-19 03:50:57 +02:00
data.Keypair = repo.Keys
}
// check to see if the user is subscribing to the repo
data.Starred, _ = store.Starred(user, repo)
2015-04-09 00:43:59 +02:00
c.JSON(200, data)
}
// PutRepo accepts a request to update the named repository
2015-04-09 00:43:59 +02:00
// in the datastore. It expects a JSON input and returns the
// updated repository in JSON format if successful.
//
// PUT /api/repos/:owner/:name
//
func PutRepo(c *gin.Context) {
store := ToDatastore(c)
perm := ToPerm(c)
user := ToUser(c)
repo := ToRepo(c)
2015-04-09 00:43:59 +02:00
in := &repoReq{}
if !c.BindWith(in, binding.JSON) {
return
}
if in.Params != nil {
repo.Params = *in.Params
2015-04-09 00:43:59 +02:00
}
if in.Hooks.Push != nil {
repo.Hooks.Push = *in.Hooks.Push
2015-04-09 00:43:59 +02:00
}
if in.Hooks.PullReqeust != nil {
repo.Hooks.PullRequest = *in.Hooks.PullReqeust
2015-04-09 00:43:59 +02:00
}
if in.Trusted != nil && user.Admin {
repo.Trusted = *in.Trusted
2015-04-09 00:43:59 +02:00
}
if in.Timeout != nil && user.Admin {
repo.Timeout = *in.Timeout
2015-04-09 00:43:59 +02:00
}
err := store.SetRepo(repo)
2015-04-09 00:43:59 +02:00
if err != nil {
c.Fail(400, err)
return
}
data := repoResp{repo, perm, nil, nil, false}
data.Params = repo.Params
2015-06-19 03:50:57 +02:00
data.Keypair = repo.Keys
data.Starred, _ = store.Starred(user, repo)
2015-04-09 00:43:59 +02:00
c.JSON(200, data)
}
// DeleteRepo accepts a request to delete the named
// repository.
//
// DEL /api/repos/:owner/:name
//
func DeleteRepo(c *gin.Context) {
ds := ToDatastore(c)
u := ToUser(c)
r := ToRepo(c)
link := fmt.Sprintf(
"%s/api/hook",
httputil.GetURL(c.Request),
)
remote := ToRemote(c)
err := remote.Deactivate(u, r, link)
if err != nil {
c.Fail(400, err)
}
err = ds.DelRepo(r)
2015-04-09 00:43:59 +02:00
if err != nil {
2015-06-26 07:37:59 +02:00
c.Fail(500, err)
2015-04-09 00:43:59 +02:00
}
c.Writer.WriteHeader(200)
}
// PostRepo accapets a request to activate the named repository
// in the datastore. It returns a 201 status created if successful
//
// POST /api/repos/:owner/:name
//
func PostRepo(c *gin.Context) {
user := ToUser(c)
store := ToDatastore(c)
owner := c.Params.ByName("owner")
name := c.Params.ByName("name")
// get the repository and user permissions
// from the remote system.
remote := ToRemote(c)
r, err := remote.Repo(user, owner, name)
if err != nil {
2015-06-26 07:37:59 +02:00
c.Fail(404, err)
2015-04-09 00:43:59 +02:00
}
m, err := remote.Perm(user, owner, name)
if err != nil {
2015-06-26 07:37:59 +02:00
c.Fail(404, err)
2015-04-09 00:43:59 +02:00
return
}
if !m.Admin {
c.Fail(403, fmt.Errorf("must be repository admin"))
return
}
// error if the repository already exists
_, err = store.RepoName(owner, name)
if err == nil {
c.String(409, "Repository already exists")
return
}
2015-04-09 00:43:59 +02:00
// set the repository owner to the
// currently authenticated user.
r.UserID = user.ID
2015-06-19 03:50:57 +02:00
r.Hooks = new(common.Hooks)
r.Hooks.Push = true
r.Hooks.PullRequest = true
2015-05-16 02:19:15 +02:00
r.Timeout = 60 // 1 hour default build time
r.Hash = common.GenerateToken()
2015-05-13 08:58:30 +02:00
r.Self = fmt.Sprintf(
"%s/%s",
httputil.GetURL(c.Request),
r.FullName,
)
2015-04-09 00:43:59 +02:00
// 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.Fail(500, err)
return
}
link := fmt.Sprintf(
"%s/api/hook?access_token=%s",
httputil.GetURL(c.Request),
sig,
)
2015-04-09 00:43:59 +02:00
// generate an RSA key and add to the repo
key, err := sshutil.GeneratePrivateKey()
if err != nil {
2015-06-26 07:37:59 +02:00
c.Fail(500, err)
2015-04-09 00:43:59 +02:00
return
}
2015-06-19 03:50:57 +02:00
r.Keys = new(common.Keypair)
r.Keys.Public = string(sshutil.MarshalPublicKey(&key.PublicKey))
r.Keys.Private = string(sshutil.MarshalPrivateKey(key))
// activate the repository before we make any
// local changes to the database.
2015-06-19 03:50:57 +02:00
err = remote.Activate(user, r, r.Keys, link)
if err != nil {
c.Fail(500, err)
return
}
2015-04-09 00:43:59 +02:00
// persist the repository
err = store.AddRepo(r)
2015-04-09 00:43:59 +02:00
if err != nil {
c.Fail(500, err)
return
}
store.AddStar(user, r)
2015-04-09 00:43:59 +02:00
c.JSON(200, r)
}
// Encrypt accapets a request to encrypt the
// body of the request using the repository secret
// key.
//
// POST /api/repos/:owner/:name/encrypt
//
func Encrypt(c *gin.Context) {
repo := ToRepo(c)
2015-09-07 21:13:27 +02:00
in, err := ioutil.ReadAll(c.Request.Body)
if err != nil {
2015-09-08 04:24:14 +02:00
c.Fail(400, err)
2015-09-07 21:13:27 +02:00
return
}
2015-09-08 00:33:55 +02:00
in, err = base64.StdEncoding.DecodeString(string(in))
if err != nil {
c.Fail(500, err)
return
}
2015-09-08 03:10:55 +02:00
2015-09-08 04:24:14 +02:00
// we found some strange characters included in
// the yaml file when entered into a browser textarea.
// these need to be removed
in = bytes.Replace(in, []byte{'\xA0'}, []byte{' '}, -1)
2015-09-08 03:10:55 +02:00
// make sure the Yaml is valid format to prevent
// a malformed value from being used in the build
err = yaml.Unmarshal(in, &yaml.MapSlice{})
if err != nil {
c.Fail(500, err)
return
}
// encrypts using go-jose
2015-09-07 21:13:27 +02:00
out, err := secure.Encrypt(string(in), repo.Keys.Private)
if err != nil {
c.Fail(500, err)
return
}
2015-09-07 21:13:27 +02:00
c.Writer.Write([]byte(out))
}
2015-08-19 16:11:24 +02:00
// Unsubscribe accapets a request to unsubscribe the
// currently authenticated user to the repository.
//
// DEL /api/subscribers/:owner/:name
//
func Unsubscribe(c *gin.Context) {
store := ToDatastore(c)
repo := ToRepo(c)
user := ToUser(c)
err := store.DelStar(user, repo)
if err != nil {
c.Fail(400, err)
} else {
c.Writer.WriteHeader(200)
}
}
// Subscribe accapets a request to subscribe the
// currently authenticated user to the repository.
//
// POST /api/subscriber/:owner/:name
//
func Subscribe(c *gin.Context) {
store := ToDatastore(c)
repo := ToRepo(c)
user := ToUser(c)
err := store.AddStar(user, repo)
if err != nil {
c.Fail(400, err)
} else {
c.Writer.WriteHeader(200)
}
}
2015-04-09 00:43:59 +02:00
// perms is a helper function that returns user permissions
// for a particular repository.
func perms(remote remote.Remote, u *common.User, r *common.Repo) *common.Perm {
switch {
case u == nil && r.Private:
return &common.Perm{}
case u == nil && r.Private == false:
return &common.Perm{Pull: true}
case u.Admin:
return &common.Perm{Pull: true, Push: true, Admin: true}
}
p, err := remote.Perm(u, r.Owner, r.Name)
if err != nil {
return &common.Perm{}
}
return p
}