Browse code

Fix authorization issue - when request is denied return forbbiden exist code (403).

- Return 403 (forbidden) when request is denied in authorization flows
(including integration test)
- Fix #22428
- Close #22431

Signed-off-by: Liron Levin <liron@twistlock.com>

Liron Levin authored on 2016/05/02 17:54:09
Showing 4 changed files
... ...
@@ -3,7 +3,6 @@ package daemon
3 3
 import (
4 4
 	"fmt"
5 5
 	"net"
6
-	"net/http"
7 6
 	"strings"
8 7
 
9 8
 	netsettings "github.com/docker/docker/daemon/network"
... ...
@@ -97,7 +96,7 @@ func (daemon *Daemon) getAllNetworks() []libnetwork.Network {
97 97
 func (daemon *Daemon) CreateNetwork(create types.NetworkCreateRequest) (*types.NetworkCreateResponse, error) {
98 98
 	if runconfig.IsPreDefinedNetwork(create.Name) {
99 99
 		err := fmt.Errorf("%s is a pre-defined network and cannot be created", create.Name)
100
-		return nil, errors.NewErrorWithStatusCode(err, http.StatusForbidden)
100
+		return nil, errors.NewRequestForbiddenError(err)
101 101
 	}
102 102
 
103 103
 	var warning string
... ...
@@ -221,7 +220,7 @@ func (daemon *Daemon) DeleteNetwork(networkID string) error {
221 221
 
222 222
 	if runconfig.IsPreDefinedNetwork(nw.Name()) {
223 223
 		err := fmt.Errorf("%s is a pre-defined network and cannot be removed", nw.Name())
224
-		return errors.NewErrorWithStatusCode(err, http.StatusForbidden)
224
+		return errors.NewRequestForbiddenError(err)
225 225
 	}
226 226
 
227 227
 	if err := nw.Delete(); err != nil {
... ...
@@ -28,6 +28,12 @@ func NewBadRequestError(err error) error {
28 28
 	return NewErrorWithStatusCode(err, http.StatusBadRequest)
29 29
 }
30 30
 
31
+// NewRequestForbiddenError creates a new API error
32
+// that has the 403 HTTP status code associated to it.
33
+func NewRequestForbiddenError(err error) error {
34
+	return NewErrorWithStatusCode(err, http.StatusForbidden)
35
+}
36
+
31 37
 // NewRequestNotFoundError creates a new API error
32 38
 // that has the 404 HTTP status code associated to it.
33 39
 func NewRequestNotFoundError(err error) error {
... ...
@@ -21,6 +21,9 @@ import (
21 21
 	"github.com/docker/docker/pkg/integration/checker"
22 22
 	"github.com/docker/docker/pkg/plugins"
23 23
 	"github.com/go-check/check"
24
+	"net"
25
+	"net/http/httputil"
26
+	"net/url"
24 27
 )
25 28
 
26 29
 const (
... ...
@@ -272,6 +275,27 @@ func (s *DockerAuthzSuite) TestAuthZPluginDenyRequest(c *check.C) {
272 272
 	c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: authorization denied by plugin %s: %s\n", testAuthZPlugin, unauthorizedMessage))
273 273
 }
274 274
 
275
+// TestAuthZPluginApiDenyResponse validates that when authorization plugin deny the request, the status code is forbidden
276
+func (s *DockerAuthzSuite) TestAuthZPluginApiDenyResponse(c *check.C) {
277
+	err := s.d.Start("--authorization-plugin=" + testAuthZPlugin)
278
+	c.Assert(err, check.IsNil)
279
+	s.ctrl.reqRes.Allow = false
280
+	s.ctrl.resRes.Msg = unauthorizedMessage
281
+
282
+	daemonURL, err := url.Parse(s.d.sock())
283
+
284
+	conn, err := net.DialTimeout(daemonURL.Scheme, daemonURL.Path, time.Second*10)
285
+	c.Assert(err, check.IsNil)
286
+	client := httputil.NewClientConn(conn, nil)
287
+	req, err := http.NewRequest("GET", "/version", nil)
288
+	c.Assert(err, check.IsNil)
289
+	resp, err := client.Do(req)
290
+
291
+	c.Assert(err, check.IsNil)
292
+	c.Assert(resp.StatusCode, checker.Equals, http.StatusForbidden)
293
+	c.Assert(err, checker.IsNil)
294
+}
295
+
275 296
 func (s *DockerAuthzSuite) TestAuthZPluginDenyResponse(c *check.C) {
276 297
 	err := s.d.Start("--authorization-plugin=" + testAuthZPlugin)
277 298
 	c.Assert(err, check.IsNil)
... ...
@@ -85,7 +85,7 @@ func (ctx *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) error {
85 85
 		}
86 86
 
87 87
 		if !authRes.Allow {
88
-			return fmt.Errorf("authorization denied by plugin %s: %s", plugin.Name(), authRes.Msg)
88
+			return newAuthorizationError(plugin.Name(), authRes.Msg)
89 89
 		}
90 90
 	}
91 91
 
... ...
@@ -110,7 +110,7 @@ func (ctx *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error {
110 110
 		}
111 111
 
112 112
 		if !authRes.Allow {
113
-			return fmt.Errorf("authorization denied by plugin %s: %s", plugin.Name(), authRes.Msg)
113
+			return newAuthorizationError(plugin.Name(), authRes.Msg)
114 114
 		}
115 115
 	}
116 116
 
... ...
@@ -163,3 +163,17 @@ func headers(header http.Header) map[string]string {
163 163
 	}
164 164
 	return v
165 165
 }
166
+
167
+// authorizationError represents an authorization deny error
168
+type authorizationError struct {
169
+	error
170
+}
171
+
172
+// HTTPErrorStatusCode returns the authorization error status code (forbidden)
173
+func (e authorizationError) HTTPErrorStatusCode() int {
174
+	return http.StatusForbidden
175
+}
176
+
177
+func newAuthorizationError(plugin, msg string) authorizationError {
178
+	return authorizationError{error: fmt.Errorf("authorization denied by plugin %s: %s", plugin, msg)}
179
+}