Commit 5d58c743 authored by k8s-merge-robot's avatar k8s-merge-robot

Merge pull request #22304 from ericchiang/bump_go_oidc

Auto commit by PR queue bot
parents 5e50302a 8df55ddb
...@@ -271,23 +271,23 @@ ...@@ -271,23 +271,23 @@
}, },
{ {
"ImportPath": "github.com/coreos/go-oidc/http", "ImportPath": "github.com/coreos/go-oidc/http",
"Rev": "024cdeee09d02fb439eb55bc422e582ac115615b" "Rev": "d7cb66526fffc811d602b6770581064f4b66b507"
}, },
{ {
"ImportPath": "github.com/coreos/go-oidc/jose", "ImportPath": "github.com/coreos/go-oidc/jose",
"Rev": "024cdeee09d02fb439eb55bc422e582ac115615b" "Rev": "d7cb66526fffc811d602b6770581064f4b66b507"
}, },
{ {
"ImportPath": "github.com/coreos/go-oidc/key", "ImportPath": "github.com/coreos/go-oidc/key",
"Rev": "024cdeee09d02fb439eb55bc422e582ac115615b" "Rev": "d7cb66526fffc811d602b6770581064f4b66b507"
}, },
{ {
"ImportPath": "github.com/coreos/go-oidc/oauth2", "ImportPath": "github.com/coreos/go-oidc/oauth2",
"Rev": "024cdeee09d02fb439eb55bc422e582ac115615b" "Rev": "d7cb66526fffc811d602b6770581064f4b66b507"
}, },
{ {
"ImportPath": "github.com/coreos/go-oidc/oidc", "ImportPath": "github.com/coreos/go-oidc/oidc",
"Rev": "024cdeee09d02fb439eb55bc422e582ac115615b" "Rev": "d7cb66526fffc811d602b6770581064f4b66b507"
}, },
{ {
"ImportPath": "github.com/coreos/go-semver/semver", "ImportPath": "github.com/coreos/go-semver/semver",
......
package http package http
import ( import "net/http"
"io/ioutil"
"net/http"
"net/http/httptest"
)
type Client interface { type Client interface {
Do(*http.Request) (*http.Response, error) Do(*http.Request) (*http.Response, error)
} }
type HandlerClient struct {
Handler http.Handler
}
func (hc *HandlerClient) Do(r *http.Request) (*http.Response, error) {
w := httptest.NewRecorder()
hc.Handler.ServeHTTP(w, r)
resp := http.Response{
StatusCode: w.Code,
Header: w.Header(),
Body: ioutil.NopCloser(w.Body),
}
return &resp, nil
}
type RequestRecorder struct {
Response *http.Response
Error error
Request *http.Request
}
func (rr *RequestRecorder) Do(req *http.Request) (*http.Response, error) {
rr.Request = req
if rr.Response == nil && rr.Error == nil {
panic("RequestRecorder Response and Error cannot both be nil")
} else if rr.Response != nil && rr.Error != nil {
panic("RequestRecorder Response and Error cannot both be non-nil")
}
return rr.Response, rr.Error
}
func (rr *RequestRecorder) RoundTrip(req *http.Request) (*http.Response, error) {
return rr.Do(req)
}
...@@ -3,6 +3,7 @@ package jose ...@@ -3,6 +3,7 @@ package jose
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"math"
"time" "time"
) )
...@@ -70,13 +71,33 @@ func (c Claims) Int64Claim(name string) (int64, bool, error) { ...@@ -70,13 +71,33 @@ func (c Claims) Int64Claim(name string) (int64, bool, error) {
return v, true, nil 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) { func (c Claims) TimeClaim(name string) (time.Time, bool, error) {
v, ok, err := c.Int64Claim(name) v, ok, err := c.Float64Claim(name)
if !ok || err != nil { if !ok || err != nil {
return time.Time{}, ok, err return time.Time{}, ok, err
} }
return time.Unix(v, 0).UTC(), true, nil 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) { func decodeClaims(payload []byte) (Claims, error) {
......
...@@ -13,6 +13,57 @@ const ( ...@@ -13,6 +13,57 @@ const (
HeaderKeyID = "kid" HeaderKeyID = "kid"
) )
const (
// Encryption Algorithm Header Parameter Values for JWS
// See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#page-6
AlgHS256 = "HS256"
AlgHS384 = "HS384"
AlgHS512 = "HS512"
AlgRS256 = "RS256"
AlgRS384 = "RS384"
AlgRS512 = "RS512"
AlgES256 = "ES256"
AlgES384 = "ES384"
AlgES512 = "ES512"
AlgPS256 = "PS256"
AlgPS384 = "PS384"
AlgPS512 = "PS512"
AlgNone = "none"
)
const (
// Algorithm Header Parameter Values for JWE
// See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-4.1
AlgRSA15 = "RSA1_5"
AlgRSAOAEP = "RSA-OAEP"
AlgRSAOAEP256 = "RSA-OAEP-256"
AlgA128KW = "A128KW"
AlgA192KW = "A192KW"
AlgA256KW = "A256KW"
AlgDir = "dir"
AlgECDHES = "ECDH-ES"
AlgECDHESA128KW = "ECDH-ES+A128KW"
AlgECDHESA192KW = "ECDH-ES+A192KW"
AlgECDHESA256KW = "ECDH-ES+A256KW"
AlgA128GCMKW = "A128GCMKW"
AlgA192GCMKW = "A192GCMKW"
AlgA256GCMKW = "A256GCMKW"
AlgPBES2HS256A128KW = "PBES2-HS256+A128KW"
AlgPBES2HS384A192KW = "PBES2-HS384+A192KW"
AlgPBES2HS512A256KW = "PBES2-HS512+A256KW"
)
const (
// Encryption Algorithm Header Parameter Values for JWE
// See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#page-22
EncA128CBCHS256 = "A128CBC-HS256"
EncA128CBCHS384 = "A128CBC-HS384"
EncA256CBCHS512 = "A256CBC-HS512"
EncA128GCM = "A128GCM"
EncA192GCM = "A192GCM"
EncA256GCM = "A256GCM"
)
type JOSEHeader map[string]string type JOSEHeader map[string]string
func (j JOSEHeader) Validate() error { func (j JOSEHeader) Validate() error {
......
...@@ -70,6 +70,10 @@ func (j *JWK) UnmarshalJSON(data []byte) error { ...@@ -70,6 +70,10 @@ func (j *JWK) UnmarshalJSON(data []byte) error {
return nil return nil
} }
type JWKSet struct {
Keys []JWK `json:"keys"`
}
func decodeExponent(e string) (int, error) { func decodeExponent(e string) (int, error) {
decE, err := decodeBase64URLPaddingOptional(e) decE, err := decodeBase64URLPaddingOptional(e)
if err != nil { if err != nil {
......
...@@ -135,7 +135,7 @@ func (s *PrivateKeySet) Active() *PrivateKey { ...@@ -135,7 +135,7 @@ func (s *PrivateKeySet) Active() *PrivateKey {
type GeneratePrivateKeyFunc func() (*PrivateKey, error) type GeneratePrivateKeyFunc func() (*PrivateKey, error)
func GeneratePrivateKey() (*PrivateKey, error) { func GeneratePrivateKey() (*PrivateKey, error) {
pk, err := rsa.GenerateKey(rand.Reader, 1024) pk, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
package key package key
import "errors" import (
"errors"
"sync"
)
var ErrorNoKeys = errors.New("no keys found") var ErrorNoKeys = errors.New("no keys found")
...@@ -22,6 +25,7 @@ func NewPrivateKeySetRepo() PrivateKeySetRepo { ...@@ -22,6 +25,7 @@ func NewPrivateKeySetRepo() PrivateKeySetRepo {
} }
type memPrivateKeySetRepo struct { type memPrivateKeySetRepo struct {
mu sync.RWMutex
pks PrivateKeySet pks PrivateKeySet
} }
...@@ -33,11 +37,17 @@ func (r *memPrivateKeySetRepo) Set(ks KeySet) error { ...@@ -33,11 +37,17 @@ func (r *memPrivateKeySetRepo) Set(ks KeySet) error {
return errors.New("nil KeySet") return errors.New("nil KeySet")
} }
r.mu.Lock()
defer r.mu.Unlock()
r.pks = *pks r.pks = *pks
return nil return nil
} }
func (r *memPrivateKeySetRepo) Get() (KeySet, error) { func (r *memPrivateKeySetRepo) Get() (KeySet, error) {
r.mu.RLock()
defer r.mu.RUnlock()
if r.pks.keys == nil { if r.pks.keys == nil {
return nil, ErrorNoKeys return nil, ErrorNoKeys
} }
......
...@@ -29,7 +29,7 @@ func (s *KeySetSyncer) Run() chan struct{} { ...@@ -29,7 +29,7 @@ func (s *KeySetSyncer) Run() chan struct{} {
var failing bool var failing bool
var next time.Duration var next time.Duration
for { for {
exp, err := sync(s.readable, s.writable, s.clock) exp, err := syncKeySet(s.readable, s.writable, s.clock)
if err != nil || exp == 0 { if err != nil || exp == 0 {
if !failing { if !failing {
failing = true failing = true
...@@ -62,12 +62,12 @@ func (s *KeySetSyncer) Run() chan struct{} { ...@@ -62,12 +62,12 @@ func (s *KeySetSyncer) Run() chan struct{} {
} }
func Sync(r ReadableKeySetRepo, w WritableKeySetRepo) (time.Duration, error) { func Sync(r ReadableKeySetRepo, w WritableKeySetRepo) (time.Duration, error) {
return sync(r, w, clockwork.NewRealClock()) return syncKeySet(r, w, clockwork.NewRealClock())
} }
// sync copies the keyset from r to the KeySet at w and returns the duration in which the KeySet will expire. // syncKeySet copies the keyset from r to the KeySet at w and returns the duration in which the KeySet will expire.
// If keyset has already expired, returns a zero duration. // If keyset has already expired, returns a zero duration.
func sync(r ReadableKeySetRepo, w WritableKeySetRepo, clock clockwork.Clock) (exp time.Duration, err error) { func syncKeySet(r ReadableKeySetRepo, w WritableKeySetRepo, clock clockwork.Clock) (exp time.Duration, err error) {
var ks KeySet var ks KeySet
ks, err = r.Get() ks, err = r.Get()
if err != nil { if err != nil {
......
...@@ -8,14 +8,49 @@ import ( ...@@ -8,14 +8,49 @@ import (
"mime" "mime"
"net/http" "net/http"
"net/url" "net/url"
"sort"
"strconv" "strconv"
"strings" "strings"
phttp "github.com/coreos/go-oidc/http" phttp "github.com/coreos/go-oidc/http"
) )
// ResponseTypesEqual compares two response_type values. If either
// contains a space, it is treated as an unordered list. For example,
// comparing "code id_token" and "id_token code" would evaluate to true.
func ResponseTypesEqual(r1, r2 string) bool {
if !strings.Contains(r1, " ") || !strings.Contains(r2, " ") {
// fast route, no split needed
return r1 == r2
}
// split, sort, and compare
r1Fields := strings.Fields(r1)
r2Fields := strings.Fields(r2)
if len(r1Fields) != len(r2Fields) {
return false
}
sort.Strings(r1Fields)
sort.Strings(r2Fields)
for i, r1Field := range r1Fields {
if r1Field != r2Fields[i] {
return false
}
}
return true
}
const ( const (
ResponseTypeCode = "code" // OAuth2.0 response types registered by OIDC.
//
// See: https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#RegistryContents
ResponseTypeCode = "code"
ResponseTypeCodeIDToken = "code id_token"
ResponseTypeCodeIDTokenToken = "code id_token token"
ResponseTypeIDToken = "id_token"
ResponseTypeIDTokenToken = "id_token token"
ResponseTypeToken = "token"
ResponseTypeNone = "none"
) )
const ( const (
...@@ -136,22 +171,24 @@ func (c *Client) commonURLValues() url.Values { ...@@ -136,22 +171,24 @@ func (c *Client) commonURLValues() url.Values {
} }
} }
func (c *Client) newAuthenticatedRequest(url string, values url.Values) (*http.Request, error) { func (c *Client) newAuthenticatedRequest(urlToken string, values url.Values) (*http.Request, error) {
var req *http.Request var req *http.Request
var err error var err error
switch c.authMethod { switch c.authMethod {
case AuthMethodClientSecretPost: case AuthMethodClientSecretPost:
values.Set("client_secret", c.creds.Secret) values.Set("client_secret", c.creds.Secret)
req, err = http.NewRequest("POST", url, strings.NewReader(values.Encode())) req, err = http.NewRequest("POST", urlToken, strings.NewReader(values.Encode()))
if err != nil { if err != nil {
return nil, err return nil, err
} }
case AuthMethodClientSecretBasic: case AuthMethodClientSecretBasic:
req, err = http.NewRequest("POST", url, strings.NewReader(values.Encode())) req, err = http.NewRequest("POST", urlToken, strings.NewReader(values.Encode()))
if err != nil { if err != nil {
return nil, err return nil, err
} }
req.SetBasicAuth(c.creds.ID, c.creds.Secret) encodedID := url.QueryEscape(c.creds.ID)
encodedSecret := url.QueryEscape(c.creds.Secret)
req.SetBasicAuth(encodedID, encodedSecret)
default: default:
panic("misconfigured client: auth method not supported") panic("misconfigured client: auth method not supported")
} }
......
...@@ -59,8 +59,8 @@ func NewClaims(iss, sub string, aud interface{}, iat, exp time.Time) jose.Claims ...@@ -59,8 +59,8 @@ func NewClaims(iss, sub string, aud interface{}, iat, exp time.Time) jose.Claims
"iss": iss, "iss": iss,
"sub": sub, "sub": sub,
"aud": aud, "aud": aud,
"iat": float64(iat.Unix()), "iat": iat.Unix(),
"exp": float64(exp.Unix()), "exp": exp.Unix(),
} }
} }
......
...@@ -99,10 +99,6 @@ func New(issuerURL, clientID, caFile, usernameClaim, groupsClaim string) (*OIDCA ...@@ -99,10 +99,6 @@ func New(issuerURL, clientID, caFile, usernameClaim, groupsClaim string) (*OIDCA
glog.Infof("Fetched provider config from %s: %#v", issuerURL, cfg) glog.Infof("Fetched provider config from %s: %#v", issuerURL, cfg)
if cfg.KeysEndpoint == "" {
return nil, fmt.Errorf("OIDC provider must provide 'jwks_uri' for public key discovery")
}
ccfg := oidc.ClientConfig{ ccfg := oidc.ClientConfig{
HTTPClient: hc, HTTPClient: hc,
Credentials: oidc.ClientCredentials{ID: clientID}, Credentials: oidc.ClientCredentials{ID: clientID},
......
...@@ -31,6 +31,7 @@ import ( ...@@ -31,6 +31,7 @@ import (
"net" "net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/url"
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
...@@ -70,8 +71,16 @@ func newOIDCProvider(t *testing.T) *oidcProvider { ...@@ -70,8 +71,16 @@ func newOIDCProvider(t *testing.T) *oidcProvider {
} }
func mustParseURL(t *testing.T, s string) *url.URL {
u, err := url.Parse(s)
if err != nil {
t.Fatalf("Failed to parse url: %v", err)
}
return u
}
func (op *oidcProvider) handleConfig(w http.ResponseWriter, req *http.Request) { func (op *oidcProvider) handleConfig(w http.ResponseWriter, req *http.Request) {
b, err := json.Marshal(op.pcfg) b, err := json.Marshal(&op.pcfg)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
...@@ -203,7 +212,7 @@ func TestOIDCDiscoveryTimeout(t *testing.T) { ...@@ -203,7 +212,7 @@ func TestOIDCDiscoveryTimeout(t *testing.T) {
func TestOIDCDiscoveryNoKeyEndpoint(t *testing.T) { func TestOIDCDiscoveryNoKeyEndpoint(t *testing.T) {
var err error var err error
expectErr := fmt.Errorf("OIDC provider must provide 'jwks_uri' for public key discovery") expectErr := fmt.Errorf("failed to fetch provider config after 3 retries")
cert := path.Join(os.TempDir(), "oidc-cert") cert := path.Join(os.TempDir(), "oidc-cert")
key := path.Join(os.TempDir(), "oidc-key") key := path.Join(os.TempDir(), "oidc-key")
...@@ -225,7 +234,7 @@ func TestOIDCDiscoveryNoKeyEndpoint(t *testing.T) { ...@@ -225,7 +234,7 @@ func TestOIDCDiscoveryNoKeyEndpoint(t *testing.T) {
// defer srv.Close() // defer srv.Close()
op.pcfg = oidc.ProviderConfig{ op.pcfg = oidc.ProviderConfig{
Issuer: srv.URL, Issuer: mustParseURL(t, srv.URL), // An invalid ProviderConfig. Keys endpoint is required.
} }
_, err = New(srv.URL, "client-foo", cert, "sub", "") _, err = New(srv.URL, "client-foo", cert, "sub", "")
...@@ -245,8 +254,8 @@ func TestOIDCDiscoverySecureConnection(t *testing.T) { ...@@ -245,8 +254,8 @@ func TestOIDCDiscoverySecureConnection(t *testing.T) {
// defer srv.Close() // defer srv.Close()
op.pcfg = oidc.ProviderConfig{ op.pcfg = oidc.ProviderConfig{
Issuer: srv.URL, Issuer: mustParseURL(t, srv.URL),
KeysEndpoint: srv.URL + "/keys", KeysEndpoint: mustParseURL(t, srv.URL+"/keys"),
} }
expectErr := fmt.Errorf("'oidc-issuer-url' (%q) has invalid scheme (%q), require 'https'", srv.URL, "http") expectErr := fmt.Errorf("'oidc-issuer-url' (%q) has invalid scheme (%q), require 'https'", srv.URL, "http")
...@@ -282,8 +291,8 @@ func TestOIDCDiscoverySecureConnection(t *testing.T) { ...@@ -282,8 +291,8 @@ func TestOIDCDiscoverySecureConnection(t *testing.T) {
// defer tlsSrv.Close() // defer tlsSrv.Close()
op.pcfg = oidc.ProviderConfig{ op.pcfg = oidc.ProviderConfig{
Issuer: tlsSrv.URL, Issuer: mustParseURL(t, tlsSrv.URL),
KeysEndpoint: tlsSrv.URL + "/keys", KeysEndpoint: mustParseURL(t, tlsSrv.URL+"/keys"),
} }
// Create a client using cert2, should fail. // Create a client using cert2, should fail.
...@@ -317,9 +326,15 @@ func TestOIDCAuthentication(t *testing.T) { ...@@ -317,9 +326,15 @@ func TestOIDCAuthentication(t *testing.T) {
// TODO: Uncomment when fix #19254 // TODO: Uncomment when fix #19254
// defer srv.Close() // defer srv.Close()
// A provider config with all required fields.
op.pcfg = oidc.ProviderConfig{ op.pcfg = oidc.ProviderConfig{
Issuer: srv.URL, Issuer: mustParseURL(t, srv.URL),
KeysEndpoint: srv.URL + "/keys", AuthEndpoint: mustParseURL(t, srv.URL+"/auth"),
TokenEndpoint: mustParseURL(t, srv.URL+"/token"),
KeysEndpoint: mustParseURL(t, srv.URL+"/keys"),
ResponseTypesSupported: []string{"code"},
SubjectTypesSupported: []string{"public"},
IDTokenSigningAlgValues: []string{"RS256"},
} }
tests := []struct { tests := []struct {
...@@ -371,7 +386,7 @@ func TestOIDCAuthentication(t *testing.T) { ...@@ -371,7 +386,7 @@ func TestOIDCAuthentication(t *testing.T) {
op.generateMalformedToken(t, srv.URL, "client-foo", "client-foo", "sub", "user-foo", "", nil), op.generateMalformedToken(t, srv.URL, "client-foo", "client-foo", "sub", "user-foo", "", nil),
nil, nil,
false, false,
"malformed JWS, unable to decode signature", "oidc: unable to verify JWT signature: no matching keys",
}, },
{ {
// Invalid 'aud'. // Invalid 'aud'.
...@@ -404,7 +419,8 @@ func TestOIDCAuthentication(t *testing.T) { ...@@ -404,7 +419,8 @@ func TestOIDCAuthentication(t *testing.T) {
for i, tt := range tests { for i, tt := range tests {
client, err := New(srv.URL, "client-foo", cert, tt.userClaim, tt.groupsClaim) client, err := New(srv.URL, "client-foo", cert, tt.userClaim, tt.groupsClaim)
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Errorf("Unexpected error: %v", err)
continue
} }
user, result, err := client.AuthenticateToken(tt.token) user, result, err := client.AuthenticateToken(tt.token)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment