replace zxq.co/ripple/hanayo

This commit is contained in:
Alicia
2019-02-23 13:29:15 +00:00
commit c3d206c173
5871 changed files with 1353715 additions and 0 deletions

125
vendor/github.com/felipeweb/gopher-utils/.gitignore generated vendored Normal file
View 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
View 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
View File

@@ -0,0 +1,5 @@
# gopher-utils
[![Build Status](https://travis-ci.org/felipeweb/gopher-utils.svg?branch=master)](https://travis-ci.org/felipeweb/gopher-utils) [![GoDoc](https://godoc.org/github.com/felipeweb/gopher-utils?status.svg)](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
View 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
}

View 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
View 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))
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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()
}

View 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)
}
})
}

View File

@@ -0,0 +1 @@
TestSaveFile

View File

@@ -0,0 +1 @@
TestSaveFileS

View File

View File

@@ -0,0 +1 @@
TestSaveFile

View File

@@ -0,0 +1 @@
TestSaveFileS

View File

@@ -0,0 +1 @@
TestSaveFile

View File

@@ -0,0 +1 @@
TestSaveFileS

101
vendor/github.com/felipeweb/gopher-utils/time.go generated vendored Normal file
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,68 @@
# osin-mysql
![Travis](https://travis-ci.org/felipeweb/osin-mysql.svg?branch=master) [![GoDoc](https://godoc.org/github.com/felipeweb/osin-mysql?status.svg)](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
View 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

View 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
View 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
View 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))
}

View 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
}