Browse code

pkg: authorization: add Err to tweak response status code

Signed-off-by: Antonio Murdaca <runcom@redhat.com>

Antonio Murdaca authored on 2015/12/15 17:49:18
Showing 5 changed files
... ...
@@ -146,13 +146,13 @@ should implement the following two methods:
146 146
 **Request**:
147 147
 
148 148
 ```json
149
-{    
150
-    "User":              "The user identification"
151
-    "UserAuthNMethod":   "The authentication method used"
152
-    "RequestMethod":     "The HTTP method"
153
-    "RequestUri":        "The HTTP request URI"
154
-    "RequestBody":       "Byte array containing the raw HTTP request body"
155
-    "RequestHeader":     "Byte array containing the raw HTTP request header as a map[string][]string "
149
+{
150
+    "User":              "The user identification",
151
+    "UserAuthNMethod":   "The authentication method used",
152
+    "RequestMethod":     "The HTTP method",
153
+    "RequestUri":        "The HTTP request URI",
154
+    "RequestBody":       "Byte array containing the raw HTTP request body",
155
+    "RequestHeader":     "Byte array containing the raw HTTP request header as a map[string][]string ",
156 156
     "RequestStatusCode": "Request status code"
157 157
 }
158 158
 ```
... ...
@@ -160,27 +160,27 @@ should implement the following two methods:
160 160
 **Response**:
161 161
 
162 162
 ```json
163
-{    
164
-    "Allow" : "Determined whether the user is allowed or not"
165
-    "Msg":    "The authorization message"
163
+{
164
+    "Allow": "Determined whether the user is allowed or not",
165
+    "Msg":   "The authorization message",
166
+    "Err":   "The error message if things go wrong"
166 167
 }
167 168
 ```
168
-
169 169
 #### /AuthzPlugin.AuthZRes
170 170
 
171 171
 **Request**:
172 172
 
173 173
 ```json
174 174
 {
175
-    "User":              "The user identification"
176
-    "UserAuthNMethod":   "The authentication method used"
177
-    "RequestMethod":     "The HTTP method"
178
-    "RequestUri":        "The HTTP request URI"
179
-    "RequestBody":       "Byte array containing the raw HTTP request body"
180
-    "RequestHeader":     "Byte array containing the raw HTTP request header as a map[string][]string"
181
-    "RequestStatusCode": "Request status code"
182
-    "ResponseBody":      "Byte array containing the raw HTTP response body"
183
-    "ResponseHeader":    "Byte array containing the raw HTTP response header as a map[string][]string"
175
+    "User":              "The user identification",
176
+    "UserAuthNMethod":   "The authentication method used",
177
+    "RequestMethod":     "The HTTP method",
178
+    "RequestUri":        "The HTTP request URI",
179
+    "RequestBody":       "Byte array containing the raw HTTP request body",
180
+    "RequestHeader":     "Byte array containing the raw HTTP request header as a map[string][]string",
181
+    "RequestStatusCode": "Request status code",
182
+    "ResponseBody":      "Byte array containing the raw HTTP response body",
183
+    "ResponseHeader":    "Byte array containing the raw HTTP response header as a map[string][]string",
184 184
     "ResponseStatusCode":"Response status code"
185 185
 }
186 186
 ```
... ...
@@ -189,11 +189,12 @@ should implement the following two methods:
189 189
 
190 190
 ```json
191 191
 {
192
-   "Allow" :               "Determined whether the user is allowed or not"
193
-   "Msg":                  "The authorization message"
194
-   "ModifiedBody":         "Byte array containing a modified body of the raw HTTP body (or nil if no changes required)"
195
-   "ModifiedHeader":       "Byte array containing a modified header of the HTTP response (or nil if no changes required)"
196
-   "ModifiedStatusCode":   "int containing the modified version of the status code (or 0 if not change is required)"
192
+   "Allow":              "Determined whether the user is allowed or not",
193
+   "Msg":                "The authorization message",
194
+   "Err":                "The error message if things go wrong",
195
+   "ModifiedBody":       "Byte array containing a modified body of the raw HTTP body (or nil if no changes required)",
196
+   "ModifiedHeader":     "Byte array containing a modified header of the HTTP response (or nil if no changes required)",
197
+   "ModifiedStatusCode": "int containing the modified version of the status code (or 0 if not change is required)"
197 198
 }
198 199
 ```
199 200
 
... ...
@@ -222,7 +223,8 @@ Request body           | []byte            | Raw request body
222 222
 Name    | Type   | Description
223 223
 --------|--------|----------------------------------------------------------------------------------
224 224
 Allow   | bool   | Boolean value indicating whether the request is allowed or denied
225
-Message | string | Authorization message (will be returned to the client in case the access is denied)
225
+Msg     | string | Authorization message (will be returned to the client in case the access is denied)
226
+Err     | string | Error message (will be returned to the client in case the plugin encounter an error)
226 227
 
227 228
 ### Response authorization
228 229
 
... ...
@@ -249,4 +251,5 @@ Response body           | []byte            | Raw docker daemon response body
249 249
 Name    | Type   | Description
250 250
 --------|--------|----------------------------------------------------------------------------------
251 251
 Allow   | bool   | Boolean value indicating whether the response is allowed or denied
252
-Message | string | Authorization message (will be returned to the client in case the access is denied)
253 252
\ No newline at end of file
253
+Msg     | string | Authorization message (will be returned to the client in case the access is denied)
254
+Err     | string | Error message (will be returned to the client in case the plugin encounter an error)
... ...
@@ -17,9 +17,12 @@ import (
17 17
 	"github.com/go-check/check"
18 18
 )
19 19
 
20
-const testAuthZPlugin = "authzplugin"
21
-const unauthorizedMessage = "User unauthorized authz plugin"
22
-const containerListAPI = "/containers/json"
20
+const (
21
+	testAuthZPlugin     = "authzplugin"
22
+	unauthorizedMessage = "User unauthorized authz plugin"
23
+	errorMessage        = "something went wrong..."
24
+	containerListAPI    = "/containers/json"
25
+)
23 26
 
24 27
 func init() {
25 28
 	check.Suite(&DockerAuthzSuite{
... ...
@@ -66,9 +69,12 @@ func (s *DockerAuthzSuite) SetUpSuite(c *check.C) {
66 66
 	})
67 67
 
68 68
 	mux.HandleFunc("/AuthZPlugin.AuthZReq", func(w http.ResponseWriter, r *http.Request) {
69
+		if s.ctrl.reqRes.Err != "" {
70
+			w.WriteHeader(http.StatusInternalServerError)
71
+		}
69 72
 		b, err := json.Marshal(s.ctrl.reqRes)
70
-		w.Write(b)
71 73
 		c.Assert(err, check.IsNil)
74
+		w.Write(b)
72 75
 		defer r.Body.Close()
73 76
 		body, err := ioutil.ReadAll(r.Body)
74 77
 		c.Assert(err, check.IsNil)
... ...
@@ -88,6 +94,9 @@ func (s *DockerAuthzSuite) SetUpSuite(c *check.C) {
88 88
 	})
89 89
 
90 90
 	mux.HandleFunc("/AuthZPlugin.AuthZRes", func(w http.ResponseWriter, r *http.Request) {
91
+		if s.ctrl.resRes.Err != "" {
92
+			w.WriteHeader(http.StatusInternalServerError)
93
+		}
91 94
 		b, err := json.Marshal(s.ctrl.resRes)
92 95
 		c.Assert(err, check.IsNil)
93 96
 		w.Write(b)
... ...
@@ -214,6 +223,31 @@ func (s *DockerAuthzSuite) TestAuthZPluginDenyResponse(c *check.C) {
214 214
 	c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: %s\n", unauthorizedMessage))
215 215
 }
216 216
 
217
+func (s *DockerAuthzSuite) TestAuthZPluginErrorResponse(c *check.C) {
218
+	err := s.d.Start("--authz-plugin=" + testAuthZPlugin)
219
+	c.Assert(err, check.IsNil)
220
+	s.ctrl.reqRes.Allow = true
221
+	s.ctrl.resRes.Err = errorMessage
222
+
223
+	// Ensure command is blocked
224
+	res, err := s.d.Cmd("ps")
225
+	c.Assert(err, check.NotNil)
226
+
227
+	c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: Plugin Error: %s, %s\n", errorMessage, authorization.AuthZApiResponse))
228
+}
229
+
230
+func (s *DockerAuthzSuite) TestAuthZPluginErrorRequest(c *check.C) {
231
+	err := s.d.Start("--authz-plugin=" + testAuthZPlugin)
232
+	c.Assert(err, check.IsNil)
233
+	s.ctrl.reqRes.Err = errorMessage
234
+
235
+	// Ensure command is blocked
236
+	res, err := s.d.Cmd("ps")
237
+	c.Assert(err, check.NotNil)
238
+
239
+	c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: Plugin Error: %s, %s\n", errorMessage, authorization.AuthZApiRequest))
240
+}
241
+
217 242
 // assertURIRecorded verifies that the given URI was sent and recorded in the authz plugin
218 243
 func assertURIRecorded(c *check.C, uris []string, uri string) {
219 244
 
... ...
@@ -43,10 +43,12 @@ type Request struct {
43 43
 
44 44
 // Response represents authZ plugin response
45 45
 type Response struct {
46
-
47 46
 	// Allow indicating whether the user is allowed or not
48 47
 	Allow bool `json:"Allow"`
49 48
 
50 49
 	// Msg stores the authorization message
51 50
 	Msg string `json:"Msg,omitempty"`
51
+
52
+	// Err stores a message in case there's an error
53
+	Err string `json:"Err,omitempty"`
52 54
 }
... ...
@@ -84,6 +84,10 @@ func (a *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) error {
84 84
 			return err
85 85
 		}
86 86
 
87
+		if authRes.Err != "" {
88
+			return fmt.Errorf(authRes.Err)
89
+		}
90
+
87 91
 		if !authRes.Allow {
88 92
 			return fmt.Errorf(authRes.Msg)
89 93
 		}
... ...
@@ -107,6 +111,10 @@ func (a *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error {
107 107
 			return err
108 108
 		}
109 109
 
110
+		if authRes.Err != "" {
111
+			return fmt.Errorf(authRes.Err)
112
+		}
113
+
110 114
 		if !authRes.Allow {
111 115
 			return fmt.Errorf(authRes.Msg)
112 116
 		}
... ...
@@ -19,6 +19,37 @@ import (
19 19
 
20 20
 const pluginAddress = "authzplugin.sock"
21 21
 
22
+func TestAuthZRequestPluginError(t *testing.T) {
23
+	server := authZPluginTestServer{t: t}
24
+	go server.start()
25
+	defer server.stop()
26
+
27
+	authZPlugin := createTestPlugin(t)
28
+
29
+	request := Request{
30
+		User:           "user",
31
+		RequestBody:    []byte("sample body"),
32
+		RequestURI:     "www.authz.com",
33
+		RequestMethod:  "GET",
34
+		RequestHeaders: map[string]string{"header": "value"},
35
+	}
36
+	server.replayResponse = Response{
37
+		Err: "an error",
38
+	}
39
+
40
+	actualResponse, err := authZPlugin.AuthZRequest(&request)
41
+	if err != nil {
42
+		t.Fatalf("Failed to authorize request %v", err)
43
+	}
44
+
45
+	if !reflect.DeepEqual(server.replayResponse, *actualResponse) {
46
+		t.Fatalf("Response must be equal")
47
+	}
48
+	if !reflect.DeepEqual(request, server.recordedRequest) {
49
+		t.Fatalf("Requests must be equal")
50
+	}
51
+}
52
+
22 53
 func TestAuthZRequestPlugin(t *testing.T) {
23 54
 	server := authZPluginTestServer{t: t}
24 55
 	go server.start()