initial commit

This commit is contained in:
Livio Amstutz 2020-01-31 15:22:16 +01:00
commit 6d0890e280
68 changed files with 5986 additions and 0 deletions

110
pkg/utils/cookie.go Normal file
View file

@ -0,0 +1,110 @@
package utils
import (
"errors"
"net/http"
"github.com/gorilla/securecookie"
)
type CookieHandler struct {
securecookie *securecookie.SecureCookie
secureOnly bool
sameSite http.SameSite
maxAge int
domain string
}
func NewCookieHandler(hashKey, encryptKey []byte, opts ...CookieHandlerOpt) *CookieHandler {
c := &CookieHandler{
securecookie: securecookie.New(hashKey, encryptKey),
secureOnly: true,
sameSite: http.SameSiteLaxMode,
}
for _, opt := range opts {
opt(c)
}
return c
}
type CookieHandlerOpt func(*CookieHandler)
func WithUnsecure() CookieHandlerOpt {
return func(c *CookieHandler) {
c.secureOnly = false
}
}
func WithSameSite(sameSite http.SameSite) CookieHandlerOpt {
return func(c *CookieHandler) {
c.sameSite = sameSite
}
}
func WithMaxAge(maxAge int) CookieHandlerOpt {
return func(c *CookieHandler) {
c.maxAge = maxAge
c.securecookie.MaxAge(maxAge)
}
}
func WithDomain(domain string) CookieHandlerOpt {
return func(c *CookieHandler) {
c.domain = domain
}
}
func (c *CookieHandler) CheckCookie(r *http.Request, name string) (string, error) {
cookie, err := r.Cookie(name)
if err != nil {
return "", err
}
var value string
if err := c.securecookie.Decode(name, cookie.Value, &value); err != nil {
return "", err
}
return value, nil
}
func (c *CookieHandler) CheckQueryCookie(r *http.Request, name string) (string, error) {
value, err := c.CheckCookie(r, name)
if err != nil {
return "", err
}
if value != r.FormValue(name) {
return "", errors.New(name + " does not compare")
}
return value, nil
}
func (c *CookieHandler) SetCookie(w http.ResponseWriter, name, value string) error {
encoded, err := c.securecookie.Encode(name, value)
if err != nil {
return err
}
http.SetCookie(w, &http.Cookie{
Name: name,
Value: encoded,
Domain: c.domain,
Path: "/",
MaxAge: c.maxAge,
HttpOnly: true,
Secure: c.secureOnly,
SameSite: c.sameSite,
})
return nil
}
func (c *CookieHandler) DeleteCookie(w http.ResponseWriter, name string) {
http.SetCookie(w, &http.Cookie{
Name: name,
Value: "",
Domain: c.domain,
Path: "/",
MaxAge: -1,
HttpOnly: true,
Secure: c.secureOnly,
SameSite: c.sameSite,
})
}

70
pkg/utils/crypto.go Normal file
View file

@ -0,0 +1,70 @@
package utils
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"errors"
"io"
)
func EncryptAES(data string, key string) (string, error) {
encrypted, err := EncryptBytesAES([]byte(data), key)
if err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(encrypted), nil
}
func EncryptBytesAES(plainText []byte, key string) ([]byte, error) {
block, err := aes.NewCipher([]byte(key))
if err != nil {
return nil, err
}
cipherText := make([]byte, aes.BlockSize+len(plainText))
iv := cipherText[:aes.BlockSize]
if _, err = io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(cipherText[aes.BlockSize:], plainText)
return cipherText, nil
}
func DecryptAES(data string, key string) (string, error) {
text, err := base64.URLEncoding.DecodeString(data)
if err != nil {
return "", nil
}
decrypted, err := DecryptBytesAES(text, key)
if err != nil {
return "", err
}
return string(decrypted), nil
}
func DecryptBytesAES(cipherText []byte, key string) ([]byte, error) {
block, err := aes.NewCipher([]byte(key))
if err != nil {
return nil, err
}
if len(cipherText) < aes.BlockSize {
err = errors.New("Ciphertext block size is too short!")
return nil, err
}
iv := cipherText[:aes.BlockSize]
cipherText = cipherText[aes.BlockSize:]
stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(cipherText, cipherText)
return cipherText, err
}

30
pkg/utils/hash.go Normal file
View file

@ -0,0 +1,30 @@
package utils
import (
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"fmt"
"hash"
"gopkg.in/square/go-jose.v2"
)
func GetHashAlgorithm(sigAlgorithm jose.SignatureAlgorithm) (hash.Hash, error) {
switch sigAlgorithm {
case jose.RS256, jose.ES256, jose.PS256:
return sha256.New(), nil
case jose.RS384, jose.ES384, jose.PS384:
return sha512.New384(), nil
case jose.RS512, jose.ES512, jose.PS512:
return sha512.New(), nil
default:
return nil, fmt.Errorf("oidc: unsupported signing algorithm %q", sigAlgorithm)
}
}
func HashString(hash hash.Hash, s string) string {
hash.Write([]byte(s)) // hash documents that Write will never return an error
sum := hash.Sum(nil)[:hash.Size()/2]
return base64.RawURLEncoding.EncodeToString(sum)
}

67
pkg/utils/http.go Normal file
View file

@ -0,0 +1,67 @@
package utils
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
"github.com/gorilla/schema"
)
var (
DefaultHTTPClient = &http.Client{
Timeout: time.Duration(30 * time.Second),
}
)
func FormRequest(endpoint string, request interface{}) (*http.Request, error) {
form := make(map[string][]string)
encoder := schema.NewEncoder()
if err := encoder.Encode(request, form); err != nil {
return nil, err
}
body := strings.NewReader(url.Values(form).Encode())
req, err := http.NewRequest("POST", endpoint, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
return req, nil
}
func HttpRequest(client *http.Client, req *http.Request, response interface{}) error {
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("unable to read response body: %v", err)
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("http status not ok: %s %s", resp.Status, body)
}
err = json.Unmarshal(body, response)
if err != nil {
return fmt.Errorf("failed to unmarshal response: %v %s", err, body)
}
return nil
}
func URLEncodeResponse(resp interface{}, encoder *schema.Encoder) (string, error) {
values := make(map[string][]string)
err := encoder.Encode(resp, values)
if err != nil {
return "", err
}
v := url.Values(values)
return v.Encode(), nil
}

21
pkg/utils/marshal.go Normal file
View file

@ -0,0 +1,21 @@
package utils
import (
"encoding/json"
"net/http"
"github.com/sirupsen/logrus"
)
func MarshalJSON(w http.ResponseWriter, i interface{}) {
b, err := json.Marshal(i)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("content-type", "application/json")
_, err = w.Write(b)
if err != nil {
logrus.Error("error writing response")
}
}

10
pkg/utils/strings.go Normal file
View file

@ -0,0 +1,10 @@
package utils
func Contains(list []string, needle string) bool {
for _, item := range list {
if item == needle {
return true
}
}
return false
}

48
pkg/utils/strings_test.go Normal file
View file

@ -0,0 +1,48 @@
package utils
import "testing"
func TestContains(t *testing.T) {
type args struct {
list []string
needle string
}
tests := []struct {
name string
args args
want bool
}{
{
"empty list false",
args{[]string{}, "needle"},
false,
},
{
"list not containing false",
args{[]string{"list"}, "needle"},
false,
},
{
"list not containing empty needle false",
args{[]string{"list", "needle"}, ""},
false,
},
{
"list containing true",
args{[]string{"list", "needle"}, "needle"},
true,
},
{
"list containing empty needle true",
args{[]string{"list", "needle", ""}, ""},
true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := Contains(tt.args.list, tt.args.needle); got != tt.want {
t.Errorf("Contains() = %v, want %v", got, tt.want)
}
})
}
}