Skip to content

Commit

Permalink
Account specifier now optional on resource IDs
Browse files Browse the repository at this point in the history
When an account is not specified, the configured
account is used.
  • Loading branch information
john-odonnell committed Jan 25, 2023
1 parent aa3595d commit 3582065
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 21 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Remove all usage of Conjur v4
[cyberark/conjur-api-go#139](https://github.com/cyberark/conjur-api-go/pull/139)

### Changed
- Resource IDs can now be partially-qualified, adhering to the form
[<account>:]<kind>:<identifier>.
[cyberark/conjur-api-go#153](https://github.com/cyberark/conjur-api-go/pull/153)

## [0.10.2] - 2022-11-14

### Fixed
Expand Down
51 changes: 30 additions & 21 deletions conjurapi/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,13 +306,10 @@ func (c *Client) OidcAuthenticateRequest(code, nonce, code_verifier string) (*ht
}

func (c *Client) RotateAPIKeyRequest(roleID string) (*http.Request, error) {
account, _, _, err := parseID(roleID)
_, _, _, err := c.parseID(roleID)
if err != nil {
return nil, err
}
if account != c.config.Account {
return nil, fmt.Errorf("Account of '%s' must match the configured account '%s'", roleID, c.config.Account)
}

rotateURL := makeRouterURL(c.authnURL(), "api_key").withFormattedQuery("role=%s", roleID).String()

Expand Down Expand Up @@ -345,7 +342,7 @@ func (c *Client) ChangeUserPasswordRequest(username string, password string, new
// 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)
account, kind, id, err := c.parseID(resourceID)
if err != nil {
return nil, err
}
Expand All @@ -364,12 +361,12 @@ func (c *Client) CheckPermissionRequest(resourceID, privilege string) (*http.Req
// 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)
account, kind, id, err := c.parseID(resourceID)
if err != nil {
return nil, err
}

roleAccount, roleKind, roleIdentifier, err := parseID(roleID)
roleAccount, roleKind, roleIdentifier, err := c.parseID(roleID)
if err != nil {
return nil, err
}
Expand All @@ -387,7 +384,7 @@ func (c *Client) CheckPermissionForRoleRequest(resourceID, roleID, privilege str
}

func (c *Client) ResourceRequest(resourceID string) (*http.Request, error) {
account, kind, id, err := parseID(resourceID)
account, kind, id, err := c.parseID(resourceID)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -431,7 +428,7 @@ func (c *Client) ResourcesRequest(filter *ResourceFilter) (*http.Request, error)
}

func (c *Client) PermittedRolesRequest(resourceID string, privilege string) (*http.Request, error) {
account, kind, id, err := parseID(resourceID)
account, kind, id, err := c.parseID(resourceID)
if err != nil {
return nil, err
}
Expand All @@ -445,7 +442,7 @@ func (c *Client) PermittedRolesRequest(resourceID string, privilege string) (*ht
}

func (c *Client) RoleRequest(roleID string) (*http.Request, error) {
account, kind, id, err := parseID(roleID)
account, kind, id, err := c.parseID(roleID)
if err != nil {
return nil, err
}
Expand All @@ -459,7 +456,7 @@ func (c *Client) RoleRequest(roleID string) (*http.Request, error) {
}

func (c *Client) RoleMembersRequest(roleID string) (*http.Request, error) {
account, kind, id, err := parseID(roleID)
account, kind, id, err := c.parseID(roleID)
if err != nil {
return nil, err
}
Expand All @@ -473,7 +470,7 @@ func (c *Client) RoleMembersRequest(roleID string) (*http.Request, error) {
}

func (c *Client) RoleMembershipsRequest(roleID string) (*http.Request, error) {
account, kind, id, err := parseID(roleID)
account, kind, id, err := c.parseID(roleID)
if err != nil {
return nil, err
}
Expand All @@ -489,7 +486,7 @@ func (c *Client) RoleMembershipsRequest(roleID string) (*http.Request, error) {
func (c *Client) LoadPolicyRequest(mode PolicyMode, policyID string, policy io.Reader) (*http.Request, error) {
fullPolicyID := makeFullId(c.config.Account, "policy", policyID)

account, kind, id, err := parseID(fullPolicyID)
account, kind, id, err := c.parseID(fullPolicyID)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -650,15 +647,15 @@ func (c *Client) createHostURL() string {
}

func (c *Client) variableURL(variableID string) (string, error) {
account, kind, id, err := parseID(variableID)
account, kind, id, err := c.parseID(variableID)
if err != nil {
return "", err
}
return makeRouterURL(c.secretsURL(account), kind, url.PathEscape(id)).String(), nil
}

func (c *Client) variableWithVersionURL(variableID string, version int) (string, error) {
account, kind, id, err := parseID(variableID)
account, kind, id, err := c.parseID(variableID)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -717,13 +714,25 @@ func makeFullId(account, kind, id string) string {
return strings.Join(tokens, ":")
}

func parseID(fullID string) (account, kind, id string, err error) {
tokens := strings.SplitN(fullID, ":", 3)
if len(tokens) != 3 {
err = fmt.Errorf("Id '%s' must be fully qualified", fullID)
return
// parseID accepts as argument a resource ID and returns its components - account,
// resource kind, and identifier. The provided ID can either be fully- or
// partially-qualified. If the ID is only partially-qualified, the configured
// account will be returned.
//
// Examples:
// c.parseID("dev:user:alice") => "dev", "user", "alice", nil
// c.parseID("user:alice") => "dev", "user", "alice", nil
// c.parseID("prod:user:alice") => "prod", "user", "alice", nil
// c.parseID("malformed") => "", "", "". error
func (c *Client) parseID(id string) (account, kind, identifier string, err error) {
tokens := strings.SplitN(id, ":", 3)
if len(tokens) == 3 {
return tokens[0], tokens[1], tokens[2], nil
} else if len(tokens) == 2 {
return c.config.Account, tokens[0], tokens[1], nil
} else {
return "", "", "", fmt.Errorf("Malformed ID '%s': must be fully- or partially-qualified, of form [<account>:]<kind>:<identifier>", id)
}
return tokens[0], tokens[1], tokens[2], nil
}

func NewClient(config Config) (*Client, error) {
Expand Down
8 changes: 8 additions & 0 deletions conjurapi/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ func TestClient_CheckPermission(t *testing.T) {
"Check a permission on a non-existent resource",
checkAndAssert(conjur, assertFailure, "cucumber:variable:foobar"),
)
t.Run(
"Check a permission on account-less resource",
checkAndAssert(conjur, assertSuccess, "variable:db-password"),
)
}

func TestClient_CheckPermissionForRole(t *testing.T) {
Expand All @@ -76,6 +80,10 @@ func TestClient_CheckPermissionForRole(t *testing.T) {
"Check a permission with empty role",
checkAndAssert(conjur, assertError, "cucumber:variable:db-password", ""),
)
t.Run(
"Check a permission for account-less role",
checkAndAssert(conjur, assertSuccess, "variable:db-password", "user:alice"),
)
}

func TestClient_ResourceExists(t *testing.T) {
Expand Down

0 comments on commit 3582065

Please sign in to comment.