Skip to content

Commit

Permalink
Implement granting and revoking role memberships
Browse files Browse the repository at this point in the history
  • Loading branch information
martinalbert committed Aug 11, 2023
1 parent 23b0cfa commit f5f680a
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 13 deletions.
61 changes: 61 additions & 0 deletions pkg/connector/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
ent "github.com/conductorone/baton-sdk/pkg/types/entitlement"
grant "github.com/conductorone/baton-sdk/pkg/types/grant"
rs "github.com/conductorone/baton-sdk/pkg/types/resource"
"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
"go.uber.org/zap"
)

type roleResourceType struct {
Expand Down Expand Up @@ -142,6 +144,65 @@ func (r *roleResourceType) Grants(ctx context.Context, resource *v2.Resource, to
return rv, pageToken, annotations, nil
}

func (r *roleResourceType) Grant(ctx context.Context, principal *v2.Resource, entitlement *v2.Entitlement) (annotations.Annotations, error) {
l := ctxzap.Extract(ctx)

if principal.Id.ResourceType != resourceTypeUser.Id {
l.Warn(
"hubspot-connector: only users can be granted role membership",
zap.String("principal_id", principal.Id.Resource),
zap.String("principal_type", principal.Id.ResourceType),
)

return nil, fmt.Errorf("hubspot-connector: only users can be granted role membership")
}

roleId := entitlement.Resource.Id.Resource

// no need to check current user role - only rewriting is supported
// grant role membership
annos, err := r.client.UpdateUser(
ctx,
principal.Id.Resource,
&hubspot.UpdateUserPayload{
RoleId: roleId,
},
)
if err != nil {
return nil, fmt.Errorf("hubspot-connector: failed to update user: %w", err)
}

return annos, nil
}

func (r *roleResourceType) Revoke(ctx context.Context, grant *v2.Grant) (annotations.Annotations, error) {
l := ctxzap.Extract(ctx)

principal := grant.Principal

if principal.Id.ResourceType != resourceTypeUser.Id {
l.Warn(
"hubspot-connector: only users can have role membership revoked",
zap.String("principal_id", principal.Id.Resource),
zap.String("principal_type", principal.Id.ResourceType),
)

return nil, fmt.Errorf("hubspot-connector: only users can have role membership revoked")
}

// revoke role membership
annos, err := r.client.UpdateUser(
ctx,
principal.Id.Resource,
&hubspot.UpdateUserPayload{},
)
if err != nil {
return nil, fmt.Errorf("hubspot-connector: failed to update user: %w", err)
}

return annos, nil
}

func roleBuilder(client *hubspot.Client) *roleResourceType {
return &roleResourceType{
resourceType: resourceTypeRole,
Expand Down
74 changes: 61 additions & 13 deletions pkg/hubspot/client.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package hubspot

import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
Expand All @@ -18,6 +20,7 @@ import (

const BaseURL = "https://api.hubapi.com/"
const UsersBaseURL = BaseURL + "settings/v3/users"
const UserBaseURL = BaseURL + "settings/v3/users/%s"
const TeamsBaseURL = BaseURL + "settings/v3/users/teams"
const RolesBaseURL = BaseURL + "settings/v3/users/roles"
const AccountBaseURL = BaseURL + "account-info/v3/details"
Expand Down Expand Up @@ -71,7 +74,7 @@ func (c *Client) GetUsers(ctx context.Context, getUsersVars GetUsersVars) ([]Use
queryParams := setupPaginationQuery(url.Values{}, getUsersVars.Limit, getUsersVars.After)
var userResponse UsersResponse

annos, err := c.doRequest(
annos, err := c.get(
ctx,
UsersBaseURL,
&userResponse,
Expand All @@ -92,7 +95,7 @@ func (c *Client) GetUsers(ctx context.Context, getUsersVars GetUsersVars) ([]Use
// GetTeams returns all teams for a single account.
func (c *Client) GetTeams(ctx context.Context) ([]Team, annotations.Annotations, error) {
var teamResponse TeamsResponse
annos, err := c.doRequest(
annos, err := c.get(
ctx,
TeamsBaseURL,
&teamResponse,
Expand All @@ -109,7 +112,7 @@ func (c *Client) GetTeams(ctx context.Context) ([]Team, annotations.Annotations,
// GetAccount returns information about single account.
func (c *Client) GetAccount(ctx context.Context) (Account, annotations.Annotations, error) {
var accountResponse Account
annos, err := c.doRequest(
annos, err := c.get(
ctx,
AccountBaseURL,
&accountResponse,
Expand All @@ -125,12 +128,10 @@ func (c *Client) GetAccount(ctx context.Context) (Account, annotations.Annotatio

// GetUser returns information about a single user.
func (c *Client) GetUser(ctx context.Context, userId string) (User, annotations.Annotations, error) {
url := fmt.Sprint(UsersBaseURL, "/", userId)

var userResponse User
annos, err := c.doRequest(
annos, err := c.get(
ctx,
url,
fmt.Sprintf(UserBaseURL, userId),
&userResponse,
nil,
)
Expand All @@ -144,17 +145,63 @@ func (c *Client) GetUser(ctx context.Context, userId string) (User, annotations.
// GetRoles returns all roles under a single account.
func (c *Client) GetRoles(ctx context.Context) ([]Role, annotations.Annotations, error) {
var rolesResponse RolesResponse
annos, err := c.doRequest(ctx, RolesBaseURL, &rolesResponse, nil)

annos, err := c.get(ctx, RolesBaseURL, &rolesResponse, nil)
if err != nil {
return nil, nil, err
}

return rolesResponse.Results, annos, nil
}

func (c *Client) doRequest(ctx context.Context, url string, resourceResponse interface{}, queryParams url.Values) (annotations.Annotations, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
type UpdateUserPayload struct {
RoleId string `json:"roleId,omitempty"`
PrimaryTeamId string `json:"primaryTeamId,omitempty"`
SecondaryTeamIds []string `json:"secondaryTeamIds,omitempty"`
}

// UpdateUser updates information about provided user.
func (c *Client) UpdateUser(ctx context.Context, userId string, payload *UpdateUserPayload) (annotations.Annotations, error) {
annos, err := c.put(
ctx,
fmt.Sprintf(UserBaseURL, userId),
payload,
nil,
)
if err != nil {
return nil, err
}

return annos, nil
}

func (c *Client) get(ctx context.Context, url string, resourceResponse interface{}, queryParams url.Values) (annotations.Annotations, error) {
return c.doRequest(ctx, url, http.MethodGet, nil, resourceResponse, queryParams)
}

func (c *Client) put(ctx context.Context, url string, data interface{}, resourceResponse interface{}) (annotations.Annotations, error) {
return c.doRequest(ctx, url, http.MethodPut, data, resourceResponse, nil)
}

func (c *Client) doRequest(
ctx context.Context,
urlAddress string,
method string,
data interface{},
resourceResponse interface{},
queryParams url.Values,
) (annotations.Annotations, error) {
var body io.Reader

if data != nil {
jsonBody, err := json.Marshal(data)
if err != nil {
return nil, err
}

body = bytes.NewBuffer(jsonBody)
}

req, err := http.NewRequestWithContext(ctx, method, urlAddress, body)
if err != nil {
return nil, err
}
Expand All @@ -163,8 +210,9 @@ func (c *Client) doRequest(ctx context.Context, url string, resourceResponse int
req.URL.RawQuery = queryParams.Encode()
}

req.Header.Add("authorization", fmt.Sprint("Bearer ", c.accessToken))
req.Header.Add("accept", "application/json")
req.Header.Add("Authorization", fmt.Sprint("Bearer ", c.accessToken))
req.Header.Add("Accept", "application/json")
req.Header.Add("Content-Type", "application/json")

rawResponse, err := c.httpClient.Do(req)
if err != nil {
Expand Down

0 comments on commit f5f680a

Please sign in to comment.