mirror of https://github.com/k3s-io/k3s
create audience unaware authenticator wrappers
parent
817cf70191
commit
c704d70d49
|
@ -5,6 +5,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
|||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"audagnostic.go",
|
||||
"audiences.go",
|
||||
"interfaces.go",
|
||||
],
|
||||
|
@ -28,6 +29,13 @@ filegroup(
|
|||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["audiences_test.go"],
|
||||
srcs = [
|
||||
"audagnostic_test.go",
|
||||
"audiences_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes 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.
|
||||
*/
|
||||
|
||||
package authenticator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func authenticate(ctx context.Context, implicitAuds Audiences, authenticate func() (*Response, bool, error)) (*Response, bool, error) {
|
||||
targetAuds, ok := AudiencesFrom(ctx)
|
||||
// We can remove this once api audiences is never empty. That will probably
|
||||
// be N releases after TokenRequest is GA.
|
||||
if !ok {
|
||||
return authenticate()
|
||||
}
|
||||
auds := implicitAuds.Intersect(targetAuds)
|
||||
if len(auds) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
resp, ok, err := authenticate()
|
||||
if err != nil || !ok {
|
||||
return nil, false, err
|
||||
}
|
||||
if len(resp.Audiences) > 0 {
|
||||
// maybe the authenticator was audience aware after all.
|
||||
return nil, false, fmt.Errorf("audience agnostic authenticator wrapped an authenticator that returned audiences: %q", resp.Audiences)
|
||||
}
|
||||
resp.Audiences = auds
|
||||
return resp, true, nil
|
||||
}
|
||||
|
||||
type audAgnosticRequestAuthenticator struct {
|
||||
implicit Audiences
|
||||
delegate Request
|
||||
}
|
||||
|
||||
var _ = Request(&audAgnosticRequestAuthenticator{})
|
||||
|
||||
func (a *audAgnosticRequestAuthenticator) AuthenticateRequest(req *http.Request) (*Response, bool, error) {
|
||||
return authenticate(req.Context(), a.implicit, func() (*Response, bool, error) {
|
||||
return a.delegate.AuthenticateRequest(req)
|
||||
})
|
||||
}
|
||||
|
||||
// WrapAudienceAgnosticRequest wraps an audience agnostic request authenticator
|
||||
// to restrict its accepted audiences to a set of implicit audiences.
|
||||
func WrapAudienceAgnosticRequest(implicit Audiences, delegate Request) Request {
|
||||
return &audAgnosticRequestAuthenticator{
|
||||
implicit: implicit,
|
||||
delegate: delegate,
|
||||
}
|
||||
}
|
||||
|
||||
type audAgnosticTokenAuthenticator struct {
|
||||
implicit Audiences
|
||||
delegate Token
|
||||
}
|
||||
|
||||
var _ = Token(&audAgnosticTokenAuthenticator{})
|
||||
|
||||
func (a *audAgnosticTokenAuthenticator) AuthenticateToken(ctx context.Context, tok string) (*Response, bool, error) {
|
||||
return authenticate(ctx, a.implicit, func() (*Response, bool, error) {
|
||||
return a.delegate.AuthenticateToken(ctx, tok)
|
||||
})
|
||||
}
|
||||
|
||||
// WrapAudienceAgnosticToken wraps an audience agnostic token authenticator to
|
||||
// restrict its accepted audiences to a set of implicit audiences.
|
||||
func WrapAudienceAgnosticToken(implicit Audiences, delegate Token) Token {
|
||||
return &audAgnosticTokenAuthenticator{
|
||||
implicit: implicit,
|
||||
delegate: delegate,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes 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.
|
||||
*/
|
||||
|
||||
package authenticator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
)
|
||||
|
||||
func TestAuthenticate(t *testing.T) {
|
||||
type treq struct {
|
||||
resp *Response
|
||||
authenticated bool
|
||||
err error
|
||||
|
||||
wantResp *Response
|
||||
wantAuthenticated bool
|
||||
wantErr bool
|
||||
}
|
||||
type taudcfg struct {
|
||||
auds Audiences
|
||||
implicitAuds Audiences
|
||||
}
|
||||
cs := []struct {
|
||||
name string
|
||||
|
||||
taudcfgs []taudcfg
|
||||
treqs []treq
|
||||
}{
|
||||
{
|
||||
name: "good audience",
|
||||
|
||||
taudcfgs: []taudcfg{
|
||||
{
|
||||
implicitAuds: Audiences{"api"},
|
||||
auds: Audiences{"api"},
|
||||
},
|
||||
{
|
||||
implicitAuds: Audiences{"api", "other"},
|
||||
auds: Audiences{"api"},
|
||||
},
|
||||
{
|
||||
implicitAuds: Audiences{"api"},
|
||||
auds: Audiences{"api", "other"},
|
||||
},
|
||||
{
|
||||
implicitAuds: Audiences{"api", "other"},
|
||||
auds: Audiences{"api", "other_other"},
|
||||
},
|
||||
},
|
||||
|
||||
treqs: []treq{
|
||||
{
|
||||
resp: &Response{
|
||||
User: &user.DefaultInfo{
|
||||
Name: "test_user",
|
||||
},
|
||||
},
|
||||
authenticated: true,
|
||||
|
||||
wantResp: &Response{
|
||||
User: &user.DefaultInfo{
|
||||
Name: "test_user",
|
||||
},
|
||||
Audiences: Audiences{"api"},
|
||||
},
|
||||
wantAuthenticated: true,
|
||||
},
|
||||
{
|
||||
err: errors.New("uhoh"),
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
authenticated: false,
|
||||
wantAuthenticated: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple good audiences",
|
||||
|
||||
taudcfgs: []taudcfg{
|
||||
{
|
||||
implicitAuds: Audiences{"api", "other_api"},
|
||||
auds: Audiences{"api", "other_api"},
|
||||
},
|
||||
{
|
||||
implicitAuds: Audiences{"api", "other_api", "other"},
|
||||
auds: Audiences{"api", "other_api"},
|
||||
},
|
||||
{
|
||||
implicitAuds: Audiences{"api", "other_api"},
|
||||
auds: Audiences{"api", "other_api", "other"},
|
||||
},
|
||||
{
|
||||
implicitAuds: Audiences{"api", "other_api", "other"},
|
||||
auds: Audiences{"api", "other_api", "other_other"},
|
||||
},
|
||||
},
|
||||
|
||||
treqs: []treq{
|
||||
{
|
||||
resp: &Response{
|
||||
User: &user.DefaultInfo{
|
||||
Name: "test_user",
|
||||
},
|
||||
},
|
||||
authenticated: true,
|
||||
|
||||
wantResp: &Response{
|
||||
User: &user.DefaultInfo{
|
||||
Name: "test_user",
|
||||
},
|
||||
Audiences: Audiences{"api", "other_api"},
|
||||
},
|
||||
wantAuthenticated: true,
|
||||
},
|
||||
{
|
||||
err: errors.New("uhoh"),
|
||||
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
authenticated: false,
|
||||
|
||||
wantAuthenticated: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad audience(s)",
|
||||
|
||||
taudcfgs: []taudcfg{
|
||||
{
|
||||
implicitAuds: Audiences{"api"},
|
||||
auds: Audiences{"other_api"},
|
||||
},
|
||||
{
|
||||
implicitAuds: Audiences{},
|
||||
auds: Audiences{"other_api"},
|
||||
},
|
||||
{
|
||||
implicitAuds: Audiences{"api"},
|
||||
auds: Audiences{},
|
||||
},
|
||||
{
|
||||
implicitAuds: Audiences{"api", "other"},
|
||||
auds: Audiences{},
|
||||
},
|
||||
{
|
||||
implicitAuds: Audiences{},
|
||||
auds: Audiences{"api", "other"},
|
||||
},
|
||||
},
|
||||
|
||||
treqs: []treq{
|
||||
{
|
||||
resp: &Response{
|
||||
User: &user.DefaultInfo{
|
||||
Name: "test_user",
|
||||
},
|
||||
},
|
||||
authenticated: true,
|
||||
|
||||
wantAuthenticated: false,
|
||||
},
|
||||
{
|
||||
err: errors.New("uhoh"),
|
||||
|
||||
wantAuthenticated: false,
|
||||
},
|
||||
{
|
||||
authenticated: false,
|
||||
|
||||
wantAuthenticated: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cs {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
for _, taudcfg := range c.taudcfgs {
|
||||
for _, treq := range c.treqs {
|
||||
t.Run(fmt.Sprintf("auds=%q,implicit=%q", taudcfg.auds, taudcfg.implicitAuds), func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ctx = WithAudiences(ctx, taudcfg.auds)
|
||||
resp, ok, err := authenticate(ctx, taudcfg.implicitAuds, func() (*Response, bool, error) {
|
||||
if treq.resp != nil {
|
||||
resp := *treq.resp
|
||||
return &resp, treq.authenticated, treq.err
|
||||
}
|
||||
return nil, treq.authenticated, treq.err
|
||||
})
|
||||
if got, want := (err != nil), treq.wantErr; got != want {
|
||||
t.Errorf("Unexpected error. got=%v, want=%v, err=%v", got, want, err)
|
||||
}
|
||||
if got, want := ok, treq.wantAuthenticated; got != want {
|
||||
t.Errorf("Unexpected authentication. got=%v, want=%v", got, want)
|
||||
}
|
||||
if got, want := resp, treq.wantResp; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("Unexpected response. diff:\n%v", diff.ObjectGoPrintDiff(got, want))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue