replace zxq.co/ripple/hanayo
This commit is contained in:
125
vendor/github.com/felipeweb/gopher-utils/.gitignore
generated
vendored
Normal file
125
vendor/github.com/felipeweb/gopher-utils/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
|
||||
|
||||
*.iml
|
||||
|
||||
## Directory-based project format:
|
||||
.idea/
|
||||
# if you remove the above rule, at least ignore the following:
|
||||
|
||||
# User-specific stuff:
|
||||
# .idea/workspace.xml
|
||||
# .idea/tasks.xml
|
||||
# .idea/dictionaries
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
# .idea/dataSources.ids
|
||||
# .idea/dataSources.xml
|
||||
# .idea/sqlDataSources.xml
|
||||
# .idea/dynamic.xml
|
||||
# .idea/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
# .idea/gradle.xml
|
||||
# .idea/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
# .idea/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
### Go template
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
### OSX template
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
### Windows template
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
### Linux template
|
||||
*~
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
12
vendor/github.com/felipeweb/gopher-utils/.travis.yml
generated
vendored
Normal file
12
vendor/github.com/felipeweb/gopher-utils/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
sudo: required
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.7.3
|
||||
|
||||
install:
|
||||
- go get -v -t ./...
|
||||
|
||||
script:
|
||||
- go test -v -race ./...
|
5
vendor/github.com/felipeweb/gopher-utils/README.md
generated
vendored
Normal file
5
vendor/github.com/felipeweb/gopher-utils/README.md
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# gopher-utils
|
||||
|
||||
[](https://travis-ci.org/felipeweb/gopher-utils) [](https://godoc.org/github.com/felipeweb/gopher-utils)
|
||||
|
||||
This is an open source project for commonly used functions for the Go programming language.
|
149
vendor/github.com/felipeweb/gopher-utils/convert.go
generated
vendored
Normal file
149
vendor/github.com/felipeweb/gopher-utils/convert.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
package gopher_utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Convert string to specify type.
|
||||
type StrTo string
|
||||
|
||||
func (f StrTo) Exist() bool {
|
||||
return string(f) != string(0x1E)
|
||||
}
|
||||
|
||||
func (f StrTo) Uint8() (uint8, error) {
|
||||
v, err := strconv.ParseUint(f.String(), 10, 8)
|
||||
return uint8(v), err
|
||||
}
|
||||
|
||||
func (f StrTo) Int() (int, error) {
|
||||
v, err := strconv.ParseInt(f.String(), 10, 0)
|
||||
return int(v), err
|
||||
}
|
||||
|
||||
func (f StrTo) Int64() (int64, error) {
|
||||
v, err := strconv.ParseInt(f.String(), 10, 64)
|
||||
return int64(v), err
|
||||
}
|
||||
|
||||
func (f StrTo) MustUint8() uint8 {
|
||||
v, _ := f.Uint8()
|
||||
return v
|
||||
}
|
||||
|
||||
func (f StrTo) MustInt() int {
|
||||
v, _ := f.Int()
|
||||
return v
|
||||
}
|
||||
|
||||
func (f StrTo) MustInt64() int64 {
|
||||
v, _ := f.Int64()
|
||||
return v
|
||||
}
|
||||
|
||||
func (f StrTo) String() string {
|
||||
if f.Exist() {
|
||||
return string(f)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// RuneToStr converts rune to string.
|
||||
// Do not use `ToStr` function for a rune, because Go will understand it as an int32, so the string will contain, erroneously, the char code.
|
||||
func RuneToStr(value rune) string {
|
||||
return string(value)
|
||||
}
|
||||
|
||||
// Convert any type to string.
|
||||
func ToStr(value interface{}, args ...int) (s string) {
|
||||
switch v := value.(type) {
|
||||
case bool:
|
||||
s = strconv.FormatBool(v)
|
||||
case float32:
|
||||
s = strconv.FormatFloat(float64(v), 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 32))
|
||||
case float64:
|
||||
s = strconv.FormatFloat(v, 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 64))
|
||||
case int:
|
||||
s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
|
||||
case int8:
|
||||
s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
|
||||
case int16:
|
||||
s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
|
||||
case int32:
|
||||
s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
|
||||
case int64:
|
||||
s = strconv.FormatInt(v, argInt(args).Get(0, 10))
|
||||
case uint:
|
||||
s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
|
||||
case uint8:
|
||||
s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
|
||||
case uint16:
|
||||
s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
|
||||
case uint32:
|
||||
s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
|
||||
case uint64:
|
||||
s = strconv.FormatUint(v, argInt(args).Get(0, 10))
|
||||
case string:
|
||||
s = v
|
||||
case []byte:
|
||||
s = string(v)
|
||||
default:
|
||||
s = fmt.Sprintf("%v", v)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
type argInt []int
|
||||
|
||||
func (a argInt) Get(i int, args ...int) (r int) {
|
||||
if i >= 0 && i < len(a) {
|
||||
r = a[i]
|
||||
} else if len(args) > 0 {
|
||||
r = args[0]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// HexStr2int converts hex format string to decimal number.
|
||||
func HexStr2int(hexStr string) (int, error) {
|
||||
num := 0
|
||||
length := len(hexStr)
|
||||
for i := 0; i < length; i++ {
|
||||
char := hexStr[length-i-1]
|
||||
factor := -1
|
||||
|
||||
switch {
|
||||
case char >= '0' && char <= '9':
|
||||
factor = int(char) - '0'
|
||||
case char >= 'a' && char <= 'f':
|
||||
factor = int(char) - 'a' + 10
|
||||
default:
|
||||
return -1, fmt.Errorf("invalid hex: %s", string(char))
|
||||
}
|
||||
|
||||
num += factor * PowInt(16, i)
|
||||
}
|
||||
return num, nil
|
||||
}
|
||||
|
||||
// Int2HexStr converts decimal number to hex format string.
|
||||
func Int2HexStr(num int) (hex string) {
|
||||
if num == 0 {
|
||||
return "0"
|
||||
}
|
||||
|
||||
for num > 0 {
|
||||
r := num % 16
|
||||
|
||||
c := "?"
|
||||
if r >= 0 && r <= 9 {
|
||||
c = string(r + '0')
|
||||
} else {
|
||||
c = string(r + 'a' - 10)
|
||||
}
|
||||
hex = c + hex
|
||||
num = num / 16
|
||||
}
|
||||
return hex
|
||||
}
|
41
vendor/github.com/felipeweb/gopher-utils/convert_test.go
generated
vendored
Normal file
41
vendor/github.com/felipeweb/gopher-utils/convert_test.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
package gopher_utils
|
||||
|
||||
import (
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHexStr2int(t *testing.T) {
|
||||
Convey("Convert hex format string to decimal", t, func() {
|
||||
hexDecs := map[string]int{
|
||||
"1": 1,
|
||||
"002": 2,
|
||||
"011": 17,
|
||||
"0a1": 161,
|
||||
"35e": 862,
|
||||
}
|
||||
|
||||
for hex, dec := range hexDecs {
|
||||
val, err := HexStr2int(hex)
|
||||
So(err, ShouldBeNil)
|
||||
So(val, ShouldEqual, dec)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestInt2HexStr(t *testing.T) {
|
||||
Convey("Convert decimal to hex format string", t, func() {
|
||||
decHexs := map[int]string{
|
||||
1: "1",
|
||||
2: "2",
|
||||
17: "11",
|
||||
161: "a1",
|
||||
862: "35e",
|
||||
}
|
||||
|
||||
for dec, hex := range decHexs {
|
||||
val := Int2HexStr(dec)
|
||||
So(val, ShouldEqual, hex)
|
||||
}
|
||||
})
|
||||
}
|
26
vendor/github.com/felipeweb/gopher-utils/digester.go
generated
vendored
Normal file
26
vendor/github.com/felipeweb/gopher-utils/digester.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package gopher_utils
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
func Sha256(str string) string {
|
||||
hash := sha256.New()
|
||||
hash.Write([]byte(str))
|
||||
return hex.EncodeToString(hash.Sum(nil))
|
||||
}
|
||||
|
||||
func Sha512(str string) string {
|
||||
hash := sha512.New()
|
||||
hash.Write([]byte(str))
|
||||
return hex.EncodeToString(hash.Sum(nil))
|
||||
}
|
||||
|
||||
func Md5(str string) string {
|
||||
hash := md5.New()
|
||||
hash.Write([]byte(str))
|
||||
return hex.EncodeToString(hash.Sum(nil))
|
||||
}
|
24
vendor/github.com/felipeweb/gopher-utils/digester_test.go
generated
vendored
Normal file
24
vendor/github.com/felipeweb/gopher-utils/digester_test.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package gopher_utils
|
||||
|
||||
import (
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMd5(t *testing.T) {
|
||||
Convey("Should encrypt using MD5", t, func() {
|
||||
So(Md5("test"), ShouldEqual, "098f6bcd4621d373cade4e832627b4f6")
|
||||
})
|
||||
}
|
||||
|
||||
func TestSha256(t *testing.T) {
|
||||
Convey("Should encrypt using SHA256", t, func() {
|
||||
So(Sha256("test"), ShouldEqual, "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08")
|
||||
})
|
||||
}
|
||||
|
||||
func TestSha512(t *testing.T) {
|
||||
Convey("Should encrypt using SHA512", t, func() {
|
||||
So(Sha512("test"), ShouldEqual, "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff")
|
||||
})
|
||||
}
|
159
vendor/github.com/felipeweb/gopher-utils/dir.go
generated
vendored
Normal file
159
vendor/github.com/felipeweb/gopher-utils/dir.go
generated
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
package gopher_utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IsDir returns true if given path is a directory,
|
||||
// or returns false when it's a file or does not exist.
|
||||
func IsDir(dir string) bool {
|
||||
f, e := os.Stat(dir)
|
||||
if e != nil {
|
||||
return false
|
||||
}
|
||||
return f.IsDir()
|
||||
}
|
||||
|
||||
func statDir(dirPath, recPath string, includeDir, isDirOnly bool) ([]string, error) {
|
||||
dir, err := os.Open(dirPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
fis, err := dir.Readdir(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
statList := make([]string, 0)
|
||||
for _, fi := range fis {
|
||||
if strings.Contains(fi.Name(), ".DS_Store") {
|
||||
continue
|
||||
}
|
||||
|
||||
relPath := path.Join(recPath, fi.Name())
|
||||
curPath := path.Join(dirPath, fi.Name())
|
||||
if fi.IsDir() {
|
||||
if includeDir {
|
||||
statList = append(statList, relPath+"/")
|
||||
}
|
||||
s, err := statDir(curPath, relPath, includeDir, isDirOnly)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
statList = append(statList, s...)
|
||||
} else if !isDirOnly {
|
||||
statList = append(statList, relPath)
|
||||
}
|
||||
}
|
||||
return statList, nil
|
||||
}
|
||||
|
||||
// StatDir gathers information of given directory by depth-first.
|
||||
// It returns slice of file list and includes subdirectories if enabled;
|
||||
// it returns error and nil slice when error occurs in underlying functions,
|
||||
// or given path is not a directory or does not exist.
|
||||
//
|
||||
// Slice does not include given path itself.
|
||||
// If subdirectories is enabled, they will have suffix '/'.
|
||||
func StatDir(rootPath string, includeDir ...bool) ([]string, error) {
|
||||
if !IsDir(rootPath) {
|
||||
return nil, errors.New("not a directory or does not exist: " + rootPath)
|
||||
}
|
||||
|
||||
isIncludeDir := false
|
||||
if len(includeDir) >= 1 {
|
||||
isIncludeDir = includeDir[0]
|
||||
}
|
||||
return statDir(rootPath, "", isIncludeDir, false)
|
||||
}
|
||||
|
||||
// GetAllSubDirs returns all subdirectories of given root path.
|
||||
// Slice does not include given path itself.
|
||||
func GetAllSubDirs(rootPath string) ([]string, error) {
|
||||
if !IsDir(rootPath) {
|
||||
return nil, errors.New("not a directory or does not exist: " + rootPath)
|
||||
}
|
||||
return statDir(rootPath, "", true, true)
|
||||
}
|
||||
|
||||
// GetFileListBySuffix returns an ordered list of file paths.
|
||||
// It recognize if given path is a file, and don't do recursive find.
|
||||
func GetFileListBySuffix(dirPath, suffix string) ([]string, error) {
|
||||
if !IsExist(dirPath) {
|
||||
return nil, fmt.Errorf("given path does not exist: %s", dirPath)
|
||||
} else if IsFile(dirPath) {
|
||||
return []string{dirPath}, nil
|
||||
}
|
||||
|
||||
// Given path is a directory.
|
||||
dir, err := os.Open(dirPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fis, err := dir.Readdir(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
files := make([]string, 0, len(fis))
|
||||
for _, fi := range fis {
|
||||
if strings.HasSuffix(fi.Name(), suffix) {
|
||||
files = append(files, path.Join(dirPath, fi.Name()))
|
||||
}
|
||||
}
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// CopyDir copy files recursively from source to target directory.
|
||||
//
|
||||
// The filter accepts a function that process the path info.
|
||||
// and should return true for need to filter.
|
||||
//
|
||||
// It returns error when error occurs in underlying functions.
|
||||
func CopyDir(srcPath, destPath string, filters ...func(filePath string) bool) error {
|
||||
// Check if target directory exists.
|
||||
if IsExist(destPath) {
|
||||
return errors.New("file or directory alreay exists: " + destPath)
|
||||
}
|
||||
|
||||
err := os.MkdirAll(destPath, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Gather directory info.
|
||||
infos, err := StatDir(srcPath, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var filter func(filePath string) bool
|
||||
if len(filters) > 0 {
|
||||
filter = filters[0]
|
||||
}
|
||||
|
||||
for _, info := range infos {
|
||||
if filter != nil && filter(info) {
|
||||
continue
|
||||
}
|
||||
|
||||
curPath := path.Join(destPath, info)
|
||||
if strings.HasSuffix(info, "/") {
|
||||
err = os.MkdirAll(curPath, os.ModePerm)
|
||||
} else {
|
||||
err = Copy(path.Join(srcPath, info), curPath)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
35
vendor/github.com/felipeweb/gopher-utils/dir_test.go
generated
vendored
Normal file
35
vendor/github.com/felipeweb/gopher-utils/dir_test.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
package gopher_utils
|
||||
|
||||
import (
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsDir(t *testing.T) {
|
||||
Convey("Check if given path is a directory", t, func() {
|
||||
Convey("Pass a file name", func() {
|
||||
So(IsDir("file.go"), ShouldEqual, false)
|
||||
})
|
||||
Convey("Pass a directory name", func() {
|
||||
So(IsDir("testdata"), ShouldEqual, true)
|
||||
})
|
||||
Convey("Pass a invalid path", func() {
|
||||
So(IsDir("foo"), ShouldEqual, false)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestCopyDir(t *testing.T) {
|
||||
Convey("Items of two slices should be same", t, func() {
|
||||
_, err := StatDir("testdata", true)
|
||||
So(err, ShouldEqual, nil)
|
||||
|
||||
err = CopyDir("testdata", "testdata2")
|
||||
So(err, ShouldEqual, nil)
|
||||
|
||||
_, err = StatDir("testdata2", true)
|
||||
os.RemoveAll("testdata2")
|
||||
So(err, ShouldEqual, nil)
|
||||
})
|
||||
}
|
2
vendor/github.com/felipeweb/gopher-utils/doc.go
generated
vendored
Normal file
2
vendor/github.com/felipeweb/gopher-utils/doc.go
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
//commonly used functions for the Go programming language.
|
||||
package gopher_utils
|
131
vendor/github.com/felipeweb/gopher-utils/file.go
generated
vendored
Normal file
131
vendor/github.com/felipeweb/gopher-utils/file.go
generated
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
package gopher_utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
// Storage unit constants.
|
||||
const (
|
||||
Byte = 1
|
||||
KByte = Byte * 1024
|
||||
MByte = KByte * 1024
|
||||
GByte = MByte * 1024
|
||||
TByte = GByte * 1024
|
||||
PByte = TByte * 1024
|
||||
EByte = PByte * 1024
|
||||
)
|
||||
|
||||
func logn(n, b float64) float64 {
|
||||
return math.Log(n) / math.Log(b)
|
||||
}
|
||||
|
||||
func humanateBytes(s uint64, base float64, sizes []string) string {
|
||||
if s < 10 {
|
||||
return fmt.Sprintf("%dB", s)
|
||||
}
|
||||
e := math.Floor(logn(float64(s), base))
|
||||
suffix := sizes[int(e)]
|
||||
val := float64(s) / math.Pow(base, math.Floor(e))
|
||||
f := "%.0f"
|
||||
if val < 10 {
|
||||
f = "%.1f"
|
||||
}
|
||||
|
||||
return fmt.Sprintf(f+"%s", val, suffix)
|
||||
}
|
||||
|
||||
// HumaneFileSize calculates the file size and generate user-friendly string.
|
||||
func HumaneFileSize(s uint64) string {
|
||||
sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"}
|
||||
return humanateBytes(s, 1024, sizes)
|
||||
}
|
||||
|
||||
// FileMTime returns file modified time and possible error.
|
||||
func FileMTime(file string) (int64, error) {
|
||||
f, err := os.Stat(file)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return f.ModTime().Unix(), nil
|
||||
}
|
||||
|
||||
// FileSize returns file size in bytes and possible error.
|
||||
func FileSize(file string) (int64, error) {
|
||||
f, err := os.Stat(file)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return f.Size(), nil
|
||||
}
|
||||
|
||||
// Copy copies file from source to target path.
|
||||
func Copy(src, dest string) error {
|
||||
// Gather file information to set back later.
|
||||
si, err := os.Lstat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Handle symbolic link.
|
||||
if si.Mode()&os.ModeSymlink != 0 {
|
||||
target, err := os.Readlink(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// NOTE: os.Chmod and os.Chtimes don't recoganize symbolic link,
|
||||
// which will lead "no such file or directory" error.
|
||||
return os.Symlink(target, dest)
|
||||
}
|
||||
|
||||
sr, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sr.Close()
|
||||
|
||||
dw, err := os.Create(dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dw.Close()
|
||||
|
||||
if _, err = io.Copy(dw, sr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set back file information.
|
||||
if err = os.Chtimes(dest, si.ModTime(), si.ModTime()); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Chmod(dest, si.Mode())
|
||||
}
|
||||
|
||||
// WriteFile writes data to a file named by filename.
|
||||
// If the file does not exist, WriteFile creates it
|
||||
// and its upper level paths.
|
||||
func WriteFile(filename string, data []byte) error {
|
||||
os.MkdirAll(path.Dir(filename), os.ModePerm)
|
||||
return ioutil.WriteFile(filename, data, 0655)
|
||||
}
|
||||
|
||||
// IsFile returns true if given path is a file,
|
||||
// or returns false when it's a directory or does not exist.
|
||||
func IsFile(filePath string) bool {
|
||||
f, e := os.Stat(filePath)
|
||||
if e != nil {
|
||||
return false
|
||||
}
|
||||
return !f.IsDir()
|
||||
}
|
||||
|
||||
// IsExist checks whether a file or directory exists.
|
||||
// It returns false when the file or directory does not exist.
|
||||
func IsExist(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil || os.IsExist(err)
|
||||
}
|
34
vendor/github.com/felipeweb/gopher-utils/file_test.go
generated
vendored
Normal file
34
vendor/github.com/felipeweb/gopher-utils/file_test.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
package gopher_utils
|
||||
|
||||
import (
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsFile(t *testing.T) {
|
||||
if !IsFile("file.go") {
|
||||
t.Errorf("IsExist:\n Expect => %v\n Got => %v\n", true, false)
|
||||
}
|
||||
|
||||
if IsFile("testdata") {
|
||||
t.Errorf("IsExist:\n Expect => %v\n Got => %v\n", false, true)
|
||||
}
|
||||
|
||||
if IsFile("files.go") {
|
||||
t.Errorf("IsExist:\n Expect => %v\n Got => %v\n", false, true)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsExist(t *testing.T) {
|
||||
Convey("Check if file or directory exists", t, func() {
|
||||
Convey("Pass a file name that exists", func() {
|
||||
So(IsExist("file.go"), ShouldEqual, true)
|
||||
})
|
||||
Convey("Pass a directory name that exists", func() {
|
||||
So(IsExist("testdata"), ShouldEqual, true)
|
||||
})
|
||||
Convey("Pass a directory name that does not exist", func() {
|
||||
So(IsExist(".hg"), ShouldEqual, false)
|
||||
})
|
||||
})
|
||||
}
|
15
vendor/github.com/felipeweb/gopher-utils/math.go
generated
vendored
Normal file
15
vendor/github.com/felipeweb/gopher-utils/math.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
package gopher_utils
|
||||
|
||||
// PowInt is int type of math.Pow function.
|
||||
func PowInt(x int, y int) int {
|
||||
if y <= 0 {
|
||||
return 1
|
||||
} else {
|
||||
if y%2 == 0 {
|
||||
sqrt := PowInt(x, y/2)
|
||||
return sqrt * sqrt
|
||||
} else {
|
||||
return PowInt(x, y-1) * x
|
||||
}
|
||||
}
|
||||
}
|
19
vendor/github.com/felipeweb/gopher-utils/math_test.go
generated
vendored
Normal file
19
vendor/github.com/felipeweb/gopher-utils/math_test.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
package gopher_utils
|
||||
|
||||
import (
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Pow(t *testing.T) {
|
||||
Convey("Power int", t, func() {
|
||||
for x := 0; x < 10; x++ {
|
||||
for y := 0; y < 8; y++ {
|
||||
result := PowInt(x, y)
|
||||
result_float := math.Pow(float64(x), float64(y))
|
||||
So(result, ShouldEqual, int(result_float))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
126
vendor/github.com/felipeweb/gopher-utils/string.go
generated
vendored
Normal file
126
vendor/github.com/felipeweb/gopher-utils/string.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
package gopher_utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// IsLetter returns true if the 'l' is an English letter.
|
||||
func IsLetter(l uint8) bool {
|
||||
n := (l | 0x20) - 'a'
|
||||
if n >= 0 && n < 26 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Reverse s string, support unicode
|
||||
func Reverse(s string) string {
|
||||
n := len(s)
|
||||
runes := make([]rune, n)
|
||||
for _, rune := range s {
|
||||
n--
|
||||
runes[n] = rune
|
||||
}
|
||||
return string(runes[n:])
|
||||
}
|
||||
|
||||
// ToSnakeCase can convert all upper case characters in a string to
|
||||
// underscore format.
|
||||
//
|
||||
// Some samples.
|
||||
// "FirstName" => "first_name"
|
||||
// "HTTPServer" => "http_server"
|
||||
// "NoHTTPS" => "no_https"
|
||||
// "GO_PATH" => "go_path"
|
||||
// "GO PATH" => "go_path" // space is converted to underscore.
|
||||
// "GO-PATH" => "go_path" // hyphen is converted to underscore.
|
||||
//
|
||||
func ToSnakeCase(str string) string {
|
||||
if len(str) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
var prev, r0, r1 rune
|
||||
var size int
|
||||
|
||||
r0 = '_'
|
||||
|
||||
for len(str) > 0 {
|
||||
prev = r0
|
||||
r0, size = utf8.DecodeRuneInString(str)
|
||||
str = str[size:]
|
||||
|
||||
switch {
|
||||
case r0 == utf8.RuneError:
|
||||
buf.WriteByte(byte(str[0]))
|
||||
|
||||
case unicode.IsUpper(r0):
|
||||
if prev != '_' {
|
||||
buf.WriteRune('_')
|
||||
}
|
||||
|
||||
buf.WriteRune(unicode.ToLower(r0))
|
||||
|
||||
if len(str) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
r0, size = utf8.DecodeRuneInString(str)
|
||||
str = str[size:]
|
||||
|
||||
if !unicode.IsUpper(r0) {
|
||||
buf.WriteRune(r0)
|
||||
break
|
||||
}
|
||||
|
||||
// find next non-upper-case character and insert `_` properly.
|
||||
// it's designed to convert `HTTPServer` to `http_server`.
|
||||
// if there are more than 2 adjacent upper case characters in a word,
|
||||
// treat them as an abbreviation plus a normal word.
|
||||
for len(str) > 0 {
|
||||
r1 = r0
|
||||
r0, size = utf8.DecodeRuneInString(str)
|
||||
str = str[size:]
|
||||
|
||||
if r0 == utf8.RuneError {
|
||||
buf.WriteRune(unicode.ToLower(r1))
|
||||
buf.WriteByte(byte(str[0]))
|
||||
break
|
||||
}
|
||||
|
||||
if !unicode.IsUpper(r0) {
|
||||
if r0 == '_' || r0 == ' ' || r0 == '-' {
|
||||
r0 = '_'
|
||||
|
||||
buf.WriteRune(unicode.ToLower(r1))
|
||||
} else {
|
||||
buf.WriteRune('_')
|
||||
buf.WriteRune(unicode.ToLower(r1))
|
||||
buf.WriteRune(r0)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
buf.WriteRune(unicode.ToLower(r1))
|
||||
}
|
||||
|
||||
if len(str) == 0 || r0 == '_' {
|
||||
buf.WriteRune(unicode.ToLower(r0))
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
if r0 == ' ' || r0 == '-' {
|
||||
r0 = '_'
|
||||
}
|
||||
|
||||
buf.WriteRune(r0)
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
54
vendor/github.com/felipeweb/gopher-utils/string_test.go
generated
vendored
Normal file
54
vendor/github.com/felipeweb/gopher-utils/string_test.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
package gopher_utils
|
||||
|
||||
import (
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsLetter(t *testing.T) {
|
||||
if IsLetter('1') {
|
||||
t.Errorf("IsLetter:\n Expect => %v\n Got => %v\n", false, true)
|
||||
}
|
||||
|
||||
if IsLetter('[') {
|
||||
t.Errorf("IsLetter:\n Expect => %v\n Got => %v\n", false, true)
|
||||
}
|
||||
|
||||
if !IsLetter('a') {
|
||||
t.Errorf("IsLetter:\n Expect => %v\n Got => %v\n", true, false)
|
||||
}
|
||||
|
||||
if !IsLetter('Z') {
|
||||
t.Errorf("IsLetter:\n Expect => %v\n Got => %v\n", true, false)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReverse(t *testing.T) {
|
||||
if Reverse("abcdefg") != "gfedcba" {
|
||||
t.Errorf("Reverse:\n Except => %s\n Got =>%s\n", "gfedcba", Reverse("abcdefg"))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ToSnakeCase(t *testing.T) {
|
||||
cases := map[string]string{
|
||||
"HTTPServer": "http_server",
|
||||
"_camelCase": "_camel_case",
|
||||
"NoHTTPS": "no_https",
|
||||
"Wi_thF": "wi_th_f",
|
||||
"_AnotherTES_TCaseP": "_another_tes_t_case_p",
|
||||
"ALL": "all",
|
||||
"_HELLO_WORLD_": "_hello_world_",
|
||||
"HELLO_WORLD": "hello_world",
|
||||
"HELLO____WORLD": "hello____world",
|
||||
"TW": "tw",
|
||||
"_C": "_c",
|
||||
|
||||
" sentence case ": "__sentence_case__",
|
||||
" Mixed-hyphen case _and SENTENCE_case and UPPER-case": "_mixed_hyphen_case__and_sentence_case_and_upper_case",
|
||||
}
|
||||
Convey("Convert string into snake case", t, func() {
|
||||
for old, new := range cases {
|
||||
So(ToSnakeCase(old), ShouldEqual, new)
|
||||
}
|
||||
})
|
||||
}
|
1
vendor/github.com/felipeweb/gopher-utils/testdata/SaveFile.txt
generated
vendored
Normal file
1
vendor/github.com/felipeweb/gopher-utils/testdata/SaveFile.txt
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
TestSaveFile
|
1
vendor/github.com/felipeweb/gopher-utils/testdata/SaveFileS.txt
generated
vendored
Normal file
1
vendor/github.com/felipeweb/gopher-utils/testdata/SaveFileS.txt
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
TestSaveFileS
|
0
vendor/github.com/felipeweb/gopher-utils/testdata/sample_file.txt
generated
vendored
Normal file
0
vendor/github.com/felipeweb/gopher-utils/testdata/sample_file.txt
generated
vendored
Normal file
1
vendor/github.com/felipeweb/gopher-utils/testdata/statDir/SaveFile.txt
generated
vendored
Normal file
1
vendor/github.com/felipeweb/gopher-utils/testdata/statDir/SaveFile.txt
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
TestSaveFile
|
1
vendor/github.com/felipeweb/gopher-utils/testdata/statDir/SaveFileS.txt
generated
vendored
Normal file
1
vendor/github.com/felipeweb/gopher-utils/testdata/statDir/SaveFileS.txt
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
TestSaveFileS
|
0
vendor/github.com/felipeweb/gopher-utils/testdata/statDir/sample_file.txt
generated
vendored
Normal file
0
vendor/github.com/felipeweb/gopher-utils/testdata/statDir/sample_file.txt
generated
vendored
Normal file
1
vendor/github.com/felipeweb/gopher-utils/testdata/statDir/secondLevel/SaveFile.txt
generated
vendored
Normal file
1
vendor/github.com/felipeweb/gopher-utils/testdata/statDir/secondLevel/SaveFile.txt
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
TestSaveFile
|
1
vendor/github.com/felipeweb/gopher-utils/testdata/statDir/secondLevel/SaveFileS.txt
generated
vendored
Normal file
1
vendor/github.com/felipeweb/gopher-utils/testdata/statDir/secondLevel/SaveFileS.txt
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
TestSaveFileS
|
0
vendor/github.com/felipeweb/gopher-utils/testdata/statDir/secondLevel/sample_file.txt
generated
vendored
Normal file
0
vendor/github.com/felipeweb/gopher-utils/testdata/statDir/secondLevel/sample_file.txt
generated
vendored
Normal file
101
vendor/github.com/felipeweb/gopher-utils/time.go
generated
vendored
Normal file
101
vendor/github.com/felipeweb/gopher-utils/time.go
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
package gopher_utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Format unix time int64 to string
|
||||
func Date(ti int64, format string) string {
|
||||
t := time.Unix(int64(ti), 0)
|
||||
return DateT(t, format)
|
||||
}
|
||||
|
||||
// Format unix time string to string
|
||||
func DateS(ts string, format string) string {
|
||||
i, _ := strconv.ParseInt(ts, 10, 64)
|
||||
return Date(i, format)
|
||||
}
|
||||
|
||||
// Format time.Time struct to string
|
||||
// MM - month - 01
|
||||
// M - month - 1, single bit
|
||||
// DD - day - 02
|
||||
// D - day 2
|
||||
// YYYY - year - 2006
|
||||
// YY - year - 06
|
||||
// HH - 24 hours - 03
|
||||
// H - 24 hours - 3
|
||||
// hh - 12 hours - 03
|
||||
// h - 12 hours - 3
|
||||
// mm - minute - 04
|
||||
// m - minute - 4
|
||||
// ss - second - 05
|
||||
// s - second = 5
|
||||
func DateT(t time.Time, format string) string {
|
||||
res := strings.Replace(format, "MM", t.Format("01"), -1)
|
||||
res = strings.Replace(res, "M", t.Format("1"), -1)
|
||||
res = strings.Replace(res, "DD", t.Format("02"), -1)
|
||||
res = strings.Replace(res, "D", t.Format("2"), -1)
|
||||
res = strings.Replace(res, "YYYY", t.Format("2006"), -1)
|
||||
res = strings.Replace(res, "YY", t.Format("06"), -1)
|
||||
res = strings.Replace(res, "HH", fmt.Sprintf("%02d", t.Hour()), -1)
|
||||
res = strings.Replace(res, "H", fmt.Sprintf("%d", t.Hour()), -1)
|
||||
res = strings.Replace(res, "hh", t.Format("03"), -1)
|
||||
res = strings.Replace(res, "h", t.Format("3"), -1)
|
||||
res = strings.Replace(res, "mm", t.Format("04"), -1)
|
||||
res = strings.Replace(res, "m", t.Format("4"), -1)
|
||||
res = strings.Replace(res, "ss", t.Format("05"), -1)
|
||||
res = strings.Replace(res, "s", t.Format("5"), -1)
|
||||
return res
|
||||
}
|
||||
|
||||
// DateFormat pattern rules.
|
||||
var datePatterns = []string{
|
||||
// year
|
||||
"Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003
|
||||
"y", "06", //A two digit representation of a year Examples: 99 or 03
|
||||
|
||||
// month
|
||||
"m", "01", // Numeric representation of a month, with leading zeros 01 through 12
|
||||
"n", "1", // Numeric representation of a month, without leading zeros 1 through 12
|
||||
"M", "Jan", // A short textual representation of a month, three letters Jan through Dec
|
||||
"F", "January", // A full textual representation of a month, such as January or March January through December
|
||||
|
||||
// day
|
||||
"d", "02", // Day of the month, 2 digits with leading zeros 01 to 31
|
||||
"j", "2", // Day of the month without leading zeros 1 to 31
|
||||
|
||||
// week
|
||||
"D", "Mon", // A textual representation of a day, three letters Mon through Sun
|
||||
"l", "Monday", // A full textual representation of the day of the week Sunday through Saturday
|
||||
|
||||
// time
|
||||
"g", "3", // 12-hour format of an hour without leading zeros 1 through 12
|
||||
"G", "15", // 24-hour format of an hour without leading zeros 0 through 23
|
||||
"h", "03", // 12-hour format of an hour with leading zeros 01 through 12
|
||||
"H", "15", // 24-hour format of an hour with leading zeros 00 through 23
|
||||
|
||||
"a", "pm", // Lowercase Ante meridiem and Post meridiem am or pm
|
||||
"A", "PM", // Uppercase Ante meridiem and Post meridiem AM or PM
|
||||
|
||||
"i", "04", // Minutes with leading zeros 00 to 59
|
||||
"s", "05", // Seconds, with leading zeros 00 through 59
|
||||
|
||||
// time zone
|
||||
"T", "MST",
|
||||
"P", "-07:00",
|
||||
"O", "-0700",
|
||||
|
||||
// RFC 2822
|
||||
"r", time.RFC1123Z,
|
||||
}
|
||||
|
||||
// Parse Date use PHP time format.
|
||||
func DateParse(dateString, format string) (time.Time, error) {
|
||||
replacer := strings.NewReplacer(datePatterns...)
|
||||
format = replacer.Replace(format)
|
||||
return time.ParseInLocation(format, dateString, time.Local)
|
||||
}
|
32
vendor/github.com/felipeweb/gopher-utils/time_test.go
generated
vendored
Normal file
32
vendor/github.com/felipeweb/gopher-utils/time_test.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package gopher_utils
|
||||
|
||||
import (
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"testing"
|
||||
"time"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func TestDate(t *testing.T) {
|
||||
Convey("Convert unix date format to string", t, func() {
|
||||
if runtime.GOOS == "darwin" {
|
||||
So(Date(1, "DD/MM/YYYY"), ShouldEqual, "31/12/1969")
|
||||
} else {
|
||||
So(Date(1, "DD/MM/YYYY"), ShouldEqual, "01/01/1970")
|
||||
}
|
||||
})
|
||||
Convey("Convert unix date in string format to humman string", t, func() {
|
||||
if runtime.GOOS == "darwin" {
|
||||
So(DateS("1", "DD/MM/YYYY"), ShouldEqual, "31/12/1969")
|
||||
} else {
|
||||
So(DateS("1", "DD/MM/YYYY"), ShouldEqual, "01/01/1970")
|
||||
}
|
||||
})
|
||||
Convey("Convert time object to string", t, func() {
|
||||
if runtime.GOOS == "darwin" {
|
||||
So(DateT(time.Unix(int64(1), 0), "DD/MM/YYYY"), ShouldEqual, "31/12/1969")
|
||||
} else {
|
||||
So(DateT(time.Unix(int64(1), 0), "DD/MM/YYYY"), ShouldEqual, "01/01/1970")
|
||||
}
|
||||
})
|
||||
}
|
74
vendor/github.com/felipeweb/osin-mysql/.gitignore
generated
vendored
Normal file
74
vendor/github.com/felipeweb/osin-mysql/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
|
||||
|
||||
*.iml
|
||||
|
||||
## Directory-based project format:
|
||||
.idea/
|
||||
# if you remove the above rule, at least ignore the following:
|
||||
|
||||
# User-specific stuff:
|
||||
# .idea/workspace.xml
|
||||
# .idea/tasks.xml
|
||||
# .idea/dictionaries
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
# .idea/dataSources.ids
|
||||
# .idea/dataSources.xml
|
||||
# .idea/sqlDataSources.xml
|
||||
# .idea/dynamic.xml
|
||||
# .idea/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
# .idea/gradle.xml
|
||||
# .idea/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
# .idea/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
### Go template
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
20
vendor/github.com/felipeweb/osin-mysql/.travis.yml
generated
vendored
Normal file
20
vendor/github.com/felipeweb/osin-mysql/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
sudo: required
|
||||
|
||||
language: go
|
||||
|
||||
os:
|
||||
- osx
|
||||
|
||||
go:
|
||||
- 1.7.3
|
||||
|
||||
before_install:
|
||||
- go get -v -t ./...
|
||||
- brew install mysql
|
||||
- mysql.server start
|
||||
|
||||
install:
|
||||
- mysql -uroot -e 'create database osin;'
|
||||
|
||||
script:
|
||||
- go test -v -race ./...
|
68
vendor/github.com/felipeweb/osin-mysql/README.md
generated
vendored
Normal file
68
vendor/github.com/felipeweb/osin-mysql/README.md
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
# osin-mysql
|
||||
|
||||
 [](https://godoc.org/github.com/felipeweb/osin-mysql)
|
||||
|
||||
A MySQL storage backend for [osin oauth2](https://github.com/RangelReale/osin).
|
||||
Additional to implementing the `osin.Storage` interface, the `github.com/felipeweb/osin-mysql/storage.Storage` interface defines new methods:
|
||||
|
||||
```
|
||||
// CreateClient stores the client in the database and returns an error, if something went wrong.
|
||||
CreateClient(client osin.Client) error
|
||||
|
||||
// UpdateClient updates the client (identified by its id) and replaces the values with the values of client.
|
||||
// Returns an error if something went wrong.
|
||||
UpdateClient(client osin.Client) error
|
||||
|
||||
// RemoveClient removes a client (identified by id) from the database. Returns an error if something went wrong.
|
||||
RemoveClient(id string) error
|
||||
```
|
||||
|
||||
## Encrypt your tokens
|
||||
|
||||
Unfortunately, the osin library offers little capabilities for storing credentials like access or refresh tokens in a
|
||||
hashed or encrypted way. An attacker could gain access to your database through various attack vectors, steal these
|
||||
tokens and gain, for example, administrative access to your application.
|
||||
|
||||
Please be aware, that this library stores all data as-is and does not perform any sort of encryption or hashing.
|
||||
|
||||
## Usage
|
||||
|
||||
First, install this library with `go get "github.com/felipeweb/osin-mysql"`.
|
||||
|
||||
```go
|
||||
import (
|
||||
"database/sql"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
|
||||
"github.com/felipeweb/osin-mysql"
|
||||
"github.com/RangelReale/osin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
url := "user:password@tcp(host:3306)/dbname?parseTime=true"
|
||||
db, err := sql.Open("mysql", url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
store := mysql.New(db,"osin_")
|
||||
store.CreateSchemas()
|
||||
server := osin.NewServer(osin.NewServerConfig(), store)
|
||||
|
||||
// See the osin documentation for more information
|
||||
// e.g.: server.HandleAuthorizeRequest(resp, r)
|
||||
}
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
TL;DR `AuthorizeData`'s `Client`'s and `AccessData`'s `UserData` field must be string due to language restrictions or an error will be thrown.
|
||||
|
||||
In osin, Client, AuthorizeData and AccessData have a `UserData` property of type `interface{}`. This does not work well
|
||||
with SQL, because it is not possible to gob decode or unmarshall the data back, since the concrete type is not known.
|
||||
Because osin's storage interface does not support setting the UserData type, **this library tries to convert UserData to string
|
||||
and return it as such.** With this, you could for example gob encode (use e.g. base64 encode for SQL storage type compatibility)
|
||||
the data before passing it to e.g. `FinishAccessRequest` and decode it when needed.
|
||||
|
||||
# 2016-10-23 BREAKING CHANGES
|
||||
- Now `New()` recives a db object and a table prefix as parameter
|
50
vendor/github.com/felipeweb/osin-mysql/doc.go
generated
vendored
Normal file
50
vendor/github.com/felipeweb/osin-mysql/doc.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
/*Package mysql is a storage backend for osin oauth2. Additional to implementing the osin.Storage interface, the github.com/felipeweb/osin-mysql/storage.Storage interface defines new methods:
|
||||
|
||||
// CreateClient stores the client in the database and returns an error, if something went wrong.
|
||||
CreateClient(client osin.Client) error
|
||||
|
||||
// UpdateClient updates the client (identified by its id) and replaces the values with the values of client.
|
||||
// Returns an error if something went wrong.
|
||||
UpdateClient(client osin.Client) error
|
||||
|
||||
// RemoveClient removes a client (identified by id) from the database. Returns an error if something went wrong.
|
||||
RemoveClient(id string) error
|
||||
Encrypt your tokens
|
||||
|
||||
Unfortunately, the osin library offers little capabilities for storing credentials like access or refresh tokens in a hashed or encrypted way. An attacker could gain access to your database through various attack vectors, steal these tokens and gain, for example, administrative access to your application.
|
||||
|
||||
Please be aware, that this library stores all data as-is and does not perform any sort of encryption or hashing.
|
||||
|
||||
Usage
|
||||
|
||||
First, install this library with go get "github.com/felipeweb/osin-mysql".
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
|
||||
"github.com/felipeweb/osin-mysql"
|
||||
"github.com/RangelReale/osin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
url := "user:password@tcp(host:3306)/dbname?parseTime=true"
|
||||
db, err := sql.Open("mysql", url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
store := mysql.New(db,"osin_")
|
||||
store.CreateSchemas()
|
||||
server := osin.NewServer(osin.NewServerConfig(), store)
|
||||
|
||||
// See the osin documentation for more information
|
||||
// e.g.: server.HandleAuthorizeRequest(resp, r)
|
||||
}
|
||||
Limitations
|
||||
|
||||
TL;DR AuthorizeData's Client's and AccessData's UserData field must be string due to language restrictions or an error will be thrown.
|
||||
|
||||
In osin, Client, AuthorizeData and AccessData have a UserData property of type interface{}. This does not work well with SQL, because it is not possible to gob decode or unmarshall the data back, since the concrete type is not known. Because osin's storage interface does not support setting the UserData type, this library tries to convert UserData to string and return it as such. With this, you could for example gob encode (use e.g. base64 encode for SQL storage type compatibility) the data before passing it to e.g. FinishAccessRequest and decode it when needed.
|
||||
*/
|
||||
package mysql
|
140
vendor/github.com/felipeweb/osin-mysql/examples/simple.go
generated
vendored
Normal file
140
vendor/github.com/felipeweb/osin-mysql/examples/simple.go
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
package main
|
||||
|
||||
// Open url in browser:
|
||||
// http://localhost:14000/app
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/RangelReale/osin"
|
||||
"github.com/RangelReale/osin/example"
|
||||
"github.com/felipeweb/osin-mysql"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func main() {
|
||||
urldb := "root:@tcp(localhost:3306)/osin?parseTime=true"
|
||||
db, err := sql.Open("mysql", urldb)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
store := mysql.New(db, "osin_")
|
||||
err = store.CreateSchemas()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cfg := osin.NewServerConfig()
|
||||
cfg.AllowGetAccessRequest = true
|
||||
cfg.AllowClientSecretInParams = true
|
||||
|
||||
server := osin.NewServer(cfg, store)
|
||||
|
||||
// Authorization code endpoint
|
||||
http.HandleFunc("/authorize", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ar := server.HandleAuthorizeRequest(resp, r); ar != nil {
|
||||
if !example.HandleLoginPage(ar, w, r) {
|
||||
return
|
||||
}
|
||||
ar.Authorized = true
|
||||
server.FinishAuthorizeRequest(resp, r, ar)
|
||||
}
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
fmt.Printf("ERROR: %s\n", resp.InternalError)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
// Access token endpoint
|
||||
http.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ar := server.HandleAccessRequest(resp, r); ar != nil {
|
||||
ar.Authorized = true
|
||||
server.FinishAccessRequest(resp, r, ar)
|
||||
}
|
||||
if resp.IsError && resp.InternalError != nil {
|
||||
fmt.Printf("ERROR: %s\n", resp.InternalError)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
// Information endpoint
|
||||
http.HandleFunc("/info", func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := server.NewResponse()
|
||||
defer resp.Close()
|
||||
|
||||
if ir := server.HandleInfoRequest(resp, r); ir != nil {
|
||||
server.FinishInfoRequest(resp, r, ir)
|
||||
}
|
||||
osin.OutputJSON(resp, w, r)
|
||||
})
|
||||
|
||||
// Application home endpoint
|
||||
http.HandleFunc("/app", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("<html><body>"))
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"/authorize?response_type=code&client_id=1234&state=xyz&scope=everything&redirect_uri=%s\">Login</a><br/>", url.QueryEscape("http://localhost:14000/appauth/code"))))
|
||||
w.Write([]byte("</body></html>"))
|
||||
})
|
||||
|
||||
// Application destination - CODE
|
||||
http.HandleFunc("/appauth/code", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
|
||||
code := r.Form.Get("code")
|
||||
|
||||
w.Write([]byte("<html><body>"))
|
||||
w.Write([]byte("APP AUTH - CODE<br/>"))
|
||||
defer w.Write([]byte("</body></html>"))
|
||||
|
||||
if code == "" {
|
||||
w.Write([]byte("Nothing to do"))
|
||||
return
|
||||
}
|
||||
|
||||
jr := make(map[string]interface{})
|
||||
|
||||
// build access code url
|
||||
aurl := fmt.Sprintf("/token?grant_type=authorization_code&client_id=1234&client_secret=aabbccdd&state=xyz&redirect_uri=%s&code=%s",
|
||||
url.QueryEscape("http://localhost:14000/appauth/code"), url.QueryEscape(code))
|
||||
|
||||
// if parse, download and parse json
|
||||
if r.Form.Get("doparse") == "1" {
|
||||
err := example.DownloadAccessToken(fmt.Sprintf("http://localhost:14000%s", aurl),
|
||||
&osin.BasicAuth{"1234", "aabbccdd"}, jr)
|
||||
if err != nil {
|
||||
w.Write([]byte(err.Error()))
|
||||
w.Write([]byte("<br/>"))
|
||||
}
|
||||
}
|
||||
|
||||
// show json error
|
||||
if erd, ok := jr["error"]; ok {
|
||||
w.Write([]byte(fmt.Sprintf("ERROR: %s<br/>\n", erd)))
|
||||
}
|
||||
|
||||
// show json access token
|
||||
if at, ok := jr["access_token"]; ok {
|
||||
w.Write([]byte(fmt.Sprintf("ACCESS TOKEN: %s<br/>\n", at)))
|
||||
}
|
||||
|
||||
w.Write([]byte(fmt.Sprintf("FULL RESULT: %+v<br/>\n", jr)))
|
||||
|
||||
// output links
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Goto Token URL</a><br/>", aurl)))
|
||||
|
||||
cururl := *r.URL
|
||||
curq := cururl.Query()
|
||||
curq.Add("doparse", "1")
|
||||
cururl.RawQuery = curq.Encode()
|
||||
w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Download Token</a><br/>", cururl.String())))
|
||||
})
|
||||
|
||||
http.ListenAndServe(":14000", nil)
|
||||
}
|
363
vendor/github.com/felipeweb/osin-mysql/mysql.go
generated
vendored
Normal file
363
vendor/github.com/felipeweb/osin-mysql/mysql.go
generated
vendored
Normal file
@@ -0,0 +1,363 @@
|
||||
// Package mysql is a osin storage implementation for mysql.
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/RangelReale/osin"
|
||||
"github.com/ansel1/merry"
|
||||
"github.com/felipeweb/gopher-utils"
|
||||
// driver for mysql db
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
var schemas = []string{`CREATE TABLE IF NOT EXISTS {prefix}client (
|
||||
id varchar(255) BINARY NOT NULL PRIMARY KEY,
|
||||
secret varchar(255) NOT NULL,
|
||||
extra varchar(255) NOT NULL,
|
||||
redirect_uri varchar(255) NOT NULL
|
||||
)`, `CREATE TABLE IF NOT EXISTS {prefix}authorize (
|
||||
client varchar(255) BINARY NOT NULL,
|
||||
code varchar(255) BINARY NOT NULL PRIMARY KEY,
|
||||
expires_in int(10) NOT NULL,
|
||||
scope varchar(255) NOT NULL,
|
||||
redirect_uri varchar(255) NOT NULL,
|
||||
state varchar(255) NOT NULL,
|
||||
extra varchar(255) NOT NULL,
|
||||
created_at timestamp NOT NULL
|
||||
)`, `CREATE TABLE IF NOT EXISTS {prefix}access (
|
||||
client varchar(255) BINARY NOT NULL,
|
||||
authorize varchar(255) BINARY NOT NULL,
|
||||
previous varchar(255) BINARY NOT NULL,
|
||||
access_token varchar(255) BINARY NOT NULL PRIMARY KEY,
|
||||
refresh_token varchar(255) BINARY NOT NULL,
|
||||
expires_in int(10) NOT NULL,
|
||||
scope varchar(255) NOT NULL,
|
||||
redirect_uri varchar(255) NOT NULL,
|
||||
extra varchar(255) NOT NULL,
|
||||
created_at timestamp NOT NULL
|
||||
)`, `CREATE TABLE IF NOT EXISTS {prefix}refresh (
|
||||
token varchar(255) BINARY NOT NULL PRIMARY KEY,
|
||||
access varchar(255) BINARY NOT NULL
|
||||
)`, `CREATE TABLE IF NOT EXISTS {prefix}expires (
|
||||
id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
token varchar(255) BINARY NOT NULL,
|
||||
expires_at timestamp NOT NULL,
|
||||
INDEX expires_index (expires_at),
|
||||
INDEX token_expires_index (token)
|
||||
)`,
|
||||
}
|
||||
|
||||
// Storage implements interface "github.com/RangelReale/osin".Storage and interface "github.com/felipeweb/osin-mysql/storage".Storage
|
||||
type Storage struct {
|
||||
db *sql.DB
|
||||
tablePrefix string
|
||||
}
|
||||
|
||||
// New returns a new mysql storage instance.
|
||||
func New(db *sql.DB, tablePrefix string) *Storage {
|
||||
return &Storage{db, tablePrefix}
|
||||
}
|
||||
|
||||
// CreateSchemas creates the schemata, if they do not exist yet in the database. Returns an error if something went wrong.
|
||||
func (s *Storage) CreateSchemas() error {
|
||||
for k, schema := range schemas {
|
||||
schema := strings.Replace(schema, "{prefix}", s.tablePrefix, 4)
|
||||
if _, err := s.db.Exec(schema); err != nil {
|
||||
log.Printf("Error creating schema %d: %s", k, schema)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clone the storage if needed. For example, using mgo, you can clone the session with session.Clone
|
||||
// to avoid concurrent access problems.
|
||||
// This is to avoid cloning the connection at each method access.
|
||||
// Can return itself if not a problem.
|
||||
func (s *Storage) Clone() osin.Storage {
|
||||
return s
|
||||
}
|
||||
|
||||
// Close the resources the Storage potentially holds (using Clone for example)
|
||||
func (s *Storage) Close() {
|
||||
}
|
||||
|
||||
// GetClient loads the client by id
|
||||
func (s *Storage) GetClient(id string) (osin.Client, error) {
|
||||
row := s.db.QueryRow(fmt.Sprintf("SELECT id, secret, redirect_uri, extra FROM %sclient WHERE id=?", s.tablePrefix), id)
|
||||
var c osin.DefaultClient
|
||||
var extra string
|
||||
|
||||
if err := row.Scan(&c.Id, &c.Secret, &c.RedirectUri, &extra); err == sql.ErrNoRows {
|
||||
return nil, osin.ErrNotFound
|
||||
} else if err != nil {
|
||||
return nil, merry.Wrap(err)
|
||||
}
|
||||
c.UserData = extra
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
// UpdateClient updates the client (identified by it's id) and replaces the values with the values of client.
|
||||
func (s *Storage) UpdateClient(c osin.Client) error {
|
||||
data := gopher_utils.ToStr(c.GetUserData())
|
||||
|
||||
if _, err := s.db.Exec(fmt.Sprintf("UPDATE %sclient SET secret=?, redirect_uri=?, extra=? WHERE id=?", s.tablePrefix), c.GetSecret(), c.GetRedirectUri(), data, c.GetId()); err != nil {
|
||||
return merry.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateClient stores the client in the database and returns an error, if something went wrong.
|
||||
func (s *Storage) CreateClient(c osin.Client) error {
|
||||
data := gopher_utils.ToStr(c.GetUserData())
|
||||
|
||||
if _, err := s.db.Exec(fmt.Sprintf("INSERT INTO %sclient (id, secret, redirect_uri, extra) VALUES (?, ?, ?, ?)", s.tablePrefix), c.GetId(), c.GetSecret(), c.GetRedirectUri(), data); err != nil {
|
||||
return merry.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveClient removes a client (identified by id) from the database. Returns an error if something went wrong.
|
||||
func (s *Storage) RemoveClient(id string) (err error) {
|
||||
if _, err = s.db.Exec(fmt.Sprintf("DELETE FROM %sclient WHERE id=?", s.tablePrefix), id); err != nil {
|
||||
return merry.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SaveAuthorize saves authorize data.
|
||||
func (s *Storage) SaveAuthorize(data *osin.AuthorizeData) (err error) {
|
||||
extra := gopher_utils.ToStr(data.UserData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = s.db.Exec(
|
||||
fmt.Sprintf("INSERT INTO %sauthorize (client, code, expires_in, scope, redirect_uri, state, created_at, extra) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", s.tablePrefix),
|
||||
data.Client.GetId(),
|
||||
data.Code,
|
||||
data.ExpiresIn,
|
||||
data.Scope,
|
||||
data.RedirectUri,
|
||||
data.State,
|
||||
data.CreatedAt,
|
||||
extra,
|
||||
); err != nil {
|
||||
return merry.Wrap(err)
|
||||
}
|
||||
if err = s.AddExpireAtData(data.Code, data.ExpireAt()); err != nil {
|
||||
return merry.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadAuthorize looks up AuthorizeData by a code.
|
||||
// Client information MUST be loaded together.
|
||||
// Optionally can return error if expired.
|
||||
func (s *Storage) LoadAuthorize(code string) (*osin.AuthorizeData, error) {
|
||||
var data osin.AuthorizeData
|
||||
var extra string
|
||||
var cid string
|
||||
if err := s.db.QueryRow(fmt.Sprintf("SELECT client, code, expires_in, scope, redirect_uri, state, created_at, extra FROM %sauthorize WHERE code=? LIMIT 1", s.tablePrefix), code).Scan(&cid, &data.Code, &data.ExpiresIn, &data.Scope, &data.RedirectUri, &data.State, &data.CreatedAt, &extra); err == sql.ErrNoRows {
|
||||
return nil, osin.ErrNotFound
|
||||
} else if err != nil {
|
||||
return nil, merry.Wrap(err)
|
||||
}
|
||||
data.UserData = extra
|
||||
|
||||
c, err := s.GetClient(cid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if data.ExpireAt().Before(time.Now()) {
|
||||
return nil, merry.Errorf("Token expired at %s.", data.ExpireAt().String())
|
||||
}
|
||||
|
||||
data.Client = c
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
// RemoveAuthorize revokes or deletes the authorization code.
|
||||
func (s *Storage) RemoveAuthorize(code string) (err error) {
|
||||
if _, err = s.db.Exec(fmt.Sprintf("DELETE FROM %sauthorize WHERE code=?", s.tablePrefix), code); err != nil {
|
||||
return merry.Wrap(err)
|
||||
}
|
||||
if err = s.RemoveExpireAtData(code); err != nil {
|
||||
return merry.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SaveAccess writes AccessData.
|
||||
// If RefreshToken is not blank, it must save in a way that can be loaded using LoadRefresh.
|
||||
func (s *Storage) SaveAccess(data *osin.AccessData) (err error) {
|
||||
prev := ""
|
||||
authorizeData := &osin.AuthorizeData{}
|
||||
|
||||
if data.AccessData != nil {
|
||||
prev = data.AccessData.AccessToken
|
||||
}
|
||||
|
||||
if data.AuthorizeData != nil {
|
||||
authorizeData = data.AuthorizeData
|
||||
}
|
||||
|
||||
extra := gopher_utils.ToStr(data.UserData)
|
||||
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
return merry.Wrap(err)
|
||||
}
|
||||
|
||||
if data.RefreshToken != "" {
|
||||
if err := s.saveRefresh(tx, data.RefreshToken, data.AccessToken); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if data.Client == nil {
|
||||
return merry.New("data.Client must not be nil")
|
||||
}
|
||||
|
||||
_, err = tx.Exec(fmt.Sprintf("INSERT INTO %saccess (client, authorize, previous, access_token, refresh_token, expires_in, scope, redirect_uri, created_at, extra) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", s.tablePrefix), data.Client.GetId(), authorizeData.Code, prev, data.AccessToken, data.RefreshToken, data.ExpiresIn, data.Scope, data.RedirectUri, data.CreatedAt, extra)
|
||||
if err != nil {
|
||||
if rbe := tx.Rollback(); rbe != nil {
|
||||
return merry.Wrap(rbe)
|
||||
}
|
||||
return merry.Wrap(err)
|
||||
}
|
||||
|
||||
if err = s.AddExpireAtData(data.AccessToken, data.ExpireAt()); err != nil {
|
||||
return merry.Wrap(err)
|
||||
}
|
||||
if err = tx.Commit(); err != nil {
|
||||
return merry.Wrap(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadAccess retrieves access data by token. Client information MUST be loaded together.
|
||||
// AuthorizeData and AccessData DON'T NEED to be loaded if not easily available.
|
||||
// Optionally can return error if expired.
|
||||
func (s *Storage) LoadAccess(code string) (*osin.AccessData, error) {
|
||||
var extra, cid, prevAccessToken, authorizeCode string
|
||||
var result osin.AccessData
|
||||
|
||||
if err := s.db.QueryRow(
|
||||
fmt.Sprintf("SELECT client, authorize, previous, access_token, refresh_token, expires_in, scope, redirect_uri, created_at, extra FROM %saccess WHERE access_token=? LIMIT 1", s.tablePrefix),
|
||||
code,
|
||||
).Scan(
|
||||
&cid,
|
||||
&authorizeCode,
|
||||
&prevAccessToken,
|
||||
&result.AccessToken,
|
||||
&result.RefreshToken,
|
||||
&result.ExpiresIn,
|
||||
&result.Scope,
|
||||
&result.RedirectUri,
|
||||
&result.CreatedAt,
|
||||
&extra,
|
||||
); err == sql.ErrNoRows {
|
||||
return nil, osin.ErrNotFound
|
||||
} else if err != nil {
|
||||
return nil, merry.Wrap(err)
|
||||
}
|
||||
|
||||
result.UserData = extra
|
||||
client, err := s.GetClient(cid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result.Client = client
|
||||
result.AuthorizeData, _ = s.LoadAuthorize(authorizeCode)
|
||||
prevAccess, _ := s.LoadAccess(prevAccessToken)
|
||||
result.AccessData = prevAccess
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// RemoveAccess revokes or deletes an AccessData.
|
||||
func (s *Storage) RemoveAccess(code string) (err error) {
|
||||
if _, err = s.db.Exec(fmt.Sprintf("DELETE FROM %saccess WHERE access_token=?", s.tablePrefix), code); err != nil {
|
||||
return merry.Wrap(err)
|
||||
}
|
||||
if err = s.RemoveExpireAtData(code); err != nil {
|
||||
return merry.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadRefresh retrieves refresh AccessData. Client information MUST be loaded together.
|
||||
// AuthorizeData and AccessData DON'T NEED to be loaded if not easily available.
|
||||
// Optionally can return error if expired.
|
||||
func (s *Storage) LoadRefresh(code string) (*osin.AccessData, error) {
|
||||
row := s.db.QueryRow(fmt.Sprintf("SELECT access FROM %srefresh WHERE token=? LIMIT 1", s.tablePrefix), code)
|
||||
var access string
|
||||
if err := row.Scan(&access); err == sql.ErrNoRows {
|
||||
return nil, osin.ErrNotFound
|
||||
} else if err != nil {
|
||||
return nil, merry.Wrap(err)
|
||||
}
|
||||
return s.LoadAccess(access)
|
||||
}
|
||||
|
||||
// RemoveRefresh revokes or deletes refresh AccessData.
|
||||
func (s *Storage) RemoveRefresh(code string) error {
|
||||
_, err := s.db.Exec(fmt.Sprintf("DELETE FROM %srefresh WHERE token=?", s.tablePrefix), code)
|
||||
if err != nil {
|
||||
return merry.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateClientWithInformation Makes easy to create a osin.DefaultClient
|
||||
func (s *Storage) CreateClientWithInformation(id string, secret string, redirectURI string, userData interface{}) osin.Client {
|
||||
return &osin.DefaultClient{
|
||||
Id: id,
|
||||
Secret: secret,
|
||||
RedirectUri: redirectURI,
|
||||
UserData: userData,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Storage) saveRefresh(tx *sql.Tx, refresh, access string) (err error) {
|
||||
_, err = tx.Exec(fmt.Sprintf("INSERT INTO %srefresh (token, access) VALUES (?, ?)", s.tablePrefix), refresh, access)
|
||||
if err != nil {
|
||||
if rbe := tx.Rollback(); rbe != nil {
|
||||
return merry.Wrap(rbe)
|
||||
}
|
||||
return merry.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddExpireAtData add info in expires table
|
||||
func (s *Storage) AddExpireAtData(code string, expireAt time.Time) error {
|
||||
if _, err := s.db.Exec(
|
||||
fmt.Sprintf("INSERT INTO %sexpires(token, expires_at) VALUES(?, ?)", s.tablePrefix),
|
||||
code,
|
||||
expireAt,
|
||||
); err != nil {
|
||||
return merry.Wrap(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveExpireAtData remove info in expires table
|
||||
func (s *Storage) RemoveExpireAtData(code string) error {
|
||||
if _, err := s.db.Exec(
|
||||
fmt.Sprintf("DELETE FROM %sexpires WHERE token=?", s.tablePrefix),
|
||||
code,
|
||||
); err != nil {
|
||||
return merry.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
243
vendor/github.com/felipeweb/osin-mysql/mysql_test.go
generated
vendored
Normal file
243
vendor/github.com/felipeweb/osin-mysql/mysql_test.go
generated
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/RangelReale/osin"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/pborman/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var db *sql.DB
|
||||
var store *Storage
|
||||
var userDataMock = "bar"
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
db, err = sql.Open("mysql", "root:@tcp(localhost:3306)/osin?parseTime=true")
|
||||
if err != nil {
|
||||
log.Fatalf("Could not open connect to database: %s", err)
|
||||
|
||||
}
|
||||
err = db.Ping()
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Could not connect to database: %s", err)
|
||||
}
|
||||
|
||||
store = New(db, "osin_")
|
||||
if err = store.CreateSchemas(); err != nil {
|
||||
log.Fatalf("Could not ping database: %v", err)
|
||||
}
|
||||
|
||||
retCode := m.Run()
|
||||
|
||||
os.Exit(retCode)
|
||||
}
|
||||
|
||||
func TestClientOperations(t *testing.T) {
|
||||
create := &osin.DefaultClient{Id: "1", Secret: "secret", RedirectUri: "http://localhost/", UserData: ""}
|
||||
createClient(t, *store, create)
|
||||
getClient(t, *store, create)
|
||||
}
|
||||
|
||||
func TestAuthorizeOperations(t *testing.T) {
|
||||
client := &osin.DefaultClient{Id: "2", Secret: "secret", RedirectUri: "http://localhost/", UserData: ""}
|
||||
createClient(t, *store, client)
|
||||
|
||||
for _, authorize := range []*osin.AuthorizeData{
|
||||
{
|
||||
Client: client,
|
||||
Code: uuid.New(),
|
||||
ExpiresIn: int32(600),
|
||||
Scope: "scope",
|
||||
RedirectUri: "http://localhost/",
|
||||
State: "state",
|
||||
CreatedAt: time.Now().Round(time.Second),
|
||||
UserData: userDataMock,
|
||||
},
|
||||
} {
|
||||
// Test save
|
||||
require.Nil(t, store.SaveAuthorize(authorize))
|
||||
|
||||
// Test fetch
|
||||
_, err := store.LoadAuthorize(authorize.Code)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, authorize.CreatedAt.Unix(), authorize.CreatedAt.Unix())
|
||||
|
||||
// Test remove
|
||||
require.Nil(t, store.RemoveAuthorize(authorize.Code))
|
||||
_, err = store.LoadAuthorize(authorize.Code)
|
||||
require.NotNil(t, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestStoreFailsOnInvalidUserData(t *testing.T) {
|
||||
client := &osin.DefaultClient{Id: "3", Secret: "secret", RedirectUri: "http://localhost/", UserData: ""}
|
||||
authorize := &osin.AuthorizeData{
|
||||
Client: client,
|
||||
Code: uuid.New(),
|
||||
ExpiresIn: int32(60),
|
||||
Scope: "scope",
|
||||
RedirectUri: "http://localhost/",
|
||||
State: "state",
|
||||
CreatedAt: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
|
||||
UserData: struct{ foo string }{"bar"},
|
||||
}
|
||||
access := &osin.AccessData{
|
||||
Client: client,
|
||||
AuthorizeData: authorize,
|
||||
AccessData: nil,
|
||||
AccessToken: uuid.New(),
|
||||
RefreshToken: uuid.New(),
|
||||
ExpiresIn: int32(60),
|
||||
Scope: "scope",
|
||||
RedirectUri: "https://localhost/",
|
||||
CreatedAt: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
|
||||
UserData: struct{ foo string }{"bar"},
|
||||
}
|
||||
assert.Nil(t, store.SaveAuthorize(authorize))
|
||||
assert.Nil(t, store.SaveAccess(access))
|
||||
}
|
||||
|
||||
func TestAccessOperations(t *testing.T) {
|
||||
client := &osin.DefaultClient{Id: "3", Secret: "secret", RedirectUri: "http://localhost/", UserData: ""}
|
||||
authorize := &osin.AuthorizeData{
|
||||
Client: client,
|
||||
Code: uuid.New(),
|
||||
ExpiresIn: int32(60),
|
||||
Scope: "scope",
|
||||
RedirectUri: "http://localhost/",
|
||||
State: "state",
|
||||
CreatedAt: time.Now().Round(time.Second),
|
||||
UserData: userDataMock,
|
||||
}
|
||||
nestedAccess := &osin.AccessData{
|
||||
Client: client,
|
||||
AuthorizeData: authorize,
|
||||
AccessData: nil,
|
||||
AccessToken: uuid.New(),
|
||||
RefreshToken: uuid.New(),
|
||||
ExpiresIn: int32(60),
|
||||
Scope: "scope",
|
||||
RedirectUri: "https://localhost/",
|
||||
CreatedAt: time.Now().Round(time.Second),
|
||||
UserData: userDataMock,
|
||||
}
|
||||
access := &osin.AccessData{
|
||||
Client: client,
|
||||
AuthorizeData: authorize,
|
||||
AccessData: nestedAccess,
|
||||
AccessToken: uuid.New(),
|
||||
RefreshToken: uuid.New(),
|
||||
ExpiresIn: int32(60),
|
||||
Scope: "scope",
|
||||
RedirectUri: "https://localhost/",
|
||||
CreatedAt: time.Now().Round(time.Second),
|
||||
UserData: userDataMock,
|
||||
}
|
||||
|
||||
require.Nil(t, store.SaveAuthorize(authorize))
|
||||
require.Nil(t, store.SaveAccess(nestedAccess))
|
||||
require.Nil(t, store.SaveAccess(access))
|
||||
|
||||
_, err := store.LoadAccess(access.AccessToken)
|
||||
require.NotNil(t, err)
|
||||
|
||||
require.Nil(t, store.RemoveAuthorize(authorize.Code))
|
||||
_, err = store.LoadAccess(access.AccessToken)
|
||||
require.NotNil(t, err)
|
||||
|
||||
require.Nil(t, store.RemoveAccess(nestedAccess.AccessToken))
|
||||
_, err = store.LoadAccess(access.AccessToken)
|
||||
require.NotNil(t, err)
|
||||
|
||||
require.Nil(t, store.RemoveAccess(access.AccessToken))
|
||||
_, err = store.LoadAccess(access.AccessToken)
|
||||
require.NotNil(t, err)
|
||||
|
||||
require.Nil(t, store.RemoveAuthorize(authorize.Code))
|
||||
}
|
||||
|
||||
func TestRefreshOperations(t *testing.T) {
|
||||
client := &osin.DefaultClient{Id: "4", Secret: "secret", RedirectUri: "http://localhost/", UserData: ""}
|
||||
type test struct {
|
||||
access *osin.AccessData
|
||||
}
|
||||
|
||||
for k, c := range []*test{
|
||||
{
|
||||
access: &osin.AccessData{
|
||||
Client: client,
|
||||
AuthorizeData: &osin.AuthorizeData{
|
||||
Client: client,
|
||||
Code: uuid.New(),
|
||||
ExpiresIn: int32(60),
|
||||
Scope: "scope",
|
||||
RedirectUri: "http://localhost/",
|
||||
State: "state",
|
||||
CreatedAt: time.Now().Round(time.Second),
|
||||
UserData: userDataMock,
|
||||
},
|
||||
AccessData: nil,
|
||||
AccessToken: uuid.New(),
|
||||
RefreshToken: uuid.New(),
|
||||
ExpiresIn: int32(60),
|
||||
Scope: "scope",
|
||||
RedirectUri: "https://localhost/",
|
||||
CreatedAt: time.Now().Round(time.Second),
|
||||
UserData: userDataMock,
|
||||
},
|
||||
},
|
||||
} {
|
||||
|
||||
_, err := store.LoadRefresh(c.access.RefreshToken)
|
||||
require.NotNil(t, err)
|
||||
|
||||
require.Nil(t, store.RemoveRefresh(c.access.RefreshToken))
|
||||
_, err = store.LoadRefresh(c.access.RefreshToken)
|
||||
|
||||
require.NotNil(t, err, "Case %d", k)
|
||||
require.Nil(t, store.RemoveAccess(c.access.AccessToken), "Case %d", k)
|
||||
require.Nil(t, store.SaveAccess(c.access), "Case %d", k)
|
||||
|
||||
_, err = store.LoadRefresh(c.access.RefreshToken)
|
||||
require.NotNil(t, err, "Case %d", k)
|
||||
|
||||
require.Nil(t, store.RemoveAccess(c.access.AccessToken), "Case %d", k)
|
||||
_, err = store.LoadRefresh(c.access.RefreshToken)
|
||||
require.NotNil(t, err, "Case %d", k)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
type ts struct{}
|
||||
|
||||
func (s *ts) String() string {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
func TestCreateClientWithInformation(t *testing.T) {
|
||||
res := store.CreateClientWithInformation("1", "123", "http://test.com/redirect", "data")
|
||||
assert.Equal(t, "1", res.GetId())
|
||||
assert.Equal(t, "123", res.GetSecret())
|
||||
assert.Equal(t, "http://test.com/redirect", res.GetRedirectUri())
|
||||
assert.Equal(t, "data", res.GetUserData())
|
||||
}
|
||||
|
||||
func getClient(t *testing.T, store Storage, set osin.Client) {
|
||||
client, err := store.GetClient(set.GetId())
|
||||
require.Nil(t, err)
|
||||
require.EqualValues(t, set, client)
|
||||
}
|
||||
|
||||
func createClient(t *testing.T, store Storage, set osin.Client) {
|
||||
require.Nil(t, store.CreateClient(set))
|
||||
}
|
19
vendor/github.com/felipeweb/osin-mysql/storage/storage.go
generated
vendored
Normal file
19
vendor/github.com/felipeweb/osin-mysql/storage/storage.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// Package storage defines an interface, which all osin-storage implementations are going to support.
|
||||
package storage
|
||||
|
||||
import "github.com/RangelReale/osin"
|
||||
|
||||
// Storage extends github.com/RangelReale/osin.Storage with create, update and delete methods for clients.
|
||||
type Storage interface {
|
||||
osin.Storage
|
||||
|
||||
// CreateClient stores the client in the database and returns an error, if something went wrong.
|
||||
CreateClient(client osin.Client) error
|
||||
|
||||
// UpdateClient updates the client (identified by it's id) and replaces the values with the values of client.
|
||||
// Returns an error if something went wrong.
|
||||
UpdateClient(client osin.Client) error
|
||||
|
||||
// RemoveClient removes a client (identified by id) from the database. Returns an error if something went wrong.
|
||||
RemoveClient(id string) error
|
||||
}
|
Reference in New Issue
Block a user