mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-10-20 20:50:15 +00:00
moved to single binary project structure
This commit is contained in:
58
shared/ccmenu/ccmenu.go
Normal file
58
shared/ccmenu/ccmenu.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package ccmenu
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone/pkg/types"
|
||||
)
|
||||
|
||||
type CCProjects struct {
|
||||
XMLName xml.Name `xml:"Projects"`
|
||||
Project *CCProject `xml:"Project"`
|
||||
}
|
||||
|
||||
type CCProject struct {
|
||||
XMLName xml.Name `xml:"Project"`
|
||||
Name string `xml:"name,attr"`
|
||||
Activity string `xml:"activity,attr"`
|
||||
LastBuildStatus string `xml:"lastBuildStatus,attr"`
|
||||
LastBuildLabel string `xml:"lastBuildLabel,attr"`
|
||||
LastBuildTime string `xml:"lastBuildTime,attr"`
|
||||
WebURL string `xml:"webUrl,attr"`
|
||||
}
|
||||
|
||||
func NewCC(r *types.Repo, b *types.Build) *CCProjects {
|
||||
proj := &CCProject{
|
||||
Name: r.Owner + "/" + r.Name,
|
||||
WebURL: r.Self,
|
||||
Activity: "Building",
|
||||
LastBuildStatus: "Unknown",
|
||||
LastBuildLabel: "Unknown",
|
||||
}
|
||||
|
||||
// if the build is not currently running then
|
||||
// we can return the latest build status.
|
||||
if b.Status != types.StatePending &&
|
||||
b.Status != types.StateRunning {
|
||||
proj.Activity = "Sleeping"
|
||||
proj.LastBuildTime = time.Unix(b.Started, 0).Format(time.RFC3339)
|
||||
proj.LastBuildLabel = strconv.Itoa(b.Number)
|
||||
}
|
||||
|
||||
// ensure the last build state accepts a valid
|
||||
// ccmenu enumeration
|
||||
switch b.Status {
|
||||
case types.StateError, types.StateKilled:
|
||||
proj.LastBuildStatus = "Exception"
|
||||
case types.StateSuccess:
|
||||
proj.LastBuildStatus = "Success"
|
||||
case types.StateFailure:
|
||||
proj.LastBuildStatus = "Failure"
|
||||
default:
|
||||
proj.LastBuildStatus = "Unknown"
|
||||
}
|
||||
|
||||
return &CCProjects{Project: proj}
|
||||
}
|
||||
72
shared/crypto/sshutil/sshutil.go
Normal file
72
shared/crypto/sshutil/sshutil.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package sshutil
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"hash"
|
||||
|
||||
"github.com/drone/drone/Godeps/_workspace/src/code.google.com/p/go.crypto/ssh"
|
||||
)
|
||||
|
||||
const (
|
||||
RSA_BITS = 2048 // Default number of bits in an RSA key
|
||||
RSA_BITS_MIN = 768 // Minimum number of bits in an RSA key
|
||||
)
|
||||
|
||||
// helper function to generate an RSA Private Key.
|
||||
func GeneratePrivateKey() (*rsa.PrivateKey, error) {
|
||||
return rsa.GenerateKey(rand.Reader, RSA_BITS)
|
||||
}
|
||||
|
||||
// helper function that marshalls an RSA Public Key to an SSH
|
||||
// .authorized_keys format
|
||||
func MarshalPublicKey(pubkey *rsa.PublicKey) []byte {
|
||||
pk, err := ssh.NewPublicKey(pubkey)
|
||||
if err != nil {
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
return ssh.MarshalAuthorizedKey(pk)
|
||||
}
|
||||
|
||||
// helper function that marshalls an RSA Private Key to
|
||||
// a PEM encoded file.
|
||||
func MarshalPrivateKey(privkey *rsa.PrivateKey) []byte {
|
||||
privateKeyMarshaled := x509.MarshalPKCS1PrivateKey(privkey)
|
||||
privateKeyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Headers: nil, Bytes: privateKeyMarshaled})
|
||||
return privateKeyPEM
|
||||
}
|
||||
|
||||
// UnMarshalPrivateKey is a helper function that unmarshals a PEM
|
||||
// bytes to an RSA Private Key
|
||||
func UnMarshalPrivateKey(privateKeyPEM []byte) *rsa.PrivateKey {
|
||||
derBlock, _ := pem.Decode(privateKeyPEM)
|
||||
privateKey, err := x509.ParsePKCS1PrivateKey(derBlock.Bytes)
|
||||
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return privateKey
|
||||
}
|
||||
|
||||
// Encrypt is helper function to encrypt a plain-text string using
|
||||
// an RSA public key.
|
||||
func Encrypt(hash hash.Hash, pubkey *rsa.PublicKey, msg string) (string, error) {
|
||||
src, err := rsa.EncryptOAEP(hash, rand.Reader, pubkey, []byte(msg), nil)
|
||||
return base64.RawURLEncoding.EncodeToString(src), err
|
||||
}
|
||||
|
||||
// Decrypt is helper function to encrypt a plain-text string using
|
||||
// an RSA public key.
|
||||
func Decrypt(hash hash.Hash, privkey *rsa.PrivateKey, secret string) (string, error) {
|
||||
decoded, err := base64.RawURLEncoding.DecodeString(secret)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
out, err := rsa.DecryptOAEP(hash, rand.Reader, privkey, decoded, nil)
|
||||
return string(out), err
|
||||
}
|
||||
40
shared/crypto/sshutil/sshutil_test.go
Normal file
40
shared/crypto/sshutil/sshutil_test.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package sshutil
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/franela/goblin"
|
||||
)
|
||||
|
||||
func TestSSHUtil(t *testing.T) {
|
||||
|
||||
g := goblin.Goblin(t)
|
||||
g.Describe("sshutil", func() {
|
||||
var encrypted, testMsg string
|
||||
|
||||
privkey, err := GeneratePrivateKey()
|
||||
g.Assert(err == nil).IsTrue()
|
||||
pubkey := privkey.PublicKey
|
||||
sha256 := sha256.New()
|
||||
testMsg = "foo=bar"
|
||||
|
||||
g.Before(func() {
|
||||
encrypted, err = Encrypt(sha256, &pubkey, testMsg)
|
||||
g.Assert(err == nil).IsTrue()
|
||||
})
|
||||
|
||||
g.It("Can decrypt encrypted msg", func() {
|
||||
decrypted, err := Decrypt(sha256, privkey, encrypted)
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(decrypted == testMsg).IsTrue()
|
||||
})
|
||||
|
||||
g.It("Unmarshals private key from PEM block", func() {
|
||||
privateKeyPEM := MarshalPrivateKey(privkey)
|
||||
privateKey := UnMarshalPrivateKey(privateKeyPEM)
|
||||
|
||||
g.Assert(privateKey.PublicKey.E == pubkey.E).IsTrue()
|
||||
})
|
||||
})
|
||||
}
|
||||
124
shared/docker/copy.go
Normal file
124
shared/docker/copy.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
StdWriterPrefixLen = 8
|
||||
StdWriterFdIndex = 0
|
||||
StdWriterSizeIndex = 4
|
||||
)
|
||||
|
||||
type StdType [StdWriterPrefixLen]byte
|
||||
|
||||
var (
|
||||
Stdin StdType = StdType{0: 0}
|
||||
Stdout StdType = StdType{0: 1}
|
||||
Stderr StdType = StdType{0: 2}
|
||||
)
|
||||
|
||||
type StdWriter struct {
|
||||
io.Writer
|
||||
prefix StdType
|
||||
sizeBuf []byte
|
||||
}
|
||||
|
||||
var ErrInvalidStdHeader = errors.New("Unrecognized input header")
|
||||
|
||||
// StdCopy is a modified version of io.Copy.
|
||||
//
|
||||
// StdCopy will demultiplex `src`, assuming that it contains two streams,
|
||||
// previously multiplexed together using a StdWriter instance.
|
||||
// As it reads from `src`, StdCopy will write to `dstout` and `dsterr`.
|
||||
//
|
||||
// StdCopy will read until it hits EOF on `src`. It will then return a nil error.
|
||||
// In other words: if `err` is non nil, it indicates a real underlying error.
|
||||
//
|
||||
// `written` will hold the total number of bytes written to `dstout` and `dsterr`.
|
||||
func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error) {
|
||||
var (
|
||||
buf = make([]byte, 32*1024+StdWriterPrefixLen+1)
|
||||
bufLen = len(buf)
|
||||
nr, nw int
|
||||
er, ew error
|
||||
out io.Writer
|
||||
frameSize int
|
||||
)
|
||||
|
||||
for {
|
||||
// Make sure we have at least a full header
|
||||
for nr < StdWriterPrefixLen {
|
||||
var nr2 int
|
||||
nr2, er = src.Read(buf[nr:])
|
||||
nr += nr2
|
||||
if er == io.EOF {
|
||||
if nr < StdWriterPrefixLen {
|
||||
return written, nil
|
||||
}
|
||||
break
|
||||
}
|
||||
if er != nil {
|
||||
return 0, er
|
||||
}
|
||||
}
|
||||
|
||||
// Check the first byte to know where to write
|
||||
switch buf[StdWriterFdIndex] {
|
||||
case 0:
|
||||
fallthrough
|
||||
case 1:
|
||||
// Write on stdout
|
||||
out = dstout
|
||||
case 2:
|
||||
// Write on stderr
|
||||
out = dsterr
|
||||
default:
|
||||
return 0, ErrInvalidStdHeader
|
||||
}
|
||||
|
||||
// Retrieve the size of the frame
|
||||
frameSize = int(binary.BigEndian.Uint32(buf[StdWriterSizeIndex : StdWriterSizeIndex+4]))
|
||||
|
||||
// Check if the buffer is big enough to read the frame.
|
||||
// Extend it if necessary.
|
||||
if frameSize+StdWriterPrefixLen > bufLen {
|
||||
buf = append(buf, make([]byte, frameSize+StdWriterPrefixLen-bufLen+1)...)
|
||||
bufLen = len(buf)
|
||||
}
|
||||
|
||||
// While the amount of bytes read is less than the size of the frame + header, we keep reading
|
||||
for nr < frameSize+StdWriterPrefixLen {
|
||||
var nr2 int
|
||||
nr2, er = src.Read(buf[nr:])
|
||||
nr += nr2
|
||||
if er == io.EOF {
|
||||
if nr < frameSize+StdWriterPrefixLen {
|
||||
return written, nil
|
||||
}
|
||||
break
|
||||
}
|
||||
if er != nil {
|
||||
return 0, er
|
||||
}
|
||||
}
|
||||
|
||||
// Write the retrieved frame (without header)
|
||||
nw, ew = out.Write(buf[StdWriterPrefixLen : frameSize+StdWriterPrefixLen])
|
||||
if ew != nil {
|
||||
return 0, ew
|
||||
}
|
||||
// If the frame has not been fully written: error
|
||||
if nw != frameSize {
|
||||
return 0, io.ErrShortWrite
|
||||
}
|
||||
written += int64(nw)
|
||||
|
||||
// Move the rest of the buffer to the beginning
|
||||
copy(buf, buf[frameSize+StdWriterPrefixLen:])
|
||||
// Move the index
|
||||
nr -= frameSize + StdWriterPrefixLen
|
||||
}
|
||||
}
|
||||
111
shared/httputil/httputil.go
Normal file
111
shared/httputil/httputil.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package httputil
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IsHttps is a helper function that evaluates the http.Request
|
||||
// and returns True if the Request uses HTTPS. It is able to detect,
|
||||
// using the X-Forwarded-Proto, if the original request was HTTPS and
|
||||
// routed through a reverse proxy with SSL termination.
|
||||
func IsHttps(r *http.Request) bool {
|
||||
switch {
|
||||
case r.URL.Scheme == "https":
|
||||
return true
|
||||
case r.TLS != nil:
|
||||
return true
|
||||
case strings.HasPrefix(r.Proto, "HTTPS"):
|
||||
return true
|
||||
case r.Header.Get("X-Forwarded-Proto") == "https":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// GetScheme is a helper function that evaluates the http.Request
|
||||
// and returns the scheme, HTTP or HTTPS. It is able to detect,
|
||||
// using the X-Forwarded-Proto, if the original request was HTTPS
|
||||
// and routed through a reverse proxy with SSL termination.
|
||||
func GetScheme(r *http.Request) string {
|
||||
switch {
|
||||
case r.URL.Scheme == "https":
|
||||
return "https"
|
||||
case r.TLS != nil:
|
||||
return "https"
|
||||
case strings.HasPrefix(r.Proto, "HTTPS"):
|
||||
return "https"
|
||||
case r.Header.Get("X-Forwarded-Proto") == "https":
|
||||
return "https"
|
||||
default:
|
||||
return "http"
|
||||
}
|
||||
}
|
||||
|
||||
// GetHost is a helper function that evaluates the http.Request
|
||||
// and returns the hostname. It is able to detect, using the
|
||||
// X-Forarded-For header, the original hostname when routed
|
||||
// through a reverse proxy.
|
||||
func GetHost(r *http.Request) string {
|
||||
switch {
|
||||
case len(r.Host) != 0:
|
||||
return r.Host
|
||||
case len(r.URL.Host) != 0:
|
||||
return r.URL.Host
|
||||
case len(r.Header.Get("X-Forwarded-For")) != 0:
|
||||
return r.Header.Get("X-Forwarded-For")
|
||||
case len(r.Header.Get("X-Host")) != 0:
|
||||
return r.Header.Get("X-Host")
|
||||
case len(r.Header.Get("XFF")) != 0:
|
||||
return r.Header.Get("XFF")
|
||||
case len(r.Header.Get("X-Real-IP")) != 0:
|
||||
return r.Header.Get("X-Real-IP")
|
||||
default:
|
||||
return "localhost:8080"
|
||||
}
|
||||
}
|
||||
|
||||
// GetURL is a helper function that evaluates the http.Request
|
||||
// and returns the URL as a string. Only the scheme + hostname
|
||||
// are included; the path is excluded.
|
||||
func GetURL(r *http.Request) string {
|
||||
return GetScheme(r) + "://" + GetHost(r)
|
||||
}
|
||||
|
||||
// GetCookie retrieves and verifies the cookie value.
|
||||
func GetCookie(r *http.Request, name string) (value string) {
|
||||
cookie, err := r.Cookie(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
value = cookie.Value
|
||||
return
|
||||
}
|
||||
|
||||
// SetCookie writes the cookie value.
|
||||
func SetCookie(w http.ResponseWriter, r *http.Request, name, value string) {
|
||||
cookie := http.Cookie{
|
||||
Name: name,
|
||||
Value: value,
|
||||
Path: "/",
|
||||
Domain: r.URL.Host,
|
||||
HttpOnly: true,
|
||||
Secure: IsHttps(r),
|
||||
}
|
||||
|
||||
http.SetCookie(w, &cookie)
|
||||
}
|
||||
|
||||
// DelCookie deletes a cookie.
|
||||
func DelCookie(w http.ResponseWriter, r *http.Request, name string) {
|
||||
cookie := http.Cookie{
|
||||
Name: name,
|
||||
Value: "deleted",
|
||||
Path: "/",
|
||||
Domain: r.URL.Host,
|
||||
MaxAge: -1,
|
||||
}
|
||||
|
||||
http.SetCookie(w, &cookie)
|
||||
}
|
||||
98
shared/token/token.go
Normal file
98
shared/token/token.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
type SecretFunc func(*Token) (string, error)
|
||||
|
||||
const (
|
||||
UserToken = "user"
|
||||
SessToken = "sess"
|
||||
HookToken = "hook"
|
||||
)
|
||||
|
||||
// Default algorithm used to sign JWT tokens.
|
||||
const SignerAlgo = "HS256"
|
||||
|
||||
type Token struct {
|
||||
Kind string
|
||||
Text string
|
||||
}
|
||||
|
||||
// Parse parses
|
||||
func Parse(raw string, fn SecretFunc) (*Token, error) {
|
||||
token := &Token{}
|
||||
parsed, err := jwt.Parse(raw, keyFunc(token, fn))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !parsed.Valid {
|
||||
return nil, jwt.ValidationError{}
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func ParseRequest(req *http.Request, fn SecretFunc) (*Token, error) {
|
||||
token := &Token{}
|
||||
parsed, err := jwt.ParseFromRequest(req, keyFunc(token, fn))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !parsed.Valid {
|
||||
return nil, jwt.ValidationError{}
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func New(kind, text string) *Token {
|
||||
return &Token{Kind: kind, Text: text}
|
||||
}
|
||||
|
||||
// Sign signs the token using the given secret hash
|
||||
// and returns the string value.
|
||||
func (t *Token) Sign(secret string) (string, error) {
|
||||
return t.SignExpires(secret, 0)
|
||||
}
|
||||
|
||||
// Sign signs the token using the given secret hash
|
||||
// with an expiration date.
|
||||
func (t *Token) SignExpires(secret string, exp int64) (string, error) {
|
||||
token := jwt.New(jwt.SigningMethodHS256)
|
||||
token.Claims["type"] = t.Kind
|
||||
token.Claims["text"] = t.Text
|
||||
if exp > 0 {
|
||||
token.Claims["exp"] = float64(exp)
|
||||
}
|
||||
return token.SignedString([]byte(secret))
|
||||
}
|
||||
|
||||
func keyFunc(token *Token, fn SecretFunc) jwt.Keyfunc {
|
||||
return func(t *jwt.Token) (interface{}, error) {
|
||||
// validate the correct algorithm is being used
|
||||
if t.Method.Alg() != SignerAlgo {
|
||||
return nil, jwt.ErrSignatureInvalid
|
||||
}
|
||||
|
||||
// extract the token kind and cast to
|
||||
// the expected type.
|
||||
kindv, ok := t.Claims["type"]
|
||||
if !ok {
|
||||
return nil, jwt.ValidationError{}
|
||||
}
|
||||
token.Kind, _ = kindv.(string)
|
||||
|
||||
// extract the token value and cast to
|
||||
// exepected type.
|
||||
textv, ok := t.Claims["text"]
|
||||
if !ok {
|
||||
return nil, jwt.ValidationError{}
|
||||
}
|
||||
token.Text, _ = textv.(string)
|
||||
|
||||
// invoke the callback function to retrieve
|
||||
// the secret key used to verify
|
||||
secret, err := fn(token)
|
||||
return []byte(secret), err
|
||||
}
|
||||
}
|
||||
1
shared/token/token_test.go
Normal file
1
shared/token/token_test.go
Normal file
@@ -0,0 +1 @@
|
||||
package token
|
||||
Reference in New Issue
Block a user