Skip to content

Commit d3d7543

Browse files
authored
[BB-1029] Additional logging and improved Workato API error handling (#5)
* More debug logging * Handle supported API errors * Add sections for required client role permissions * Stub disable-custom-roles-sync flag * Only sync base roles with custom roles sync is disabled * fixup * Handle folder sync when custom roles sync is disabled * Update README.md * Disable custom roles sync in ci * Documention list custom roles permission
1 parent 48bb059 commit d3d7543

File tree

12 files changed

+102
-43
lines changed

12 files changed

+102
-43
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ jobs:
5353
BATON_WORKATO_API_KEY: ${{ secrets.BATON_WORKATO_API_KEY }}
5454
BATON_CONNECTOR: ./baton-workato
5555
BATON_WORKATO_ENV: dev
56+
BATON_DISABLE_CUSTOM_ROLES_SYNC: true
5657

5758
steps:
5859
- name: Install Go

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ Check out [Baton](https://github.com/conductorone/baton) to learn more the proje
1414

1515
You must be an Admin or have a role with access to API Clients.
1616

17+
### Required Client Role permissions
18+
19+
| Area | Section | Action | API Endpoint |
20+
|--------------|----------------------|-----------------------------|------------------------------------|
21+
| **Projects** | Projects & Folders | List projects | `GET /api/projects` |
22+
| | Projects & Folders | List folders | `GET /api/folders` |
23+
| | | List custom roles | `GET /api/roles` |
24+
| **Admin** | Collaborators | Get collaborators | `GET /api/members` |
25+
| | Collaborators | Get collaborator | `GET /api/members/:id` |
26+
| | Collaborators | Update collaborator’s roles | `PUT /api/members/:id` |
27+
| | Collaborators | Get collaborator privileges | `GET /api/members/:id/privileges` |
28+
1729
### Using Workato commercial platform:
1830

1931
Generate an API KEY:
@@ -91,6 +103,7 @@ Available Commands:
91103
Flags:
92104
--client-id string The client ID used to authenticate with ConductorOne ($BATON_CLIENT_ID)
93105
--client-secret string The client secret used to authenticate with ConductorOne ($BATON_CLIENT_SECRET)
106+
--disable-custom-roles-sync Disable custom roles sync ($BATON_DISABLE_CUSTOM_ROLES_SYNC)
94107
-f, --file string The path to the c1z file to sync with ($BATON_FILE) (default "sync.c1z")
95108
-h, --help help for baton-workato
96109
--log-format string The output format for logs: json, console ($BATON_LOG_FORMAT) (default "json")

cmd/baton-workato/conf/config.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,20 @@ var (
2929
field.WithDefaultValue("dev"),
3030
)
3131

32+
DisableCustomRolesSync = field.BoolField(
33+
"disable-custom-roles-sync",
34+
field.WithDescription("Disable custom roles sync"),
35+
field.WithDefaultValue(false),
36+
)
37+
3238
// ConfigurationFields defines the external configuration required for the
3339
// connector to run. Note: these fields can be marked as optional or
3440
// required.
3541
ConfigurationFields = []field.SchemaField{
3642
ApiKeyField,
3743
WorkatoDataCenterFiekd,
3844
WorkatoEnv,
45+
DisableCustomRolesSync,
3946
}
4047

4148
// FieldRelationships defines relationships between the fields listed in

cmd/baton-workato/main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,14 @@ func getConnector(ctx context.Context, v *viper.Viper) (types.ConnectorServer, e
6262
return nil, err
6363
}
6464

65+
disableCustomRolesSync := v.GetBool(conf.DisableCustomRolesSync.FieldName)
66+
6567
workatoClient, err := client.NewWorkatoClient(ctx, key, dataCenterUrl)
6668
if err != nil {
6769
return nil, err
6870
}
6971

70-
cb, err := connector.New(ctx, workatoClient, env)
72+
cb, err := connector.New(ctx, workatoClient, env, disableCustomRolesSync)
7173
if err != nil {
7274
l.Error("error creating connector", zap.Error(err))
7375
return nil, err

pkg/connector/client/colaborator.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"fmt"
66
"net/http"
77

8+
"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
9+
"go.uber.org/zap"
810
"google.golang.org/grpc/codes"
911
"google.golang.org/grpc/status"
1012
)
@@ -21,8 +23,8 @@ func (c *WorkatoClient) GetCollaborators(ctx context.Context) ([]Collaborator, e
2123
}
2224

2325
func (c *WorkatoClient) GetCollaboratorPrivileges(ctx context.Context, id int) ([]*CollaboratorPrivilege, error) {
26+
l := ctxzap.Extract(ctx)
2427
var response CommonPagination[*CollaboratorPrivilege]
25-
2628
pathString := fmt.Sprintf(GetCollaboratorByIdPath, id)
2729

2830
err := c.doRequest(ctx, http.MethodGet, c.getPath(pathString), &response, nil)
@@ -31,6 +33,7 @@ func (c *WorkatoClient) GetCollaboratorPrivileges(ctx context.Context, id int) (
3133
}
3234

3335
if len(response.Data) != 1 {
36+
l.Warn("expected 1 collaborator, got %d. API client may have insufficient permissions", zap.Int("count", len(response.Data)))
3437
return nil, status.Errorf(codes.NotFound, "baton-workato: expected 1 collaborator, got %d", len(response.Data))
3538
}
3639

pkg/connector/client/helpers.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ func (c *WorkatoClient) doRequest(ctx context.Context, method string, urlAddress
8080

8181
defer resp.Body.Close()
8282

83-
if resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusBadRequest {
83+
// Handle supported API errors https://docs.workato.com/en/workato-api.html#http-response-codes
84+
switch resp.StatusCode {
85+
case http.StatusNotFound, http.StatusBadRequest, http.StatusUnauthorized, http.StatusForbidden, http.StatusInternalServerError:
8486
return getError(err, resp)
8587
}
8688

pkg/connector/collaborator.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2"
1010
"github.com/conductorone/baton-sdk/pkg/annotations"
1111
"github.com/conductorone/baton-sdk/pkg/pagination"
12+
13+
"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
1214
)
1315

1416
type collaboratorBuilder struct {
@@ -22,6 +24,9 @@ func (o *collaboratorBuilder) ResourceType(ctx context.Context) *v2.ResourceType
2224
// List returns all the users from the database as resource objects.
2325
// Users include a UserTrait because they are the 'shape' of a standard user.
2426
func (o *collaboratorBuilder) List(ctx context.Context, parentResourceID *v2.ResourceId, pToken *pagination.Token) ([]*v2.Resource, string, annotations.Annotations, error) {
27+
l := ctxzap.Extract(ctx)
28+
l.Debug("Listing collaborators")
29+
2530
collaborators, err := o.client.GetCollaborators(ctx)
2631
if err != nil {
2732
return nil, "", nil, err

pkg/connector/connector.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,18 @@ import (
1414
)
1515

1616
type Connector struct {
17-
client *client.WorkatoClient
18-
env workato.Environment
17+
client *client.WorkatoClient
18+
env workato.Environment
19+
disableCustomRolesSync bool
1920
}
2021

2122
// ResourceSyncers returns a ResourceSyncer for each resource type that should be synced from the upstream service.
2223
func (d *Connector) ResourceSyncers(ctx context.Context) []connectorbuilder.ResourceSyncer {
2324
return []connectorbuilder.ResourceSyncer{
2425
newCollaboratorBuilder(d.client),
2526
newPrivilegeBuilder(d.client, d.env),
26-
newRoleBuilder(d.client, d.env),
27-
newFolderBuilder(d.client, d.env),
27+
newRoleBuilder(d.client, d.env, d.disableCustomRolesSync),
28+
newFolderBuilder(d.client, d.env, d.disableCustomRolesSync),
2829
newProjectBuilder(d.client),
2930
}
3031
}
@@ -50,9 +51,10 @@ func (d *Connector) Validate(ctx context.Context) (annotations.Annotations, erro
5051
}
5152

5253
// New returns a new instance of the connector.
53-
func New(ctx context.Context, workatoClient *client.WorkatoClient, env workato.Environment) (*Connector, error) {
54+
func New(ctx context.Context, workatoClient *client.WorkatoClient, env workato.Environment, disableCustomRolesSync bool) (*Connector, error) {
5455
return &Connector{
55-
client: workatoClient,
56-
env: env,
56+
client: workatoClient,
57+
env: env,
58+
disableCustomRolesSync: disableCustomRolesSync,
5759
}, nil
5860
}

pkg/connector/folder.go

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ const (
2626
)
2727

2828
type folderBuilder struct {
29-
client *client.WorkatoClient
30-
cache *collaboratorCache
31-
roleCache *roleCache
29+
client *client.WorkatoClient
30+
cache *collaboratorCache
31+
roleCache *roleCache
32+
disableCustomRolesSync bool
3233
}
3334

3435
func (o *folderBuilder) ResourceType(ctx context.Context) *v2.ResourceType {
@@ -39,6 +40,7 @@ func (o *folderBuilder) ResourceType(ctx context.Context) *v2.ResourceType {
3940
// Users include a UserTrait because they are the 'shape' of a standard user.
4041
func (o *folderBuilder) List(ctx context.Context, parentResourceID *v2.ResourceId, pToken *pagination.Token) ([]*v2.Resource, string, annotations.Annotations, error) {
4142
l := ctxzap.Extract(ctx)
43+
l.Debug("Listing folders")
4244

4345
// Init cache
4446
if pToken.Token == "" && parentResourceID == nil {
@@ -47,9 +49,11 @@ func (o *folderBuilder) List(ctx context.Context, parentResourceID *v2.ResourceI
4749
return nil, "", nil, err
4850
}
4951

50-
err = o.roleCache.buildCache(ctx)
51-
if err != nil {
52-
return nil, "", nil, err
52+
if !o.disableCustomRolesSync {
53+
err = o.roleCache.buildCache(ctx)
54+
if err != nil {
55+
return nil, "", nil, err
56+
}
5357
}
5458
}
5559

@@ -179,7 +183,7 @@ func (o *folderBuilder) Grants(ctx context.Context, resource *v2.Resource, pToke
179183
}
180184
}
181185

182-
if state.ResourceTypeID == roleResourceType.Id {
186+
if state.ResourceTypeID == roleResourceType.Id && !o.disableCustomRolesSync {
183187
folderId, err := strconv.Atoi(resource.Id.Resource)
184188
if err != nil {
185189
return nil, "", nil, err
@@ -213,11 +217,12 @@ func (o *folderBuilder) Grants(ctx context.Context, resource *v2.Resource, pToke
213217
return rv, nextToken, nil, nil
214218
}
215219

216-
func newFolderBuilder(client *client.WorkatoClient, env workato.Environment) *folderBuilder {
220+
func newFolderBuilder(client *client.WorkatoClient, env workato.Environment, disableCustomRolesSync bool) *folderBuilder {
217221
return &folderBuilder{
218-
client: client,
219-
cache: newCollaboratorCache(client, env),
220-
roleCache: newRoleCache(client),
222+
client: client,
223+
cache: newCollaboratorCache(client, env),
224+
roleCache: newRoleCache(client),
225+
disableCustomRolesSync: disableCustomRolesSync,
221226
}
222227
}
223228

pkg/connector/privilege.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func (o *privilegeBuilder) ResourceType(ctx context.Context) *v2.ResourceType {
3434
// Users include a UserTrait because they are the 'shape' of a standard user.
3535
func (o *privilegeBuilder) List(ctx context.Context, parentResourceID *v2.ResourceId, pToken *pagination.Token) ([]*v2.Resource, string, annotations.Annotations, error) {
3636
l := ctxzap.Extract(ctx)
37+
l.Debug("Listing privileges")
3738

3839
if pToken == nil || pToken.Token == "" {
3940
err := o.cache.buildCache(ctx)
@@ -62,7 +63,7 @@ func (o *privilegeBuilder) List(ctx context.Context, parentResourceID *v2.Resour
6263
func (o *privilegeBuilder) Entitlements(_ context.Context, resource *v2.Resource, _ *pagination.Token) ([]*v2.Entitlement, string, annotations.Annotations, error) {
6364
var rv []*v2.Entitlement
6465
assigmentOptions := []entitlement.EntitlementOption{
65-
entitlement.WithGrantableTo(collaboratorResourceType, roleResourceType),
66+
entitlement.WithGrantableTo(collaboratorResourceType),
6667
entitlement.WithDescription(fmt.Sprintf("Assigned %s to scopes", collaboratorResourceType.DisplayName)),
6768
entitlement.WithDisplayName(fmt.Sprintf("%s have %s`", collaboratorResourceType.DisplayName, resource.DisplayName)),
6869
}

0 commit comments

Comments
 (0)