123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- // +build !windows
- // TODO Windows: This uses a Unix socket for testing. This might be possible
- // to port to Windows using a named pipe instead.
- package authorization // import "github.com/docker/docker/pkg/authorization"
- import (
- "bytes"
- "encoding/json"
- "io/ioutil"
- "net"
- "net/http"
- "net/http/httptest"
- "os"
- "path"
- "reflect"
- "strings"
- "testing"
- "github.com/docker/docker/pkg/plugins"
- "github.com/docker/go-connections/tlsconfig"
- "github.com/gorilla/mux"
- )
- const (
- pluginAddress = "authz-test-plugin.sock"
- )
- func TestAuthZRequestPluginError(t *testing.T) {
- server := authZPluginTestServer{t: t}
- server.start()
- defer server.stop()
- authZPlugin := createTestPlugin(t)
- request := Request{
- User: "user",
- RequestBody: []byte("sample body"),
- RequestURI: "www.authz.com/auth",
- RequestMethod: "GET",
- RequestHeaders: map[string]string{"header": "value"},
- }
- server.replayResponse = Response{
- Err: "an error",
- }
- actualResponse, err := authZPlugin.AuthZRequest(&request)
- if err != nil {
- t.Fatalf("Failed to authorize request %v", err)
- }
- if !reflect.DeepEqual(server.replayResponse, *actualResponse) {
- t.Fatal("Response must be equal")
- }
- if !reflect.DeepEqual(request, server.recordedRequest) {
- t.Fatal("Requests must be equal")
- }
- }
- func TestAuthZRequestPlugin(t *testing.T) {
- server := authZPluginTestServer{t: t}
- server.start()
- defer server.stop()
- authZPlugin := createTestPlugin(t)
- request := Request{
- User: "user",
- RequestBody: []byte("sample body"),
- RequestURI: "www.authz.com/auth",
- RequestMethod: "GET",
- RequestHeaders: map[string]string{"header": "value"},
- }
- server.replayResponse = Response{
- Allow: true,
- Msg: "Sample message",
- }
- actualResponse, err := authZPlugin.AuthZRequest(&request)
- if err != nil {
- t.Fatalf("Failed to authorize request %v", err)
- }
- if !reflect.DeepEqual(server.replayResponse, *actualResponse) {
- t.Fatal("Response must be equal")
- }
- if !reflect.DeepEqual(request, server.recordedRequest) {
- t.Fatal("Requests must be equal")
- }
- }
- func TestAuthZResponsePlugin(t *testing.T) {
- server := authZPluginTestServer{t: t}
- server.start()
- defer server.stop()
- authZPlugin := createTestPlugin(t)
- request := Request{
- User: "user",
- RequestURI: "something.com/auth",
- RequestBody: []byte("sample body"),
- }
- server.replayResponse = Response{
- Allow: true,
- Msg: "Sample message",
- }
- actualResponse, err := authZPlugin.AuthZResponse(&request)
- if err != nil {
- t.Fatalf("Failed to authorize request %v", err)
- }
- if !reflect.DeepEqual(server.replayResponse, *actualResponse) {
- t.Fatal("Response must be equal")
- }
- if !reflect.DeepEqual(request, server.recordedRequest) {
- t.Fatal("Requests must be equal")
- }
- }
- func TestResponseModifier(t *testing.T) {
- r := httptest.NewRecorder()
- m := NewResponseModifier(r)
- m.Header().Set("h1", "v1")
- m.Write([]byte("body"))
- m.WriteHeader(http.StatusInternalServerError)
- m.FlushAll()
- if r.Header().Get("h1") != "v1" {
- t.Fatalf("Header value must exists %s", r.Header().Get("h1"))
- }
- if !reflect.DeepEqual(r.Body.Bytes(), []byte("body")) {
- t.Fatalf("Body value must exists %s", r.Body.Bytes())
- }
- if r.Code != http.StatusInternalServerError {
- t.Fatalf("Status code must be correct %d", r.Code)
- }
- }
- func TestDrainBody(t *testing.T) {
- tests := []struct {
- length int // length is the message length send to drainBody
- expectedBodyLength int // expectedBodyLength is the expected body length after drainBody is called
- }{
- {10, 10}, // Small message size
- {maxBodySize - 1, maxBodySize - 1}, // Max message size
- {maxBodySize * 2, 0}, // Large message size (skip copying body)
- }
- for _, test := range tests {
- msg := strings.Repeat("a", test.length)
- body, closer, err := drainBody(ioutil.NopCloser(bytes.NewReader([]byte(msg))))
- if err != nil {
- t.Fatal(err)
- }
- if len(body) != test.expectedBodyLength {
- t.Fatalf("Body must be copied, actual length: '%d'", len(body))
- }
- if closer == nil {
- t.Fatal("Closer must not be nil")
- }
- modified, err := ioutil.ReadAll(closer)
- if err != nil {
- t.Fatalf("Error must not be nil: '%v'", err)
- }
- if len(modified) != len(msg) {
- t.Fatalf("Result should not be truncated. Original length: '%d', new length: '%d'", len(msg), len(modified))
- }
- }
- }
- func TestSendBody(t *testing.T) {
- var (
- testcases = []struct {
- url string
- contentType string
- expected bool
- }{
- {
- contentType: "application/json",
- expected: true,
- },
- {
- contentType: "Application/json",
- expected: true,
- },
- {
- contentType: "application/JSON",
- expected: true,
- },
- {
- contentType: "APPLICATION/JSON",
- expected: true,
- },
- {
- contentType: "application/json; charset=utf-8",
- expected: true,
- },
- {
- contentType: "application/json;charset=utf-8",
- expected: true,
- },
- {
- contentType: "application/json; charset=UTF8",
- expected: true,
- },
- {
- contentType: "application/json;charset=UTF8",
- expected: true,
- },
- {
- contentType: "text/html",
- expected: false,
- },
- {
- contentType: "",
- expected: false,
- },
- {
- url: "nothing.com/auth",
- contentType: "",
- expected: false,
- },
- {
- url: "nothing.com/auth",
- contentType: "application/json;charset=UTF8",
- expected: false,
- },
- {
- url: "nothing.com/auth?p1=test",
- contentType: "application/json;charset=UTF8",
- expected: false,
- },
- {
- url: "nothing.com/test?p1=/auth",
- contentType: "application/json;charset=UTF8",
- expected: true,
- },
- {
- url: "nothing.com/something/auth",
- contentType: "application/json;charset=UTF8",
- expected: true,
- },
- {
- url: "nothing.com/auth/test",
- contentType: "application/json;charset=UTF8",
- expected: false,
- },
- {
- url: "nothing.com/v1.24/auth/test",
- contentType: "application/json;charset=UTF8",
- expected: false,
- },
- {
- url: "nothing.com/v1/auth/test",
- contentType: "application/json;charset=UTF8",
- expected: false,
- },
- {
- url: "www.nothing.com/v1.24/auth/test",
- contentType: "application/json;charset=UTF8",
- expected: false,
- },
- {
- url: "https://www.nothing.com/v1.24/auth/test",
- contentType: "application/json;charset=UTF8",
- expected: false,
- },
- {
- url: "http://nothing.com/v1.24/auth/test",
- contentType: "application/json;charset=UTF8",
- expected: false,
- },
- {
- url: "www.nothing.com/test?p1=/auth",
- contentType: "application/json;charset=UTF8",
- expected: true,
- },
- {
- url: "http://www.nothing.com/test?p1=/auth",
- contentType: "application/json;charset=UTF8",
- expected: true,
- },
- {
- url: "www.nothing.com/something/auth",
- contentType: "application/json;charset=UTF8",
- expected: true,
- },
- {
- url: "https://www.nothing.com/something/auth",
- contentType: "application/json;charset=UTF8",
- expected: true,
- },
- }
- )
- for _, testcase := range testcases {
- header := http.Header{}
- header.Set("Content-Type", testcase.contentType)
- if testcase.url == "" {
- testcase.url = "nothing.com"
- }
- if b := sendBody(testcase.url, header); b != testcase.expected {
- t.Fatalf("sendBody failed: url: %s, content-type: %s; Expected: %t, Actual: %t", testcase.url, testcase.contentType, testcase.expected, b)
- }
- }
- }
- func TestResponseModifierOverride(t *testing.T) {
- r := httptest.NewRecorder()
- m := NewResponseModifier(r)
- m.Header().Set("h1", "v1")
- m.Write([]byte("body"))
- m.WriteHeader(http.StatusInternalServerError)
- overrideHeader := make(http.Header)
- overrideHeader.Add("h1", "v2")
- overrideHeaderBytes, err := json.Marshal(overrideHeader)
- if err != nil {
- t.Fatalf("override header failed %v", err)
- }
- m.OverrideHeader(overrideHeaderBytes)
- m.OverrideBody([]byte("override body"))
- m.OverrideStatusCode(http.StatusNotFound)
- m.FlushAll()
- if r.Header().Get("h1") != "v2" {
- t.Fatalf("Header value must exists %s", r.Header().Get("h1"))
- }
- if !reflect.DeepEqual(r.Body.Bytes(), []byte("override body")) {
- t.Fatalf("Body value must exists %s", r.Body.Bytes())
- }
- if r.Code != http.StatusNotFound {
- t.Fatalf("Status code must be correct %d", r.Code)
- }
- }
- // createTestPlugin creates a new sample authorization plugin
- func createTestPlugin(t *testing.T) *authorizationPlugin {
- pwd, err := os.Getwd()
- if err != nil {
- t.Fatal(err)
- }
- client, err := plugins.NewClient("unix:///"+path.Join(pwd, pluginAddress), &tlsconfig.Options{InsecureSkipVerify: true})
- if err != nil {
- t.Fatalf("Failed to create client %v", err)
- }
- return &authorizationPlugin{name: "plugin", plugin: client}
- }
- // AuthZPluginTestServer is a simple server that implements the authZ plugin interface
- type authZPluginTestServer struct {
- listener net.Listener
- t *testing.T
- // request stores the request sent from the daemon to the plugin
- recordedRequest Request
- // response stores the response sent from the plugin to the daemon
- replayResponse Response
- server *httptest.Server
- }
- // start starts the test server that implements the plugin
- func (t *authZPluginTestServer) start() {
- r := mux.NewRouter()
- l, err := net.Listen("unix", pluginAddress)
- if err != nil {
- t.t.Fatal(err)
- }
- t.listener = l
- r.HandleFunc("/Plugin.Activate", t.activate)
- r.HandleFunc("/"+AuthZApiRequest, t.auth)
- r.HandleFunc("/"+AuthZApiResponse, t.auth)
- t.server = &httptest.Server{
- Listener: l,
- Config: &http.Server{
- Handler: r,
- Addr: pluginAddress,
- },
- }
- t.server.Start()
- }
- // stop stops the test server that implements the plugin
- func (t *authZPluginTestServer) stop() {
- t.server.Close()
- os.Remove(pluginAddress)
- if t.listener != nil {
- t.listener.Close()
- }
- }
- // auth is a used to record/replay the authentication api messages
- func (t *authZPluginTestServer) auth(w http.ResponseWriter, r *http.Request) {
- t.recordedRequest = Request{}
- body, err := ioutil.ReadAll(r.Body)
- if err != nil {
- t.t.Fatal(err)
- }
- r.Body.Close()
- json.Unmarshal(body, &t.recordedRequest)
- b, err := json.Marshal(t.replayResponse)
- if err != nil {
- t.t.Fatal(err)
- }
- w.Write(b)
- }
- func (t *authZPluginTestServer) activate(w http.ResponseWriter, r *http.Request) {
- b, err := json.Marshal(plugins.Manifest{Implements: []string{AuthZApiImplements}})
- if err != nil {
- t.t.Fatal(err)
- }
- w.Write(b)
- }
|