initial commit
This commit is contained in:
commit
6d0890e280
68 changed files with 5986 additions and 0 deletions
110
pkg/utils/cookie.go
Normal file
110
pkg/utils/cookie.go
Normal 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
70
pkg/utils/crypto.go
Normal 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
30
pkg/utils/hash.go
Normal 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
67
pkg/utils/http.go
Normal 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
21
pkg/utils/marshal.go
Normal 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
10
pkg/utils/strings.go
Normal 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
48
pkg/utils/strings_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue