Skip to content

Commit

Permalink
pkg: authorization: add Err to tweak response status code
Browse files Browse the repository at this point in the history
Signed-off-by: Antonio Murdaca <[email protected]>
  • Loading branch information
runcom committed Dec 17, 2015
1 parent 905f333 commit 46e3a24
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 32 deletions.
57 changes: 30 additions & 27 deletions docs/extend/authorization.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,41 +146,41 @@ should implement the following two methods:
**Request**:

```json
{
"User": "The user identification"
"UserAuthNMethod": "The authentication method used"
"RequestMethod": "The HTTP method"
"RequestUri": "The HTTP request URI"
"RequestBody": "Byte array containing the raw HTTP request body"
"RequestHeader": "Byte array containing the raw HTTP request header as a map[string][]string "
{
"User": "The user identification",
"UserAuthNMethod": "The authentication method used",
"RequestMethod": "The HTTP method",
"RequestUri": "The HTTP request URI",
"RequestBody": "Byte array containing the raw HTTP request body",
"RequestHeader": "Byte array containing the raw HTTP request header as a map[string][]string ",
"RequestStatusCode": "Request status code"
}
```

**Response**:

```json
{
"Allow" : "Determined whether the user is allowed or not"
"Msg": "The authorization message"
{
"Allow": "Determined whether the user is allowed or not",
"Msg": "The authorization message",
"Err": "The error message if things go wrong"
}
```

#### /AuthzPlugin.AuthZRes

**Request**:

```json
{
"User": "The user identification"
"UserAuthNMethod": "The authentication method used"
"RequestMethod": "The HTTP method"
"RequestUri": "The HTTP request URI"
"RequestBody": "Byte array containing the raw HTTP request body"
"RequestHeader": "Byte array containing the raw HTTP request header as a map[string][]string"
"RequestStatusCode": "Request status code"
"ResponseBody": "Byte array containing the raw HTTP response body"
"ResponseHeader": "Byte array containing the raw HTTP response header as a map[string][]string"
"User": "The user identification",
"UserAuthNMethod": "The authentication method used",
"RequestMethod": "The HTTP method",
"RequestUri": "The HTTP request URI",
"RequestBody": "Byte array containing the raw HTTP request body",
"RequestHeader": "Byte array containing the raw HTTP request header as a map[string][]string",
"RequestStatusCode": "Request status code",
"ResponseBody": "Byte array containing the raw HTTP response body",
"ResponseHeader": "Byte array containing the raw HTTP response header as a map[string][]string",
"ResponseStatusCode":"Response status code"
}
```
Expand All @@ -189,11 +189,12 @@ should implement the following two methods:

```json
{
"Allow" : "Determined whether the user is allowed or not"
"Msg": "The authorization message"
"ModifiedBody": "Byte array containing a modified body of the raw HTTP body (or nil if no changes required)"
"ModifiedHeader": "Byte array containing a modified header of the HTTP response (or nil if no changes required)"
"ModifiedStatusCode": "int containing the modified version of the status code (or 0 if not change is required)"
"Allow": "Determined whether the user is allowed or not",
"Msg": "The authorization message",
"Err": "The error message if things go wrong",
"ModifiedBody": "Byte array containing a modified body of the raw HTTP body (or nil if no changes required)",
"ModifiedHeader": "Byte array containing a modified header of the HTTP response (or nil if no changes required)",
"ModifiedStatusCode": "int containing the modified version of the status code (or 0 if not change is required)"
}
```

Expand Down Expand Up @@ -222,7 +223,8 @@ Request body | []byte | Raw request body
Name | Type | Description
--------|--------|----------------------------------------------------------------------------------
Allow | bool | Boolean value indicating whether the request is allowed or denied
Message | string | Authorization message (will be returned to the client in case the access is denied)
Msg | string | Authorization message (will be returned to the client in case the access is denied)
Err | string | Error message (will be returned to the client in case the plugin encounter an error)

### Response authorization

Expand All @@ -249,4 +251,5 @@ Response body | []byte | Raw docker daemon response body
Name | Type | Description
--------|--------|----------------------------------------------------------------------------------
Allow | bool | Boolean value indicating whether the response is allowed or denied
Message | string | Authorization message (will be returned to the client in case the access is denied)
Msg | string | Authorization message (will be returned to the client in case the access is denied)
Err | string | Error message (will be returned to the client in case the plugin encounter an error)
42 changes: 38 additions & 4 deletions integration-cli/docker_cli_authz_unix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ import (
"github.com/go-check/check"
)

const testAuthZPlugin = "authzplugin"
const unauthorizedMessage = "User unauthorized authz plugin"
const containerListAPI = "/containers/json"
const (
testAuthZPlugin = "authzplugin"
unauthorizedMessage = "User unauthorized authz plugin"
errorMessage = "something went wrong..."
containerListAPI = "/containers/json"
)

func init() {
check.Suite(&DockerAuthzSuite{
Expand Down Expand Up @@ -66,9 +69,12 @@ func (s *DockerAuthzSuite) SetUpSuite(c *check.C) {
})

mux.HandleFunc("/AuthZPlugin.AuthZReq", func(w http.ResponseWriter, r *http.Request) {
if s.ctrl.reqRes.Err != "" {
w.WriteHeader(http.StatusInternalServerError)
}
b, err := json.Marshal(s.ctrl.reqRes)
w.Write(b)
c.Assert(err, check.IsNil)
w.Write(b)
defer r.Body.Close()
body, err := ioutil.ReadAll(r.Body)
c.Assert(err, check.IsNil)
Expand All @@ -88,6 +94,9 @@ func (s *DockerAuthzSuite) SetUpSuite(c *check.C) {
})

mux.HandleFunc("/AuthZPlugin.AuthZRes", func(w http.ResponseWriter, r *http.Request) {
if s.ctrl.resRes.Err != "" {
w.WriteHeader(http.StatusInternalServerError)
}
b, err := json.Marshal(s.ctrl.resRes)
c.Assert(err, check.IsNil)
w.Write(b)
Expand Down Expand Up @@ -214,6 +223,31 @@ func (s *DockerAuthzSuite) TestAuthZPluginDenyResponse(c *check.C) {
c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: %s\n", unauthorizedMessage))
}

func (s *DockerAuthzSuite) TestAuthZPluginErrorResponse(c *check.C) {
err := s.d.Start("--authz-plugin=" + testAuthZPlugin)
c.Assert(err, check.IsNil)
s.ctrl.reqRes.Allow = true
s.ctrl.resRes.Err = errorMessage

// Ensure command is blocked
res, err := s.d.Cmd("ps")
c.Assert(err, check.NotNil)

c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: Plugin Error: %s, %s\n", errorMessage, authorization.AuthZApiResponse))
}

func (s *DockerAuthzSuite) TestAuthZPluginErrorRequest(c *check.C) {
err := s.d.Start("--authz-plugin=" + testAuthZPlugin)
c.Assert(err, check.IsNil)
s.ctrl.reqRes.Err = errorMessage

// Ensure command is blocked
res, err := s.d.Cmd("ps")
c.Assert(err, check.NotNil)

c.Assert(res, check.Equals, fmt.Sprintf("Error response from daemon: Plugin Error: %s, %s\n", errorMessage, authorization.AuthZApiRequest))
}

// assertURIRecorded verifies that the given URI was sent and recorded in the authz plugin
func assertURIRecorded(c *check.C, uris []string, uri string) {

Expand Down
4 changes: 3 additions & 1 deletion pkg/authorization/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,12 @@ type Request struct {

// Response represents authZ plugin response
type Response struct {

// Allow indicating whether the user is allowed or not
Allow bool `json:"Allow"`

// Msg stores the authorization message
Msg string `json:"Msg,omitempty"`

// Err stores a message in case there's an error
Err string `json:"Err,omitempty"`
}
8 changes: 8 additions & 0 deletions pkg/authorization/authz.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ func (a *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) error {
return err
}

if authRes.Err != "" {
return fmt.Errorf(authRes.Err)
}

if !authRes.Allow {
return fmt.Errorf(authRes.Msg)
}
Expand All @@ -107,6 +111,10 @@ func (a *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error {
return err
}

if authRes.Err != "" {
return fmt.Errorf(authRes.Err)
}

if !authRes.Allow {
return fmt.Errorf(authRes.Msg)
}
Expand Down
31 changes: 31 additions & 0 deletions pkg/authorization/authz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,37 @@ import (

const pluginAddress = "authzplugin.sock"

func TestAuthZRequestPluginError(t *testing.T) {
server := authZPluginTestServer{t: t}
go server.start()
defer server.stop()

authZPlugin := createTestPlugin(t)

request := Request{
User: "user",
RequestBody: []byte("sample body"),
RequestURI: "www.authz.com",
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.Fatalf("Response must be equal")
}
if !reflect.DeepEqual(request, server.recordedRequest) {
t.Fatalf("Requests must be equal")
}
}

func TestAuthZRequestPlugin(t *testing.T) {
server := authZPluginTestServer{t: t}
go server.start()
Expand Down

0 comments on commit 46e3a24

Please sign in to comment.