Skip to content

Commit bd14af8

Browse files
authored
feat(rbac): allow creating orgs to tokens with proper permissions (#2703)
Signed-off-by: Jose I. Paris <jiparis@chainloop.dev>
1 parent 87925bc commit bd14af8

3 files changed

Lines changed: 47 additions & 21 deletions

File tree

app/controlplane/internal/service/organization.go

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ func NewOrganizationService(muc *biz.MembershipUseCase, ouc *biz.OrganizationUse
4343
}
4444
}
4545

46-
// Create persists an organization with a given name and associate it to the current user.
46+
// Create persists an organization with a given name and associates it with the current user.
4747
func (s *OrganizationService) Create(ctx context.Context, req *pb.OrganizationServiceCreateRequest) (*pb.OrganizationServiceCreateResponse, error) {
48-
currentUser, err := requireCurrentUser(ctx)
48+
currentUser, _, err := requireCurrentUserOrAPIToken(ctx)
4949
if err != nil {
5050
return nil, err
5151
}
@@ -65,8 +65,11 @@ func (s *OrganizationService) Create(ctx context.Context, req *pb.OrganizationSe
6565
return nil, handleUseCaseErr(err, s.log)
6666
}
6767

68-
if _, err := s.membershipUC.Create(ctx, org.ID, currentUser.ID, biz.WithMembershipRole(authz.RoleOwner), biz.WithCurrentMembership()); err != nil {
69-
return nil, handleUseCaseErr(err, s.log)
68+
// Add membership if invoker is a user
69+
if currentUser != nil {
70+
if _, err := s.membershipUC.Create(ctx, org.ID, currentUser.ID, biz.WithMembershipRole(authz.RoleOwner), biz.WithCurrentMembership()); err != nil {
71+
return nil, handleUseCaseErr(err, s.log)
72+
}
7073
}
7174

7275
return &pb.OrganizationServiceCreateResponse{Result: bizOrgToPb(org)}, nil
@@ -211,25 +214,15 @@ func (s *OrganizationService) UpdateMembership(ctx context.Context, req *pb.Orga
211214
}
212215

213216
func (s *OrganizationService) canCreateOrganization(ctx context.Context) (bool, error) {
214-
// Restricted org creation is disabled, allow creation
215-
if !s.authz.RestrictOrgCreation {
217+
// if org creation restriction is disabled, allow creation to all users
218+
if !s.authz.RestrictOrgCreation && entities.CurrentUser(ctx) != nil {
216219
return true, nil
217220
}
218221

219-
m := entities.CurrentMembership(ctx)
220-
for _, rm := range m.Resources {
221-
if rm.ResourceType != authz.ResourceTypeInstance {
222-
continue
223-
}
224-
225-
pass, err := s.authz.Enforce(ctx, string(rm.Role), authz.PolicyOrganizationCreate)
226-
if err != nil {
227-
return false, handleUseCaseErr(err, s.log)
228-
}
229-
if pass {
230-
return true, nil
231-
}
222+
// otherwise, check for permissions (both users and API tokens)
223+
if err := s.checkPolicy(ctx, authz.PolicyOrganizationCreate); err != nil {
224+
return false, err
232225
}
233226

234-
return false, nil
227+
return true, nil
235228
}

app/controlplane/internal/service/service.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,38 @@ func (s *service) visibleProjects(ctx context.Context) []uuid.UUID {
329329
return projects
330330
}
331331

332+
// checkPolicy Checks a policy against a user or a token
333+
func (s *service) checkPolicy(ctx context.Context, policy *authz.Policy) error {
334+
// Token case
335+
sub := usercontext.CurrentAuthzSubject(ctx)
336+
if sub != "" {
337+
ok, err := s.authz.Enforce(ctx, sub, policy)
338+
if err != nil {
339+
return handleUseCaseErr(err, s.log)
340+
}
341+
if ok {
342+
return nil
343+
}
344+
}
345+
346+
// Other cases
347+
m := entities.CurrentMembership(ctx)
348+
if m == nil {
349+
return errors.Forbidden("forbidden", "not allowed")
350+
}
351+
for _, rm := range m.Resources {
352+
pass, err := s.authz.Enforce(ctx, string(rm.Role), authz.PolicyOrganizationCreate)
353+
if err != nil {
354+
return handleUseCaseErr(err, s.log)
355+
}
356+
if pass {
357+
return nil
358+
}
359+
}
360+
361+
return errors.Forbidden("forbidden", "not allowed")
362+
}
363+
332364
// isUserOrgAdmin checks if the current user is an org admin or owner
333365
func isUserOrgAdmin(ctx context.Context) bool {
334366
userRole := usercontext.CurrentAuthzSubject(ctx)

app/controlplane/pkg/authz/authz.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,8 @@ var ServerOperationsMap = map[string][]*Policy{
386386
"/controlplane.v1.ContextService/Current": {PolicyOrganizationRead},
387387
// Listing, create or selecting an organization does not have any required permissions,
388388
// since all the permissions here are in the context of an organization
389-
// Create new organization
389+
// Create new organization. No user required at middleware level. The endpoint will handle
390+
// it based on diverse conditions
390391
"/controlplane.v1.OrganizationService/Create": {},
391392
// Delete an organization makes checks at the service level since the
392393
// user can explicitly set the org they want to delete and might not be the current one

0 commit comments

Comments
 (0)