diff --git a/conjurapi/client.go b/conjurapi/client.go index 13b297c..87ea98b 100644 --- a/conjurapi/client.go +++ b/conjurapi/client.go @@ -342,19 +342,43 @@ func (c *Client) ChangeUserPasswordRequest(username string, password string, new return req, nil } -func (c *Client) CheckPermissionRequest(resourceID string, roleID string, privilege string) (*http.Request, error) { +// CheckPermissionForRoleRequest crafts an HTTP request to Conjur's /resource endpoint +// to check if the authenticated user has the given privilege on the given resourceID. +func (c *Client) CheckPermissionRequest(resourceID, privilege string) (*http.Request, error) { account, kind, id, err := parseID(resourceID) if err != nil { return nil, err } - var checkURL string - if len(roleID) != 0 { - checkURL = makeRouterURL(c.resourcesURL(account), kind, url.QueryEscape(id)).withFormattedQuery("check=true&role=%s&privilege=%s", url.QueryEscape(roleID), url.QueryEscape(privilege)).String() - } else { - checkURL = makeRouterURL(c.resourcesURL(account), kind, url.QueryEscape(id)).withFormattedQuery("check=true&privilege=%s", url.QueryEscape(privilege)).String() + query := fmt.Sprintf("check=true&privilege=%s", url.QueryEscape(privilege)) + + checkURL := makeRouterURL(c.resourcesURL(account), kind, url.QueryEscape(id)).withQuery(query).String() + + return http.NewRequest( + "GET", + checkURL, + nil, + ) +} + +// CheckPermissionForRoleRequest crafts an HTTP request to Conjur's /resource endpoint +// to check if a given role has the given privilege on the given resourceID. +func (c *Client) CheckPermissionForRoleRequest(resourceID, roleID, privilege string) (*http.Request, error) { + account, kind, id, err := parseID(resourceID) + if err != nil { + return nil, err } + roleAccount, roleKind, roleIdentifier, err := parseID(roleID) + if err != nil { + return nil, err + } + fullyQualifiedRoleID := strings.Join([]string{roleAccount, roleKind, roleIdentifier}, ":") + + query := fmt.Sprintf("check=true&privilege=%s&role=%s", url.QueryEscape(privilege), url.QueryEscape(fullyQualifiedRoleID)) + + checkURL := makeRouterURL(c.resourcesURL(account), kind, url.QueryEscape(id)).withQuery(query).String() + return http.NewRequest( "GET", checkURL, diff --git a/conjurapi/resource.go b/conjurapi/resource.go index d1a5e47..8de7d0e 100644 --- a/conjurapi/resource.go +++ b/conjurapi/resource.go @@ -3,6 +3,7 @@ package conjurapi import ( "encoding/json" "fmt" + "net/http" "github.com/cyberark/conjur-api-go/conjurapi/response" ) @@ -16,12 +17,27 @@ type ResourceFilter struct { // CheckPermission determines whether the authenticated user has a specified privilege // on a resource. -func (c *Client) CheckPermission(resourceID string, roleID string, privilege string) (bool, error) { - req, err := c.CheckPermissionRequest(resourceID, roleID, privilege) +func (c *Client) CheckPermission(resourceID string, privilege string) (bool, error) { + req, err := c.CheckPermissionRequest(resourceID, privilege) if err != nil { return false, err } + return c.processPermissionCheck(req) +} + +// CheckPermissionForRole determines whether the provided role has a specific +// privilege on a resource. +func (c *Client) CheckPermissionForRole(resourceID string, roleID string, privilege string) (bool, error) { + req, err := c.CheckPermissionForRoleRequest(resourceID, roleID, privilege) + if err != nil { + return false, err + } + + return c.processPermissionCheck(req) +} + +func (c *Client) processPermissionCheck(req *http.Request) (bool, error) { resp, err := c.SubmitRequest(req) if err != nil { return false, err diff --git a/conjurapi/resource_test.go b/conjurapi/resource_test.go index 5be051d..c646917 100644 --- a/conjurapi/resource_test.go +++ b/conjurapi/resource_test.go @@ -6,36 +6,80 @@ import ( "github.com/stretchr/testify/assert" ) -func TestClient_CheckPermission(t *testing.T) { - checkAllowed := func(conjur *Client, id string, role string) func(t *testing.T) { - return func(t *testing.T) { - allowed, err := conjur.CheckPermission(id, role, "execute") +type checkAssertion func(t *testing.T, result bool, err error) - assert.NoError(t, err) - assert.True(t, allowed) - } - } +func assertSuccess(t *testing.T, result bool, err error) { + assert.True(t, result) + assert.NoError(t, err) +} - checkNotAllowed := func(conjur *Client, id string, role string) func(t *testing.T) { - return func(t *testing.T) { - allowed, err := conjur.CheckPermission(id, role, "execute") +func assertFailure(t *testing.T, result bool, err error) { + assert.False(t, result) + assert.NoError(t, err) +} - assert.NoError(t, err) - assert.False(t, allowed) +func assertError(t *testing.T, result bool, err error) { + assert.False(t, result) + assert.Error(t, err) +} + +func checkAndAssert( + conjur *Client, + assertion checkAssertion, + args ...string, +) func(t *testing.T) { + return func(t *testing.T) { + var result bool + var err error + + if len(args) == 1 { + result, err = conjur.CheckPermission(args[0], "execute") + } else if len(args) == 2 { + result, err = conjur.CheckPermissionForRole(args[0], args[1], "execute") } + + assertion(t, result, err) } +} + +func TestClient_CheckPermission(t *testing.T) { + conjur, err := conjurSetup(&Config{}, defaultTestPolicy) + assert.NoError(t, err) + + t.Run( + "Check an allowed permission for default role", + checkAndAssert(conjur, assertSuccess, "cucumber:variable:db-password"), + ) + t.Run( + "Check a permission on a non-existent resource", + checkAndAssert(conjur, assertFailure, "cucumber:variable:foobar"), + ) +} +func TestClient_CheckPermissionForRole(t *testing.T) { conjur, err := conjurSetup(&Config{}, defaultTestPolicy) assert.NoError(t, err) - t.Run("Check an allowed permission for default admin role", checkAllowed(conjur, "cucumber:variable:db-password", "")) - t.Run("Check an allowed permission for a role", checkAllowed(conjur, "cucumber:variable:db-password", "cucumber:user:alice")) - t.Run("Check a permission on a non-existent resource", checkNotAllowed(conjur, "cucumber:variable:foobar", "cucumber:user:alice")) - t.Run("Check no permission for a role", checkNotAllowed(conjur, "cucumber:variable:db-password", "cucumber:host:bob")) + t.Run( + "Check an allowed permission for a role", + checkAndAssert(conjur, assertSuccess, "cucumber:variable:db-password", "cucumber:user:alice"), + ) + t.Run( + "Check a permission on a non-existent resource", + checkAndAssert(conjur, assertFailure, "cucumber:variable:foobar", "cucumber:user:alice"), + ) + t.Run( + "Check no permission for a role", + checkAndAssert(conjur, assertFailure, "cucumber:variable:db-password", "cucumber:host:bob"), + ) + t.Run( + "Check a permission with empty role", + checkAndAssert(conjur, assertError, "cucumber:variable:db-password", ""), + ) } func TestClient_ResourceExists(t *testing.T) { - resourceExistent := func(conjur *Client, id string) func (t *testing.T) { + resourceExistent := func(conjur *Client, id string) func(t *testing.T) { return func(t *testing.T) { exists, err := conjur.ResourceExists(id) assert.NoError(t, err) @@ -43,7 +87,7 @@ func TestClient_ResourceExists(t *testing.T) { } } - resourceNonexistent := func(conjur *Client, id string) func (t *testing.T) { + resourceNonexistent := func(conjur *Client, id string) func(t *testing.T) { return func(t *testing.T) { exists, err := conjur.ResourceExists(id) assert.NoError(t, err)