diff --git a/go.mod b/go.mod index dc00404f7b..18802d5f0b 100644 --- a/go.mod +++ b/go.mod @@ -21,10 +21,10 @@ require ( k8s.io/client-go v0.27.6 k8s.io/code-generator v0.27.6 k8s.io/kube-openapi v0.0.0-20230928205116-a78145627833 - knative.dev/eventing v0.39.1-0.20231120114720-140482e3189a + knative.dev/eventing v0.39.1-0.20231120220132-67f382d60b43 knative.dev/hack v0.0.0-20231109190034-5deaddeb51a7 - knative.dev/pkg v0.0.0-20231115001034-97c7258e3a98 - knative.dev/reconciler-test v0.0.0-20231115072946-99723665c94d + knative.dev/pkg v0.0.0-20231120182734-703c8b0d5c34 + knative.dev/reconciler-test v0.0.0-20231121121446-f747d069af11 sigs.k8s.io/controller-runtime v0.15.2 ) diff --git a/go.sum b/go.sum index a40d05e612..5244cd33a4 100644 --- a/go.sum +++ b/go.sum @@ -1870,14 +1870,14 @@ k8s.io/kube-openapi v0.0.0-20230928205116-a78145627833 h1:iFFEmmB7szQhJP42AvRD2+ k8s.io/kube-openapi v0.0.0-20230928205116-a78145627833/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -knative.dev/eventing v0.39.1-0.20231120114720-140482e3189a h1:fsve5uqqYxzrBkXAynlMmWkw4L1S6aYUuaLafBkBIE4= -knative.dev/eventing v0.39.1-0.20231120114720-140482e3189a/go.mod h1:LGZfBR1ykKiLBF06aX+C7vqe4HogiNc9MmFpJz9lvtw= +knative.dev/eventing v0.39.1-0.20231120220132-67f382d60b43 h1:5i2VuGz0/liRoMa48DjB4LMpyOsHtFi721uEGHc3dlU= +knative.dev/eventing v0.39.1-0.20231120220132-67f382d60b43/go.mod h1:m+tzwZOSkMbZPRkSKOIY+nbMfPURejGKnhFYxytCyAs= knative.dev/hack v0.0.0-20231109190034-5deaddeb51a7 h1:HXf7M7n9jwn+Hp904r0HXRSymf+DLXSciFpXVpCg+Bs= knative.dev/hack v0.0.0-20231109190034-5deaddeb51a7/go.mod h1:yk2OjGDsbEnQjfxdm0/HJKS2WqTLEFg/N6nUs6Rqx3Q= -knative.dev/pkg v0.0.0-20231115001034-97c7258e3a98 h1:uvOLwp5Ar7oJlaYEszh51CemuZc1sRRI14xzKhUEF3U= -knative.dev/pkg v0.0.0-20231115001034-97c7258e3a98/go.mod h1:56Qcm0ai7xPWqGxpOnjRi4sAX9fZM9UDTk7fKyjUqZM= -knative.dev/reconciler-test v0.0.0-20231115072946-99723665c94d h1:pvpbn1jetRm2qk2nGFS0Wrujmmu+dnkEi3a3aNlVVbQ= -knative.dev/reconciler-test v0.0.0-20231115072946-99723665c94d/go.mod h1:V5dY5ZYfAwVe2JzJ6+WSwg1v9uzTrDR/vb0EHC01C+g= +knative.dev/pkg v0.0.0-20231120182734-703c8b0d5c34 h1:bMt0eapwDBD4oBGbyXrGk00DRtFgAGjRHq2B29DwhSE= +knative.dev/pkg v0.0.0-20231120182734-703c8b0d5c34/go.mod h1:56Qcm0ai7xPWqGxpOnjRi4sAX9fZM9UDTk7fKyjUqZM= +knative.dev/reconciler-test v0.0.0-20231121121446-f747d069af11 h1:pju0pg3MqvAKb3KabjO3/a3074QNu+ZJ75RapLomjhw= +knative.dev/reconciler-test v0.0.0-20231121121446-f747d069af11/go.mod h1:V5dY5ZYfAwVe2JzJ6+WSwg1v9uzTrDR/vb0EHC01C+g= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= diff --git a/vendor/github.com/go-jose/go-jose/v3/jwt/builder.go b/vendor/github.com/go-jose/go-jose/v3/jwt/builder.go new file mode 100644 index 0000000000..7df270cc39 --- /dev/null +++ b/vendor/github.com/go-jose/go-jose/v3/jwt/builder.go @@ -0,0 +1,334 @@ +/*- + * Copyright 2016 Zbigniew Mandziejewicz + * Copyright 2016 Square, Inc. + * + * 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. + */ + +package jwt + +import ( + "bytes" + "reflect" + + "github.com/go-jose/go-jose/v3/json" + + "github.com/go-jose/go-jose/v3" +) + +// Builder is a utility for making JSON Web Tokens. Calls can be chained, and +// errors are accumulated until the final call to CompactSerialize/FullSerialize. +type Builder interface { + // Claims encodes claims into JWE/JWS form. Multiple calls will merge claims + // into single JSON object. If you are passing private claims, make sure to set + // struct field tags to specify the name for the JSON key to be used when + // serializing. + Claims(i interface{}) Builder + // Token builds a JSONWebToken from provided data. + Token() (*JSONWebToken, error) + // FullSerialize serializes a token using the JWS/JWE JSON Serialization format. + FullSerialize() (string, error) + // CompactSerialize serializes a token using the compact serialization format. + CompactSerialize() (string, error) +} + +// NestedBuilder is a utility for making Signed-Then-Encrypted JSON Web Tokens. +// Calls can be chained, and errors are accumulated until final call to +// CompactSerialize/FullSerialize. +type NestedBuilder interface { + // Claims encodes claims into JWE/JWS form. Multiple calls will merge claims + // into single JSON object. If you are passing private claims, make sure to set + // struct field tags to specify the name for the JSON key to be used when + // serializing. + Claims(i interface{}) NestedBuilder + // Token builds a NestedJSONWebToken from provided data. + Token() (*NestedJSONWebToken, error) + // FullSerialize serializes a token using the JSON Serialization format. + FullSerialize() (string, error) + // CompactSerialize serializes a token using the compact serialization format. + CompactSerialize() (string, error) +} + +type builder struct { + payload map[string]interface{} + err error +} + +type signedBuilder struct { + builder + sig jose.Signer +} + +type encryptedBuilder struct { + builder + enc jose.Encrypter +} + +type nestedBuilder struct { + builder + sig jose.Signer + enc jose.Encrypter +} + +// Signed creates builder for signed tokens. +func Signed(sig jose.Signer) Builder { + return &signedBuilder{ + sig: sig, + } +} + +// Encrypted creates builder for encrypted tokens. +func Encrypted(enc jose.Encrypter) Builder { + return &encryptedBuilder{ + enc: enc, + } +} + +// SignedAndEncrypted creates builder for signed-then-encrypted tokens. +// ErrInvalidContentType will be returned if encrypter doesn't have JWT content type. +func SignedAndEncrypted(sig jose.Signer, enc jose.Encrypter) NestedBuilder { + if contentType, _ := enc.Options().ExtraHeaders[jose.HeaderContentType].(jose.ContentType); contentType != "JWT" { + return &nestedBuilder{ + builder: builder{ + err: ErrInvalidContentType, + }, + } + } + return &nestedBuilder{ + sig: sig, + enc: enc, + } +} + +func (b builder) claims(i interface{}) builder { + if b.err != nil { + return b + } + + m, ok := i.(map[string]interface{}) + switch { + case ok: + return b.merge(m) + case reflect.Indirect(reflect.ValueOf(i)).Kind() == reflect.Struct: + m, err := normalize(i) + if err != nil { + return builder{ + err: err, + } + } + return b.merge(m) + default: + return builder{ + err: ErrInvalidClaims, + } + } +} + +func normalize(i interface{}) (map[string]interface{}, error) { + m := make(map[string]interface{}) + + raw, err := json.Marshal(i) + if err != nil { + return nil, err + } + + d := json.NewDecoder(bytes.NewReader(raw)) + d.SetNumberType(json.UnmarshalJSONNumber) + + if err := d.Decode(&m); err != nil { + return nil, err + } + + return m, nil +} + +func (b *builder) merge(m map[string]interface{}) builder { + p := make(map[string]interface{}) + for k, v := range b.payload { + p[k] = v + } + for k, v := range m { + p[k] = v + } + + return builder{ + payload: p, + } +} + +func (b *builder) token(p func(interface{}) ([]byte, error), h []jose.Header) (*JSONWebToken, error) { + return &JSONWebToken{ + payload: p, + Headers: h, + }, nil +} + +func (b *signedBuilder) Claims(i interface{}) Builder { + return &signedBuilder{ + builder: b.builder.claims(i), + sig: b.sig, + } +} + +func (b *signedBuilder) Token() (*JSONWebToken, error) { + sig, err := b.sign() + if err != nil { + return nil, err + } + + h := make([]jose.Header, len(sig.Signatures)) + for i, v := range sig.Signatures { + h[i] = v.Header + } + + return b.builder.token(sig.Verify, h) +} + +func (b *signedBuilder) CompactSerialize() (string, error) { + sig, err := b.sign() + if err != nil { + return "", err + } + + return sig.CompactSerialize() +} + +func (b *signedBuilder) FullSerialize() (string, error) { + sig, err := b.sign() + if err != nil { + return "", err + } + + return sig.FullSerialize(), nil +} + +func (b *signedBuilder) sign() (*jose.JSONWebSignature, error) { + if b.err != nil { + return nil, b.err + } + + p, err := json.Marshal(b.payload) + if err != nil { + return nil, err + } + + return b.sig.Sign(p) +} + +func (b *encryptedBuilder) Claims(i interface{}) Builder { + return &encryptedBuilder{ + builder: b.builder.claims(i), + enc: b.enc, + } +} + +func (b *encryptedBuilder) CompactSerialize() (string, error) { + enc, err := b.encrypt() + if err != nil { + return "", err + } + + return enc.CompactSerialize() +} + +func (b *encryptedBuilder) FullSerialize() (string, error) { + enc, err := b.encrypt() + if err != nil { + return "", err + } + + return enc.FullSerialize(), nil +} + +func (b *encryptedBuilder) Token() (*JSONWebToken, error) { + enc, err := b.encrypt() + if err != nil { + return nil, err + } + + return b.builder.token(enc.Decrypt, []jose.Header{enc.Header}) +} + +func (b *encryptedBuilder) encrypt() (*jose.JSONWebEncryption, error) { + if b.err != nil { + return nil, b.err + } + + p, err := json.Marshal(b.payload) + if err != nil { + return nil, err + } + + return b.enc.Encrypt(p) +} + +func (b *nestedBuilder) Claims(i interface{}) NestedBuilder { + return &nestedBuilder{ + builder: b.builder.claims(i), + sig: b.sig, + enc: b.enc, + } +} + +func (b *nestedBuilder) Token() (*NestedJSONWebToken, error) { + enc, err := b.signAndEncrypt() + if err != nil { + return nil, err + } + + return &NestedJSONWebToken{ + enc: enc, + Headers: []jose.Header{enc.Header}, + }, nil +} + +func (b *nestedBuilder) CompactSerialize() (string, error) { + enc, err := b.signAndEncrypt() + if err != nil { + return "", err + } + + return enc.CompactSerialize() +} + +func (b *nestedBuilder) FullSerialize() (string, error) { + enc, err := b.signAndEncrypt() + if err != nil { + return "", err + } + + return enc.FullSerialize(), nil +} + +func (b *nestedBuilder) signAndEncrypt() (*jose.JSONWebEncryption, error) { + if b.err != nil { + return nil, b.err + } + + p, err := json.Marshal(b.payload) + if err != nil { + return nil, err + } + + sig, err := b.sig.Sign(p) + if err != nil { + return nil, err + } + + p2, err := sig.CompactSerialize() + if err != nil { + return nil, err + } + + return b.enc.Encrypt([]byte(p2)) +} diff --git a/vendor/github.com/go-jose/go-jose/v3/jwt/claims.go b/vendor/github.com/go-jose/go-jose/v3/jwt/claims.go new file mode 100644 index 0000000000..286be1d2fe --- /dev/null +++ b/vendor/github.com/go-jose/go-jose/v3/jwt/claims.go @@ -0,0 +1,130 @@ +/*- + * Copyright 2016 Zbigniew Mandziejewicz + * Copyright 2016 Square, Inc. + * + * 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. + */ + +package jwt + +import ( + "strconv" + "time" + + "github.com/go-jose/go-jose/v3/json" +) + +// Claims represents public claim values (as specified in RFC 7519). +type Claims struct { + Issuer string `json:"iss,omitempty"` + Subject string `json:"sub,omitempty"` + Audience Audience `json:"aud,omitempty"` + Expiry *NumericDate `json:"exp,omitempty"` + NotBefore *NumericDate `json:"nbf,omitempty"` + IssuedAt *NumericDate `json:"iat,omitempty"` + ID string `json:"jti,omitempty"` +} + +// NumericDate represents date and time as the number of seconds since the +// epoch, ignoring leap seconds. Non-integer values can be represented +// in the serialized format, but we round to the nearest second. +// See RFC7519 Section 2: https://tools.ietf.org/html/rfc7519#section-2 +type NumericDate int64 + +// NewNumericDate constructs NumericDate from time.Time value. +func NewNumericDate(t time.Time) *NumericDate { + if t.IsZero() { + return nil + } + + // While RFC 7519 technically states that NumericDate values may be + // non-integer values, we don't bother serializing timestamps in + // claims with sub-second accurancy and just round to the nearest + // second instead. Not convined sub-second accuracy is useful here. + out := NumericDate(t.Unix()) + return &out +} + +// MarshalJSON serializes the given NumericDate into its JSON representation. +func (n NumericDate) MarshalJSON() ([]byte, error) { + return []byte(strconv.FormatInt(int64(n), 10)), nil +} + +// UnmarshalJSON reads a date from its JSON representation. +func (n *NumericDate) UnmarshalJSON(b []byte) error { + s := string(b) + + f, err := strconv.ParseFloat(s, 64) + if err != nil { + return ErrUnmarshalNumericDate + } + + *n = NumericDate(f) + return nil +} + +// Time returns time.Time representation of NumericDate. +func (n *NumericDate) Time() time.Time { + if n == nil { + return time.Time{} + } + return time.Unix(int64(*n), 0) +} + +// Audience represents the recipients that the token is intended for. +type Audience []string + +// UnmarshalJSON reads an audience from its JSON representation. +func (s *Audience) UnmarshalJSON(b []byte) error { + var v interface{} + if err := json.Unmarshal(b, &v); err != nil { + return err + } + + switch v := v.(type) { + case string: + *s = []string{v} + case []interface{}: + a := make([]string, len(v)) + for i, e := range v { + s, ok := e.(string) + if !ok { + return ErrUnmarshalAudience + } + a[i] = s + } + *s = a + default: + return ErrUnmarshalAudience + } + + return nil +} + +// MarshalJSON converts audience to json representation. +func (s Audience) MarshalJSON() ([]byte, error) { + if len(s) == 1 { + return json.Marshal(s[0]) + } + return json.Marshal([]string(s)) +} + +//Contains checks whether a given string is included in the Audience +func (s Audience) Contains(v string) bool { + for _, a := range s { + if a == v { + return true + } + } + return false +} diff --git a/vendor/github.com/go-jose/go-jose/v3/jwt/doc.go b/vendor/github.com/go-jose/go-jose/v3/jwt/doc.go new file mode 100644 index 0000000000..4cf97b54e7 --- /dev/null +++ b/vendor/github.com/go-jose/go-jose/v3/jwt/doc.go @@ -0,0 +1,22 @@ +/*- + * Copyright 2017 Square Inc. + * + * 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. + */ + +/* + +Package jwt provides an implementation of the JSON Web Token standard. + +*/ +package jwt diff --git a/vendor/github.com/go-jose/go-jose/v3/jwt/errors.go b/vendor/github.com/go-jose/go-jose/v3/jwt/errors.go new file mode 100644 index 0000000000..27388e5449 --- /dev/null +++ b/vendor/github.com/go-jose/go-jose/v3/jwt/errors.go @@ -0,0 +1,53 @@ +/*- + * Copyright 2016 Zbigniew Mandziejewicz + * Copyright 2016 Square, Inc. + * + * 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. + */ + +package jwt + +import "errors" + +// ErrUnmarshalAudience indicates that aud claim could not be unmarshalled. +var ErrUnmarshalAudience = errors.New("go-jose/go-jose/jwt: expected string or array value to unmarshal to Audience") + +// ErrUnmarshalNumericDate indicates that JWT NumericDate could not be unmarshalled. +var ErrUnmarshalNumericDate = errors.New("go-jose/go-jose/jwt: expected number value to unmarshal NumericDate") + +// ErrInvalidClaims indicates that given claims have invalid type. +var ErrInvalidClaims = errors.New("go-jose/go-jose/jwt: expected claims to be value convertible into JSON object") + +// ErrInvalidIssuer indicates invalid iss claim. +var ErrInvalidIssuer = errors.New("go-jose/go-jose/jwt: validation failed, invalid issuer claim (iss)") + +// ErrInvalidSubject indicates invalid sub claim. +var ErrInvalidSubject = errors.New("go-jose/go-jose/jwt: validation failed, invalid subject claim (sub)") + +// ErrInvalidAudience indicated invalid aud claim. +var ErrInvalidAudience = errors.New("go-jose/go-jose/jwt: validation failed, invalid audience claim (aud)") + +// ErrInvalidID indicates invalid jti claim. +var ErrInvalidID = errors.New("go-jose/go-jose/jwt: validation failed, invalid ID claim (jti)") + +// ErrNotValidYet indicates that token is used before time indicated in nbf claim. +var ErrNotValidYet = errors.New("go-jose/go-jose/jwt: validation failed, token not valid yet (nbf)") + +// ErrExpired indicates that token is used after expiry time indicated in exp claim. +var ErrExpired = errors.New("go-jose/go-jose/jwt: validation failed, token is expired (exp)") + +// ErrIssuedInTheFuture indicates that the iat field is in the future. +var ErrIssuedInTheFuture = errors.New("go-jose/go-jose/jwt: validation field, token issued in the future (iat)") + +// ErrInvalidContentType indicates that token requires JWT cty header. +var ErrInvalidContentType = errors.New("go-jose/go-jose/jwt: expected content type to be JWT (cty header)") diff --git a/vendor/github.com/go-jose/go-jose/v3/jwt/jwt.go b/vendor/github.com/go-jose/go-jose/v3/jwt/jwt.go new file mode 100644 index 0000000000..8553fc50b0 --- /dev/null +++ b/vendor/github.com/go-jose/go-jose/v3/jwt/jwt.go @@ -0,0 +1,133 @@ +/*- + * Copyright 2016 Zbigniew Mandziejewicz + * Copyright 2016 Square, Inc. + * + * 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. + */ + +package jwt + +import ( + "fmt" + "strings" + + jose "github.com/go-jose/go-jose/v3" + "github.com/go-jose/go-jose/v3/json" +) + +// JSONWebToken represents a JSON Web Token (as specified in RFC7519). +type JSONWebToken struct { + payload func(k interface{}) ([]byte, error) + unverifiedPayload func() []byte + Headers []jose.Header +} + +type NestedJSONWebToken struct { + enc *jose.JSONWebEncryption + Headers []jose.Header +} + +// Claims deserializes a JSONWebToken into dest using the provided key. +func (t *JSONWebToken) Claims(key interface{}, dest ...interface{}) error { + b, err := t.payload(key) + if err != nil { + return err + } + + for _, d := range dest { + if err := json.Unmarshal(b, d); err != nil { + return err + } + } + + return nil +} + +// UnsafeClaimsWithoutVerification deserializes the claims of a +// JSONWebToken into the dests. For signed JWTs, the claims are not +// verified. This function won't work for encrypted JWTs. +func (t *JSONWebToken) UnsafeClaimsWithoutVerification(dest ...interface{}) error { + if t.unverifiedPayload == nil { + return fmt.Errorf("go-jose/go-jose: Cannot get unverified claims") + } + claims := t.unverifiedPayload() + for _, d := range dest { + if err := json.Unmarshal(claims, d); err != nil { + return err + } + } + return nil +} + +func (t *NestedJSONWebToken) Decrypt(decryptionKey interface{}) (*JSONWebToken, error) { + b, err := t.enc.Decrypt(decryptionKey) + if err != nil { + return nil, err + } + + sig, err := ParseSigned(string(b)) + if err != nil { + return nil, err + } + + return sig, nil +} + +// ParseSigned parses token from JWS form. +func ParseSigned(s string) (*JSONWebToken, error) { + sig, err := jose.ParseSigned(s) + if err != nil { + return nil, err + } + headers := make([]jose.Header, len(sig.Signatures)) + for i, signature := range sig.Signatures { + headers[i] = signature.Header + } + + return &JSONWebToken{ + payload: sig.Verify, + unverifiedPayload: sig.UnsafePayloadWithoutVerification, + Headers: headers, + }, nil +} + +// ParseEncrypted parses token from JWE form. +func ParseEncrypted(s string) (*JSONWebToken, error) { + enc, err := jose.ParseEncrypted(s) + if err != nil { + return nil, err + } + + return &JSONWebToken{ + payload: enc.Decrypt, + Headers: []jose.Header{enc.Header}, + }, nil +} + +// ParseSignedAndEncrypted parses signed-then-encrypted token from JWE form. +func ParseSignedAndEncrypted(s string) (*NestedJSONWebToken, error) { + enc, err := jose.ParseEncrypted(s) + if err != nil { + return nil, err + } + + contentType, _ := enc.Header.ExtraHeaders[jose.HeaderContentType].(string) + if strings.ToUpper(contentType) != "JWT" { + return nil, ErrInvalidContentType + } + + return &NestedJSONWebToken{ + enc: enc, + Headers: []jose.Header{enc.Header}, + }, nil +} diff --git a/vendor/github.com/go-jose/go-jose/v3/jwt/validation.go b/vendor/github.com/go-jose/go-jose/v3/jwt/validation.go new file mode 100644 index 0000000000..09d8541f4c --- /dev/null +++ b/vendor/github.com/go-jose/go-jose/v3/jwt/validation.go @@ -0,0 +1,120 @@ +/*- + * Copyright 2016 Zbigniew Mandziejewicz + * Copyright 2016 Square, Inc. + * + * 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. + */ + +package jwt + +import "time" + +const ( + // DefaultLeeway defines the default leeway for matching NotBefore/Expiry claims. + DefaultLeeway = 1.0 * time.Minute +) + +// Expected defines values used for protected claims validation. +// If field has zero value then validation is skipped, with the exception of +// Time, where the zero value means "now." To skip validating them, set the +// corresponding field in the Claims struct to nil. +type Expected struct { + // Issuer matches the "iss" claim exactly. + Issuer string + // Subject matches the "sub" claim exactly. + Subject string + // Audience matches the values in "aud" claim, regardless of their order. + Audience Audience + // ID matches the "jti" claim exactly. + ID string + // Time matches the "exp", "nbf" and "iat" claims with leeway. + Time time.Time +} + +// WithTime copies expectations with new time. +func (e Expected) WithTime(t time.Time) Expected { + e.Time = t + return e +} + +// Validate checks claims in a token against expected values. +// A default leeway value of one minute is used to compare time values. +// +// The default leeway will cause the token to be deemed valid until one +// minute after the expiration time. If you're a server application that +// wants to give an extra minute to client tokens, use this +// function. If you're a client application wondering if the server +// will accept your token, use ValidateWithLeeway with a leeway <=0, +// otherwise this function might make you think a token is valid when +// it is not. +func (c Claims) Validate(e Expected) error { + return c.ValidateWithLeeway(e, DefaultLeeway) +} + +// ValidateWithLeeway checks claims in a token against expected values. A +// custom leeway may be specified for comparing time values. You may pass a +// zero value to check time values with no leeway, but you should note that +// numeric date values are rounded to the nearest second and sub-second +// precision is not supported. +// +// The leeway gives some extra time to the token from the server's +// point of view. That is, if the token is expired, ValidateWithLeeway +// will still accept the token for 'leeway' amount of time. This fails +// if you're using this function to check if a server will accept your +// token, because it will think the token is valid even after it +// expires. So if you're a client validating if the token is valid to +// be submitted to a server, use leeway <=0, if you're a server +// validation a token, use leeway >=0. +func (c Claims) ValidateWithLeeway(e Expected, leeway time.Duration) error { + if e.Issuer != "" && e.Issuer != c.Issuer { + return ErrInvalidIssuer + } + + if e.Subject != "" && e.Subject != c.Subject { + return ErrInvalidSubject + } + + if e.ID != "" && e.ID != c.ID { + return ErrInvalidID + } + + if len(e.Audience) != 0 { + for _, v := range e.Audience { + if !c.Audience.Contains(v) { + return ErrInvalidAudience + } + } + } + + // validate using the e.Time, or time.Now if not provided + validationTime := e.Time + if validationTime.IsZero() { + validationTime = time.Now() + } + + if c.NotBefore != nil && validationTime.Add(leeway).Before(c.NotBefore.Time()) { + return ErrNotValidYet + } + + if c.Expiry != nil && validationTime.Add(-leeway).After(c.Expiry.Time()) { + return ErrExpired + } + + // IssuedAt is optional but cannot be in the future. This is not required by the RFC, but + // something is misconfigured if this happens and we should not trust it. + if c.IssuedAt != nil && validationTime.Add(leeway).Before(c.IssuedAt.Time()) { + return ErrIssuedInTheFuture + } + + return nil +} diff --git a/vendor/knative.dev/eventing/pkg/apis/sources/v1/sinkbinding_lifecycle.go b/vendor/knative.dev/eventing/pkg/apis/sources/v1/sinkbinding_lifecycle.go index 5a8d100355..45fe072553 100644 --- a/vendor/knative.dev/eventing/pkg/apis/sources/v1/sinkbinding_lifecycle.go +++ b/vendor/knative.dev/eventing/pkg/apis/sources/v1/sinkbinding_lifecycle.go @@ -33,9 +33,14 @@ import ( "knative.dev/pkg/tracker" ) +const ( + oidcTokenVolumeName = "oidc-token" +) + var sbCondSet = apis.NewLivingConditionSet( SinkBindingConditionSinkProvided, SinkBindingConditionOIDCIdentityCreated, + SinkBindingConditionOIDCTokenSecretCreated, ) // GetConditionSet retrieves the condition set for this resource. Implements the KRShaped interface. @@ -90,6 +95,7 @@ func (sbs *SinkBindingStatus) MarkSink(addr *duckv1.Addressable) { if addr != nil { sbs.SinkURI = addr.URL sbs.SinkCACerts = addr.CACerts + sbs.SinkAudience = addr.Audience sbCondSet.Manage(sbs).MarkTrue(SinkBindingConditionSinkProvided) } else { sbCondSet.Manage(sbs).MarkFalse(SinkBindingConditionSinkProvided, "SinkEmpty", "Sink has resolved to empty.%s", "") @@ -112,6 +118,22 @@ func (sbs *SinkBindingStatus) MarkOIDCIdentityCreatedUnknown(reason, messageForm sbCondSet.Manage(sbs).MarkUnknown(SinkBindingConditionOIDCIdentityCreated, reason, messageFormat, messageA...) } +func (sbs *SinkBindingStatus) MarkOIDCTokenSecretCreatedSuccceeded() { + sbCondSet.Manage(sbs).MarkTrue(SinkBindingConditionOIDCTokenSecretCreated) +} + +func (sbs *SinkBindingStatus) MarkOIDCTokenSecretCreatedSuccceededWithReason(reason, messageFormat string, messageA ...interface{}) { + sbCondSet.Manage(sbs).MarkTrueWithReason(SinkBindingConditionOIDCTokenSecretCreated, reason, messageFormat, messageA...) +} + +func (sbs *SinkBindingStatus) MarkOIDCTokenSecretCreatedFailed(reason, messageFormat string, messageA ...interface{}) { + sbCondSet.Manage(sbs).MarkFalse(SinkBindingConditionOIDCTokenSecretCreated, reason, messageFormat, messageA...) +} + +func (sbs *SinkBindingStatus) MarkOIDCTokenSecretCreatedUnknown(reason, messageFormat string, messageA ...interface{}) { + sbCondSet.Manage(sbs).MarkUnknown(SinkBindingConditionOIDCTokenSecretCreated, reason, messageFormat, messageA...) +} + // Do implements psbinding.Bindable func (sb *SinkBinding) Do(ctx context.Context, ps *duckv1.WithPod) { // First undo so that we can just unconditionally append below. @@ -171,6 +193,38 @@ func (sb *SinkBinding) Do(ctx context.Context, ps *duckv1.WithPod) { Value: ceOverrides, }) } + + if sb.Status.OIDCTokenSecretName != nil { + ps.Spec.Template.Spec.Volumes = append(ps.Spec.Template.Spec.Volumes, corev1.Volume{ + Name: oidcTokenVolumeName, + VolumeSource: corev1.VolumeSource{ + Projected: &corev1.ProjectedVolumeSource{ + Sources: []corev1.VolumeProjection{ + { + Secret: &corev1.SecretProjection{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: *sb.Status.OIDCTokenSecretName, + }, + }, + }, + }, + }, + }, + }) + + for i := range spec.Containers { + spec.Containers[i].VolumeMounts = append(spec.Containers[i].VolumeMounts, corev1.VolumeMount{ + Name: oidcTokenVolumeName, + MountPath: "/oidc", + }) + } + for i := range spec.InitContainers { + spec.InitContainers[i].VolumeMounts = append(spec.InitContainers[i].VolumeMounts, corev1.VolumeMount{ + Name: oidcTokenVolumeName, + MountPath: "/oidc", + }) + } + } } func (sb *SinkBinding) Undo(ctx context.Context, ps *duckv1.WithPod) { @@ -189,6 +243,17 @@ func (sb *SinkBinding) Undo(ctx context.Context, ps *duckv1.WithPod) { } } spec.InitContainers[i].Env = env + + if len(spec.InitContainers[i].VolumeMounts) > 0 { + volumeMounts := make([]corev1.VolumeMount, 0, len(spec.InitContainers[i].VolumeMounts)) + for j, vol := range c.VolumeMounts { + if vol.Name == oidcTokenVolumeName { + continue + } + volumeMounts = append(volumeMounts, spec.InitContainers[i].VolumeMounts[j]) + } + spec.InitContainers[i].VolumeMounts = volumeMounts + } } for i, c := range spec.Containers { if len(c.Env) == 0 { @@ -204,5 +269,27 @@ func (sb *SinkBinding) Undo(ctx context.Context, ps *duckv1.WithPod) { } } spec.Containers[i].Env = env + + if len(spec.Containers[i].VolumeMounts) > 0 { + volumeMounts := make([]corev1.VolumeMount, 0, len(spec.Containers[i].VolumeMounts)) + for j, vol := range c.VolumeMounts { + if vol.Name == oidcTokenVolumeName { + continue + } + volumeMounts = append(volumeMounts, spec.Containers[i].VolumeMounts[j]) + } + spec.Containers[i].VolumeMounts = volumeMounts + } + } + + if len(spec.Volumes) > 0 { + volumes := make([]corev1.Volume, 0, len(spec.Volumes)) + for i, vol := range spec.Volumes { + if vol.Name == oidcTokenVolumeName { + continue + } + volumes = append(volumes, spec.Volumes[i]) + } + ps.Spec.Template.Spec.Volumes = volumes } } diff --git a/vendor/knative.dev/eventing/pkg/apis/sources/v1/sinkbinding_types.go b/vendor/knative.dev/eventing/pkg/apis/sources/v1/sinkbinding_types.go index 512d687d31..e8afbc1451 100644 --- a/vendor/knative.dev/eventing/pkg/apis/sources/v1/sinkbinding_types.go +++ b/vendor/knative.dev/eventing/pkg/apis/sources/v1/sinkbinding_types.go @@ -81,6 +81,10 @@ const ( // SinkBindingConditionOIDCIdentityCreated is configured to indicate whether // the OIDC identity has been created for the sink. SinkBindingConditionOIDCIdentityCreated apis.ConditionType = "OIDCIdentityCreated" + + // SinkBindingConditionOIDCTokenSecretCreated is configured to indicate whether + // the secret containing the OIDC token has been created for the sink. + SinkBindingConditionOIDCTokenSecretCreated apis.ConditionType = "OIDCTokenSecretCreated" ) // SinkBindingStatus communicates the observed state of the SinkBinding (from the controller). @@ -93,6 +97,10 @@ type SinkBindingStatus struct { // * SinkURI - the current active sink URI that has been configured for the // Source. duckv1.SourceStatus `json:",inline"` + + // OIDCTokenSecretName is the name of the secret containing the token for + // this SinkBindings OIDC authentication + OIDCTokenSecretName *string `json:"oidcTokenSecretName,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/vendor/knative.dev/eventing/pkg/apis/sources/v1/zz_generated.deepcopy.go b/vendor/knative.dev/eventing/pkg/apis/sources/v1/zz_generated.deepcopy.go index 551322eab4..6d175e3c96 100644 --- a/vendor/knative.dev/eventing/pkg/apis/sources/v1/zz_generated.deepcopy.go +++ b/vendor/knative.dev/eventing/pkg/apis/sources/v1/zz_generated.deepcopy.go @@ -454,6 +454,11 @@ func (in *SinkBindingSpec) DeepCopy() *SinkBindingSpec { func (in *SinkBindingStatus) DeepCopyInto(out *SinkBindingStatus) { *out = *in in.SourceStatus.DeepCopyInto(&out.SourceStatus) + if in.OIDCTokenSecretName != nil { + in, out := &in.OIDCTokenSecretName, &out.OIDCTokenSecretName + *out = new(string) + **out = **in + } return } diff --git a/vendor/knative.dev/eventing/pkg/auth/token_provider.go b/vendor/knative.dev/eventing/pkg/auth/token_provider.go index d35a6e29f7..6fb115f9d4 100644 --- a/vendor/knative.dev/eventing/pkg/auth/token_provider.go +++ b/vendor/knative.dev/eventing/pkg/auth/token_provider.go @@ -27,11 +27,13 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/cache" "k8s.io/client-go/kubernetes" + "k8s.io/utils/pointer" kubeclient "knative.dev/pkg/client/injection/kube/client" "knative.dev/pkg/logging" ) const ( + TokenExpirationTime = time.Hour expirationBufferTime = 5 * time.Minute ) @@ -58,9 +60,16 @@ func (c *OIDCTokenProvider) GetJWT(serviceAccount types.NamespacedName, audience } // if not found in cache: request new token + return c.GetNewJWT(serviceAccount, audience) +} + +// GetNewJWT returns a new JWT from the given service account for the given audience without using the token cache. +func (c *OIDCTokenProvider) GetNewJWT(serviceAccount types.NamespacedName, audience string) (string, error) { + // request new token tokenRequest := authv1.TokenRequest{ Spec: authv1.TokenRequestSpec{ - Audiences: []string{audience}, + Audiences: []string{audience}, + ExpirationSeconds: pointer.Int64(int64(TokenExpirationTime.Seconds())), }, } diff --git a/vendor/knative.dev/eventing/pkg/auth/utils.go b/vendor/knative.dev/eventing/pkg/auth/utils.go index 0f52c34364..81e7145a8f 100644 --- a/vendor/knative.dev/eventing/pkg/auth/utils.go +++ b/vendor/knative.dev/eventing/pkg/auth/utils.go @@ -20,6 +20,9 @@ import ( "fmt" "net/http" "strings" + "time" + + "github.com/go-jose/go-jose/v3/jwt" ) const ( @@ -40,3 +43,22 @@ func GetJWTFromHeader(header http.Header) string { func SetAuthHeader(jwt string, header http.Header) { header.Set(AuthHeaderKey, fmt.Sprintf("Bearer %s", jwt)) } + +// GetJWTExpiry returns the expiry time of the token in UTC +func GetJWTExpiry(token string) (time.Time, error) { + t, err := jwt.ParseSigned(token) + if err != nil { + return time.Time{}, err + } + + var claims jwt.Claims + if err := t.UnsafeClaimsWithoutVerification(&claims); err != nil { + return time.Time{}, err + } + + if claims.Expiry == nil { + return time.Time{}, fmt.Errorf("no expiry set in JWT") + } + + return claims.Expiry.Time(), nil +} diff --git a/vendor/knative.dev/reconciler-test/pkg/environment/flags.go b/vendor/knative.dev/reconciler-test/pkg/environment/flags.go index 59efc52e9a..80fda98629 100644 --- a/vendor/knative.dev/reconciler-test/pkg/environment/flags.go +++ b/vendor/knative.dev/reconciler-test/pkg/environment/flags.go @@ -21,8 +21,10 @@ import ( "fmt" "strconv" "strings" + "time" "knative.dev/reconciler-test/pkg/feature" + "knative.dev/reconciler-test/pkg/state" ) var ( @@ -34,6 +36,9 @@ var ( ipFilePath = new(string) teardownOnFail = new(bool) + + pollTimeout = new(time.Duration) + pollInterval = new(time.Duration) ) // InitFlags registers the requirement and state filter flags supported by the @@ -62,7 +67,8 @@ func InitFlags(fs *flag.FlagSet) { fs.StringVar(ipFilePath, "images.producer.file", "", "file path for file-based image producer") fs.StringVar(testNamespace, "environment.namespace", "", "Test namespace") - + fs.DurationVar(pollTimeout, "poll.timeout", state.DefaultPollTimeout, "Poll timeout") + fs.DurationVar(pollInterval, "poll.interval", state.DefaultPollInterval, "Poll interval") fs.BoolVar(teardownOnFail, "teardown.on.fail", false, "Set this flag to do teardown even if test fails.") } diff --git a/vendor/knative.dev/reconciler-test/pkg/environment/magic.go b/vendor/knative.dev/reconciler-test/pkg/environment/magic.go index a1808d8560..a0400bcb22 100644 --- a/vendor/knative.dev/reconciler-test/pkg/environment/magic.go +++ b/vendor/knative.dev/reconciler-test/pkg/environment/magic.go @@ -198,6 +198,7 @@ func (mr *MagicGlobalEnvironment) Environment(opts ...EnvOpts) (context.Context, } ctx := ContextWith(mr.c, env) + ctx = ContextWithPollTimings(ctx, *pollInterval, *pollTimeout) for _, opt := range opts { if nctx, err := opt(ctx, env); err != nil { diff --git a/vendor/knative.dev/reconciler-test/pkg/state/timings.go b/vendor/knative.dev/reconciler-test/pkg/state/timings.go index 88a3dc1e6d..5a79de9e10 100644 --- a/vendor/knative.dev/reconciler-test/pkg/state/timings.go +++ b/vendor/knative.dev/reconciler-test/pkg/state/timings.go @@ -48,5 +48,5 @@ func PollTimingsFromContext(ctx context.Context) (time.Duration, time.Duration) if t, ok := ctx.Value(timingsKey{}).(timingsType); ok { return t.interval, t.timeout } - return DefaultPollInterval, DefaultPollTimeout + panic("no poll timings found in context") } diff --git a/vendor/modules.txt b/vendor/modules.txt index eaa7aa021c..fba2ea1444 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -97,6 +97,7 @@ github.com/fsnotify/fsnotify github.com/go-jose/go-jose/v3 github.com/go-jose/go-jose/v3/cipher github.com/go-jose/go-jose/v3/json +github.com/go-jose/go-jose/v3/jwt # github.com/go-kit/log v0.2.1 ## explicit; go 1.17 github.com/go-kit/log @@ -1085,7 +1086,7 @@ k8s.io/utils/pointer k8s.io/utils/ptr k8s.io/utils/strings/slices k8s.io/utils/trace -# knative.dev/eventing v0.39.1-0.20231120114720-140482e3189a +# knative.dev/eventing v0.39.1-0.20231120220132-67f382d60b43 ## explicit; go 1.19 knative.dev/eventing/cmd/heartbeats knative.dev/eventing/pkg/adapter/v2 @@ -1199,7 +1200,7 @@ knative.dev/eventing/test/test_images/print # knative.dev/hack v0.0.0-20231109190034-5deaddeb51a7 ## explicit; go 1.18 knative.dev/hack -# knative.dev/pkg v0.0.0-20231115001034-97c7258e3a98 +# knative.dev/pkg v0.0.0-20231120182734-703c8b0d5c34 ## explicit; go 1.18 knative.dev/pkg/apis knative.dev/pkg/apis/duck @@ -1304,7 +1305,7 @@ knative.dev/pkg/webhook/json knative.dev/pkg/webhook/resourcesemantics knative.dev/pkg/webhook/resourcesemantics/defaulting knative.dev/pkg/webhook/resourcesemantics/validation -# knative.dev/reconciler-test v0.0.0-20231115072946-99723665c94d +# knative.dev/reconciler-test v0.0.0-20231121121446-f747d069af11 ## explicit; go 1.20 knative.dev/reconciler-test/cmd/eventshub knative.dev/reconciler-test/pkg/environment