Skip to content

Commit

Permalink
Merge pull request #10 from ConductorOne/logan/paul/provisioning
Browse files Browse the repository at this point in the history
Implement provisioning for google workspace v2
  • Loading branch information
loganintech authored Apr 4, 2024
2 parents 7147e2a + 18658f3 commit c92fe45
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 8 deletions.
60 changes: 56 additions & 4 deletions pkg/connector/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ package connector

import (
"context"
"errors"
"fmt"
"net/http"

v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2"
"github.com/conductorone/baton-sdk/pkg/annotations"
"github.com/conductorone/baton-sdk/pkg/pagination"
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"
Expand Down Expand Up @@ -122,10 +125,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)
}

Expand Down Expand Up @@ -154,3 +155,54 @@ 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(entitlement.Resource.Id.Resource, &admin.Member{Id: principal.GetId().GetResource()})
assignment, err := r.Context(ctx).Do()
if err != nil {
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())
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")
}
l := ctxzap.Extract(ctx)

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)
}

return nil, nil
}
67 changes: 63 additions & 4 deletions pkg/connector/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package connector

import (
"context"
"errors"
"fmt"
"net/http"
"strconv"

v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2"
Expand All @@ -14,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 (
Expand Down Expand Up @@ -122,10 +125,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)
}

Expand All @@ -151,3 +152,61 @@ 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,
ScopeType: "CUSTOMER",
})
assignment, err := r.Context(ctx).Do()
if err != nil {
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{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)
}

return nil, nil
}

0 comments on commit c92fe45

Please sign in to comment.