Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ linters-settings:
rules:
- name: atomic
- name: line-length-limit
arguments: [ 200 ]
arguments: [ 300 ]
# These are functions that we use without checking the errors often. Most of these can't return an error even
# though they implement an interface that can.
- name: unhandled-error
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ CREATE USER baton WITH PASSWORD 'secure-password';
```postgresql
GRANT SELECT ("id", "name", "organizationId", "universalAccess", "universalResourceAccess", "universalQueryLibraryAccess",
"userListAccess", "auditLogAccess", "unpublishedReleaseAccess") ON groups TO baton;
GRANT SELECT, INSERT, UPDATE ("id", "accessLevel") ON group_pages TO baton;
GRANT SELECT, INSERT, UPDATE ("id", "accessLevel"), DELETE ON group_pages TO baton;
GRANT SELECT, INSERT, UPDATE ("id", "accessLevel") ON group_folder_defaults TO baton;
GRANT SELECT, INSERT, UPDATE ("id", "accessLevel") on group_resources TO baton;
GRANT SELECT, INSERT, UPDATE ("id", "accessLevel") on group_resource_folder_defaults TO baton;
Expand Down
81 changes: 80 additions & 1 deletion pkg/client/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,60 @@ func (c *Client) GetGroupPage(ctx context.Context, groupID int64, pageID int64)
return &ret, nil
}

func (c *Client) InsertGroupPage(ctx context.Context, groupID int64, pageID int64, accessLevel string) error {
l := ctxzap.Extract(ctx)
l.Debug("inserting group page", zap.Int64("group_id", groupID), zap.Int64("page_id", pageID), zap.String("access_level", accessLevel))

args := []interface{}{groupID, pageID, accessLevel}
sb := &strings.Builder{}
_, _ = sb.WriteString(`INSERT INTO group_pages ("groupId", "pageId", "accessLevel", "createdAt", "updatedAt") VALUES ($1, $2, $3, NOW(), NOW())`)

if _, err := c.db.Exec(ctx, sb.String(), args...); err != nil {
return err
}

return nil
}

func (c *Client) UpdateGroupPage(ctx context.Context, id int64, accessLevel string) error {
l := ctxzap.Extract(ctx)
l.Debug("updating group page", zap.Int64("id", id), zap.String("access_level", accessLevel))

args := []interface{}{accessLevel, id}
sb := &strings.Builder{}
_, _ = sb.WriteString(`UPDATE group_pages SET "accessLevel" = $1 WHERE "id"=$2`)

_, err := c.db.Exec(ctx, sb.String(), args...)

if err != nil {
return err
}

return nil
}

func (c *Client) DeleteGroupPage(ctx context.Context, id int64) error {
l := ctxzap.Extract(ctx)
l.Debug("deleting group page", zap.Int64("id", id))

args := []interface{}{id}
sb := &strings.Builder{}
_, _ = sb.WriteString(`DELETE FROM group_pages WHERE "id"=$1`)

if _, err := c.db.Exec(ctx, sb.String(), args...); err != nil {
return err
}

return nil
}

func (c *Client) GetGroupFolderDefault(ctx context.Context, groupID int64, folderID int64) (*GroupFolderDefault, error) {
l := ctxzap.Extract(ctx)
l.Debug("getting group folder default", zap.Int64("group_id", groupID), zap.Int64("folder_id", folderID))

args := []interface{}{groupID, folderID}
sb := &strings.Builder{}
_, _ = sb.WriteString(`select "id", "accessLevel" from group_folder_defaults WHERE "groupId"=$1 AND "folderId"=$2`)
_, _ = sb.WriteString(``)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should that remain without a SQL statement tho?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was not supposed to be empty, I fix it.


var ret GroupFolderDefault
err := pgxscan.Get(ctx, c.db, &ret, sb.String(), args...)
Expand Down Expand Up @@ -219,6 +266,38 @@ func (c *Client) GetGroup(ctx context.Context, groupID int64) (*GroupModel, erro
return &ret, nil
}

func (c *Client) GetGroupByName(ctx context.Context, organizationID *int64, name string) (*GroupModel, error) {
l := ctxzap.Extract(ctx)
l.Debug("getting group by name", zap.Any("organization_id", organizationID), zap.String("name", name))

args := []interface{}{organizationID, name}
sb := &strings.Builder{}
_, _ = sb.WriteString(`select "id", "name", "organizationId", "universalAccess", "universalResourceAccess",
"universalQueryLibraryAccess", "userListAccess", "auditLogAccess", "unpublishedReleaseAccess"
from groups WHERE "organizationId"=$1 AND "name"=$2`)

var ret GroupModel
err := pgxscan.Get(ctx, c.db, &ret, sb.String(), args...)
if err != nil {
return nil, err
}

return &ret, nil
}

func (c *Client) CreateGroup(ctx context.Context, organizationID *int64, name string) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think they'll need to update the permissions they give our connectors. I will let CS know

l := ctxzap.Extract(ctx)
l.Debug("create group", zap.Any("organization_id", organizationID), zap.String("name", name))

args := []interface{}{name, organizationID}

if _, err := c.db.Exec(ctx, `INSERT INTO groups ("name", "organizationId", "createdAt", "updatedAt", "archivedAt", "usageAnalyticsAccess", "themeAccess", "unpublishedReleaseAccess", "accountDetailsAccess") VALUES ($1, $2,NOW(), NOW(), NULL, false, false, false, false)`, args...); err != nil {
return err
}

return nil
}

func (c *Client) ListGroupsForOrg(ctx context.Context, orgID int64, pager *Pager) ([]*GroupModel, string, error) {
l := ctxzap.Extract(ctx)
l.Debug("listing groups for org", zap.Int64("org_id", orgID))
Expand Down
2 changes: 1 addition & 1 deletion pkg/connector/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (c *ConnectorImpl) ResourceSyncers(ctx context.Context) []connectorbuilder.
}

if !c.skipPages {
syncers = append(syncers, newPageSyncer(ctx, c.client, c.skipDisabledUsers))
syncers = append(syncers, newPageSyncer(c.client, c.skipDisabledUsers))
}

if !c.skipResources {
Expand Down
100 changes: 37 additions & 63 deletions pkg/connector/groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import (
v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2"
"github.com/conductorone/baton-sdk/pkg/annotations"
"github.com/conductorone/baton-sdk/pkg/pagination"
resources "github.com/conductorone/baton-sdk/pkg/types/resource"
ent "github.com/conductorone/baton-sdk/pkg/types/entitlement"
"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"
)
Expand All @@ -24,14 +26,6 @@ const (
memberEntitlementSlug = "member"
)

func adminEntitlementId(groupID *v2.ResourceId) string {
return fmt.Sprintf("entitlement:%s:admin", groupID.Resource)
}

func memberEntitlementId(groupID *v2.ResourceId) string {
return fmt.Sprintf("entitlement:%s:member", groupID.Resource)
}

type groupSyncer struct {
resourceType *v2.ResourceType
client *client.Client
Expand Down Expand Up @@ -66,61 +60,48 @@ func (s *groupSyncer) List(
}

var ret []*v2.Resource
for _, o := range groups {
var annos annotations.Annotations

for _, o := range groups {
p := make(map[string]interface{})

if o.OrganizationID != nil {
p["organization_id"] = o.GetOrgID()
}

gt, err := resources.NewGroupTrait(resources.WithGroupProfile(p))
options := []rs.ResourceOption{
rs.WithGroupTrait(rs.WithGroupProfile(p)),
rs.WithParentResourceID(parentResourceID),
}

resource, err := rs.NewResource(o.GetName(), s.resourceType, formatObjectID(resourceTypeGroup.Id, o.ID), options...)
if err != nil {
return nil, "", nil, err
}

annos.Append(gt)

ret = append(ret, &v2.Resource{
DisplayName: o.GetName(),
Id: &v2.ResourceId{
ResourceType: s.resourceType.Id,
Resource: formatObjectID(s.resourceType.Id, o.ID),
},
ParentResourceId: parentResourceID,
Annotations: annos,
})
ret = append(ret, resource)
}

return ret, nextPageToken, nil, nil
}

func (s *groupSyncer) Entitlements(ctx context.Context, resource *v2.Resource, pToken *pagination.Token) ([]*v2.Entitlement, string, annotations.Annotations, error) {
var ret []*v2.Entitlement
var annos annotations.Annotations

ret = append(ret, &v2.Entitlement{
Resource: resource,
Id: memberEntitlementId(resource.Id),
DisplayName: fmt.Sprintf("%s Group Member", resource.DisplayName),
Description: fmt.Sprintf("Is member of the %s organization", resource.DisplayName),
GrantableTo: []*v2.ResourceType{resourceTypeUser},
Annotations: annos,
Purpose: v2.Entitlement_PURPOSE_VALUE_ASSIGNMENT,
Slug: memberEntitlementSlug,
})

ret = append(ret, &v2.Entitlement{
Resource: resource,
Id: adminEntitlementId(resource.Id),
DisplayName: fmt.Sprintf("%s Group Admin", resource.DisplayName),
Description: fmt.Sprintf("Is admin of the %s group", resource.DisplayName),
GrantableTo: []*v2.ResourceType{resourceTypeUser},
Annotations: annos,
Purpose: v2.Entitlement_PURPOSE_VALUE_ASSIGNMENT,
Slug: adminEntitlementSlug,
})
ret := []*v2.Entitlement{
ent.NewAssignmentEntitlement(
resource,
memberEntitlementSlug,
ent.WithGrantableTo(resourceTypeUser),
ent.WithDisplayName(fmt.Sprintf("%s Group Member", resource.DisplayName)),
ent.WithDescription(fmt.Sprintf("Is member of the %s group", resource.DisplayName)),
),
ent.NewAssignmentEntitlement(
resource,
adminEntitlementSlug,
ent.WithGrantableTo(resourceTypeUser),
ent.WithDisplayName(fmt.Sprintf("%s Group Admin", resource.DisplayName)),
ent.WithDescription(fmt.Sprintf("Is admin of the %s group", resource.DisplayName)),
),
}

return ret, "", nil, nil
}

Expand All @@ -142,22 +123,15 @@ func (s *groupSyncer) Grants(ctx context.Context, resource *v2.Resource, pToken
if m.IsAdmin {
level = "admin"
}
entitlementID := fmt.Sprintf("entitlement:%s:%s", resource.Id.Resource, level)
principalID := formatObjectID(resourceTypeUser.Id, m.GetUserID())

ret = append(ret, &v2.Grant{
Entitlement: &v2.Entitlement{
Id: entitlementID,
Resource: resource,
},
Principal: &v2.Resource{
Id: &v2.ResourceId{
ResourceType: resourceTypeUser.Id,
Resource: principalID,
},
},
Id: fmt.Sprintf("grant:%s:%s", entitlementID, principalID),
})

principalID, err := rs.NewResourceID(resourceTypeUser, formatObjectID(resourceTypeUser.Id, m.GetUserID()))
if err != nil {
return nil, "", nil, err
}

newGrant := grant.NewGrant(resource, level, principalID)

ret = append(ret, newGrant)
}

return ret, nextPageToken, nil, nil
Expand Down
10 changes: 2 additions & 8 deletions pkg/connector/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,17 @@ package connector
import (
"fmt"
"strconv"
"strings"

v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2"
"github.com/conductorone/baton-sdk/pkg/pagination"
)

func formatObjectID(resourceTypeID string, id int64) string {
return fmt.Sprintf("%s:%d", resourceTypeID, id)
return fmt.Sprintf("%c%d", resourceTypeID[0], id)
Comment on lines -13 to +12
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

%c will print the character "represented by the corresponding Unicode code point", are you sure this is what you want?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

}

func parseObjectID(id string) (int64, error) {
parts := strings.SplitN(id, ":", 2)
if len(parts) != 2 {
return 0, fmt.Errorf("invalid object ID %s", id)
}

return strconv.ParseInt(parts[1], 10, 64)
return strconv.ParseInt(id[1:], 10, 64)
}

func formatGroupObjectID(id int64) string {
Expand Down
Loading
Loading