mirror of
https://github.com/netbirdio/dex.git
synced 2026-05-22 18:43:53 -07:00
vendor: revendor using glide-vc
This commit is contained in:
Generated
+2
-2
@@ -1,5 +1,5 @@
|
||||
hash: 773c45cb2136423f907496cc1ba67e0c58b35e237b15b0d5f212dce598265442
|
||||
updated: 2016-12-01T13:12:54.401738528-08:00
|
||||
hash: 2d3cc27484f672337349fffc8d7839462df7b1fbedc9200ae0a87a84afeed896
|
||||
updated: 2016-12-07T13:06:21.940111139-08:00
|
||||
imports:
|
||||
- name: github.com/cockroachdb/cockroach-go
|
||||
version: 31611c0501c812f437d4861d87d117053967c955
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
*~
|
||||
.#*
|
||||
*.test
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
# Copyright 2016 The Cockroach Authors.
|
||||
#
|
||||
# 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. See the AUTHORS file
|
||||
# for names of contributors.
|
||||
#
|
||||
# Author: Spencer Kimball (spencer.kimball@gmail.com)
|
||||
#
|
||||
|
||||
# Cockroach build rules.
|
||||
GO ?= go
|
||||
# Allow setting of go build flags from the command line.
|
||||
GOFLAGS :=
|
||||
|
||||
.PHONY: all
|
||||
all: test check
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
$(GO) test -v -i ./...
|
||||
$(GO) test -v ./...
|
||||
|
||||
.PHONY: deps
|
||||
deps:
|
||||
$(GO) get -d -t ./...
|
||||
|
||||
.PHONY: check
|
||||
check:
|
||||
@echo "checking for \"path\" imports"
|
||||
@! git grep -F '"path"' -- '*.go'
|
||||
@echo "errcheck"
|
||||
@errcheck ./...
|
||||
@echo "vet"
|
||||
@! go tool vet . 2>&1 | \
|
||||
grep -vE '^vet: cannot process directory .git'
|
||||
@echo "vet --shadow"
|
||||
@! go tool vet --shadow . 2>&1 | \
|
||||
grep -vE '(declaration of err shadows|^vet: cannot process directory \.git)'
|
||||
@echo "golint"
|
||||
@! golint ./... | grep -vE '(\.pb\.go)'
|
||||
@echo "varcheck"
|
||||
@varcheck -e ./...
|
||||
@echo "gofmt (simplify)"
|
||||
@! gofmt -s -d -l . 2>&1 | grep -vE '^\.git/'
|
||||
@echo "goimports"
|
||||
@! goimports -l . | grep -vF 'No Exceptions'
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
# testing
|
||||
Testing helpers for cockroach clients.
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
machine:
|
||||
environment:
|
||||
GOROOT: ${HOME}/go
|
||||
PATH: ${PATH}:${HOME}/go/bin
|
||||
post:
|
||||
- sudo rm -rf /usr/local/go
|
||||
- if [ ! -e go1.6.linux-amd64.tar.gz ]; then curl -O https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz; fi
|
||||
- tar -C ${HOME} -xzf go1.6.linux-amd64.tar.gz
|
||||
|
||||
dependencies:
|
||||
override:
|
||||
- make deps
|
||||
cache_directories:
|
||||
- ~/go1.6.linux-amd64.tar.gz
|
||||
|
||||
test:
|
||||
override:
|
||||
- make test
|
||||
-119
@@ -1,119 +0,0 @@
|
||||
package testserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
awsBaseURL = "https://s3.amazonaws.com/cockroach/cockroach"
|
||||
latestSuffix = "LATEST"
|
||||
localBinaryPath = "/var/tmp"
|
||||
finishedFileMode = 0555
|
||||
)
|
||||
|
||||
func binaryName() string {
|
||||
return fmt.Sprintf("cockroach.%s-%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
func binaryNameWithSha(sha string) string {
|
||||
return fmt.Sprintf("%s.%s", binaryName(), sha)
|
||||
}
|
||||
|
||||
func binaryPath(sha string) string {
|
||||
return filepath.Join(localBinaryPath, binaryNameWithSha(sha))
|
||||
}
|
||||
|
||||
func latestMarkerURL() string {
|
||||
return fmt.Sprintf("%s/%s.%s", awsBaseURL, binaryName(), latestSuffix)
|
||||
}
|
||||
|
||||
func binaryURL(sha string) string {
|
||||
return fmt.Sprintf("%s/%s.%s", awsBaseURL, binaryName(), sha)
|
||||
}
|
||||
|
||||
func findLatestSha() (string, error) {
|
||||
markerURL := latestMarkerURL()
|
||||
marker, err := http.Get(markerURL)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not download %s: %s", markerURL)
|
||||
}
|
||||
if marker.StatusCode == 404 {
|
||||
return "", fmt.Errorf("for 404 from GET %s: make sure OS and ARCH are supported",
|
||||
markerURL)
|
||||
} else if marker.StatusCode != 200 {
|
||||
return "", fmt.Errorf("bad response got GET %s: %d (%s)",
|
||||
markerURL, marker.StatusCode, marker.Status)
|
||||
}
|
||||
|
||||
defer marker.Body.Close()
|
||||
body, err := ioutil.ReadAll(marker.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(string(body)), nil
|
||||
}
|
||||
|
||||
func downloadFile(url, filePath string) error {
|
||||
output, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0200)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating %s: %s", filePath, "-", err)
|
||||
}
|
||||
defer output.Close()
|
||||
|
||||
log.Printf("downloading %s to %s, this may take some time", url, filePath)
|
||||
|
||||
response, err := http.Get(url)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error downloading %s: %s", url, err)
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != 200 {
|
||||
return fmt.Errorf("error downloading %s: %d (%s)", url, response.StatusCode, response.Status)
|
||||
}
|
||||
|
||||
_, err = io.Copy(output, response.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("problem downloading %s to %s: %s", url, filePath, err)
|
||||
}
|
||||
|
||||
// Download was successful, add the rw bits.
|
||||
return os.Chmod(filePath, finishedFileMode)
|
||||
}
|
||||
|
||||
func downloadLatestBinary() (string, error) {
|
||||
sha, err := findLatestSha()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
localFile := binaryPath(sha)
|
||||
for {
|
||||
finfo, err := os.Stat(localFile)
|
||||
if err != nil {
|
||||
// File does not exist: download it.
|
||||
break
|
||||
}
|
||||
// File already present: check mode.
|
||||
if finfo.Mode().Perm() == finishedFileMode {
|
||||
return localFile, nil
|
||||
}
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
}
|
||||
|
||||
err = downloadFile(binaryURL(sha), localFile)
|
||||
if err != nil {
|
||||
_ = os.Remove(localFile)
|
||||
return "", err
|
||||
}
|
||||
|
||||
return localFile, nil
|
||||
}
|
||||
-415
@@ -1,415 +0,0 @@
|
||||
// Copyright 2016 The Cockroach Authors.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Author: Marc Berhault (marc@cockroachlabs.com)
|
||||
|
||||
// Package testserver provides helpers to run a cockroach binary within tests.
|
||||
// It automatically downloads the latest cockroach binary for your platform
|
||||
// (Linux-amd64 and Darwin-amd64 only for now), or attempts to run "cockroach"
|
||||
// from your PATH.
|
||||
//
|
||||
// A normal invocation is (check err every time):
|
||||
// ts, err := testserver.NewTestServer()
|
||||
// err = ts.Start()
|
||||
// defer ts.Stop()
|
||||
// url := ts.PGURL()
|
||||
//
|
||||
// To use, run as follows:
|
||||
// import "github.com/cockroachdb/cockroach-go/testserver"
|
||||
// import "testing"
|
||||
// import "time"
|
||||
//
|
||||
// func TestRunServer(t *testing.T) {
|
||||
// ts, err := testserver.NewTestServer()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// err := ts.Start()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// defer ts.Stop()
|
||||
//
|
||||
// url := ts.PGURL()
|
||||
// if url != nil {
|
||||
// t.FatalF("url not found")
|
||||
// }
|
||||
// t.Logf("URL: %s", url.String())
|
||||
//
|
||||
// db, err := sql.Open("postgres", url.String())
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// }
|
||||
package testserver
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var sqlURLRegexp = regexp.MustCompile("sql:\\s+(postgresql:.+)\n")
|
||||
|
||||
const (
|
||||
stateNew = iota
|
||||
stateRunning = iota
|
||||
stateStopped = iota
|
||||
stateFailed = iota
|
||||
|
||||
socketPort = 26257
|
||||
socketFileBase = ".s.PGSQL"
|
||||
)
|
||||
|
||||
// TestServer is a helper to run a real cockroach node.
|
||||
type TestServer struct {
|
||||
mu sync.RWMutex
|
||||
state int
|
||||
baseDir string
|
||||
pgURL *url.URL
|
||||
cmd *exec.Cmd
|
||||
args []string
|
||||
stdout string
|
||||
stderr string
|
||||
stdoutBuf logWriter
|
||||
stderrBuf logWriter
|
||||
}
|
||||
|
||||
// NewDBForTest creates a new CockroachDB TestServer instance and
|
||||
// opens a SQL database connection to it. Returns a sql *DB instance a
|
||||
// shutdown function. The caller is responsible for executing the
|
||||
// returned shutdown function on exit.
|
||||
func NewDBForTest(t *testing.T) (*sql.DB, func()) {
|
||||
return NewDBForTestWithDatabase(t, "")
|
||||
}
|
||||
|
||||
// NewDBForTestWithDatabase creates a new CockroachDB TestServer
|
||||
// instance and opens a SQL database connection to it. If database is
|
||||
// specified, the returned connection will explicitly connect to
|
||||
// it. Returns a sql *DB instance a shutdown function. The caller is
|
||||
// responsible for executing the returned shutdown function on exit.
|
||||
func NewDBForTestWithDatabase(t *testing.T, database string) (*sql.DB, func()) {
|
||||
ts, err := NewTestServer()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ts.Start()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
url := ts.PGURL()
|
||||
if url == nil {
|
||||
t.Fatalf("url not found")
|
||||
}
|
||||
if len(database) > 0 {
|
||||
url.Path = database
|
||||
}
|
||||
|
||||
db, err := sql.Open("postgres", url.String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ts.WaitForInit(db)
|
||||
|
||||
return db, func() {
|
||||
_ = db.Close()
|
||||
ts.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
// NewTestServer creates a new TestServer, but does not start it.
|
||||
// The cockroach binary for your OS and ARCH is downloaded automatically.
|
||||
// If the download fails, we attempt just call "cockroach", hoping it is
|
||||
// found in your path.
|
||||
func NewTestServer() (*TestServer, error) {
|
||||
cockroachBinary, err := downloadLatestBinary()
|
||||
if err == nil {
|
||||
log.Printf("Using automatically-downloaded binary: %s", cockroachBinary)
|
||||
} else {
|
||||
log.Printf("Attempting to use cockroach binary from your PATH")
|
||||
cockroachBinary = "cockroach"
|
||||
}
|
||||
|
||||
// Force "/tmp/" so avoid OSX's really long temp directory names
|
||||
// which get us over the socket filename length limit.
|
||||
baseDir, err := ioutil.TempDir("/tmp", "cockroach-testserver")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create temp directory: %s", err)
|
||||
}
|
||||
|
||||
logDir := filepath.Join(baseDir, "logs")
|
||||
if err := os.MkdirAll(logDir, 0755); err != nil {
|
||||
return nil, fmt.Errorf("could not create logs directory: %s: %s", logDir, err)
|
||||
}
|
||||
|
||||
options := url.Values{
|
||||
"host": []string{baseDir},
|
||||
}
|
||||
pgurl := &url.URL{
|
||||
Scheme: "postgres",
|
||||
User: url.User("root"),
|
||||
Host: fmt.Sprintf(":%d", socketPort),
|
||||
RawQuery: options.Encode(),
|
||||
}
|
||||
socketPath := filepath.Join(baseDir, fmt.Sprintf("%s.%d", socketFileBase, socketPort))
|
||||
|
||||
args := []string{
|
||||
cockroachBinary,
|
||||
"start",
|
||||
"--logtostderr",
|
||||
"--insecure",
|
||||
"--port=0",
|
||||
"--http-port=0",
|
||||
"--socket=" + socketPath,
|
||||
"--store=" + baseDir,
|
||||
}
|
||||
|
||||
ts := &TestServer{
|
||||
baseDir: baseDir,
|
||||
pgURL: pgurl,
|
||||
args: args,
|
||||
stdout: filepath.Join(logDir, "cockroach.stdout"),
|
||||
stderr: filepath.Join(logDir, "cockroach.stderr"),
|
||||
}
|
||||
return ts, nil
|
||||
}
|
||||
|
||||
// Stdout returns the entire contents of the process' stdout.
|
||||
func (ts *TestServer) Stdout() string {
|
||||
return ts.stdoutBuf.String()
|
||||
}
|
||||
|
||||
// Stderr returns the entire contents of the process' stderr.
|
||||
func (ts *TestServer) Stderr() string {
|
||||
return ts.stderrBuf.String()
|
||||
}
|
||||
|
||||
// PGURL returns the postgres connection URL to reach the started
|
||||
// cockroach node.
|
||||
// It loops until the expected unix socket file exists.
|
||||
// This does not timeout, relying instead on test timeouts.
|
||||
func (ts *TestServer) PGURL() *url.URL {
|
||||
socketPath := filepath.Join(ts.baseDir, fmt.Sprintf("%s.%d", socketFileBase, socketPort))
|
||||
for {
|
||||
if _, err := os.Stat(socketPath); err == nil {
|
||||
return ts.pgURL
|
||||
}
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WaitForInit repeatedly looks up the list of databases until
|
||||
// the "system" database exists. It ignores all errors as we are
|
||||
// waiting for the process to start and complete initialization.
|
||||
// This does not timeout, relying instead on test timeouts.
|
||||
func (ts *TestServer) WaitForInit(db *sql.DB) {
|
||||
for {
|
||||
// We issue a query that fails both on connection errors and on the
|
||||
// system database not existing.
|
||||
if _, err := db.Query("SHOW DATABASES"); err == nil {
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
}
|
||||
}
|
||||
|
||||
// Start runs the process, returning an error on any problems,
|
||||
// including being unable to start, but not unexpected failure.
|
||||
// It should only be called once in the lifetime of a TestServer object.
|
||||
func (ts *TestServer) Start() error {
|
||||
ts.mu.Lock()
|
||||
if ts.state != stateNew {
|
||||
ts.mu.Unlock()
|
||||
return errors.New("Start() can only be called once")
|
||||
}
|
||||
ts.state = stateRunning
|
||||
ts.mu.Unlock()
|
||||
|
||||
ts.cmd = exec.Command(ts.args[0], ts.args[1:]...)
|
||||
ts.cmd.Env = []string{"COCKROACH_MAX_OFFSET=1ns"}
|
||||
|
||||
if len(ts.stdout) > 0 {
|
||||
wr, err := newFileLogWriter(ts.stdout)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to open file %s: %s", ts.stdout, err)
|
||||
}
|
||||
ts.stdoutBuf = wr
|
||||
}
|
||||
ts.cmd.Stdout = ts.stdoutBuf
|
||||
|
||||
if len(ts.stderr) > 0 {
|
||||
wr, err := newFileLogWriter(ts.stderr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to open file %s: %s", ts.stderr, err)
|
||||
}
|
||||
ts.stderrBuf = wr
|
||||
}
|
||||
ts.cmd.Stderr = ts.stderrBuf
|
||||
|
||||
for k, v := range defaultEnv() {
|
||||
ts.cmd.Env = append(ts.cmd.Env, k+"="+v)
|
||||
}
|
||||
|
||||
err := ts.cmd.Start()
|
||||
if ts.cmd.Process != nil {
|
||||
log.Printf("process %d started: %s", ts.cmd.Process.Pid, strings.Join(ts.args, " "))
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf(err.Error())
|
||||
ts.stdoutBuf.Close()
|
||||
ts.stderrBuf.Close()
|
||||
|
||||
ts.mu.Lock()
|
||||
ts.state = stateFailed
|
||||
ts.mu.Unlock()
|
||||
|
||||
return fmt.Errorf("failure starting process: %s", err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
ts.cmd.Wait()
|
||||
|
||||
ts.stdoutBuf.Close()
|
||||
ts.stderrBuf.Close()
|
||||
|
||||
ps := ts.cmd.ProcessState
|
||||
sy := ps.Sys().(syscall.WaitStatus)
|
||||
|
||||
log.Printf("Process %d exited with status %d", ps.Pid(), sy.ExitStatus())
|
||||
log.Printf(ps.String())
|
||||
|
||||
ts.mu.Lock()
|
||||
if sy.ExitStatus() == 0 {
|
||||
ts.state = stateStopped
|
||||
} else {
|
||||
ts.state = stateFailed
|
||||
}
|
||||
ts.mu.Unlock()
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop kills the process if it is still running and cleans its directory.
|
||||
// It should only be called once in the lifetime of a TestServer object.
|
||||
// Logs fatal if the process has already failed.
|
||||
func (ts *TestServer) Stop() {
|
||||
ts.mu.RLock()
|
||||
defer ts.mu.RUnlock()
|
||||
|
||||
if ts.state == stateNew {
|
||||
log.Fatal("Stop() called, but Start() was never called")
|
||||
}
|
||||
if ts.state == stateFailed {
|
||||
log.Fatalf("Stop() called, but process exited unexpectedly. Stdout:\n%s\nStderr:\n%s\n",
|
||||
ts.Stdout(), ts.Stderr())
|
||||
return
|
||||
}
|
||||
|
||||
if ts.state != stateStopped {
|
||||
// Only call kill if not running. It could have exited properly.
|
||||
ts.cmd.Process.Kill()
|
||||
}
|
||||
|
||||
// Only cleanup on intentional stops.
|
||||
_ = os.RemoveAll(ts.baseDir)
|
||||
}
|
||||
|
||||
type logWriter interface {
|
||||
Write(p []byte) (n int, err error)
|
||||
String() string
|
||||
Len() int64
|
||||
Close()
|
||||
}
|
||||
|
||||
type fileLogWriter struct {
|
||||
filename string
|
||||
file *os.File
|
||||
}
|
||||
|
||||
func newFileLogWriter(file string) (*fileLogWriter, error) {
|
||||
f, err := os.Create(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &fileLogWriter{
|
||||
filename: file,
|
||||
file: f,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (w fileLogWriter) Close() {
|
||||
w.file.Close()
|
||||
}
|
||||
|
||||
func (w fileLogWriter) Write(p []byte) (n int, err error) {
|
||||
return w.file.Write(p)
|
||||
}
|
||||
|
||||
func (w fileLogWriter) String() string {
|
||||
b, err := ioutil.ReadFile(w.filename)
|
||||
if err == nil {
|
||||
return string(b)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (w fileLogWriter) Len() int64 {
|
||||
s, err := os.Stat(w.filename)
|
||||
if err == nil {
|
||||
return s.Size()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func defaultEnv() map[string]string {
|
||||
vars := map[string]string{}
|
||||
u, err := user.Current()
|
||||
if err == nil {
|
||||
if _, ok := vars["USER"]; !ok {
|
||||
vars["USER"] = u.Username
|
||||
}
|
||||
if _, ok := vars["UID"]; !ok {
|
||||
vars["UID"] = u.Uid
|
||||
}
|
||||
if _, ok := vars["GID"]; !ok {
|
||||
vars["GID"] = u.Gid
|
||||
}
|
||||
if _, ok := vars["HOME"]; !ok {
|
||||
vars["HOME"] = u.HomeDir
|
||||
}
|
||||
}
|
||||
if _, ok := vars["PATH"]; !ok {
|
||||
vars["PATH"] = os.Getenv("PATH")
|
||||
}
|
||||
return vars
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
// Copyright 2016 The Cockroach Authors.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Author: Marc Berhault (marc@cockroachlabs.com)
|
||||
|
||||
package testserver_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
// Needed for postgres driver test.
|
||||
"github.com/cockroachdb/cockroach-go/testserver"
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
func TestRunServer(t *testing.T) {
|
||||
db, stop := testserver.NewDBForTest(t)
|
||||
defer stop()
|
||||
|
||||
_, err := db.Exec("SELECT 1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
# Examples
|
||||
|
||||
These are example uses of the oidc package. Each requires a Google account and the client ID and secret of a registered OAuth2 application. To create one:
|
||||
|
||||
1. Visit your [Google Developer Console][google-developer-console].
|
||||
2. Click "Credentials" on the left column.
|
||||
3. Click the "Create credentials" button followed by "OAuth client ID".
|
||||
4. Select "Web application" and add "http://127.0.0.1:5556/auth/google/callback" as an authorized redirect URI.
|
||||
5. Click create and add the printed client ID and secret to your environment using the following variables:
|
||||
|
||||
```
|
||||
GOOGLE_OAUTH2_CLIENT_ID
|
||||
GOOGLE_OAUTH2_CLIENT_SECRET
|
||||
```
|
||||
|
||||
Finally run the examples using the Go tool and navigate to http://127.0.0.1:5556.
|
||||
|
||||
```
|
||||
go run ./examples/idtoken/app.go
|
||||
```
|
||||
[google-developer-console]: https://console.developers.google.com/apis/dashboard
|
||||
-89
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
This is an example application to demonstrate parsing an ID Token.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
oidc "github.com/coreos/go-oidc"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
var (
|
||||
clientID = os.Getenv("GOOGLE_OAUTH2_CLIENT_ID")
|
||||
clientSecret = os.Getenv("GOOGLE_OAUTH2_CLIENT_SECRET")
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
provider, err := oidc.NewProvider(ctx, "https://accounts.google.com")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
verifier := provider.Verifier()
|
||||
|
||||
config := oauth2.Config{
|
||||
ClientID: clientID,
|
||||
ClientSecret: clientSecret,
|
||||
Endpoint: provider.Endpoint(),
|
||||
RedirectURL: "http://127.0.0.1:5556/auth/google/callback",
|
||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
||||
}
|
||||
|
||||
state := "foobar" // Don't do this in production.
|
||||
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, config.AuthCodeURL(state), http.StatusFound)
|
||||
})
|
||||
|
||||
http.HandleFunc("/auth/google/callback", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("state") != state {
|
||||
http.Error(w, "state did not match", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
oauth2Token, err := config.Exchange(ctx, r.URL.Query().Get("code"))
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
|
||||
if !ok {
|
||||
http.Error(w, "No id_token field in oauth2 token.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
idToken, err := verifier.Verify(ctx, rawIDToken)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
oauth2Token.AccessToken = "*REDACTED*"
|
||||
|
||||
resp := struct {
|
||||
OAuth2Token *oauth2.Token
|
||||
IDTokenClaims *json.RawMessage // ID Token payload is just JSON.
|
||||
}{oauth2Token, new(json.RawMessage)}
|
||||
|
||||
if err := idToken.Claims(&resp.IDTokenClaims); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
data, err := json.MarshalIndent(resp, "", " ")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Write(data)
|
||||
})
|
||||
|
||||
log.Printf("listening on http://%s/", "127.0.0.1:5556")
|
||||
log.Fatal(http.ListenAndServe("127.0.0.1:5556", nil))
|
||||
}
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
This is an example application to demonstrate verifying an ID Token with a nonce.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
oidc "github.com/coreos/go-oidc"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
var (
|
||||
clientID = os.Getenv("GOOGLE_OAUTH2_CLIENT_ID")
|
||||
clientSecret = os.Getenv("GOOGLE_OAUTH2_CLIENT_SECRET")
|
||||
)
|
||||
|
||||
const appNonce = "a super secret nonce"
|
||||
|
||||
// Create a nonce source.
|
||||
type nonceSource struct{}
|
||||
|
||||
func (n nonceSource) ClaimNonce(nonce string) error {
|
||||
if nonce != appNonce {
|
||||
return errors.New("unregonized nonce")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
provider, err := oidc.NewProvider(ctx, "https://accounts.google.com")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Use the nonce source to create a custom ID Token verifier.
|
||||
nonceEnabledVerifier := provider.Verifier(oidc.VerifyNonce(nonceSource{}))
|
||||
|
||||
config := oauth2.Config{
|
||||
ClientID: clientID,
|
||||
ClientSecret: clientSecret,
|
||||
Endpoint: provider.Endpoint(),
|
||||
RedirectURL: "http://127.0.0.1:5556/auth/google/callback",
|
||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
||||
}
|
||||
|
||||
state := "foobar" // Don't do this in production.
|
||||
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, config.AuthCodeURL(state, oidc.Nonce(appNonce)), http.StatusFound)
|
||||
})
|
||||
|
||||
http.HandleFunc("/auth/google/callback", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("state") != state {
|
||||
http.Error(w, "state did not match", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
oauth2Token, err := config.Exchange(ctx, r.URL.Query().Get("code"))
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
|
||||
if !ok {
|
||||
http.Error(w, "No id_token field in oauth2 token.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// Verify the ID Token signature and nonce.
|
||||
idToken, err := nonceEnabledVerifier.Verify(ctx, rawIDToken)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
resp := struct {
|
||||
OAuth2Token *oauth2.Token
|
||||
IDTokenClaims *json.RawMessage // ID Token payload is just JSON.
|
||||
}{oauth2Token, new(json.RawMessage)}
|
||||
|
||||
if err := idToken.Claims(&resp.IDTokenClaims); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
data, err := json.MarshalIndent(resp, "", " ")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Write(data)
|
||||
})
|
||||
|
||||
log.Printf("listening on http://%s/", "127.0.0.1:5556")
|
||||
log.Fatal(http.ListenAndServe("127.0.0.1:5556", nil))
|
||||
}
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
This is an example application to demonstrate querying the user info endpoint.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
oidc "github.com/coreos/go-oidc"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
var (
|
||||
clientID = os.Getenv("GOOGLE_OAUTH2_CLIENT_ID")
|
||||
clientSecret = os.Getenv("GOOGLE_OAUTH2_CLIENT_SECRET")
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
provider, err := oidc.NewProvider(ctx, "https://accounts.google.com")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
config := oauth2.Config{
|
||||
ClientID: clientID,
|
||||
ClientSecret: clientSecret,
|
||||
Endpoint: provider.Endpoint(),
|
||||
RedirectURL: "http://127.0.0.1:5556/auth/google/callback",
|
||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
||||
}
|
||||
|
||||
state := "foobar" // Don't do this in production.
|
||||
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, config.AuthCodeURL(state), http.StatusFound)
|
||||
})
|
||||
|
||||
http.HandleFunc("/auth/google/callback", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("state") != state {
|
||||
http.Error(w, "state did not match", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
oauth2Token, err := config.Exchange(ctx, r.URL.Query().Get("code"))
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
userInfo, err := provider.UserInfo(ctx, oauth2.StaticTokenSource(oauth2Token))
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to get userinfo: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
resp := struct {
|
||||
OAuth2Token *oauth2.Token
|
||||
UserInfo *oidc.UserInfo
|
||||
}{oauth2Token, userInfo}
|
||||
data, err := json.MarshalIndent(resp, "", " ")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Write(data)
|
||||
})
|
||||
|
||||
log.Printf("listening on http://%s/", "127.0.0.1:5556")
|
||||
log.Fatal(http.ListenAndServe("127.0.0.1:5556", nil))
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
package http
|
||||
|
||||
import "net/http"
|
||||
|
||||
type Client interface {
|
||||
Do(*http.Request) (*http.Response, error)
|
||||
}
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
// Package http is DEPRECATED. Use net/http instead.
|
||||
package http
|
||||
-156
@@ -1,156 +0,0 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func WriteError(w http.ResponseWriter, code int, msg string) {
|
||||
e := struct {
|
||||
Error string `json:"error"`
|
||||
}{
|
||||
Error: msg,
|
||||
}
|
||||
b, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
log.Printf("go-oidc: failed to marshal %#v: %v", e, err)
|
||||
code = http.StatusInternalServerError
|
||||
b = []byte(`{"error":"server_error"}`)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(code)
|
||||
w.Write(b)
|
||||
}
|
||||
|
||||
// BasicAuth parses a username and password from the request's
|
||||
// Authorization header. This was pulled from golang master:
|
||||
// https://codereview.appspot.com/76540043
|
||||
func BasicAuth(r *http.Request) (username, password string, ok bool) {
|
||||
auth := r.Header.Get("Authorization")
|
||||
if auth == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(auth, "Basic ") {
|
||||
return
|
||||
}
|
||||
c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(auth, "Basic "))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cs := string(c)
|
||||
s := strings.IndexByte(cs, ':')
|
||||
if s < 0 {
|
||||
return
|
||||
}
|
||||
return cs[:s], cs[s+1:], true
|
||||
}
|
||||
|
||||
func cacheControlMaxAge(hdr string) (time.Duration, bool, error) {
|
||||
for _, field := range strings.Split(hdr, ",") {
|
||||
parts := strings.SplitN(strings.TrimSpace(field), "=", 2)
|
||||
k := strings.ToLower(strings.TrimSpace(parts[0]))
|
||||
if k != "max-age" {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(parts) == 1 {
|
||||
return 0, false, errors.New("max-age has no value")
|
||||
}
|
||||
|
||||
v := strings.TrimSpace(parts[1])
|
||||
if v == "" {
|
||||
return 0, false, errors.New("max-age has empty value")
|
||||
}
|
||||
|
||||
age, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
return 0, false, err
|
||||
}
|
||||
|
||||
if age <= 0 {
|
||||
return 0, false, nil
|
||||
}
|
||||
|
||||
return time.Duration(age) * time.Second, true, nil
|
||||
}
|
||||
|
||||
return 0, false, nil
|
||||
}
|
||||
|
||||
func expires(date, expires string) (time.Duration, bool, error) {
|
||||
if date == "" || expires == "" {
|
||||
return 0, false, nil
|
||||
}
|
||||
|
||||
te, err := time.Parse(time.RFC1123, expires)
|
||||
if err != nil {
|
||||
return 0, false, err
|
||||
}
|
||||
|
||||
td, err := time.Parse(time.RFC1123, date)
|
||||
if err != nil {
|
||||
return 0, false, err
|
||||
}
|
||||
|
||||
ttl := te.Sub(td)
|
||||
|
||||
// headers indicate data already expired, caller should not
|
||||
// have to care about this case
|
||||
if ttl <= 0 {
|
||||
return 0, false, nil
|
||||
}
|
||||
|
||||
return ttl, true, nil
|
||||
}
|
||||
|
||||
func Cacheable(hdr http.Header) (time.Duration, bool, error) {
|
||||
ttl, ok, err := cacheControlMaxAge(hdr.Get("Cache-Control"))
|
||||
if err != nil || ok {
|
||||
return ttl, ok, err
|
||||
}
|
||||
|
||||
return expires(hdr.Get("Date"), hdr.Get("Expires"))
|
||||
}
|
||||
|
||||
// MergeQuery appends additional query values to an existing URL.
|
||||
func MergeQuery(u url.URL, q url.Values) url.URL {
|
||||
uv := u.Query()
|
||||
for k, vs := range q {
|
||||
for _, v := range vs {
|
||||
uv.Add(k, v)
|
||||
}
|
||||
}
|
||||
u.RawQuery = uv.Encode()
|
||||
return u
|
||||
}
|
||||
|
||||
// NewResourceLocation appends a resource id to the end of the requested URL path.
|
||||
func NewResourceLocation(reqURL *url.URL, id string) string {
|
||||
var u url.URL
|
||||
u = *reqURL
|
||||
u.Path = path.Join(u.Path, id)
|
||||
u.RawQuery = ""
|
||||
u.Fragment = ""
|
||||
return u.String()
|
||||
}
|
||||
|
||||
// CopyRequest returns a clone of the provided *http.Request.
|
||||
// The returned object is a shallow copy of the struct and a
|
||||
// deep copy of its Header field.
|
||||
func CopyRequest(r *http.Request) *http.Request {
|
||||
r2 := *r
|
||||
r2.Header = make(http.Header)
|
||||
for k, s := range r.Header {
|
||||
r2.Header[k] = s
|
||||
}
|
||||
return &r2
|
||||
}
|
||||
-380
@@ -1,380 +0,0 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCacheControlMaxAgeSuccess(t *testing.T) {
|
||||
tests := []struct {
|
||||
hdr string
|
||||
wantAge time.Duration
|
||||
wantOK bool
|
||||
}{
|
||||
{"max-age=12", 12 * time.Second, true},
|
||||
{"max-age=-12", 0, false},
|
||||
{"max-age=0", 0, false},
|
||||
{"public, max-age=12", 12 * time.Second, true},
|
||||
{"public, max-age=40192, must-revalidate", 40192 * time.Second, true},
|
||||
{"public, not-max-age=12, must-revalidate", time.Duration(0), false},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
maxAge, ok, err := cacheControlMaxAge(tt.hdr)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: err=%v", i, err)
|
||||
}
|
||||
if tt.wantAge != maxAge {
|
||||
t.Errorf("case %d: want=%d got=%d", i, tt.wantAge, maxAge)
|
||||
}
|
||||
if tt.wantOK != ok {
|
||||
t.Errorf("case %d: incorrect ok value: want=%t got=%t", i, tt.wantOK, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheControlMaxAgeFail(t *testing.T) {
|
||||
tests := []string{
|
||||
"max-age=aasdf",
|
||||
"max-age=",
|
||||
"max-age",
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
_, ok, err := cacheControlMaxAge(tt)
|
||||
if ok {
|
||||
t.Errorf("case %d: want ok=false, got true", i)
|
||||
}
|
||||
if err == nil {
|
||||
t.Errorf("case %d: want non-nil err", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeQuery(t *testing.T) {
|
||||
tests := []struct {
|
||||
u string
|
||||
q url.Values
|
||||
w string
|
||||
}{
|
||||
// No values
|
||||
{
|
||||
u: "http://example.com",
|
||||
q: nil,
|
||||
w: "http://example.com",
|
||||
},
|
||||
// No additional values
|
||||
{
|
||||
u: "http://example.com?foo=bar",
|
||||
q: nil,
|
||||
w: "http://example.com?foo=bar",
|
||||
},
|
||||
// Simple addition
|
||||
{
|
||||
u: "http://example.com",
|
||||
q: url.Values{
|
||||
"foo": []string{"bar"},
|
||||
},
|
||||
w: "http://example.com?foo=bar",
|
||||
},
|
||||
// Addition with existing values
|
||||
{
|
||||
u: "http://example.com?dog=boo",
|
||||
q: url.Values{
|
||||
"foo": []string{"bar"},
|
||||
},
|
||||
w: "http://example.com?dog=boo&foo=bar",
|
||||
},
|
||||
// Merge
|
||||
{
|
||||
u: "http://example.com?dog=boo",
|
||||
q: url.Values{
|
||||
"dog": []string{"elroy"},
|
||||
},
|
||||
w: "http://example.com?dog=boo&dog=elroy",
|
||||
},
|
||||
// Add and merge
|
||||
{
|
||||
u: "http://example.com?dog=boo",
|
||||
q: url.Values{
|
||||
"dog": []string{"elroy"},
|
||||
"foo": []string{"bar"},
|
||||
},
|
||||
w: "http://example.com?dog=boo&dog=elroy&foo=bar",
|
||||
},
|
||||
// Multivalue merge
|
||||
{
|
||||
u: "http://example.com?dog=boo",
|
||||
q: url.Values{
|
||||
"dog": []string{"elroy", "penny"},
|
||||
},
|
||||
w: "http://example.com?dog=boo&dog=elroy&dog=penny",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
ur, err := url.Parse(tt.u)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: failed parsing test url: %v, error: %v", i, tt.u, err)
|
||||
}
|
||||
|
||||
got := MergeQuery(*ur, tt.q)
|
||||
want, err := url.Parse(tt.w)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: failed parsing want url: %v, error: %v", i, tt.w, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*want, got) {
|
||||
t.Errorf("case %d: want: %v, got: %v", i, *want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpiresPass(t *testing.T) {
|
||||
tests := []struct {
|
||||
date string
|
||||
exp string
|
||||
wantTTL time.Duration
|
||||
wantOK bool
|
||||
}{
|
||||
// Expires and Date properly set
|
||||
{
|
||||
date: "Thu, 01 Dec 1983 22:00:00 GMT",
|
||||
exp: "Fri, 02 Dec 1983 01:00:00 GMT",
|
||||
wantTTL: 10800 * time.Second,
|
||||
wantOK: true,
|
||||
},
|
||||
// empty headers
|
||||
{
|
||||
date: "",
|
||||
exp: "",
|
||||
wantOK: false,
|
||||
},
|
||||
// lack of Expirs short-ciruits Date parsing
|
||||
{
|
||||
date: "foo",
|
||||
exp: "",
|
||||
wantOK: false,
|
||||
},
|
||||
// lack of Date short-ciruits Expires parsing
|
||||
{
|
||||
date: "",
|
||||
exp: "foo",
|
||||
wantOK: false,
|
||||
},
|
||||
// no Date
|
||||
{
|
||||
exp: "Thu, 01 Dec 1983 22:00:00 GMT",
|
||||
wantTTL: 0,
|
||||
wantOK: false,
|
||||
},
|
||||
// no Expires
|
||||
{
|
||||
date: "Thu, 01 Dec 1983 22:00:00 GMT",
|
||||
wantTTL: 0,
|
||||
wantOK: false,
|
||||
},
|
||||
// Expires < Date
|
||||
{
|
||||
date: "Fri, 02 Dec 1983 01:00:00 GMT",
|
||||
exp: "Thu, 01 Dec 1983 22:00:00 GMT",
|
||||
wantTTL: 0,
|
||||
wantOK: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
ttl, ok, err := expires(tt.date, tt.exp)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: err=%v", i, err)
|
||||
}
|
||||
if tt.wantTTL != ttl {
|
||||
t.Errorf("case %d: want=%d got=%d", i, tt.wantTTL, ttl)
|
||||
}
|
||||
if tt.wantOK != ok {
|
||||
t.Errorf("case %d: incorrect ok value: want=%t got=%t", i, tt.wantOK, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpiresFail(t *testing.T) {
|
||||
tests := []struct {
|
||||
date string
|
||||
exp string
|
||||
}{
|
||||
// malformed Date header
|
||||
{
|
||||
date: "foo",
|
||||
exp: "Fri, 02 Dec 1983 01:00:00 GMT",
|
||||
},
|
||||
// malformed exp header
|
||||
{
|
||||
date: "Fri, 02 Dec 1983 01:00:00 GMT",
|
||||
exp: "bar",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
_, _, err := expires(tt.date, tt.exp)
|
||||
if err == nil {
|
||||
t.Errorf("case %d: expected non-nil error", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheablePass(t *testing.T) {
|
||||
tests := []struct {
|
||||
headers http.Header
|
||||
wantTTL time.Duration
|
||||
wantOK bool
|
||||
}{
|
||||
// valid Cache-Control
|
||||
{
|
||||
headers: http.Header{
|
||||
"Cache-Control": []string{"max-age=100"},
|
||||
},
|
||||
wantTTL: 100 * time.Second,
|
||||
wantOK: true,
|
||||
},
|
||||
// valid Date/Expires
|
||||
{
|
||||
headers: http.Header{
|
||||
"Date": []string{"Thu, 01 Dec 1983 22:00:00 GMT"},
|
||||
"Expires": []string{"Fri, 02 Dec 1983 01:00:00 GMT"},
|
||||
},
|
||||
wantTTL: 10800 * time.Second,
|
||||
wantOK: true,
|
||||
},
|
||||
// Cache-Control supersedes Date/Expires
|
||||
{
|
||||
headers: http.Header{
|
||||
"Cache-Control": []string{"max-age=100"},
|
||||
"Date": []string{"Thu, 01 Dec 1983 22:00:00 GMT"},
|
||||
"Expires": []string{"Fri, 02 Dec 1983 01:00:00 GMT"},
|
||||
},
|
||||
wantTTL: 100 * time.Second,
|
||||
wantOK: true,
|
||||
},
|
||||
// no caching headers
|
||||
{
|
||||
headers: http.Header{},
|
||||
wantOK: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
ttl, ok, err := Cacheable(tt.headers)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: err=%v", i, err)
|
||||
continue
|
||||
}
|
||||
if tt.wantTTL != ttl {
|
||||
t.Errorf("case %d: want=%d got=%d", i, tt.wantTTL, ttl)
|
||||
}
|
||||
if tt.wantOK != ok {
|
||||
t.Errorf("case %d: incorrect ok value: want=%t got=%t", i, tt.wantOK, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheableFail(t *testing.T) {
|
||||
tests := []http.Header{
|
||||
// invalid Cache-Control short-circuits
|
||||
http.Header{
|
||||
"Cache-Control": []string{"max-age"},
|
||||
"Date": []string{"Thu, 01 Dec 1983 22:00:00 GMT"},
|
||||
"Expires": []string{"Fri, 02 Dec 1983 01:00:00 GMT"},
|
||||
},
|
||||
// no Cache-Control, invalid Expires
|
||||
http.Header{
|
||||
"Date": []string{"Thu, 01 Dec 1983 22:00:00 GMT"},
|
||||
"Expires": []string{"boo"},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
_, _, err := Cacheable(tt)
|
||||
if err == nil {
|
||||
t.Errorf("case %d: want non-nil err", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewResourceLocation(t *testing.T) {
|
||||
tests := []struct {
|
||||
ru *url.URL
|
||||
id string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
ru: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: "example.com",
|
||||
},
|
||||
id: "foo",
|
||||
want: "http://example.com/foo",
|
||||
},
|
||||
// https
|
||||
{
|
||||
ru: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "example.com",
|
||||
},
|
||||
id: "foo",
|
||||
want: "https://example.com/foo",
|
||||
},
|
||||
// with path
|
||||
{
|
||||
ru: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: "example.com",
|
||||
Path: "one/two/three",
|
||||
},
|
||||
id: "foo",
|
||||
want: "http://example.com/one/two/three/foo",
|
||||
},
|
||||
// with fragment
|
||||
{
|
||||
ru: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: "example.com",
|
||||
Fragment: "frag",
|
||||
},
|
||||
id: "foo",
|
||||
want: "http://example.com/foo",
|
||||
},
|
||||
// with query
|
||||
{
|
||||
ru: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: "example.com",
|
||||
RawQuery: "dog=elroy",
|
||||
},
|
||||
id: "foo",
|
||||
want: "http://example.com/foo",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
got := NewResourceLocation(tt.ru, tt.id)
|
||||
if tt.want != got {
|
||||
t.Errorf("case %d: want=%s, got=%s", i, tt.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyRequest(t *testing.T) {
|
||||
r1, err := http.NewRequest("GET", "http://example.com", strings.NewReader("foo"))
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
r2 := CopyRequest(r1)
|
||||
if !reflect.DeepEqual(r1, r2) {
|
||||
t.Fatalf("Result of CopyRequest incorrect: %#v != %#v", r1, r2)
|
||||
}
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// ParseNonEmptyURL checks that a string is a parsable URL which is also not empty
|
||||
// since `url.Parse("")` does not return an error. Must contian a scheme and a host.
|
||||
func ParseNonEmptyURL(u string) (*url.URL, error) {
|
||||
if u == "" {
|
||||
return nil, errors.New("url is empty")
|
||||
}
|
||||
|
||||
ur, err := url.Parse(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ur.Scheme == "" {
|
||||
return nil, errors.New("url scheme is empty")
|
||||
}
|
||||
|
||||
if ur.Host == "" {
|
||||
return nil, errors.New("url host is empty")
|
||||
}
|
||||
|
||||
return ur, nil
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseNonEmptyURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
u string
|
||||
ok bool
|
||||
}{
|
||||
{"", false},
|
||||
{"http://", false},
|
||||
{"example.com", false},
|
||||
{"example", false},
|
||||
{"http://example", true},
|
||||
{"http://example:1234", true},
|
||||
{"http://example.com", true},
|
||||
{"http://example.com:1234", true},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
u, err := ParseNonEmptyURL(tt.u)
|
||||
if err != nil {
|
||||
t.Logf("err: %v", err)
|
||||
if tt.ok {
|
||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if !tt.ok {
|
||||
t.Errorf("case %d: expected error but got none", i)
|
||||
continue
|
||||
}
|
||||
|
||||
uu, err := url.Parse(tt.u)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if uu.String() != u.String() {
|
||||
t.Errorf("case %d: incorrect url value, want: %q, got: %q", i, uu.String(), u.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
-126
@@ -1,126 +0,0 @@
|
||||
package jose
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Claims map[string]interface{}
|
||||
|
||||
func (c Claims) Add(name string, value interface{}) {
|
||||
c[name] = value
|
||||
}
|
||||
|
||||
func (c Claims) StringClaim(name string) (string, bool, error) {
|
||||
cl, ok := c[name]
|
||||
if !ok {
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
v, ok := cl.(string)
|
||||
if !ok {
|
||||
return "", false, fmt.Errorf("unable to parse claim as string: %v", name)
|
||||
}
|
||||
|
||||
return v, true, nil
|
||||
}
|
||||
|
||||
func (c Claims) StringsClaim(name string) ([]string, bool, error) {
|
||||
cl, ok := c[name]
|
||||
if !ok {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
if v, ok := cl.([]string); ok {
|
||||
return v, true, nil
|
||||
}
|
||||
|
||||
// When unmarshaled, []string will become []interface{}.
|
||||
if v, ok := cl.([]interface{}); ok {
|
||||
var ret []string
|
||||
for _, vv := range v {
|
||||
str, ok := vv.(string)
|
||||
if !ok {
|
||||
return nil, false, fmt.Errorf("unable to parse claim as string array: %v", name)
|
||||
}
|
||||
ret = append(ret, str)
|
||||
}
|
||||
return ret, true, nil
|
||||
}
|
||||
|
||||
return nil, false, fmt.Errorf("unable to parse claim as string array: %v", name)
|
||||
}
|
||||
|
||||
func (c Claims) Int64Claim(name string) (int64, bool, error) {
|
||||
cl, ok := c[name]
|
||||
if !ok {
|
||||
return 0, false, nil
|
||||
}
|
||||
|
||||
v, ok := cl.(int64)
|
||||
if !ok {
|
||||
vf, ok := cl.(float64)
|
||||
if !ok {
|
||||
return 0, false, fmt.Errorf("unable to parse claim as int64: %v", name)
|
||||
}
|
||||
v = int64(vf)
|
||||
}
|
||||
|
||||
return v, true, nil
|
||||
}
|
||||
|
||||
func (c Claims) Float64Claim(name string) (float64, bool, error) {
|
||||
cl, ok := c[name]
|
||||
if !ok {
|
||||
return 0, false, nil
|
||||
}
|
||||
|
||||
v, ok := cl.(float64)
|
||||
if !ok {
|
||||
vi, ok := cl.(int64)
|
||||
if !ok {
|
||||
return 0, false, fmt.Errorf("unable to parse claim as float64: %v", name)
|
||||
}
|
||||
v = float64(vi)
|
||||
}
|
||||
|
||||
return v, true, nil
|
||||
}
|
||||
|
||||
func (c Claims) TimeClaim(name string) (time.Time, bool, error) {
|
||||
v, ok, err := c.Float64Claim(name)
|
||||
if !ok || err != nil {
|
||||
return time.Time{}, ok, err
|
||||
}
|
||||
|
||||
s := math.Trunc(v)
|
||||
ns := (v - s) * math.Pow(10, 9)
|
||||
return time.Unix(int64(s), int64(ns)).UTC(), true, nil
|
||||
}
|
||||
|
||||
func decodeClaims(payload []byte) (Claims, error) {
|
||||
var c Claims
|
||||
if err := json.Unmarshal(payload, &c); err != nil {
|
||||
return nil, fmt.Errorf("malformed JWT claims, unable to decode: %v", err)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func marshalClaims(c Claims) ([]byte, error) {
|
||||
b, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func encodeClaims(c Claims) (string, error) {
|
||||
b, err := marshalClaims(c)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return encodeSegment(b), nil
|
||||
}
|
||||
-328
@@ -1,328 +0,0 @@
|
||||
package jose
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
tests := []struct {
|
||||
cl Claims
|
||||
key string
|
||||
ok bool
|
||||
err bool
|
||||
val string
|
||||
}{
|
||||
// ok, no err, claim exists
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": "bar",
|
||||
},
|
||||
key: "foo",
|
||||
val: "bar",
|
||||
ok: true,
|
||||
err: false,
|
||||
},
|
||||
// no claims
|
||||
{
|
||||
cl: Claims{},
|
||||
key: "foo",
|
||||
val: "",
|
||||
ok: false,
|
||||
err: false,
|
||||
},
|
||||
// missing claim
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": "bar",
|
||||
},
|
||||
key: "xxx",
|
||||
val: "",
|
||||
ok: false,
|
||||
err: false,
|
||||
},
|
||||
// unparsable: type
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": struct{}{},
|
||||
},
|
||||
key: "foo",
|
||||
val: "",
|
||||
ok: false,
|
||||
err: true,
|
||||
},
|
||||
// unparsable: nil value
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": nil,
|
||||
},
|
||||
key: "foo",
|
||||
val: "",
|
||||
ok: false,
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
val, ok, err := tt.cl.StringClaim(tt.key)
|
||||
|
||||
if tt.err && err == nil {
|
||||
t.Errorf("case %d: want err=non-nil, got err=nil", i)
|
||||
} else if !tt.err && err != nil {
|
||||
t.Errorf("case %d: want err=nil, got err=%v", i, err)
|
||||
}
|
||||
|
||||
if tt.ok != ok {
|
||||
t.Errorf("case %d: want ok=%v, got ok=%v", i, tt.ok, ok)
|
||||
}
|
||||
|
||||
if tt.val != val {
|
||||
t.Errorf("case %d: want val=%v, got val=%v", i, tt.val, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt64(t *testing.T) {
|
||||
tests := []struct {
|
||||
cl Claims
|
||||
key string
|
||||
ok bool
|
||||
err bool
|
||||
val int64
|
||||
}{
|
||||
// ok, no err, claim exists
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": int64(100),
|
||||
},
|
||||
key: "foo",
|
||||
val: int64(100),
|
||||
ok: true,
|
||||
err: false,
|
||||
},
|
||||
// no claims
|
||||
{
|
||||
cl: Claims{},
|
||||
key: "foo",
|
||||
val: 0,
|
||||
ok: false,
|
||||
err: false,
|
||||
},
|
||||
// missing claim
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": "bar",
|
||||
},
|
||||
key: "xxx",
|
||||
val: 0,
|
||||
ok: false,
|
||||
err: false,
|
||||
},
|
||||
// unparsable: type
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": struct{}{},
|
||||
},
|
||||
key: "foo",
|
||||
val: 0,
|
||||
ok: false,
|
||||
err: true,
|
||||
},
|
||||
// unparsable: nil value
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": nil,
|
||||
},
|
||||
key: "foo",
|
||||
val: 0,
|
||||
ok: false,
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
val, ok, err := tt.cl.Int64Claim(tt.key)
|
||||
|
||||
if tt.err && err == nil {
|
||||
t.Errorf("case %d: want err=non-nil, got err=nil", i)
|
||||
} else if !tt.err && err != nil {
|
||||
t.Errorf("case %d: want err=nil, got err=%v", i, err)
|
||||
}
|
||||
|
||||
if tt.ok != ok {
|
||||
t.Errorf("case %d: want ok=%v, got ok=%v", i, tt.ok, ok)
|
||||
}
|
||||
|
||||
if tt.val != val {
|
||||
t.Errorf("case %d: want val=%v, got val=%v", i, tt.val, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTime(t *testing.T) {
|
||||
now := time.Now().UTC()
|
||||
unixNow := now.Unix()
|
||||
|
||||
tests := []struct {
|
||||
cl Claims
|
||||
key string
|
||||
ok bool
|
||||
err bool
|
||||
val time.Time
|
||||
}{
|
||||
// ok, no err, claim exists
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": unixNow,
|
||||
},
|
||||
key: "foo",
|
||||
val: time.Unix(now.Unix(), 0).UTC(),
|
||||
ok: true,
|
||||
err: false,
|
||||
},
|
||||
// no claims
|
||||
{
|
||||
cl: Claims{},
|
||||
key: "foo",
|
||||
val: time.Time{},
|
||||
ok: false,
|
||||
err: false,
|
||||
},
|
||||
// missing claim
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": "bar",
|
||||
},
|
||||
key: "xxx",
|
||||
val: time.Time{},
|
||||
ok: false,
|
||||
err: false,
|
||||
},
|
||||
// unparsable: type
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": struct{}{},
|
||||
},
|
||||
key: "foo",
|
||||
val: time.Time{},
|
||||
ok: false,
|
||||
err: true,
|
||||
},
|
||||
// unparsable: nil value
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": nil,
|
||||
},
|
||||
key: "foo",
|
||||
val: time.Time{},
|
||||
ok: false,
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
val, ok, err := tt.cl.TimeClaim(tt.key)
|
||||
|
||||
if tt.err && err == nil {
|
||||
t.Errorf("case %d: want err=non-nil, got err=nil", i)
|
||||
} else if !tt.err && err != nil {
|
||||
t.Errorf("case %d: want err=nil, got err=%v", i, err)
|
||||
}
|
||||
|
||||
if tt.ok != ok {
|
||||
t.Errorf("case %d: want ok=%v, got ok=%v", i, tt.ok, ok)
|
||||
}
|
||||
|
||||
if tt.val != val {
|
||||
t.Errorf("case %d: want val=%v, got val=%v", i, tt.val, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringArray(t *testing.T) {
|
||||
tests := []struct {
|
||||
cl Claims
|
||||
key string
|
||||
ok bool
|
||||
err bool
|
||||
val []string
|
||||
}{
|
||||
// ok, no err, claim exists
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": []string{"bar", "faf"},
|
||||
},
|
||||
key: "foo",
|
||||
val: []string{"bar", "faf"},
|
||||
ok: true,
|
||||
err: false,
|
||||
},
|
||||
// ok, no err, []interface{}
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": []interface{}{"bar", "faf"},
|
||||
},
|
||||
key: "foo",
|
||||
val: []string{"bar", "faf"},
|
||||
ok: true,
|
||||
err: false,
|
||||
},
|
||||
// no claims
|
||||
{
|
||||
cl: Claims{},
|
||||
key: "foo",
|
||||
val: nil,
|
||||
ok: false,
|
||||
err: false,
|
||||
},
|
||||
// missing claim
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": "bar",
|
||||
},
|
||||
key: "xxx",
|
||||
val: nil,
|
||||
ok: false,
|
||||
err: false,
|
||||
},
|
||||
// unparsable: type
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": struct{}{},
|
||||
},
|
||||
key: "foo",
|
||||
val: nil,
|
||||
ok: false,
|
||||
err: true,
|
||||
},
|
||||
// unparsable: nil value
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": nil,
|
||||
},
|
||||
key: "foo",
|
||||
val: nil,
|
||||
ok: false,
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
val, ok, err := tt.cl.StringsClaim(tt.key)
|
||||
|
||||
if tt.err && err == nil {
|
||||
t.Errorf("case %d: want err=non-nil, got err=nil", i)
|
||||
} else if !tt.err && err != nil {
|
||||
t.Errorf("case %d: want err=nil, got err=%v", i, err)
|
||||
}
|
||||
|
||||
if tt.ok != ok {
|
||||
t.Errorf("case %d: want ok=%v, got ok=%v", i, tt.ok, ok)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tt.val, val) {
|
||||
t.Errorf("case %d: want val=%v, got val=%v", i, tt.val, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user