Skip to content

Commit

Permalink
Add grant page to user
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcusGoldschmidt committed Feb 13, 2025
1 parent 59cc4de commit ab582c1
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 69 deletions.
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
32 changes: 32 additions & 0 deletions pkg/client/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,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 {
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
259 changes: 192 additions & 67 deletions pkg/connector/pages.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,19 @@ func (s *pageSyncer) Entitlements(ctx context.Context, resource *v2.Resource, pT
ret = append(ret, entitlement)
}

for _, level := range accessLevels {
entitlement := ent.NewPermissionEntitlement(
resource,
fmt.Sprintf("%s:%s", "user", level),
ent.WithGrantableTo(resourceTypeUser),
ent.WithDisplayName(fmt.Sprintf("User can %s on %s", titleCase(accessLevelDisplayNames[level]), resource.DisplayName)),
ent.WithDescription(fmt.Sprintf("Has %s access on the %s page", accessLevelDisplayNames[level], resource.DisplayName)),
)
entitlement.Slug = accessLevelDisplayNames[level]

ret = append(ret, entitlement)
}

return ret, "", nil, nil
}

Expand Down Expand Up @@ -205,109 +218,221 @@ func (s *pageSyncer) Grants(ctx context.Context, resource *v2.Resource, pToken *
}

func (s *pageSyncer) Grant(ctx context.Context, resource *v2.Resource, entitlement *v2.Entitlement) ([]*v2.Grant, annotations.Annotations, error) {
if resource.Id.ResourceType != resourceTypeGroup.Id {
return nil, nil, fmt.Errorf("unexpected resource type while processing page grant: %s", resource.Id.ResourceType)
}

groupID, err := parseObjectID(entitlement.Resource.Id.Resource)
if err != nil {
return nil, nil, err
}
switch resource.Id.ResourceType {
// Grant user to a page
case resourceTypeUser.Id:
userId, err := parseObjectID(entitlement.Resource.Id.Resource)
if err != nil {
return nil, nil, err
}

pageID, err := parseObjectID(resource.Id.Resource)
if err != nil {
return nil, nil, err
}
pageID, err := parseObjectID(resource.Id.Resource)
if err != nil {
return nil, nil, err
}

splitV := strings.Split(entitlement.Id, ":")
if len(splitV) != 3 {
return nil, nil, fmt.Errorf("unexpected entitlement ID format while processing page grant: %s", entitlement.Id)
}
accessLevel := splitV[len(splitV)-1]
splitV := strings.Split(entitlement.Id, ":")
if len(splitV) != 4 {
return nil, nil, fmt.Errorf("unexpected entitlement ID format while processing page grant: %s", entitlement.Id)
}
accessLevel := splitV[len(splitV)-1]

page, err := s.client.GetGroupPage(ctx, pageID, groupID)
if err != nil {
if !errors.Is(err, pgx.ErrNoRows) {
err = findPageGroupPermission(ctx, s.client, pageID, accessLevel, userId)
if err != nil {
return nil, nil, err
}
}

// Update the group page
if page != nil {
if page.AccessLevel == accessLevel {
return nil, annotations.New(&v2.GrantAlreadyExists{}), nil
}
newGrant := grant.NewGrant(resource, accessLevel, resource.Id)

err := s.client.UpdateGroupPage(ctx, page.ID, accessLevel)
return []*v2.Grant{newGrant}, nil, nil
// Grant group
case resourceTypeGroup.Id:
groupID, err := parseObjectID(entitlement.Resource.Id.Resource)
if err != nil {
return nil, nil, err
}
} else {
// Create the group page
err := s.client.InsertGroupPage(ctx, pageID, groupID, accessLevel)

pageID, err := parseObjectID(resource.Id.Resource)
if err != nil {
return nil, nil, err
}
}

grantExpandable := &v2.GrantExpandable{
EntitlementIds: []string{
fmt.Sprintf("group:%s:member", resource.Id.Resource),
fmt.Sprintf("group:%s:admin", resource.Id.Resource),
},
}
splitV := strings.Split(entitlement.Id, ":")
if len(splitV) != 3 {
return nil, nil, fmt.Errorf("unexpected entitlement ID format while processing page grant: %s", entitlement.Id)
}
accessLevel := splitV[len(splitV)-1]

newGrant := grant.NewGrant(resource, accessLevel, resource.Id, grant.WithAnnotation(grantExpandable))
page, err := s.client.GetGroupPage(ctx, pageID, groupID)
if err != nil {
if !errors.Is(err, pgx.ErrNoRows) {
return nil, nil, err
}
}

// Update the group page
if page != nil {
if page.AccessLevel == accessLevel {
return nil, annotations.New(&v2.GrantAlreadyExists{}), nil
}

err := s.client.UpdateGroupPage(ctx, page.ID, accessLevel)
if err != nil {
return nil, nil, err
}
} else {
// Create the group page
err := s.client.InsertGroupPage(ctx, pageID, groupID, accessLevel)
if err != nil {
return nil, nil, err
}
}

grantExpandable := &v2.GrantExpandable{
EntitlementIds: []string{
fmt.Sprintf("group:%s:member", resource.Id.Resource),
fmt.Sprintf("group:%s:admin", resource.Id.Resource),
},
}

return []*v2.Grant{newGrant}, nil, nil
newGrant := grant.NewGrant(resource, accessLevel, resource.Id, grant.WithAnnotation(grantExpandable))

return []*v2.Grant{newGrant}, nil, nil
default:
return nil, nil, fmt.Errorf("unexpected resource type while processing page grant: %s", resource.Id.ResourceType)
}
}

func (s *pageSyncer) Revoke(ctx context.Context, grant *v2.Grant) (annotations.Annotations, error) {
if grant.Principal.Id.ResourceType != resourceTypeGroup.Id {
return nil, fmt.Errorf("unexpected resource type while processing page grant: %s", grant.Principal.Id.ResourceType)
}

groupID, err := parseObjectID(grant.Principal.Id.Resource)
if err != nil {
return nil, err
}
switch grant.Principal.Id.ResourceType {
case resourceTypeUser.Id:
userId, err := parseObjectID(grant.Principal.Id.Resource)
if err != nil {
return nil, err
}

pageID, err := parseObjectID(grant.Entitlement.Resource.Id.Resource)
if err != nil {
return nil, err
}
pageID, err := parseObjectID(grant.Entitlement.Resource.Id.Resource)
if err != nil {
return nil, err
}

splitV := strings.Split(grant.Entitlement.Id, ":")
if len(splitV) != 3 {
return nil, fmt.Errorf("unexpected entitlement ID format while processing page grant: %s", grant.Entitlement.Id)
}
accessLevel := splitV[len(splitV)-1]
splitV := strings.Split(grant.Entitlement.Id, ":")
if len(splitV) != 4 {
return nil, fmt.Errorf("unexpected entitlement ID format while processing page grant: %s", grant.Entitlement.Id)
}
permission := splitV[len(splitV)-1]

page, err := s.client.GetGroupPage(ctx, groupID, pageID)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return annotations.New(&v2.GrantAlreadyRevoked{}), nil
} else {
page, err := s.client.GetPage(ctx, pageID)
if err != nil {
return nil, err
}
}

if page.AccessLevel != accessLevel {
return annotations.New(&v2.GrantAlreadyRevoked{}), nil
}
groupName := fmt.Sprintf("%d%d-group-%s-%s", page.OrganizationID, page.ID, page.Name, permission)

err = s.client.DeleteGroupPage(ctx, page.ID)
if err != nil {
return nil, err
}
group, err := s.client.GetGroupByName(ctx, page.OrganizationID, groupName)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return annotations.New(&v2.GrantAlreadyRevoked{}), nil
} else {
return nil, err
}
}

return nil, nil
err = s.client.RemoveGroupMember(ctx, group.ID, userId)
if err != nil {
return nil, err
}

return nil, nil

case resourceTypeGroup.Id:
groupID, err := parseObjectID(grant.Principal.Id.Resource)
if err != nil {
return nil, err
}

pageID, err := parseObjectID(grant.Entitlement.Resource.Id.Resource)
if err != nil {
return nil, err
}

splitV := strings.Split(grant.Entitlement.Id, ":")
if len(splitV) != 3 {
return nil, fmt.Errorf("unexpected entitlement ID format while processing page grant: %s", grant.Entitlement.Id)
}
accessLevel := splitV[len(splitV)-1]

page, err := s.client.GetGroupPage(ctx, groupID, pageID)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return annotations.New(&v2.GrantAlreadyRevoked{}), nil
} else {
return nil, err
}
}

if page.AccessLevel != accessLevel {
return annotations.New(&v2.GrantAlreadyRevoked{}), nil
}

err = s.client.DeleteGroupPage(ctx, page.ID)
if err != nil {
return nil, err
}

return nil, nil
default:
return nil, fmt.Errorf("unexpected resource type while processing page grant: %s", grant.Principal.Id.ResourceType)
}
}

func newPageSyncer(ctx context.Context, c *client.Client, skipDisabledUsers bool) *pageSyncer {
func newPageSyncer(c *client.Client, skipDisabledUsers bool) *pageSyncer {
return &pageSyncer{
resourceType: resourceTypePage,
client: c,
skipDisabledUsers: skipDisabledUsers,
}
}

func findPageGroupPermission(ctx context.Context, client *client.Client, pageId int64, permission string, userId int64) error {
page, err := client.GetPage(ctx, pageId)
if err != nil {
return err
}

groupName := fmt.Sprintf("%d%d-group-%s-%s", page.OrganizationID, page.ID, page.Name, permission)

group, err := client.GetGroupByName(ctx, page.OrganizationID, groupName)
if err != nil {
// Should create group
if errors.Is(err, pgx.ErrNoRows) {
err := client.CreateGroup(ctx, page.OrganizationID, groupName)
if err != nil {
return err
}

group, err = client.GetGroupByName(ctx, page.OrganizationID, groupName)
if err != nil {
return err
}

err = client.InsertGroupPage(ctx, group.ID, page.ID, permission)
if err != nil {
return err
}
} else {
return err
}
}

err = client.AddGroupMember(ctx, group.ID, userId, false)
if err != nil {
return err
}

return nil
}

0 comments on commit ab582c1

Please sign in to comment.