From 3967293760407fe821d02466f5ccf0c496e281a2 Mon Sep 17 00:00:00 2001 From: Logan Saso Date: Wed, 3 Apr 2024 16:11:29 -0700 Subject: [PATCH 1/6] Checkin provisioning --- pkg/connector/group.go | 31 +++++++++++++++++++++++++++++++ pkg/connector/role.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/pkg/connector/group.go b/pkg/connector/group.go index b182a37..a982949 100644 --- a/pkg/connector/group.go +++ b/pkg/connector/group.go @@ -2,6 +2,7 @@ package connector import ( "context" + "errors" "fmt" v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" @@ -154,3 +155,33 @@ func groupProfile(ctx context.Context, group *admin.Group) map[string]interface{ profile["group_email"] = group.Email return profile } + +func (o *groupResourceType) Grant(ctx context.Context, principal *v2.Resource, entitlement *v2.Entitlement) ([]*v2.Grant, annotations.Annotations, error) { + if principal.GetId().GetResourceType() != resourceTypeUser.Id { + return nil, nil, errors.New("google-workspace-v2: user principal is required") + } + + r := o.groupService.Members.Insert(o.customerId, &admin.Member{Id: principal.GetId().GetResource()}) + assignment, err := r.Context(ctx).Do() + if err != nil { + return nil, nil, fmt.Errorf("google-workspace-v2: failed to insert group member: %w", err) + } + + grant := sdkGrant.NewGrant(entitlement.Resource, roleMemberEntitlement, principal.GetId()) + grant.Id = assignment.Id + return []*v2.Grant{grant}, nil, nil +} + +func (o *groupResourceType) Revoke(ctx context.Context, grant *v2.Grant) (annotations.Annotations, error) { + if grant.Principal.GetId().GetResourceType() != resourceTypeUser.Id { + return nil, errors.New("google-workspace-v2: user principal is required") + } + + r := o.groupService.Members.Delete(o.customerId, grant.Id) + err := r.Context(ctx).Do() + if err != nil { + return nil, fmt.Errorf("google-workspace-v2: failed to remove group member: %w", err) + } + + return nil, nil +} diff --git a/pkg/connector/role.go b/pkg/connector/role.go index 8f41fff..882d449 100644 --- a/pkg/connector/role.go +++ b/pkg/connector/role.go @@ -2,6 +2,7 @@ package connector import ( "context" + "errors" "fmt" "strconv" @@ -151,3 +152,37 @@ func roleProfile(ctx context.Context, role *admin.Role) map[string]interface{} { profile["role_name"] = role.RoleName return profile } + +func (o *roleResourceType) Grant(ctx context.Context, principal *v2.Resource, entitlement *v2.Entitlement) ([]*v2.Grant, annotations.Annotations, error) { + if principal.GetId().GetResourceType() != resourceTypeUser.Id { + return nil, nil, errors.New("google-workspace-v2: user principal is required") + } + + tempRoleId, err := strconv.ParseInt(entitlement.Resource.Id.Resource, 10, 64) + if err != nil { + return nil, nil, fmt.Errorf("google-workspace-v2: failed to convert roleId to string: %w", err) + } + r := o.roleService.RoleAssignments.Insert(o.customerId, &admin.RoleAssignment{AssignedTo: principal.GetId().GetResource(), RoleId: tempRoleId}) + assignment, err := r.Context(ctx).Do() + if err != nil { + return nil, nil, fmt.Errorf("google-workspace-v2: failed to insert role member: %w", err) + } + + grant := sdkGrant.NewGrant(entitlement.Resource, roleMemberEntitlement, principal.GetId()) + grant.Id = strconv.FormatInt(assignment.RoleAssignmentId, 10) + return []*v2.Grant{}, nil, nil +} + +func (o *roleResourceType) Revoke(ctx context.Context, grant *v2.Grant) (annotations.Annotations, error) { + if grant.Principal.GetId().GetResourceType() != resourceTypeUser.Id { + return nil, errors.New("google-workspace-v2: user principal is required") + } + + r := o.roleService.RoleAssignments.Delete(o.customerId, grant.Id) + err := r.Context(ctx).Do() + if err != nil { + return nil, fmt.Errorf("google-workspace-v2: failed to remove role member: %w", err) + } + + return nil, nil +} From c42afe2318cedae8e492f92bf42d90c6a908162a Mon Sep 17 00:00:00 2001 From: Logan Saso Date: Wed, 3 Apr 2024 16:34:08 -0700 Subject: [PATCH 2/6] Change the way IDs are set on the listed grants --- pkg/connector/group.go | 6 ++---- pkg/connector/role.go | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/pkg/connector/group.go b/pkg/connector/group.go index a982949..6074aa4 100644 --- a/pkg/connector/group.go +++ b/pkg/connector/group.go @@ -123,10 +123,8 @@ func (o *groupResourceType) Grants(ctx context.Context, resource *v2.Resource, p if err != nil { return nil, "", nil, err } - grant := sdkGrant.NewGrant(resource, groupMemberEntitlement, gmID) - annos := annotations.Annotations(grant.Annotations) - annos.Update(v1Identifier) - grant.Annotations = annos + grant := sdkGrant.NewGrant(resource, groupMemberEntitlement, gmID, sdkGrant.WithAnnotation(v1Identifier)) + grant.Id = member.Id rv = append(rv, grant) } diff --git a/pkg/connector/role.go b/pkg/connector/role.go index 882d449..6760757 100644 --- a/pkg/connector/role.go +++ b/pkg/connector/role.go @@ -123,10 +123,8 @@ func (o *roleResourceType) Grants(ctx context.Context, resource *v2.Resource, pt if err != nil { return nil, "", nil, err } - grant := sdkGrant.NewGrant(resource, roleMemberEntitlement, uID) - annos := annotations.Annotations(grant.Annotations) - annos.Update(v1Identifier) - grant.Annotations = annos + grant := sdkGrant.NewGrant(resource, roleMemberEntitlement, uID, sdkGrant.WithAnnotation(v1Identifier)) + grant.Id = tempRoleAssignmentId rv = append(rv, grant) } From d761d3934a8d02680ca3c3817b7e324fa6e3a60f Mon Sep 17 00:00:00 2001 From: Logan Saso Date: Wed, 3 Apr 2024 18:08:57 -0700 Subject: [PATCH 3/6] Working? --- pkg/connector/group.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/connector/group.go b/pkg/connector/group.go index 6074aa4..7b312d5 100644 --- a/pkg/connector/group.go +++ b/pkg/connector/group.go @@ -159,7 +159,7 @@ func (o *groupResourceType) Grant(ctx context.Context, principal *v2.Resource, e return nil, nil, errors.New("google-workspace-v2: user principal is required") } - r := o.groupService.Members.Insert(o.customerId, &admin.Member{Id: principal.GetId().GetResource()}) + r := o.groupService.Members.Insert(entitlement.Resource.Id.Resource, &admin.Member{Id: principal.GetId().GetResource()}) assignment, err := r.Context(ctx).Do() if err != nil { return nil, nil, fmt.Errorf("google-workspace-v2: failed to insert group member: %w", err) From 754fa84566b354667de5114a7e17e72efc186326 Mon Sep 17 00:00:00 2001 From: Logan Saso Date: Thu, 4 Apr 2024 10:38:36 -0700 Subject: [PATCH 4/6] Groups working and behaving well for double-actions --- pkg/connector/group.go | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/pkg/connector/group.go b/pkg/connector/group.go index 7b312d5..e4e92f4 100644 --- a/pkg/connector/group.go +++ b/pkg/connector/group.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "net/http" v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" "github.com/conductorone/baton-sdk/pkg/annotations" @@ -11,6 +12,7 @@ import ( sdkEntitlement "github.com/conductorone/baton-sdk/pkg/types/entitlement" sdkGrant "github.com/conductorone/baton-sdk/pkg/types/grant" sdkResource "github.com/conductorone/baton-sdk/pkg/types/resource" + "google.golang.org/api/googleapi" "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" "go.uber.org/zap" @@ -162,7 +164,17 @@ func (o *groupResourceType) Grant(ctx context.Context, principal *v2.Resource, e r := o.groupService.Members.Insert(entitlement.Resource.Id.Resource, &admin.Member{Id: principal.GetId().GetResource()}) assignment, err := r.Context(ctx).Do() if err != nil { - return nil, nil, fmt.Errorf("google-workspace-v2: failed to insert group member: %w", err) + gerr := &googleapi.Error{} + if errors.As(err, &gerr) { + if gerr.Code == http.StatusConflict { + assignment, err = o.groupService.Members.Get(entitlement.Resource.Id.Resource, principal.GetId().GetResource()).Context(ctx).Do() + if err != nil { + return nil, nil, err + } + } + } else { + return nil, nil, fmt.Errorf("google-workspace-v2: failed to insert group member: %w", err) + } } grant := sdkGrant.NewGrant(entitlement.Resource, roleMemberEntitlement, principal.GetId()) @@ -174,10 +186,21 @@ func (o *groupResourceType) Revoke(ctx context.Context, grant *v2.Grant) (annota if grant.Principal.GetId().GetResourceType() != resourceTypeUser.Id { return nil, errors.New("google-workspace-v2: user principal is required") } + l := ctxzap.Extract(ctx) - r := o.groupService.Members.Delete(o.customerId, grant.Id) + r := o.groupService.Members.Delete(grant.Entitlement.Resource.Id.Resource, grant.Principal.GetId().GetResource()) err := r.Context(ctx).Do() if err != nil { + gerr := &googleapi.Error{} + if errors.As(err, &gerr) { + if gerr.Code == http.StatusNotFound { + // This should only hit if someone double-revokes, but I'd rather we log something about it + l.Info("google-workspace-v2: group member is being deleted but doesn't exist", + zap.String("group_id", grant.Entitlement.Resource.Id.Resource), + zap.String("user_id", grant.Principal.GetId().GetResource())) + return nil, nil + } + } return nil, fmt.Errorf("google-workspace-v2: failed to remove group member: %w", err) } From f75246534c8be1bd2f0c833d4db4a1b8474799fa Mon Sep 17 00:00:00 2001 From: Logan Saso Date: Thu, 4 Apr 2024 11:35:01 -0700 Subject: [PATCH 5/6] :adhesive_bandage: role provisioning --- pkg/connector/role.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/connector/role.go b/pkg/connector/role.go index 6760757..195b2b9 100644 --- a/pkg/connector/role.go +++ b/pkg/connector/role.go @@ -160,7 +160,11 @@ func (o *roleResourceType) Grant(ctx context.Context, principal *v2.Resource, en if err != nil { return nil, nil, fmt.Errorf("google-workspace-v2: failed to convert roleId to string: %w", err) } - r := o.roleService.RoleAssignments.Insert(o.customerId, &admin.RoleAssignment{AssignedTo: principal.GetId().GetResource(), RoleId: tempRoleId}) + r := o.roleService.RoleAssignments.Insert(o.customerId, &admin.RoleAssignment{ + AssignedTo: principal.GetId().GetResource(), + RoleId: tempRoleId, + ScopeType: "CUSTOMER", + }) assignment, err := r.Context(ctx).Do() if err != nil { return nil, nil, fmt.Errorf("google-workspace-v2: failed to insert role member: %w", err) From 18658f376226541bb72fbc2da7697f5ea86b5724 Mon Sep 17 00:00:00 2001 From: Logan Saso Date: Thu, 4 Apr 2024 11:41:16 -0700 Subject: [PATCH 6/6] Some graceful error handling for double-actions for roles --- pkg/connector/role.go | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/pkg/connector/role.go b/pkg/connector/role.go index 195b2b9..71642e8 100644 --- a/pkg/connector/role.go +++ b/pkg/connector/role.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "net/http" "strconv" v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" @@ -15,6 +16,7 @@ import ( "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" "go.uber.org/zap" admin "google.golang.org/api/admin/directory/v1" + "google.golang.org/api/googleapi" ) const ( @@ -167,22 +169,42 @@ func (o *roleResourceType) Grant(ctx context.Context, principal *v2.Resource, en }) assignment, err := r.Context(ctx).Do() if err != nil { - return nil, nil, fmt.Errorf("google-workspace-v2: failed to insert role member: %w", err) + gerr := &googleapi.Error{} + if errors.As(err, &gerr) { + if gerr.Code == http.StatusConflict { + // We don't need to do anything here, the user is already a member of the role + // We unfortunately can't get the role assignment to return as a grant, so we just return nil + return nil, nil, nil + } + } else { + return nil, nil, fmt.Errorf("google-workspace-v2: failed to insert role member: %w", err) + } } grant := sdkGrant.NewGrant(entitlement.Resource, roleMemberEntitlement, principal.GetId()) grant.Id = strconv.FormatInt(assignment.RoleAssignmentId, 10) - return []*v2.Grant{}, nil, nil + return []*v2.Grant{grant}, nil, nil } func (o *roleResourceType) Revoke(ctx context.Context, grant *v2.Grant) (annotations.Annotations, error) { if grant.Principal.GetId().GetResourceType() != resourceTypeUser.Id { return nil, errors.New("google-workspace-v2: user principal is required") } + l := ctxzap.Extract(ctx) r := o.roleService.RoleAssignments.Delete(o.customerId, grant.Id) err := r.Context(ctx).Do() if err != nil { + gerr := &googleapi.Error{} + if errors.As(err, &gerr) { + if gerr.Code == http.StatusNotFound { + // This should only hit if someone double-revokes, but I'd rather we log something about it + l.Info("google-workspace-v2: role member is being deleted but doesn't exist", + zap.String("group_id", grant.Entitlement.Resource.Id.Resource), + zap.String("user_id", grant.Principal.GetId().GetResource())) + return nil, nil + } + } return nil, fmt.Errorf("google-workspace-v2: failed to remove role member: %w", err) }