introduce authy challenger
This commit is contained in:
parent
68627eb835
commit
a48f4238e7
27 changed files with 2136 additions and 0 deletions
35
Gopkg.lock
generated
35
Gopkg.lock
generated
|
|
@ -58,6 +58,14 @@
|
|||
revision = "7f2434bc10da710debe5c4315ed6d4df454b4024"
|
||||
version = "v0.1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ce22663ae8e32354d55110a3791d8c8ceaf67118e5f4510f8daf3077d8469f7a"
|
||||
name = "github.com/dcu/go-authy"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "0c8491e20fe9225f4902f789480d7166eab098f7"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:3a0e8b1ac13f6daa8e6e18b0129a8f1498ade5530fb55cba67c7312ddbb1c78c"
|
||||
|
|
@ -85,6 +93,24 @@
|
|||
revision = "72cd26f257d44c1114970e19afddcd812016007e"
|
||||
version = "v1.4.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ce81ffad3aa4553bf838a989f1ae04a8eecbcfff602af18a27da447c610e8115"
|
||||
name = "github.com/gojektech/heimdall"
|
||||
packages = [
|
||||
".",
|
||||
"httpclient",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "v5.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:8a48298b54e39f555ae307275ec80b17deeb8825edd97b81cd313787ab86a54a"
|
||||
name = "github.com/gojektech/valkyrie"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "6aee720afcdffc337029305c126e0079491063f0"
|
||||
version = "v1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:745fa6a19eb7613f207a17b42db0c4425d553fd38ee9387bc686a4b87beb178b"
|
||||
|
|
@ -170,6 +196,14 @@
|
|||
pruneopts = "NUT"
|
||||
revision = "f4cd9f5e29232537a12db1678f48c702ad6896b7"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:14715f705ff5dfe0ffd6571d7d201dd8e921030f8070321a79380d8ca4ec1a24"
|
||||
name = "github.com/pkg/errors"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4"
|
||||
version = "v0.8.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:f2805adeca595d7dbd25173b57f83daaa79f44d43475263c4e34b05020eac9a7"
|
||||
name = "go.opencensus.io"
|
||||
|
|
@ -341,6 +375,7 @@
|
|||
"github.com/Azure/go-autorest/autorest",
|
||||
"github.com/Azure/go-autorest/autorest/adal",
|
||||
"github.com/Azure/go-autorest/autorest/azure",
|
||||
"github.com/dcu/go-authy",
|
||||
"github.com/go-sql-driver/mysql",
|
||||
"github.com/gokyle/sshkey",
|
||||
"github.com/jessevdk/go-flags",
|
||||
|
|
|
|||
21
sshpiperd/challenger/authy/authy.go
Normal file
21
sshpiperd/challenger/authy/authy.go
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package authy
|
||||
|
||||
import (
|
||||
"github.com/tg123/sshpiper/sshpiperd/challenger"
|
||||
)
|
||||
|
||||
func (authyClient) GetName() string {
|
||||
return "authy"
|
||||
}
|
||||
|
||||
func (a *authyClient) GetOpts() interface{} {
|
||||
return &a.Config
|
||||
}
|
||||
|
||||
func (a *authyClient) GetHandler() challenger.Handler {
|
||||
return a.challenge
|
||||
}
|
||||
|
||||
func init() {
|
||||
challenger.Register("authy", &authyClient{})
|
||||
}
|
||||
31
sshpiperd/challenger/authy/authyid.go
Normal file
31
sshpiperd/challenger/authy/authyid.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package authy
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (a authyClient) findAuthyId(user string) (string, error) {
|
||||
// TODO a better way to handle large database
|
||||
|
||||
file, err := os.Open(a.Config.File)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
fields := strings.Fields(scanner.Text())
|
||||
|
||||
if len(fields) >= 2 {
|
||||
if fields[0] == user {
|
||||
return fields[1], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("authy id for user not found")
|
||||
}
|
||||
50
sshpiperd/challenger/authy/authyid_test.go
Normal file
50
sshpiperd/challenger/authy/authyid_test.go
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package authy
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_findAuthyId(t *testing.T) {
|
||||
|
||||
a := authyClient{}
|
||||
tmpfile, err := ioutil.TempFile("", "authyid")
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("cannot create temp file %v", err)
|
||||
}
|
||||
|
||||
defer os.Remove(tmpfile.Name())
|
||||
a.Config.File = tmpfile.Name()
|
||||
|
||||
ioutil.WriteFile(tmpfile.Name(), []byte(`
|
||||
piper 123
|
||||
hook 456
|
||||
hook 789
|
||||
`), os.ModePerm)
|
||||
|
||||
{
|
||||
id, err := a.findAuthyId("piper")
|
||||
if err != nil {
|
||||
t.Fatalf("findId failed %v", err)
|
||||
}
|
||||
|
||||
if id != "123" {
|
||||
t.Error("find id return wrong value")
|
||||
}
|
||||
|
||||
}
|
||||
{
|
||||
id, err := a.findAuthyId("hook")
|
||||
if err != nil {
|
||||
t.Fatalf("findId failed %v", err)
|
||||
}
|
||||
|
||||
if id != "456" {
|
||||
t.Error("find id return wrong value")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
96
sshpiperd/challenger/authy/handler.go
Normal file
96
sshpiperd/challenger/authy/handler.go
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
package authy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
"github.com/dcu/go-authy"
|
||||
)
|
||||
|
||||
type authyClient struct {
|
||||
Config struct {
|
||||
APIKey string `long:"challenger-authy-apikey" description:"Authy API Key" env:"SSHPIPERD_CHALLENGER_AUTHY_APIKEY" ini-name:"challenger-authy-apikey"`
|
||||
Method string `long:"challenger-authy-method" default:"token" description:"Authy authentication method" env:"SSHPIPERD_CHALLENGER_AUTHY_METHOD" ini-name:"challenger-authy-method" choice:"token" choice:"onetouch"`
|
||||
File string `long:"challenger-authy-idfile" description:"Path to a file with ssh_name [space] authy_id per line (first line win if duplicate)" env:"SSHPIPERD_CHALLENGER_AUTHY_IDFILE" ini-name:"challenger-authy-idfile"`
|
||||
}
|
||||
|
||||
authyAPI *authy.Authy
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func (a *authyClient) Init(logger *log.Logger) error {
|
||||
a.logger = logger
|
||||
a.authyAPI = authy.NewAuthyAPI(a.Config.APIKey)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *authyClient) challenge(conn ssh.ConnMetadata, client ssh.KeyboardInteractiveChallenge) (ssh.AdditionalChallengeContext, error) {
|
||||
user := conn.User()
|
||||
|
||||
authyId, err := a.findAuthyId(user)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch a.Config.Method {
|
||||
case "token":
|
||||
|
||||
ans, err := client(user, "", []string{"Please input your Authy token: "}, []bool{true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
verification, err := a.authyAPI.VerifyToken(authyId, ans[0], url.Values{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if verification.Valid() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf(verification.Message)
|
||||
case "onetouch":
|
||||
_, err = client(conn.User(), "Please verify login on your Authy app", nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
details := authy.Details{
|
||||
"User": user,
|
||||
"ClientIP": conn.RemoteAddr().String(),
|
||||
}
|
||||
|
||||
approvalRequest, err := a.authyAPI.SendApprovalRequest(authyId, "Log to SSH server", details, url.Values{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
status, err := a.authyAPI.WaitForApprovalRequest(approvalRequest.UUID, time.Second*30, url.Values{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if status == authy.OneTouchStatusApproved {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
_, err = client(conn.User(), "Authy OneTouch failed", nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("one touch faild code: %v", status)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported authy method")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -4,6 +4,7 @@ import (
|
|||
_ "github.com/tg123/sshpiper/sshpiperd/upstream/database"
|
||||
_ "github.com/tg123/sshpiper/sshpiperd/upstream/workingdir"
|
||||
|
||||
_ "github.com/tg123/sshpiper/sshpiperd/challenger/authy"
|
||||
_ "github.com/tg123/sshpiper/sshpiperd/challenger/azdevicecode"
|
||||
_ "github.com/tg123/sshpiper/sshpiperd/challenger/pam"
|
||||
|
||||
|
|
|
|||
20
vendor/github.com/dcu/go-authy/LICENSE.txt
generated
vendored
Normal file
20
vendor/github.com/dcu/go-authy/LICENSE.txt
generated
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2015
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
306
vendor/github.com/dcu/go-authy/api.go
generated
vendored
Normal file
306
vendor/github.com/dcu/go-authy/api.go
generated
vendored
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
package authy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gojektech/heimdall"
|
||||
"github.com/gojektech/heimdall/httpclient"
|
||||
)
|
||||
|
||||
var (
|
||||
// Logger is the default logger of this package. You can override it with your own.
|
||||
Logger = log.New(os.Stderr, "[authy] ", log.LstdFlags)
|
||||
|
||||
_Dialer = &net.Dialer{
|
||||
Timeout: 5 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}
|
||||
|
||||
// DefaultTransport is the default transport struct for the HTTP client
|
||||
DefaultTransport = &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: _Dialer.DialContext,
|
||||
MaxIdleConns: 128,
|
||||
IdleConnTimeout: 30 * time.Second,
|
||||
TLSHandshakeTimeout: 5 * time.Second,
|
||||
ExpectContinueTimeout: 3 * time.Second,
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
longPollingDelay = 2000 * time.Millisecond
|
||||
)
|
||||
|
||||
const (
|
||||
// SMS indicates the message will be delivered via SMS
|
||||
SMS = "sms"
|
||||
|
||||
// Voice indicates the message will be delivered via phone call
|
||||
Voice = "call"
|
||||
)
|
||||
|
||||
// Details for OneTouch transaction
|
||||
type Details map[string]string
|
||||
|
||||
// Authy contains credentials to connect to the Authy's API
|
||||
type Authy struct {
|
||||
APIKey string
|
||||
BaseURL string
|
||||
Client heimdall.Client
|
||||
}
|
||||
|
||||
// NewAuthyAPI returns an instance of Authy pointing to production.
|
||||
func NewAuthyAPI(apiKey string) *Authy {
|
||||
apiURL := "https://api.authy.com"
|
||||
|
||||
initalTimeout := 2 * time.Millisecond
|
||||
maxTimeout := 1000 * time.Millisecond
|
||||
exponentFactor := 2.0
|
||||
maximumJitterInterval := 2 * time.Millisecond
|
||||
backoff := heimdall.NewExponentialBackoff(initalTimeout, maxTimeout, exponentFactor, maximumJitterInterval)
|
||||
|
||||
client := httpclient.NewClient(
|
||||
httpclient.WithHTTPTimeout(1*time.Second),
|
||||
httpclient.WithRetrier(heimdall.NewRetrier(backoff)),
|
||||
httpclient.WithRetryCount(4),
|
||||
httpclient.WithHTTPClient(&http.Client{
|
||||
Transport: DefaultTransport,
|
||||
}),
|
||||
)
|
||||
|
||||
return &Authy{
|
||||
APIKey: apiKey,
|
||||
BaseURL: apiURL,
|
||||
Client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterUser register a new user given an email and phone number.
|
||||
func (authy *Authy) RegisterUser(email string, countryCode int, phoneNumber string, params url.Values) (*User, error) {
|
||||
Logger.Println("Creating Authy user with", email, ",", phoneNumber, "and", countryCode)
|
||||
|
||||
path := "/protected/json/users/new"
|
||||
|
||||
params.Set("user[cellphone]", phoneNumber)
|
||||
params.Set("user[country_code]", strconv.Itoa(countryCode))
|
||||
params.Set("user[email]", email)
|
||||
|
||||
response, err := authy.DoRequest("POST", path, params)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userResponse, err := NewUser(response)
|
||||
return userResponse, err
|
||||
}
|
||||
|
||||
// UserStatus returns a set of data about a user.
|
||||
func (authy *Authy) UserStatus(id string, params url.Values) (*UserStatus, error) {
|
||||
Logger.Println("Finding Authy user with id", id)
|
||||
|
||||
path := fmt.Sprintf("/protected/json/users/%s/status", id)
|
||||
|
||||
response, err := authy.DoRequest("GET", path, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
statusResponse, err := NewUserStatus(response)
|
||||
return statusResponse, err
|
||||
}
|
||||
|
||||
// VerifyToken verifies the given token
|
||||
func (authy *Authy) VerifyToken(userID string, token string, params url.Values) (*TokenVerification, error) {
|
||||
path := "/protected/json/verify/" + url.QueryEscape(token) + "/" + url.QueryEscape(userID)
|
||||
|
||||
response, err := authy.DoRequest("GET", path, params)
|
||||
|
||||
if err != nil {
|
||||
Logger.Println("Error while contacting the API:", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer closeResponseBody(response)
|
||||
|
||||
tokenVerification, err := NewTokenVerification(response)
|
||||
return tokenVerification, err
|
||||
}
|
||||
|
||||
// RequestSMS requests a SMS for the given userID
|
||||
func (authy *Authy) RequestSMS(userID string, params url.Values) (*SMSRequest, error) {
|
||||
path := "/protected/json/sms/" + url.QueryEscape(userID)
|
||||
response, err := authy.DoRequest("GET", path, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer closeResponseBody(response)
|
||||
smsVerification, err := NewSMSRequest(response)
|
||||
return smsVerification, err
|
||||
}
|
||||
|
||||
// RequestPhoneCall requests a phone call for the given user
|
||||
func (authy *Authy) RequestPhoneCall(userID string, params url.Values) (*PhoneCallRequest, error) {
|
||||
path := "/protected/json/call/" + url.QueryEscape(userID)
|
||||
|
||||
response, err := authy.DoRequest("GET", path, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer closeResponseBody(response)
|
||||
smsVerification, err := NewPhoneCallRequest(response)
|
||||
return smsVerification, err
|
||||
}
|
||||
|
||||
// SendApprovalRequest sends a OneTouch's approval request to the given user.
|
||||
func (authy *Authy) SendApprovalRequest(userID string, message string, details Details, params url.Values) (*ApprovalRequest, error) {
|
||||
addParamsForOneTouch(params, message, details)
|
||||
path := fmt.Sprintf(`/onetouch/json/users/%s/approval_requests`, url.QueryEscape(userID))
|
||||
|
||||
response, err := authy.DoRequest("POST", path, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer closeResponseBody(response)
|
||||
return NewApprovalRequest(response)
|
||||
}
|
||||
|
||||
// FindApprovalRequest finds an approval request given its uuid.
|
||||
func (authy *Authy) FindApprovalRequest(uuid string, params url.Values) (*ApprovalRequest, error) {
|
||||
path := fmt.Sprintf("/onetouch/json/approval_requests/%s", uuid)
|
||||
response, err := authy.DoRequest("GET", path, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer closeResponseBody(response)
|
||||
approvalRequest, err := NewApprovalRequest(response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
approvalRequest.UUID = uuid
|
||||
return approvalRequest, nil
|
||||
}
|
||||
|
||||
// WaitForApprovalRequest waits until the status of an approval request has changed or times out.
|
||||
func (authy *Authy) WaitForApprovalRequest(uuid string, maxDuration time.Duration, params url.Values) (OneTouchStatus, error) {
|
||||
for maxDuration > 0 {
|
||||
request, err := authy.FindApprovalRequest(uuid, url.Values{})
|
||||
if err != nil {
|
||||
return OneTouchStatusPending, err
|
||||
}
|
||||
|
||||
if request.Status != OneTouchStatusPending {
|
||||
return request.Status, nil
|
||||
}
|
||||
|
||||
maxDuration -= longPollingDelay
|
||||
time.Sleep(longPollingDelay)
|
||||
}
|
||||
|
||||
return OneTouchStatusExpired, nil
|
||||
}
|
||||
|
||||
// StartPhoneVerification starts the phone verification process.
|
||||
func (authy *Authy) StartPhoneVerification(countryCode int, phoneNumber string, via string, params url.Values) (*PhoneVerificationStart, error) {
|
||||
params.Set("country_code", strconv.Itoa(countryCode))
|
||||
params.Set("phone_number", phoneNumber)
|
||||
params.Set("via", via)
|
||||
|
||||
path := fmt.Sprintf("/protected/json/phones/verification/start")
|
||||
response, err := authy.DoRequest("POST", path, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer closeResponseBody(response)
|
||||
return NewPhoneVerificationStart(response)
|
||||
}
|
||||
|
||||
// CheckPhoneVerification checks the given verification code.
|
||||
func (authy *Authy) CheckPhoneVerification(countryCode int, phoneNumber string, verificationCode string, params url.Values) (*PhoneVerificationCheck, error) {
|
||||
params.Set("country_code", strconv.Itoa(countryCode))
|
||||
params.Set("phone_number", phoneNumber)
|
||||
params.Set("verification_code", verificationCode)
|
||||
|
||||
path := fmt.Sprintf("/protected/json/phones/verification/check")
|
||||
response, err := authy.DoRequest("GET", path, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer closeResponseBody(response)
|
||||
return NewPhoneVerificationCheck(response)
|
||||
}
|
||||
|
||||
// DoRequest performs a HTTP request to the Authy API
|
||||
func (authy *Authy) DoRequest(method string, path string, params url.Values) (*http.Response, error) {
|
||||
apiURL := authy.buildURL(path)
|
||||
|
||||
// Set api_key to all requests.
|
||||
params.Set("api_key", authy.APIKey)
|
||||
|
||||
var bodyReader io.Reader
|
||||
switch method {
|
||||
case "POST":
|
||||
{
|
||||
encodedParams := params.Encode()
|
||||
bodyReader = strings.NewReader(encodedParams)
|
||||
}
|
||||
case "GET":
|
||||
{
|
||||
apiURL += "?" + params.Encode()
|
||||
}
|
||||
}
|
||||
|
||||
request, err := http.NewRequest(method, apiURL, bodyReader)
|
||||
if method == "POST" {
|
||||
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
Logger.Println("Error creating HTTP request:", err)
|
||||
return nil, err
|
||||
}
|
||||
response, err := authy.Client.Do(request)
|
||||
|
||||
return response, err
|
||||
}
|
||||
|
||||
func (authy *Authy) buildURL(path string) string {
|
||||
if path[0] != '/' {
|
||||
path = "/" + path
|
||||
}
|
||||
url := authy.BaseURL + path
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
func closeResponseBody(response *http.Response) {
|
||||
err := response.Body.Close()
|
||||
if err != nil {
|
||||
Logger.Println("Error closing response body:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func addParamsForOneTouch(params url.Values, message string, details map[string]string) url.Values {
|
||||
params.Set("message", message)
|
||||
for key, value := range details {
|
||||
params.Set(fmt.Sprintf("details[%s]", key), value)
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
66
vendor/github.com/dcu/go-authy/approval_request.go
generated
vendored
Normal file
66
vendor/github.com/dcu/go-authy/approval_request.go
generated
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
package authy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// OneTouchStatus is the type of the OneTouch statuses.
|
||||
type OneTouchStatus string
|
||||
|
||||
var (
|
||||
// OneTouchStatusApproved is the approved status of an approval request
|
||||
OneTouchStatusApproved OneTouchStatus = "approved"
|
||||
|
||||
// OneTouchStatusPending is the pending status of an approval request
|
||||
OneTouchStatusPending OneTouchStatus = "pending"
|
||||
|
||||
// OneTouchStatusDenied is the denied status of an approval request
|
||||
OneTouchStatusDenied OneTouchStatus = "denied"
|
||||
|
||||
// OneTouchStatusExpired is the expired status of an approval request
|
||||
OneTouchStatusExpired OneTouchStatus = "expired"
|
||||
)
|
||||
|
||||
// ApprovalRequest is the approval request response.
|
||||
type ApprovalRequest struct {
|
||||
HTTPResponse *http.Response
|
||||
|
||||
Status OneTouchStatus `json:"status"`
|
||||
UUID string `json:"uuid"`
|
||||
Notified bool `json:"notified"`
|
||||
}
|
||||
|
||||
// NewApprovalRequest returns an instance of ApprovalRequest.
|
||||
func NewApprovalRequest(response *http.Response) (*ApprovalRequest, error) {
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
jsonResponse := struct {
|
||||
Success bool `json:"success"`
|
||||
ApprovalRequest *ApprovalRequest `json:"approval_request"`
|
||||
Message string `json:"message"`
|
||||
}{}
|
||||
|
||||
err = json.Unmarshal(body, &jsonResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !jsonResponse.Success {
|
||||
return nil, fmt.Errorf("invalid approval request response: %s", jsonResponse.Message)
|
||||
}
|
||||
approvalRequest := jsonResponse.ApprovalRequest
|
||||
approvalRequest.HTTPResponse = response
|
||||
|
||||
return approvalRequest, nil
|
||||
}
|
||||
|
||||
// Valid returns true if the approval request was valid.
|
||||
func (request *ApprovalRequest) Valid() bool {
|
||||
return request.HTTPResponse.StatusCode == 200
|
||||
}
|
||||
37
vendor/github.com/dcu/go-authy/phone_call_request.go
generated
vendored
Normal file
37
vendor/github.com/dcu/go-authy/phone_call_request.go
generated
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package authy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// PhoneCallRequest encapsulates the response from the Authy API
|
||||
type PhoneCallRequest struct {
|
||||
HTTPResponse *http.Response
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// NewPhoneCallRequest returns an instance of a PhoneCallRequest
|
||||
func NewPhoneCallRequest(response *http.Response) (*PhoneCallRequest, error) {
|
||||
request := &PhoneCallRequest{HTTPResponse: response}
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
|
||||
if err != nil {
|
||||
Logger.Println("Error reading from API:", err)
|
||||
return request, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &request)
|
||||
if err != nil {
|
||||
Logger.Println("Error parsing JSON:", err)
|
||||
return request, err
|
||||
}
|
||||
|
||||
return request, nil
|
||||
}
|
||||
|
||||
// Valid returns true if the request was valid.
|
||||
func (request *PhoneCallRequest) Valid() bool {
|
||||
return request.HTTPResponse.StatusCode == 200
|
||||
}
|
||||
57
vendor/github.com/dcu/go-authy/phone_verification.go
generated
vendored
Normal file
57
vendor/github.com/dcu/go-authy/phone_verification.go
generated
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package authy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// PhoneVerificationStart encapsulates the response from the Authy API when requesting a phone verification.
|
||||
type PhoneVerificationStart struct {
|
||||
HTTPResponse *http.Response
|
||||
UUID string `json:"uuid"`
|
||||
Message string `json:"message"`
|
||||
Success bool `json:"success"`
|
||||
Carrier string `json:"carrier"`
|
||||
}
|
||||
|
||||
// NewPhoneVerificationStart receives a http request, parses the body and return an instance of PhoneVerification
|
||||
func NewPhoneVerificationStart(response *http.Response) (*PhoneVerificationStart, error) {
|
||||
phoneVerification := &PhoneVerificationStart{HTTPResponse: response}
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
|
||||
if err != nil {
|
||||
return phoneVerification, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &phoneVerification)
|
||||
if err != nil {
|
||||
return phoneVerification, err
|
||||
}
|
||||
|
||||
return phoneVerification, nil
|
||||
}
|
||||
|
||||
// PhoneVerificationCheck encapsulates the response from the Authy API when checking a phone verification.
|
||||
type PhoneVerificationCheck struct {
|
||||
HTTPResponse *http.Response
|
||||
Message string `json:"message"`
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
// NewPhoneVerificationCheck receives a http request, parses the body and return an instance of PhoneVerification
|
||||
func NewPhoneVerificationCheck(response *http.Response) (*PhoneVerificationCheck, error) {
|
||||
phoneVerification := &PhoneVerificationCheck{HTTPResponse: response}
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
|
||||
if err != nil {
|
||||
return phoneVerification, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &phoneVerification)
|
||||
if err != nil {
|
||||
return phoneVerification, err
|
||||
}
|
||||
|
||||
return phoneVerification, nil
|
||||
}
|
||||
37
vendor/github.com/dcu/go-authy/sms_request.go
generated
vendored
Normal file
37
vendor/github.com/dcu/go-authy/sms_request.go
generated
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package authy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// SMSRequest encapsulates the response from the Authy API when requesting a SMS
|
||||
type SMSRequest struct {
|
||||
HTTPResponse *http.Response
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// NewSMSRequest returns an instance of SMSRequest
|
||||
func NewSMSRequest(response *http.Response) (*SMSRequest, error) {
|
||||
request := &SMSRequest{HTTPResponse: response}
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
|
||||
if err != nil {
|
||||
Logger.Println("Error reading from API:", err)
|
||||
return request, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &request)
|
||||
if err != nil {
|
||||
Logger.Println("Error parsing JSON:", err)
|
||||
return request, err
|
||||
}
|
||||
|
||||
return request, nil
|
||||
}
|
||||
|
||||
// Valid returns true if the SMS was sent
|
||||
func (request *SMSRequest) Valid() bool {
|
||||
return request.HTTPResponse.StatusCode == 200
|
||||
}
|
||||
43
vendor/github.com/dcu/go-authy/token_verification.go
generated
vendored
Normal file
43
vendor/github.com/dcu/go-authy/token_verification.go
generated
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package authy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// TokenVerification encapsulates the response from Authy API when verifying a token.
|
||||
type TokenVerification struct {
|
||||
HTTPResponse *http.Response
|
||||
Message string `json:"message"`
|
||||
Token string `json:"token"`
|
||||
Success interface{} `json:"success"`
|
||||
}
|
||||
|
||||
// NewTokenVerification creates an instance of a TokenVerification
|
||||
func NewTokenVerification(response *http.Response) (*TokenVerification, error) {
|
||||
tokenVerification := &TokenVerification{HTTPResponse: response}
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
|
||||
if err != nil {
|
||||
Logger.Println("Error reading from API:", err)
|
||||
return tokenVerification, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &tokenVerification)
|
||||
if err != nil {
|
||||
Logger.Println("Error parsing JSON:", err)
|
||||
return tokenVerification, err
|
||||
}
|
||||
|
||||
return tokenVerification, nil
|
||||
}
|
||||
|
||||
// Valid returns true if the verification was valid.
|
||||
func (verification *TokenVerification) Valid() bool {
|
||||
if verification.HTTPResponse.StatusCode == 200 && verification.Token == "is valid" {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
84
vendor/github.com/dcu/go-authy/user.go
generated
vendored
Normal file
84
vendor/github.com/dcu/go-authy/user.go
generated
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
package authy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// User is an Authy User
|
||||
type User struct {
|
||||
HTTPResponse *http.Response
|
||||
ID string
|
||||
UserData struct {
|
||||
ID int `json:"id"`
|
||||
} `json:"user"`
|
||||
Errors map[string]string `json:"errors"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// UserStatus is a user with information loaded from Authy API
|
||||
type UserStatus struct {
|
||||
HTTPResponse *http.Response
|
||||
ID string
|
||||
StatusData struct {
|
||||
ID int `json:"authy_id"`
|
||||
Confirmed bool `json:"confirmed"`
|
||||
Registered bool `json:"registered"`
|
||||
Country int `json:"country_code"`
|
||||
PhoneNumber string `json:"phone_number"`
|
||||
Devices []string `json:"devices"`
|
||||
} `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
// NewUser returns an instance of User
|
||||
func NewUser(httpResponse *http.Response) (*User, error) {
|
||||
userResponse := &User{HTTPResponse: httpResponse}
|
||||
|
||||
defer closeResponseBody(httpResponse)
|
||||
body, err := ioutil.ReadAll(httpResponse.Body)
|
||||
|
||||
if err != nil {
|
||||
Logger.Println("Error reading from API:", err)
|
||||
return userResponse, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, userResponse)
|
||||
if err != nil {
|
||||
Logger.Println("Error parsing JSON:", err)
|
||||
return userResponse, err
|
||||
}
|
||||
|
||||
userResponse.ID = strconv.Itoa(userResponse.UserData.ID)
|
||||
return userResponse, nil
|
||||
}
|
||||
|
||||
// NewUserStatus returns an instance of UserStatus
|
||||
func NewUserStatus(httpResponse *http.Response) (*UserStatus, error) {
|
||||
statusResponse := &UserStatus{HTTPResponse: httpResponse}
|
||||
|
||||
defer closeResponseBody(httpResponse)
|
||||
|
||||
body, err := ioutil.ReadAll(httpResponse.Body)
|
||||
if err != nil {
|
||||
Logger.Println("Error reading from API:", err)
|
||||
return statusResponse, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, statusResponse)
|
||||
if err != nil {
|
||||
Logger.Println("Error parsing JSON:", err)
|
||||
return statusResponse, err
|
||||
}
|
||||
|
||||
statusResponse.ID = strconv.Itoa(statusResponse.StatusData.ID)
|
||||
return statusResponse, nil
|
||||
}
|
||||
|
||||
// Valid returns true if the user was created successfully
|
||||
func (response *User) Valid() bool {
|
||||
return response.HTTPResponse.StatusCode == 200
|
||||
}
|
||||
5
vendor/github.com/gojektech/heimdall/AUTHORS.md
generated
vendored
Normal file
5
vendor/github.com/gojektech/heimdall/AUTHORS.md
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Heimdall - Authors
|
||||
|
||||
For people who've contributed to [Heimdall](https://github.com/gojektech/heimdall),
|
||||
_please checkout [Contributors Graphs](https://github.com/gojektech/heimdall/graphs/contributors)
|
||||
on [GO-JEK Tech's GitHub](https://github.com/gojektech)._
|
||||
202
vendor/github.com/gojektech/heimdall/LICENSE
generated
vendored
Normal file
202
vendor/github.com/gojektech/heimdall/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
64
vendor/github.com/gojektech/heimdall/backoff.go
generated
vendored
Normal file
64
vendor/github.com/gojektech/heimdall/backoff.go
generated
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package heimdall
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Backoff interface defines contract for backoff strategies
|
||||
type Backoff interface {
|
||||
Next(retry int) time.Duration
|
||||
}
|
||||
|
||||
type constantBackoff struct {
|
||||
backoffInterval int64
|
||||
maximumJitterInterval int64
|
||||
}
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// NewConstantBackoff returns an instance of ConstantBackoff
|
||||
func NewConstantBackoff(backoffInterval, maximumJitterInterval time.Duration) Backoff {
|
||||
return &constantBackoff{
|
||||
backoffInterval: int64(backoffInterval / time.Millisecond),
|
||||
maximumJitterInterval: int64(maximumJitterInterval / time.Millisecond),
|
||||
}
|
||||
}
|
||||
|
||||
// Next returns next time for retrying operation with constant strategy
|
||||
func (cb *constantBackoff) Next(retry int) time.Duration {
|
||||
if retry <= 0 {
|
||||
return 0 * time.Millisecond
|
||||
}
|
||||
|
||||
return (time.Duration(cb.backoffInterval) * time.Millisecond) + (time.Duration(rand.Int63n(cb.maximumJitterInterval)) * time.Millisecond)
|
||||
}
|
||||
|
||||
type exponentialBackoff struct {
|
||||
exponentFactor float64
|
||||
initialTimeout float64
|
||||
maxTimeout float64
|
||||
maximumJitterInterval int64
|
||||
}
|
||||
|
||||
// NewExponentialBackoff returns an instance of ExponentialBackoff
|
||||
func NewExponentialBackoff(initialTimeout, maxTimeout time.Duration, exponentFactor float64, maximumJitterInterval time.Duration) Backoff {
|
||||
return &exponentialBackoff{
|
||||
exponentFactor: exponentFactor,
|
||||
initialTimeout: float64(initialTimeout / time.Millisecond),
|
||||
maxTimeout: float64(maxTimeout / time.Millisecond),
|
||||
maximumJitterInterval: int64(maximumJitterInterval / time.Millisecond),
|
||||
}
|
||||
}
|
||||
|
||||
// Next returns next time for retrying operation with exponential strategy
|
||||
func (eb *exponentialBackoff) Next(retry int) time.Duration {
|
||||
if retry <= 0 {
|
||||
return 0 * time.Millisecond
|
||||
}
|
||||
|
||||
return time.Duration(math.Min(eb.initialTimeout+math.Pow(eb.exponentFactor, float64(retry)), eb.maxTimeout)+float64(rand.Int63n(eb.maximumJitterInterval))) * time.Millisecond
|
||||
}
|
||||
22
vendor/github.com/gojektech/heimdall/client.go
generated
vendored
Normal file
22
vendor/github.com/gojektech/heimdall/client.go
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package heimdall
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Doer interface has the method required to use a type as custom http client.
|
||||
// The net/*http.Client type satisfies this interface.
|
||||
type Doer interface {
|
||||
Do(*http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// Client Is a generic HTTP client interface
|
||||
type Client interface {
|
||||
Get(url string, headers http.Header) (*http.Response, error)
|
||||
Post(url string, body io.Reader, headers http.Header) (*http.Response, error)
|
||||
Put(url string, body io.Reader, headers http.Header) (*http.Response, error)
|
||||
Patch(url string, body io.Reader, headers http.Header) (*http.Response, error)
|
||||
Delete(url string, headers http.Header) (*http.Response, error)
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
167
vendor/github.com/gojektech/heimdall/httpclient/client.go
generated
vendored
Normal file
167
vendor/github.com/gojektech/heimdall/httpclient/client.go
generated
vendored
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
package httpclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gojektech/heimdall"
|
||||
"github.com/gojektech/valkyrie"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Client is the http client implementation
|
||||
type Client struct {
|
||||
client heimdall.Doer
|
||||
|
||||
timeout time.Duration
|
||||
retryCount int
|
||||
retrier heimdall.Retriable
|
||||
}
|
||||
|
||||
const (
|
||||
defaultRetryCount = 0
|
||||
defaultHTTPTimeout = 30 * time.Second
|
||||
)
|
||||
|
||||
var _ heimdall.Client = (*Client)(nil)
|
||||
|
||||
// NewClient returns a new instance of http Client
|
||||
func NewClient(opts ...Option) *Client {
|
||||
client := Client{
|
||||
timeout: defaultHTTPTimeout,
|
||||
retryCount: defaultRetryCount,
|
||||
retrier: heimdall.NewNoRetrier(),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(&client)
|
||||
}
|
||||
|
||||
if client.client == nil {
|
||||
client.client = &http.Client{
|
||||
Timeout: client.timeout,
|
||||
}
|
||||
}
|
||||
|
||||
return &client
|
||||
}
|
||||
|
||||
// Get makes a HTTP GET request to provided URL
|
||||
func (c *Client) Get(url string, headers http.Header) (*http.Response, error) {
|
||||
var response *http.Response
|
||||
request, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return response, errors.Wrap(err, "GET - request creation failed")
|
||||
}
|
||||
|
||||
request.Header = headers
|
||||
|
||||
return c.Do(request)
|
||||
}
|
||||
|
||||
// Post makes a HTTP POST request to provided URL and requestBody
|
||||
func (c *Client) Post(url string, body io.Reader, headers http.Header) (*http.Response, error) {
|
||||
var response *http.Response
|
||||
request, err := http.NewRequest(http.MethodPost, url, body)
|
||||
if err != nil {
|
||||
return response, errors.Wrap(err, "POST - request creation failed")
|
||||
}
|
||||
|
||||
request.Header = headers
|
||||
|
||||
return c.Do(request)
|
||||
}
|
||||
|
||||
// Put makes a HTTP PUT request to provided URL and requestBody
|
||||
func (c *Client) Put(url string, body io.Reader, headers http.Header) (*http.Response, error) {
|
||||
var response *http.Response
|
||||
request, err := http.NewRequest(http.MethodPut, url, body)
|
||||
if err != nil {
|
||||
return response, errors.Wrap(err, "PUT - request creation failed")
|
||||
}
|
||||
|
||||
request.Header = headers
|
||||
|
||||
return c.Do(request)
|
||||
}
|
||||
|
||||
// Patch makes a HTTP PATCH request to provided URL and requestBody
|
||||
func (c *Client) Patch(url string, body io.Reader, headers http.Header) (*http.Response, error) {
|
||||
var response *http.Response
|
||||
request, err := http.NewRequest(http.MethodPatch, url, body)
|
||||
if err != nil {
|
||||
return response, errors.Wrap(err, "PATCH - request creation failed")
|
||||
}
|
||||
|
||||
request.Header = headers
|
||||
|
||||
return c.Do(request)
|
||||
}
|
||||
|
||||
// Delete makes a HTTP DELETE request with provided URL
|
||||
func (c *Client) Delete(url string, headers http.Header) (*http.Response, error) {
|
||||
var response *http.Response
|
||||
request, err := http.NewRequest(http.MethodDelete, url, nil)
|
||||
if err != nil {
|
||||
return response, errors.Wrap(err, "DELETE - request creation failed")
|
||||
}
|
||||
|
||||
request.Header = headers
|
||||
|
||||
return c.Do(request)
|
||||
}
|
||||
|
||||
// Do makes an HTTP request with the native `http.Do` interface
|
||||
func (c *Client) Do(request *http.Request) (*http.Response, error) {
|
||||
request.Close = true
|
||||
|
||||
var bodyReader *bytes.Reader
|
||||
|
||||
if request.Body != nil {
|
||||
reqData, err := ioutil.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bodyReader = bytes.NewReader(reqData)
|
||||
request.Body = ioutil.NopCloser(bodyReader) // prevents closing the body between retries
|
||||
}
|
||||
|
||||
multiErr := &valkyrie.MultiError{}
|
||||
var response *http.Response
|
||||
|
||||
for i := 0; i <= c.retryCount; i++ {
|
||||
if response != nil {
|
||||
response.Body.Close()
|
||||
}
|
||||
|
||||
var err error
|
||||
response, err = c.client.Do(request)
|
||||
if bodyReader != nil {
|
||||
// Reset the body reader after the request since at this point it's already read
|
||||
// Note that it's safe to ignore the error here since the 0,0 position is always valid
|
||||
_, _ = bodyReader.Seek(0, 0)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
multiErr.Push(err.Error())
|
||||
|
||||
backoffTime := c.retrier.NextInterval(i)
|
||||
time.Sleep(backoffTime)
|
||||
continue
|
||||
}
|
||||
|
||||
if response.StatusCode >= http.StatusInternalServerError {
|
||||
backoffTime := c.retrier.NextInterval(i)
|
||||
time.Sleep(backoffTime)
|
||||
continue
|
||||
}
|
||||
|
||||
multiErr = &valkyrie.MultiError{} // Clear errors if any iteration succeeds
|
||||
break
|
||||
}
|
||||
|
||||
return response, multiErr.HasError()
|
||||
}
|
||||
38
vendor/github.com/gojektech/heimdall/httpclient/options.go
generated
vendored
Normal file
38
vendor/github.com/gojektech/heimdall/httpclient/options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
package httpclient
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gojektech/heimdall"
|
||||
)
|
||||
|
||||
// Option represents the client options
|
||||
type Option func(*Client)
|
||||
|
||||
// WithHTTPTimeout sets hystrix timeout
|
||||
func WithHTTPTimeout(timeout time.Duration) Option {
|
||||
return func(c *Client) {
|
||||
c.timeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
// WithRetryCount sets the retry count for the hystrixHTTPClient
|
||||
func WithRetryCount(retryCount int) Option {
|
||||
return func(c *Client) {
|
||||
c.retryCount = retryCount
|
||||
}
|
||||
}
|
||||
|
||||
// WithRetrier sets the strategy for retrying
|
||||
func WithRetrier(retrier heimdall.Retriable) Option {
|
||||
return func(c *Client) {
|
||||
c.retrier = retrier
|
||||
}
|
||||
}
|
||||
|
||||
// WithHTTPClient sets a custom http client
|
||||
func WithHTTPClient(client heimdall.Doer) Option {
|
||||
return func(c *Client) {
|
||||
c.client = client
|
||||
}
|
||||
}
|
||||
51
vendor/github.com/gojektech/heimdall/retry.go
generated
vendored
Normal file
51
vendor/github.com/gojektech/heimdall/retry.go
generated
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package heimdall
|
||||
|
||||
import "time"
|
||||
|
||||
// Retriable defines contract for retriers to implement
|
||||
type Retriable interface {
|
||||
NextInterval(retry int) time.Duration
|
||||
}
|
||||
|
||||
// RetriableFunc is an adapter to allow the use of ordinary functions
|
||||
// as a Retriable
|
||||
type RetriableFunc func(retry int) time.Duration
|
||||
|
||||
// NextInterval calls f(retry)
|
||||
func (f RetriableFunc) NextInterval(retry int) time.Duration {
|
||||
return f(retry)
|
||||
}
|
||||
|
||||
type retrier struct {
|
||||
backoff Backoff
|
||||
}
|
||||
|
||||
// NewRetrier returns retrier with some backoff strategy
|
||||
func NewRetrier(backoff Backoff) Retriable {
|
||||
return &retrier{
|
||||
backoff: backoff,
|
||||
}
|
||||
}
|
||||
|
||||
// NewRetrierFunc returns a retrier with a retry function defined
|
||||
func NewRetrierFunc(f RetriableFunc) Retriable {
|
||||
return f
|
||||
}
|
||||
|
||||
// NextInterval returns next retriable time
|
||||
func (r *retrier) NextInterval(retry int) time.Duration {
|
||||
return r.backoff.Next(retry)
|
||||
}
|
||||
|
||||
type noRetrier struct {
|
||||
}
|
||||
|
||||
// NewNoRetrier returns a null object for retriable
|
||||
func NewNoRetrier() Retriable {
|
||||
return &noRetrier{}
|
||||
}
|
||||
|
||||
// NextInterval returns next retriable time, always 0
|
||||
func (r *noRetrier) NextInterval(retry int) time.Duration {
|
||||
return 0 * time.Millisecond
|
||||
}
|
||||
5
vendor/github.com/gojektech/valkyrie/AUTHORS.md
generated
vendored
Normal file
5
vendor/github.com/gojektech/valkyrie/AUTHORS.md
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Valkyrie - Authors
|
||||
|
||||
For people who've contributed to [Valkyrie](https://github.com/gojektech/valkyrie),
|
||||
_please checkout [Contributors Graphs](https://github.com/gojektech/valkyrie/graphs/contributors)
|
||||
on [GO-JEK Tech's GitHub](https://github.com/gojektech)._
|
||||
202
vendor/github.com/gojektech/valkyrie/LICENSE
generated
vendored
Normal file
202
vendor/github.com/gojektech/valkyrie/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
44
vendor/github.com/gojektech/valkyrie/multierror.go
generated
vendored
Normal file
44
vendor/github.com/gojektech/valkyrie/multierror.go
generated
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package valkyrie
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// MultiError implements error interface.
|
||||
// An instance of MultiError has zero or more errors.
|
||||
type MultiError struct {
|
||||
mutex sync.Mutex
|
||||
errs []error
|
||||
}
|
||||
|
||||
// Push adds an error to MultiError.
|
||||
func (m *MultiError) Push(errString string) {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
m.errs = append(m.errs, errors.New(errString))
|
||||
}
|
||||
|
||||
// HasError checks if MultiError has any error.
|
||||
func (m *MultiError) HasError() error {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
if len(m.errs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// Error implements error interface.
|
||||
func (m *MultiError) Error() string {
|
||||
formattedError := make([]string, len(m.errs))
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
for i, e := range m.errs {
|
||||
formattedError[i] = e.Error()
|
||||
}
|
||||
|
||||
return strings.Join(formattedError, ", ")
|
||||
}
|
||||
23
vendor/github.com/pkg/errors/LICENSE
generated
vendored
Normal file
23
vendor/github.com/pkg/errors/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
Copyright (c) 2015, Dave Cheney <dave@cheney.net>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
282
vendor/github.com/pkg/errors/errors.go
generated
vendored
Normal file
282
vendor/github.com/pkg/errors/errors.go
generated
vendored
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
// Package errors provides simple error handling primitives.
|
||||
//
|
||||
// The traditional error handling idiom in Go is roughly akin to
|
||||
//
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// which when applied recursively up the call stack results in error reports
|
||||
// without context or debugging information. The errors package allows
|
||||
// programmers to add context to the failure path in their code in a way
|
||||
// that does not destroy the original value of the error.
|
||||
//
|
||||
// Adding context to an error
|
||||
//
|
||||
// The errors.Wrap function returns a new error that adds context to the
|
||||
// original error by recording a stack trace at the point Wrap is called,
|
||||
// together with the supplied message. For example
|
||||
//
|
||||
// _, err := ioutil.ReadAll(r)
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "read failed")
|
||||
// }
|
||||
//
|
||||
// If additional control is required, the errors.WithStack and
|
||||
// errors.WithMessage functions destructure errors.Wrap into its component
|
||||
// operations: annotating an error with a stack trace and with a message,
|
||||
// respectively.
|
||||
//
|
||||
// Retrieving the cause of an error
|
||||
//
|
||||
// Using errors.Wrap constructs a stack of errors, adding context to the
|
||||
// preceding error. Depending on the nature of the error it may be necessary
|
||||
// to reverse the operation of errors.Wrap to retrieve the original error
|
||||
// for inspection. Any error value which implements this interface
|
||||
//
|
||||
// type causer interface {
|
||||
// Cause() error
|
||||
// }
|
||||
//
|
||||
// can be inspected by errors.Cause. errors.Cause will recursively retrieve
|
||||
// the topmost error that does not implement causer, which is assumed to be
|
||||
// the original cause. For example:
|
||||
//
|
||||
// switch err := errors.Cause(err).(type) {
|
||||
// case *MyError:
|
||||
// // handle specifically
|
||||
// default:
|
||||
// // unknown error
|
||||
// }
|
||||
//
|
||||
// Although the causer interface is not exported by this package, it is
|
||||
// considered a part of its stable public interface.
|
||||
//
|
||||
// Formatted printing of errors
|
||||
//
|
||||
// All error values returned from this package implement fmt.Formatter and can
|
||||
// be formatted by the fmt package. The following verbs are supported:
|
||||
//
|
||||
// %s print the error. If the error has a Cause it will be
|
||||
// printed recursively.
|
||||
// %v see %s
|
||||
// %+v extended format. Each Frame of the error's StackTrace will
|
||||
// be printed in detail.
|
||||
//
|
||||
// Retrieving the stack trace of an error or wrapper
|
||||
//
|
||||
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
|
||||
// invoked. This information can be retrieved with the following interface:
|
||||
//
|
||||
// type stackTracer interface {
|
||||
// StackTrace() errors.StackTrace
|
||||
// }
|
||||
//
|
||||
// The returned errors.StackTrace type is defined as
|
||||
//
|
||||
// type StackTrace []Frame
|
||||
//
|
||||
// The Frame type represents a call site in the stack trace. Frame supports
|
||||
// the fmt.Formatter interface that can be used for printing information about
|
||||
// the stack trace of this error. For example:
|
||||
//
|
||||
// if err, ok := err.(stackTracer); ok {
|
||||
// for _, f := range err.StackTrace() {
|
||||
// fmt.Printf("%+s:%d", f)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Although the stackTracer interface is not exported by this package, it is
|
||||
// considered a part of its stable public interface.
|
||||
//
|
||||
// See the documentation for Frame.Format for more details.
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// New returns an error with the supplied message.
|
||||
// New also records the stack trace at the point it was called.
|
||||
func New(message string) error {
|
||||
return &fundamental{
|
||||
msg: message,
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf formats according to a format specifier and returns the string
|
||||
// as a value that satisfies error.
|
||||
// Errorf also records the stack trace at the point it was called.
|
||||
func Errorf(format string, args ...interface{}) error {
|
||||
return &fundamental{
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// fundamental is an error that has a message and a stack, but no caller.
|
||||
type fundamental struct {
|
||||
msg string
|
||||
*stack
|
||||
}
|
||||
|
||||
func (f *fundamental) Error() string { return f.msg }
|
||||
|
||||
func (f *fundamental) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
io.WriteString(s, f.msg)
|
||||
f.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, f.msg)
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", f.msg)
|
||||
}
|
||||
}
|
||||
|
||||
// WithStack annotates err with a stack trace at the point WithStack was called.
|
||||
// If err is nil, WithStack returns nil.
|
||||
func WithStack(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
type withStack struct {
|
||||
error
|
||||
*stack
|
||||
}
|
||||
|
||||
func (w *withStack) Cause() error { return w.error }
|
||||
|
||||
func (w *withStack) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprintf(s, "%+v", w.Cause())
|
||||
w.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, w.Error())
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", w.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap returns an error annotating err with a stack trace
|
||||
// at the point Wrap is called, and the supplied message.
|
||||
// If err is nil, Wrap returns nil.
|
||||
func Wrap(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
err = &withMessage{
|
||||
cause: err,
|
||||
msg: message,
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapf returns an error annotating err with a stack trace
|
||||
// at the point Wrapf is called, and the format specifier.
|
||||
// If err is nil, Wrapf returns nil.
|
||||
func Wrapf(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
err = &withMessage{
|
||||
cause: err,
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessage annotates err with a new message.
|
||||
// If err is nil, WithMessage returns nil.
|
||||
func WithMessage(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withMessage{
|
||||
cause: err,
|
||||
msg: message,
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessagef annotates err with the format specifier.
|
||||
// If err is nil, WithMessagef returns nil.
|
||||
func WithMessagef(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withMessage{
|
||||
cause: err,
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
type withMessage struct {
|
||||
cause error
|
||||
msg string
|
||||
}
|
||||
|
||||
func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
|
||||
func (w *withMessage) Cause() error { return w.cause }
|
||||
|
||||
func (w *withMessage) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprintf(s, "%+v\n", w.Cause())
|
||||
io.WriteString(s, w.msg)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's', 'q':
|
||||
io.WriteString(s, w.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Cause returns the underlying cause of the error, if possible.
|
||||
// An error value has a cause if it implements the following
|
||||
// interface:
|
||||
//
|
||||
// type causer interface {
|
||||
// Cause() error
|
||||
// }
|
||||
//
|
||||
// If the error does not implement Cause, the original error will
|
||||
// be returned. If the error is nil, nil will be returned without further
|
||||
// investigation.
|
||||
func Cause(err error) error {
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
|
||||
for err != nil {
|
||||
cause, ok := err.(causer)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
err = cause.Cause()
|
||||
}
|
||||
return err
|
||||
}
|
||||
147
vendor/github.com/pkg/errors/stack.go
generated
vendored
Normal file
147
vendor/github.com/pkg/errors/stack.go
generated
vendored
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Frame represents a program counter inside a stack frame.
|
||||
type Frame uintptr
|
||||
|
||||
// pc returns the program counter for this frame;
|
||||
// multiple frames may have the same PC value.
|
||||
func (f Frame) pc() uintptr { return uintptr(f) - 1 }
|
||||
|
||||
// file returns the full path to the file that contains the
|
||||
// function for this Frame's pc.
|
||||
func (f Frame) file() string {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return "unknown"
|
||||
}
|
||||
file, _ := fn.FileLine(f.pc())
|
||||
return file
|
||||
}
|
||||
|
||||
// line returns the line number of source code of the
|
||||
// function for this Frame's pc.
|
||||
func (f Frame) line() int {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return 0
|
||||
}
|
||||
_, line := fn.FileLine(f.pc())
|
||||
return line
|
||||
}
|
||||
|
||||
// Format formats the frame according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s source file
|
||||
// %d source line
|
||||
// %n function name
|
||||
// %v equivalent to %s:%d
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+s function name and path of source file relative to the compile time
|
||||
// GOPATH separated by \n\t (<funcname>\n\t<path>)
|
||||
// %+v equivalent to %+s:%d
|
||||
func (f Frame) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 's':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
pc := f.pc()
|
||||
fn := runtime.FuncForPC(pc)
|
||||
if fn == nil {
|
||||
io.WriteString(s, "unknown")
|
||||
} else {
|
||||
file, _ := fn.FileLine(pc)
|
||||
fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
|
||||
}
|
||||
default:
|
||||
io.WriteString(s, path.Base(f.file()))
|
||||
}
|
||||
case 'd':
|
||||
fmt.Fprintf(s, "%d", f.line())
|
||||
case 'n':
|
||||
name := runtime.FuncForPC(f.pc()).Name()
|
||||
io.WriteString(s, funcname(name))
|
||||
case 'v':
|
||||
f.Format(s, 's')
|
||||
io.WriteString(s, ":")
|
||||
f.Format(s, 'd')
|
||||
}
|
||||
}
|
||||
|
||||
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
|
||||
type StackTrace []Frame
|
||||
|
||||
// Format formats the stack of Frames according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s lists source files for each Frame in the stack
|
||||
// %v lists the source file and line number for each Frame in the stack
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+v Prints filename, function, and line number for each Frame in the stack.
|
||||
func (st StackTrace) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
for _, f := range st {
|
||||
fmt.Fprintf(s, "\n%+v", f)
|
||||
}
|
||||
case s.Flag('#'):
|
||||
fmt.Fprintf(s, "%#v", []Frame(st))
|
||||
default:
|
||||
fmt.Fprintf(s, "%v", []Frame(st))
|
||||
}
|
||||
case 's':
|
||||
fmt.Fprintf(s, "%s", []Frame(st))
|
||||
}
|
||||
}
|
||||
|
||||
// stack represents a stack of program counters.
|
||||
type stack []uintptr
|
||||
|
||||
func (s *stack) Format(st fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case st.Flag('+'):
|
||||
for _, pc := range *s {
|
||||
f := Frame(pc)
|
||||
fmt.Fprintf(st, "\n%+v", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stack) StackTrace() StackTrace {
|
||||
f := make([]Frame, len(*s))
|
||||
for i := 0; i < len(f); i++ {
|
||||
f[i] = Frame((*s)[i])
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func callers() *stack {
|
||||
const depth = 32
|
||||
var pcs [depth]uintptr
|
||||
n := runtime.Callers(3, pcs[:])
|
||||
var st stack = pcs[0:n]
|
||||
return &st
|
||||
}
|
||||
|
||||
// funcname removes the path prefix component of a function's name reported by func.Name().
|
||||
func funcname(name string) string {
|
||||
i := strings.LastIndex(name, "/")
|
||||
name = name[i+1:]
|
||||
i = strings.Index(name, ".")
|
||||
return name[i+1:]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue