Skip to content

Commit

Permalink
feat: list groups having access to project
Browse files Browse the repository at this point in the history
- invitations now list organization in response
- roles have scopes for filter

Signed-off-by: Kush Sharma <[email protected]>
  • Loading branch information
kushsharma committed Sep 23, 2023
1 parent 0c01431 commit 1cb55f9
Show file tree
Hide file tree
Showing 26 changed files with 6,405 additions and 5,473 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ TAG := $(shell git rev-list --tags --max-count=1)
VERSION := $(shell git describe --tags ${TAG})
.PHONY: build check fmt lint test test-race vet test-cover-html help install proto ui
.DEFAULT_GOAL := build
PROTON_COMMIT := "a581f01a8c3e6cc8593363b6e06c2fa715455a47"
PROTON_COMMIT := "a5ec9897fc9f126c14f278cb1be646d8b768bac6"

ui:
@echo " > generating ui build"
Expand Down
6 changes: 3 additions & 3 deletions cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,11 +278,11 @@ func buildAPIDependencies(
domainRepository := postgres.NewDomainRepository(logger, dbc)
domainService := domain.NewService(logger, domainRepository, userService, organizationService)

projectRepository := postgres.NewProjectRepository(dbc)
projectService := project.NewService(projectRepository, relationService, userService, policyService, authnService, serviceUserService)

metaschemaRepository := postgres.NewMetaSchemaRepository(logger, dbc)
metaschemaService := metaschema.NewService(metaschemaRepository)
projectRepository := postgres.NewProjectRepository(dbc)
projectService := project.NewService(projectRepository, relationService, userService, policyService,
authnService, serviceUserService, groupService)

resourcePGRepository := postgres.NewResourceRepository(dbc)
resourceService := resource.NewService(
Expand Down
4 changes: 4 additions & 0 deletions core/group/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ func (s Service) Get(ctx context.Context, id string) (Group, error) {
return s.repository.GetByID(ctx, id)
}

func (s Service) GetByIDs(ctx context.Context, ids []string) ([]Group, error) {
return s.repository.GetByIDs(ctx, ids, Filter{})
}

func (s Service) List(ctx context.Context, flt Filter) ([]Group, error) {
return s.repository.List(ctx, flt)
}
Expand Down
52 changes: 44 additions & 8 deletions core/project/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"errors"
"fmt"

"github.com/raystack/frontier/core/group"

"github.com/raystack/frontier/core/serviceuser"

"github.com/raystack/frontier/core/authenticate"
Expand All @@ -27,7 +29,7 @@ type RelationService interface {
type UserService interface {
GetByID(ctx context.Context, id string) (user.User, error)
GetByIDs(ctx context.Context, userIDs []string) ([]user.User, error)
IsSudo(ctx context.Context, id string) (bool, error)
IsSudos(ctx context.Context, ids []string) ([]relation.Relation, error)
}

type ServiceuserService interface {
Expand All @@ -36,30 +38,38 @@ type ServiceuserService interface {

type PolicyService interface {
Create(ctx context.Context, policy policy.Policy) (policy.Policy, error)
List(ctx context.Context, flt policy.Filter) ([]policy.Policy, error)
}

type AuthnService interface {
GetPrincipal(ctx context.Context, via ...authenticate.ClientAssertion) (authenticate.Principal, error)
}

type GroupService interface {
GetByIDs(ctx context.Context, ids []string) ([]group.Group, error)
}

type Service struct {
repository Repository
relationService RelationService
userService UserService
suserService ServiceuserService
policyService PolicyService
authnService AuthnService
groupService GroupService
}

func NewService(repository Repository, relationService RelationService, userService UserService,
policyService PolicyService, authnService AuthnService, suserService ServiceuserService) *Service {
policyService PolicyService, authnService AuthnService, suserService ServiceuserService,
groupService GroupService) *Service {
return &Service{
repository: repository,
relationService: relationService,
userService: userService,
policyService: policyService,
authnService: authnService,
suserService: suserService,
groupService: groupService,
}
}

Expand Down Expand Up @@ -161,14 +171,16 @@ func (s Service) ListUsers(ctx context.Context, id string, permissionFilter stri
}

// filter superusers from the list of users who have the permission
// TODO(kushsharma): checking sudo one by one is slow, we need a batch test
suRelations, err := s.userService.IsSudos(ctx, userIDs)
if err != nil {
return nil, fmt.Errorf("failed to filter sudo users: %w", err)
}
superUserIDs := utils.Map(suRelations, func(r relation.Relation) string {
return r.Subject.ID
})
nonSuperUserIDs := make([]string, 0)
for _, userID := range userIDs {
isSudo, err := s.userService.IsSudo(ctx, userID)
if err != nil {
return nil, err
}
if !isSudo {
if !utils.Contains(superUserIDs, userID) {
nonSuperUserIDs = append(nonSuperUserIDs, userID)
}
}
Expand Down Expand Up @@ -201,6 +213,30 @@ func (s Service) ListServiceUsers(ctx context.Context, id string, permissionFilt
return s.suserService.GetByIDs(ctx, userIDs)
}

func (s Service) ListGroups(ctx context.Context, id string) ([]group.Group, error) {
requestedProject, err := s.Get(ctx, id)
if err != nil {
return nil, err
}
// Note(kushsharma): we don't need relation service here as we don't care about inheritance for now
// if we do ever need it, we will have to use relation service
groupPolicies, err := s.policyService.List(ctx, policy.Filter{
PrincipalType: schema.GroupPrincipal,
ProjectID: requestedProject.ID,
})
if err != nil {
return nil, err
}
if len(groupPolicies) == 0 {
// no groups
return []group.Group{}, nil
}
groupIDs := utils.Map(groupPolicies, func(p policy.Policy) string {
return p.PrincipalID
})
return s.groupService.GetByIDs(ctx, groupIDs)
}

func (s Service) addProjectToOrg(ctx context.Context, prj Project, orgID string) error {
rel := relation.Relation{
Object: relation.Object{
Expand Down
3 changes: 2 additions & 1 deletion core/role/filter.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package role

type Filter struct {
OrgID string
OrgID string
Scopes []string
}
1 change: 1 addition & 0 deletions core/role/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type Role struct {
Title string
Permissions []string
State State
Scopes []string // used for filtering
Metadata metadata.Metadata
CreatedAt time.Time
UpdatedAt time.Time
Expand Down
43 changes: 30 additions & 13 deletions core/user/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (

type RelationService interface {
Create(ctx context.Context, rel relation.Relation) (relation.Relation, error)
CheckPermission(ctx context.Context, rel relation.Relation) (bool, error)
BatchCheckPermission(ctx context.Context, relations []relation.Relation) ([]relation.CheckPair, error)
Delete(ctx context.Context, rel relation.Relation) error
LookupSubjects(ctx context.Context, rel relation.Relation) ([]string, error)
LookupResources(ctx context.Context, rel relation.Relation) ([]string, error)
Expand Down Expand Up @@ -207,21 +207,38 @@ func (s Service) Sudo(ctx context.Context, id string) error {
}

func (s Service) IsSudo(ctx context.Context, id string) (bool, error) {
status, err := s.relationService.CheckPermission(ctx, relation.Relation{
Subject: relation.Subject{
ID: id,
Namespace: schema.UserPrincipal,
},
Object: relation.Object{
ID: schema.PlatformID,
Namespace: schema.PlatformNamespace,
},
RelationName: schema.SudoPermission,
})
status, err := s.IsSudos(ctx, []string{id})
if err != nil {
return false, err
}
return status, nil
return len(status) > 0, nil
}

func (s Service) IsSudos(ctx context.Context, ids []string) ([]relation.Relation, error) {
relations := utils.Map(ids, func(id string) relation.Relation {
return relation.Relation{
Subject: relation.Subject{
ID: id,
Namespace: schema.UserPrincipal,
},
Object: relation.Object{
ID: schema.PlatformID,
Namespace: schema.PlatformNamespace,
},
RelationName: schema.SudoPermission,
}
})
statusForIDs, err := s.relationService.BatchCheckPermission(ctx, relations)
if err != nil {
return nil, err
}

successChecks := utils.Filter(statusForIDs, func(pair relation.CheckPair) bool {
return pair.Status
})
return utils.Map(successChecks, func(pair relation.CheckPair) relation.Relation {
return pair.Relation
}), nil
}

func isValidEmail(str string) bool {
Expand Down
29 changes: 24 additions & 5 deletions internal/api/v1beta1/invitations.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package v1beta1
import (
"context"
"errors"
"fmt"

"github.com/google/uuid"

Expand Down Expand Up @@ -74,22 +75,40 @@ func (h Handler) ListCurrentUserInvitations(ctx context.Context, request *fronti
return nil, status.Errorf(codes.Internal, "invalid user")
}

invite, err := h.invitationService.ListByUser(ctx, principal.User.Email)
invites, err := h.invitationService.ListByUser(ctx, principal.User.Email)
if err != nil {
logger.Error(err.Error())
return nil, status.Errorf(codes.Internal, err.Error())
}
var pbinvs []*frontierv1beta1.Invitation
for _, inv := range invite {
var invPBs []*frontierv1beta1.Invitation
var orgIds []string
for _, inv := range invites {
pbInv, err := transformInvitationToPB(inv)
if err != nil {
logger.Error(err.Error())
return nil, status.Errorf(codes.Internal, err.Error())
}
pbinvs = append(pbinvs, pbInv)
invPBs = append(invPBs, pbInv)
orgIds = append(orgIds, inv.OrgID)
}

var orgPBs []*frontierv1beta1.Organization
for _, org := range orgIds {
orgResp, err := h.orgService.Get(ctx, org)
if err != nil {
logger.Error(err.Error())
return nil, status.Errorf(codes.Internal, fmt.Errorf("failed to get org: %w", err).Error())
}
orgPB, err := transformOrgToPB(orgResp)
if err != nil {
logger.Error(err.Error())
return nil, status.Errorf(codes.Internal, fmt.Errorf("failed to transform org to pb: %w", err).Error())
}
orgPBs = append(orgPBs, orgPB)
}
return &frontierv1beta1.ListCurrentUserInvitationsResponse{
Invitations: pbinvs,
Invitations: invPBs,
Orgs: orgPBs,
}, nil
}

Expand Down
59 changes: 58 additions & 1 deletion internal/api/v1beta1/mocks/project_service.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 1cb55f9

Please sign in to comment.