Signed-off-by: Antonio Murdaca <runcom@redhat.com>
| ... | ... |
@@ -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() |