Merge branch 'master' into feature/static_password_env

This commit is contained in:
Krishna Durai
2020-01-06 23:21:20 -08:00
committed by GitHub
46 changed files with 897 additions and 279 deletions

View File

@@ -161,6 +161,8 @@ func testAuthRequestCRUD(t *testing.T, s storage.Storage) {
t.Fatalf("failed to delete auth request: %v", err)
}
_, err = s.GetAuthRequest(a1.ID)
mustBeErrNotFound(t, "auth request", err)
}
func testAuthCodeCRUD(t *testing.T, s storage.Storage) {
@@ -214,7 +216,7 @@ func testAuthCodeCRUD(t *testing.T, s storage.Storage) {
got, err := s.GetAuthCode(a1.ID)
if err != nil {
t.Fatalf("failed to get auth req: %v", err)
t.Fatalf("failed to get auth code: %v", err)
}
if a1.Expiry.Unix() != got.Expiry.Unix() {
t.Errorf("auth code expiry did not match want=%s vs got=%s", a1.Expiry, got.Expiry)
@@ -509,7 +511,6 @@ func testPasswordCRUD(t *testing.T, s storage.Storage) {
_, err = s.GetPassword(password1.Email)
mustBeErrNotFound(t, "password", err)
}
func testOfflineSessionCRUD(t *testing.T, s storage.Storage) {
@@ -814,7 +815,7 @@ func testGC(t *testing.T, s storage.Storage) {
}
}
if _, err := s.GetAuthRequest(a.ID); err != nil {
t.Errorf("expected to be able to get auth code after GC: %v", err)
t.Errorf("expected to be able to get auth request after GC: %v", err)
}
}
@@ -825,7 +826,7 @@ func testGC(t *testing.T, s storage.Storage) {
}
if _, err := s.GetAuthRequest(a.ID); err == nil {
t.Errorf("expected auth code to be GC'd")
t.Errorf("expected auth request to be GC'd")
} else if err != storage.ErrNotFound {
t.Errorf("expected storage.ErrNotFound, got %v", err)
}

View File

@@ -135,8 +135,8 @@ func testKeysConcurrentUpdate(t *testing.T, s storage.Storage) {
for i := 0; i < 2; i++ {
n := time.Now().UTC().Round(time.Second)
keys1 := storage.Keys{
SigningKey: jsonWebKeys[0].Private,
SigningKeyPub: jsonWebKeys[0].Public,
SigningKey: jsonWebKeys[i].Private,
SigningKeyPub: jsonWebKeys[i].Public,
NextRotation: n,
}

View File

@@ -156,7 +156,7 @@ func (c *conn) UpdateRefreshToken(id string, updater func(old storage.RefreshTok
return c.txnUpdate(ctx, keyID(refreshTokenPrefix, id), func(currentValue []byte) ([]byte, error) {
var current RefreshToken
if len(currentValue) > 0 {
if err := json.Unmarshal([]byte(currentValue), &current); err != nil {
if err := json.Unmarshal(currentValue, &current); err != nil {
return nil, err
}
}

View File

@@ -55,14 +55,14 @@ type client struct {
}
// idToName maps an arbitrary ID, such as an email or client ID to a Kubernetes object name.
func (c *client) idToName(s string) string {
return idToName(s, c.hash)
func (cli *client) idToName(s string) string {
return idToName(s, cli.hash)
}
// offlineTokenName maps two arbitrary IDs, to a single Kubernetes object name.
// This is used when more than one field is used to uniquely identify the object.
func (c *client) offlineTokenName(userID string, connID string) string {
return offlineTokenName(userID, connID, c.hash)
func (cli *client) offlineTokenName(userID string, connID string) string {
return offlineTokenName(userID, connID, cli.hash)
}
// Kubernetes names must match the regexp '[a-z0-9]([-a-z0-9]*[a-z0-9])?'.
@@ -79,7 +79,7 @@ func offlineTokenName(userID string, connID string, h func() hash.Hash) string {
return strings.TrimRight(encoding.EncodeToString(hash.Sum(nil)), "=")
}
func (c *client) urlFor(apiVersion, namespace, resource, name string) string {
func (cli *client) urlFor(apiVersion, namespace, resource, name string) string {
basePath := "apis/"
if apiVersion == "v1" {
basePath = "api/"
@@ -91,10 +91,10 @@ func (c *client) urlFor(apiVersion, namespace, resource, name string) string {
} else {
p = path.Join(basePath, apiVersion, resource, name)
}
if strings.HasSuffix(c.baseURL, "/") {
return c.baseURL + p
if strings.HasSuffix(cli.baseURL, "/") {
return cli.baseURL + p
}
return c.baseURL + "/" + p
return cli.baseURL + "/" + p
}
// Define an error interface so we can get at the underlying status code if it's
@@ -156,13 +156,13 @@ func closeResp(r *http.Response) {
r.Body.Close()
}
func (c *client) get(resource, name string, v interface{}) error {
return c.getResource(c.apiVersion, c.namespace, resource, name, v)
func (cli *client) get(resource, name string, v interface{}) error {
return cli.getResource(cli.apiVersion, cli.namespace, resource, name, v)
}
func (c *client) getResource(apiVersion, namespace, resource, name string, v interface{}) error {
url := c.urlFor(apiVersion, namespace, resource, name)
resp, err := c.client.Get(url)
func (cli *client) getResource(apiVersion, namespace, resource, name string, v interface{}) error {
url := cli.urlFor(apiVersion, namespace, resource, name)
resp, err := cli.client.Get(url)
if err != nil {
return err
}
@@ -173,22 +173,22 @@ func (c *client) getResource(apiVersion, namespace, resource, name string, v int
return json.NewDecoder(resp.Body).Decode(v)
}
func (c *client) list(resource string, v interface{}) error {
return c.get(resource, "", v)
func (cli *client) list(resource string, v interface{}) error {
return cli.get(resource, "", v)
}
func (c *client) post(resource string, v interface{}) error {
return c.postResource(c.apiVersion, c.namespace, resource, v)
func (cli *client) post(resource string, v interface{}) error {
return cli.postResource(cli.apiVersion, cli.namespace, resource, v)
}
func (c *client) postResource(apiVersion, namespace, resource string, v interface{}) error {
func (cli *client) postResource(apiVersion, namespace, resource string, v interface{}) error {
body, err := json.Marshal(v)
if err != nil {
return fmt.Errorf("marshal object: %v", err)
}
url := c.urlFor(apiVersion, namespace, resource, "")
resp, err := c.client.Post(url, "application/json", bytes.NewReader(body))
url := cli.urlFor(apiVersion, namespace, resource, "")
resp, err := cli.client.Post(url, "application/json", bytes.NewReader(body))
if err != nil {
return err
}
@@ -196,13 +196,13 @@ func (c *client) postResource(apiVersion, namespace, resource string, v interfac
return checkHTTPErr(resp, http.StatusCreated)
}
func (c *client) delete(resource, name string) error {
url := c.urlFor(c.apiVersion, c.namespace, resource, name)
func (cli *client) delete(resource, name string) error {
url := cli.urlFor(cli.apiVersion, cli.namespace, resource, name)
req, err := http.NewRequest("DELETE", url, nil)
if err != nil {
return fmt.Errorf("create delete request: %v", err)
}
resp, err := c.client.Do(req)
resp, err := cli.client.Do(req)
if err != nil {
return fmt.Errorf("delete request: %v", err)
}
@@ -210,7 +210,7 @@ func (c *client) delete(resource, name string) error {
return checkHTTPErr(resp, http.StatusOK)
}
func (c *client) deleteAll(resource string) error {
func (cli *client) deleteAll(resource string) error {
var list struct {
k8sapi.TypeMeta `json:",inline"`
k8sapi.ListMeta `json:"metadata,omitempty"`
@@ -219,24 +219,24 @@ func (c *client) deleteAll(resource string) error {
k8sapi.ObjectMeta `json:"metadata,omitempty"`
} `json:"items"`
}
if err := c.list(resource, &list); err != nil {
if err := cli.list(resource, &list); err != nil {
return err
}
for _, item := range list.Items {
if err := c.delete(resource, item.Name); err != nil {
if err := cli.delete(resource, item.Name); err != nil {
return err
}
}
return nil
}
func (c *client) put(resource, name string, v interface{}) error {
func (cli *client) put(resource, name string, v interface{}) error {
body, err := json.Marshal(v)
if err != nil {
return fmt.Errorf("marshal object: %v", err)
}
url := c.urlFor(c.apiVersion, c.namespace, resource, name)
url := cli.urlFor(cli.apiVersion, cli.namespace, resource, name)
req, err := http.NewRequest("PUT", url, bytes.NewReader(body))
if err != nil {
return fmt.Errorf("create patch request: %v", err)
@@ -244,7 +244,7 @@ func (c *client) put(resource, name string, v interface{}) error {
req.Header.Set("Content-Length", strconv.Itoa(len(body)))
resp, err := c.client.Do(req)
resp, err := cli.client.Do(req)
if err != nil {
return fmt.Errorf("patch request: %v", err)
}

View File

@@ -43,7 +43,7 @@ type CustomResourceDefinitionNames struct {
ListKind string `json:"listKind,omitempty" protobuf:"bytes,5,opt,name=listKind"`
}
// ResourceScope is an enum defining the different scopes availabe to a custom resource
// ResourceScope is an enum defining the different scopes available to a custom resource
type ResourceScope string
const (

View File

@@ -1,39 +1,104 @@
package kubernetes
import (
"fmt"
"io/ioutil"
"os"
"strings"
"testing"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/suite"
"sigs.k8s.io/testing_frameworks/integration"
"github.com/dexidp/dex/storage"
"github.com/dexidp/dex/storage/conformance"
)
const testKubeConfigEnv = "DEX_KUBECONFIG"
const kubeconfigTemplate = `apiVersion: v1
kind: Config
clusters:
- name: local
cluster:
server: SERVERURL
users:
- name: local
user:
contexts:
- context:
cluster: local
user: local
`
func TestLoadClient(t *testing.T) {
loadClient(t)
func TestStorage(t *testing.T) {
if os.Getenv("TEST_ASSET_KUBE_APISERVER") == "" || os.Getenv("TEST_ASSET_ETCD") == "" {
t.Skip("control plane binaries are missing")
}
suite.Run(t, new(StorageTestSuite))
}
func loadClient(t *testing.T) *client {
type StorageTestSuite struct {
suite.Suite
controlPlane *integration.ControlPlane
client *client
}
func (s *StorageTestSuite) SetupSuite() {
s.controlPlane = &integration.ControlPlane{}
err := s.controlPlane.Start()
s.Require().NoError(err)
}
func (s *StorageTestSuite) TearDownSuite() {
s.controlPlane.Stop()
}
func (s *StorageTestSuite) SetupTest() {
f, err := ioutil.TempFile("", "dex-kubeconfig-*")
s.Require().NoError(err)
defer f.Close()
_, err = f.WriteString(strings.ReplaceAll(kubeconfigTemplate, "SERVERURL", s.controlPlane.APIURL().String()))
s.Require().NoError(err)
config := Config{
KubeConfigFile: os.Getenv(testKubeConfigEnv),
}
if config.KubeConfigFile == "" {
t.Skipf("test environment variable %q not set, skipping", testKubeConfigEnv)
KubeConfigFile: f.Name(),
}
logger := &logrus.Logger{
Out: os.Stderr,
Formatter: &logrus.TextFormatter{DisableColors: true},
Level: logrus.DebugLevel,
}
s, err := config.open(logger, true)
if err != nil {
t.Fatal(err)
client, err := config.open(logger, true)
s.Require().NoError(err)
s.client = client
}
func (s *StorageTestSuite) TestStorage() {
newStorage := func() storage.Storage {
for _, resource := range []string{
resourceAuthCode,
resourceAuthRequest,
resourceClient,
resourceRefreshToken,
resourceKeys,
resourcePassword,
} {
if err := s.client.deleteAll(resource); err != nil {
s.T().Fatalf("delete all %q failed: %v", resource, err)
}
}
return s.client
}
return s
conformance.RunTests(s.T(), newStorage)
conformance.RunTransactionTests(s.T(), newStorage)
}
func TestURLFor(t *testing.T) {
@@ -83,27 +148,3 @@ func TestURLFor(t *testing.T) {
}
}
}
func TestStorage(t *testing.T) {
client := loadClient(t)
newStorage := func() storage.Storage {
for _, resource := range []string{
resourceAuthCode,
resourceAuthRequest,
resourceClient,
resourceRefreshToken,
resourceKeys,
resourcePassword,
} {
if err := client.deleteAll(resource); err != nil {
// Fatalf sometimes doesn't print the error message.
fmt.Fprintf(os.Stderr, "delete all %q failed: %v\n", resource, err)
t.Fatalf("delete all %q failed: %v", resource, err)
}
}
return client
}
conformance.RunTests(t, newStorage)
conformance.RunTransactionTests(t, newStorage)
}

View File

@@ -308,10 +308,17 @@ func (s *MySQL) open(logger log.Logger) (*conn, error) {
return nil, err
}
if s.MaxIdleConns == 0 {
/*Override default behaviour to fix https://github.com/dexidp/dex/issues/1608*/
db.SetMaxIdleConns(0)
} else {
db.SetMaxIdleConns(s.MaxIdleConns)
}
err = db.Ping()
if err != nil {
if mysqlErr, ok := err.(*mysql.MySQLError); ok && mysqlErr.Number == mysqlErrUnknownSysVar {
logger.Info("reconnecting with MySQL pre-5.7.20 compatibilty mode")
logger.Info("reconnecting with MySQL pre-5.7.20 compatibility mode")
// MySQL 5.7.20 introduced transaction_isolation and deprecated tx_isolation.
// MySQL 8.0 doesn't have tx_isolation at all.

View File

@@ -169,7 +169,6 @@ func (c *conn) UpdateAuthRequest(id string, updater func(a storage.AuthRequest)
}
return nil
})
}
func (c *conn) GetAuthRequest(id string) (storage.AuthRequest, error) {