published by bot

(https://github.com/kubernetes/contrib/tree/master/mungegithub)

copied from https://github.com/kubernetes/kubernetes.git, branch master,
last commit is 238ffdd0d6d79d610cea2ebe3a03868a197283c8
This commit is contained in:
Kubernetes Publisher
2016-12-03 08:16:09 +00:00
parent 5d8c36c93c
commit 41a99d711a
13 changed files with 370 additions and 69 deletions

View File

@@ -34,8 +34,8 @@ type Config struct {
// Bearer token for authentication
BearerToken string
// Impersonate is the username that this Config will impersonate
Impersonate string
// Impersonate is the config that this Config will impersonate using
Impersonate ImpersonationConfig
// Transport may be used for custom HTTP behavior. This attribute may
// not be specified with the TLS client certificate options. Use
@@ -50,6 +50,16 @@ type Config struct {
WrapTransport func(rt http.RoundTripper) http.RoundTripper
}
// ImpersonationConfig has all the available impersonation options
type ImpersonationConfig struct {
// UserName matches user.Info.GetName()
UserName string
// Groups matches user.Info.GetGroups()
Groups []string
// Extra matches user.Info.GetExtra()
Extra map[string][]string
}
// HasCA returns whether the configuration has a certificate authority or not.
func (c *Config) HasCA() bool {
return len(c.TLS.CAData) > 0 || len(c.TLS.CAFile) > 0

View File

@@ -48,7 +48,9 @@ func HTTPWrappersForConfig(config *Config, rt http.RoundTripper) (http.RoundTrip
if len(config.UserAgent) > 0 {
rt = NewUserAgentRoundTripper(config.UserAgent, rt)
}
if len(config.Impersonate) > 0 {
if len(config.Impersonate.UserName) > 0 ||
len(config.Impersonate.Groups) > 0 ||
len(config.Impersonate.Extra) > 0 {
rt = NewImpersonatingRoundTripper(config.Impersonate, rt)
}
return rt, nil
@@ -133,22 +135,53 @@ func (rt *basicAuthRoundTripper) CancelRequest(req *http.Request) {
func (rt *basicAuthRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt }
// These correspond to the headers used in pkg/apis/authentication. We don't want the package dependency,
// but you must not change the values.
const (
// ImpersonateUserHeader is used to impersonate a particular user during an API server request
ImpersonateUserHeader = "Impersonate-User"
// ImpersonateGroupHeader is used to impersonate a particular group during an API server request.
// It can be repeated multiplied times for multiple groups.
ImpersonateGroupHeader = "Impersonate-Group"
// ImpersonateUserExtraHeaderPrefix is a prefix for a header used to impersonate an entry in the
// extra map[string][]string for user.Info. The key for the `extra` map is suffix.
// The same key can be repeated multiple times to have multiple elements in the slice under a single key.
// For instance:
// Impersonate-Extra-Foo: one
// Impersonate-Extra-Foo: two
// results in extra["Foo"] = []string{"one", "two"}
ImpersonateUserExtraHeaderPrefix = "Impersonate-Extra-"
)
type impersonatingRoundTripper struct {
impersonate string
impersonate ImpersonationConfig
delegate http.RoundTripper
}
// NewImpersonatingRoundTripper will add an Act-As header to a request unless it has already been set.
func NewImpersonatingRoundTripper(impersonate string, delegate http.RoundTripper) http.RoundTripper {
func NewImpersonatingRoundTripper(impersonate ImpersonationConfig, delegate http.RoundTripper) http.RoundTripper {
return &impersonatingRoundTripper{impersonate, delegate}
}
func (rt *impersonatingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
if len(req.Header.Get("Impersonate-User")) != 0 {
// use the user header as marker for the rest.
if len(req.Header.Get(ImpersonateUserHeader)) != 0 {
return rt.delegate.RoundTrip(req)
}
req = cloneRequest(req)
req.Header.Set("Impersonate-User", rt.impersonate)
req.Header.Set(ImpersonateUserHeader, rt.impersonate.UserName)
for _, group := range rt.impersonate.Groups {
req.Header.Add(ImpersonateGroupHeader, group)
}
for k, vv := range rt.impersonate.Extra {
for _, v := range vv {
req.Header.Add(ImpersonateUserExtraHeaderPrefix+k, v)
}
}
return rt.delegate.RoundTrip(req)
}

View File

@@ -18,6 +18,7 @@ package transport
import (
"net/http"
"reflect"
"testing"
)
@@ -99,3 +100,58 @@ func TestUserAgentRoundTripper(t *testing.T) {
t.Errorf("unexpected user agent header: %#v", rt.Request)
}
}
func TestImpersonationRoundTripper(t *testing.T) {
tcs := []struct {
name string
impersonationConfig ImpersonationConfig
expected map[string][]string
}{
{
name: "all",
impersonationConfig: ImpersonationConfig{
UserName: "user",
Groups: []string{"one", "two"},
Extra: map[string][]string{
"first": {"A", "a"},
"second": {"B", "b"},
},
},
expected: map[string][]string{
ImpersonateUserHeader: {"user"},
ImpersonateGroupHeader: {"one", "two"},
ImpersonateUserExtraHeaderPrefix + "First": {"A", "a"},
ImpersonateUserExtraHeaderPrefix + "Second": {"B", "b"},
},
},
}
for _, tc := range tcs {
rt := &testRoundTripper{}
req := &http.Request{
Header: make(http.Header),
}
NewImpersonatingRoundTripper(tc.impersonationConfig, rt).RoundTrip(req)
for k, v := range rt.Request.Header {
expected, ok := tc.expected[k]
if !ok {
t.Errorf("%v missing %v=%v", tc.name, k, v)
continue
}
if !reflect.DeepEqual(expected, v) {
t.Errorf("%v expected %v: %v, got %v", tc.name, k, expected, v)
}
}
for k, v := range tc.expected {
expected, ok := rt.Request.Header[k]
if !ok {
t.Errorf("%v missing %v=%v", tc.name, k, v)
continue
}
if !reflect.DeepEqual(expected, v) {
t.Errorf("%v expected %v: %v, got %v", tc.name, k, expected, v)
}
}
}
}