2015-04-08 22:43:59 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/gin-gonic/gin/binding"
|
|
|
|
|
|
|
|
"github.com/drone/drone/common"
|
|
|
|
"github.com/drone/drone/common/httputil"
|
|
|
|
"github.com/drone/drone/common/sshutil"
|
|
|
|
"github.com/drone/drone/remote"
|
|
|
|
)
|
|
|
|
|
|
|
|
// repoResp is a data structure used for sending
|
|
|
|
// repository data to the client, augmented with
|
|
|
|
// additional repository meta-data.
|
|
|
|
type repoResp struct {
|
|
|
|
*common.Repo
|
2015-05-11 07:45:31 +00:00
|
|
|
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-08 22:43:59 +00: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 {
|
2015-05-11 07:45:31 +00:00
|
|
|
PostCommit *bool `json:"post_commits"`
|
|
|
|
PullRequest *bool `json:"pull_requests"`
|
|
|
|
Trusted *bool `json:"privileged"`
|
|
|
|
Timeout *int64 `json:"timeout"`
|
2015-04-08 22:43:59 +00: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)
|
2015-05-11 07:45:31 +00:00
|
|
|
data := repoResp{repo, perm, nil, nil, false}
|
2015-05-09 19:54:38 +00:00
|
|
|
|
2015-04-08 22:43:59 +00:00
|
|
|
// if the user is authenticated, we should display
|
|
|
|
// if she is watching the current repository.
|
2015-04-09 05:18:25 +00:00
|
|
|
if user == nil {
|
|
|
|
c.JSON(200, data)
|
|
|
|
return
|
2015-04-08 22:43:59 +00:00
|
|
|
}
|
2015-04-09 05:18:25 +00:00
|
|
|
|
2015-05-11 07:45:31 +00: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
|
|
|
|
data.Keypair = &common.Keypair{
|
|
|
|
Public: repo.PublicKey,
|
|
|
|
}
|
|
|
|
}
|
2015-04-09 05:18:25 +00:00
|
|
|
// check to see if the user is subscribing to the repo
|
2015-05-11 07:45:31 +00:00
|
|
|
data.Starred, _ = store.Starred(user, repo)
|
2015-04-09 05:18:25 +00:00
|
|
|
|
2015-04-08 22:43:59 +00:00
|
|
|
c.JSON(200, data)
|
|
|
|
}
|
|
|
|
|
2015-05-09 19:54:38 +00:00
|
|
|
// PutRepo accepts a request to update the named repository
|
2015-04-08 22:43:59 +00: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)
|
2015-04-09 05:18:25 +00:00
|
|
|
user := ToUser(c)
|
|
|
|
repo := ToRepo(c)
|
2015-04-08 22:43:59 +00:00
|
|
|
|
|
|
|
in := &repoReq{}
|
|
|
|
if !c.BindWith(in, binding.JSON) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if in.Params != nil {
|
2015-05-11 07:45:31 +00:00
|
|
|
repo.Params = *in.Params
|
2015-04-08 22:43:59 +00:00
|
|
|
}
|
2015-05-11 07:45:31 +00:00
|
|
|
|
|
|
|
if in.PostCommit != nil {
|
|
|
|
repo.PullRequest = *in.PullRequest
|
2015-04-08 22:43:59 +00:00
|
|
|
}
|
2015-05-11 07:45:31 +00:00
|
|
|
if in.PullRequest != nil {
|
|
|
|
repo.PullRequest = *in.PullRequest
|
2015-04-08 22:43:59 +00:00
|
|
|
}
|
2015-04-09 05:18:25 +00:00
|
|
|
if in.Trusted != nil && user.Admin {
|
|
|
|
repo.Trusted = *in.Trusted
|
2015-04-08 22:43:59 +00:00
|
|
|
}
|
2015-04-09 05:18:25 +00:00
|
|
|
if in.Timeout != nil && user.Admin {
|
|
|
|
repo.Timeout = *in.Timeout
|
2015-04-08 22:43:59 +00:00
|
|
|
}
|
|
|
|
|
2015-04-15 05:04:38 +00:00
|
|
|
err := store.SetRepo(repo)
|
2015-04-08 22:43:59 +00:00
|
|
|
if err != nil {
|
|
|
|
c.Fail(400, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-05-11 07:45:31 +00:00
|
|
|
data := repoResp{repo, perm, nil, nil, false}
|
|
|
|
data.Params = repo.Params
|
|
|
|
data.Keypair = &common.Keypair{
|
|
|
|
Public: repo.PublicKey,
|
2015-05-09 19:54:38 +00:00
|
|
|
}
|
2015-05-11 07:45:31 +00:00
|
|
|
data.Starred, _ = store.Starred(user, repo)
|
2015-04-09 05:18:25 +00:00
|
|
|
|
2015-04-08 22:43:59 +00: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)
|
|
|
|
}
|
|
|
|
|
2015-04-15 05:04:38 +00:00
|
|
|
err = ds.DelRepo(r)
|
2015-04-08 22:43:59 +00:00
|
|
|
if err != nil {
|
|
|
|
c.Fail(400, err)
|
|
|
|
}
|
|
|
|
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)
|
2015-04-30 21:23:46 +00:00
|
|
|
sess := ToSession(c)
|
2015-04-08 22:43:59 +00:00
|
|
|
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 {
|
|
|
|
c.Fail(400, err)
|
|
|
|
}
|
|
|
|
m, err := remote.Perm(user, owner, name)
|
|
|
|
if err != nil {
|
|
|
|
c.Fail(400, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !m.Admin {
|
|
|
|
c.Fail(403, fmt.Errorf("must be repository admin"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-05-11 07:45:31 +00:00
|
|
|
// error if the repository already exists
|
|
|
|
_, err = store.RepoName(owner, name)
|
|
|
|
if err == nil {
|
|
|
|
c.String(409, "Repository already exists")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-04-30 21:23:46 +00:00
|
|
|
token := &common.Token{}
|
|
|
|
token.Kind = common.TokenHook
|
|
|
|
token.Label = r.FullName
|
|
|
|
tokenstr, err := sess.GenerateToken(token)
|
|
|
|
if err != nil {
|
|
|
|
c.Fail(500, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
link := fmt.Sprintf(
|
|
|
|
"%s/api/hook?access_token=%s",
|
|
|
|
httputil.GetURL(c.Request),
|
|
|
|
tokenstr,
|
|
|
|
)
|
|
|
|
|
2015-04-08 22:43:59 +00:00
|
|
|
// set the repository owner to the
|
|
|
|
// currently authenticated user.
|
2015-05-11 07:45:31 +00:00
|
|
|
r.UserID = user.ID
|
|
|
|
r.PostCommit = true
|
|
|
|
r.PullRequest = true
|
2015-05-16 00:19:15 +00:00
|
|
|
r.Timeout = 60 // 1 hour default build time
|
2015-05-13 06:58:30 +00:00
|
|
|
r.Self = fmt.Sprintf(
|
|
|
|
"%s/%s",
|
|
|
|
httputil.GetURL(c.Request),
|
|
|
|
r.FullName,
|
|
|
|
)
|
2015-04-08 22:43:59 +00:00
|
|
|
|
|
|
|
// generate an RSA key and add to the repo
|
|
|
|
key, err := sshutil.GeneratePrivateKey()
|
|
|
|
if err != nil {
|
|
|
|
c.Fail(400, err)
|
|
|
|
return
|
|
|
|
}
|
2015-05-17 00:46:29 +00:00
|
|
|
r.PublicKey = string(sshutil.MarshalPublicKey(&key.PublicKey))
|
|
|
|
r.PrivateKey = string(sshutil.MarshalPrivateKey(key))
|
2015-05-11 07:45:31 +00:00
|
|
|
keypair := &common.Keypair{
|
|
|
|
Public: r.PublicKey,
|
|
|
|
Private: r.PrivateKey,
|
|
|
|
}
|
2015-04-09 05:18:25 +00:00
|
|
|
|
|
|
|
// activate the repository before we make any
|
|
|
|
// local changes to the database.
|
2015-04-29 23:08:21 +00:00
|
|
|
err = remote.Activate(user, r, keypair, link)
|
|
|
|
if err != nil {
|
|
|
|
c.Fail(500, err)
|
|
|
|
return
|
|
|
|
}
|
2015-04-08 22:43:59 +00:00
|
|
|
|
2015-04-09 05:18:25 +00:00
|
|
|
// persist the repository
|
2015-05-11 07:45:31 +00:00
|
|
|
err = store.AddRepo(r)
|
2015-04-08 22:43:59 +00:00
|
|
|
if err != nil {
|
|
|
|
c.Fail(500, err)
|
|
|
|
return
|
|
|
|
}
|
2015-04-09 05:18:25 +00:00
|
|
|
|
2015-05-11 07:45:31 +00:00
|
|
|
store.AddStar(user, r)
|
2015-04-08 22:43:59 +00:00
|
|
|
|
|
|
|
c.JSON(200, r)
|
|
|
|
}
|
|
|
|
|
2015-04-16 21:45:05 +00:00
|
|
|
// Unubscribe 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)
|
|
|
|
|
2015-05-11 07:45:31 +00:00
|
|
|
err := store.DelStar(user, repo)
|
2015-04-16 21:45:05 +00:00
|
|
|
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)
|
|
|
|
|
2015-05-11 07:45:31 +00:00
|
|
|
err := store.AddStar(user, repo)
|
2015-04-16 21:45:05 +00:00
|
|
|
if err != nil {
|
|
|
|
c.Fail(400, err)
|
|
|
|
} else {
|
2015-05-11 07:45:31 +00:00
|
|
|
c.Writer.WriteHeader(200)
|
2015-04-16 21:45:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-08 22:43:59 +00: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
|
|
|
|
}
|