Skip to content

Commit

Permalink
Merge branch 'main' into partner-dashboards-rill-cloud-ux
Browse files Browse the repository at this point in the history
  • Loading branch information
ericpgreen2 committed Sep 7, 2023
2 parents 349b418 + 2d51bcb commit 5bec5aa
Show file tree
Hide file tree
Showing 26 changed files with 7,037 additions and 3,921 deletions.
15 changes: 15 additions & 0 deletions admin/deployments.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ func (s *Service) createDeployment(ctx context.Context, opts *createDeploymentOp
ingestionLimit = alloc.StorageBytes

olapDSN = fmt.Sprintf("%s.db?rill_pool_size=%d&max_memory=%dGB", path.Join(alloc.DataDir, instanceID), alloc.CPU, alloc.MemoryGB)
} else if olapDriver == "duckdb-vip" {
if olapDSN != "" {
return nil, fmt.Errorf("passing a DSN is not allowed for driver 'duckdb-vip'")
}
if opts.ProdSlots == 0 {
return nil, fmt.Errorf("slot count can't be 0 for driver 'duckdb-vip'")
}

// NOTE: Rewriting to a "duckdb" driver without CPU, memory, or storage limits

embedCatalog = true
ingestionLimit = 0

olapDriver = "duckdb"
olapDSN = fmt.Sprintf("%s.db?rill_pool_size=8", path.Join(alloc.DataDir, instanceID))
}

// Open a runtime client
Expand Down
126 changes: 126 additions & 0 deletions admin/permissions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package admin

import (
"context"

"github.com/rilldata/rill/admin/database"
adminv1 "github.com/rilldata/rill/proto/gen/rill/admin/v1"
)

// OrganizationPermissionsForUser resolves organization permissions for a user.
func (s *Service) OrganizationPermissionsForUser(ctx context.Context, orgID, userID string) (*adminv1.OrganizationPermissions, error) {
roles, err := s.DB.ResolveOrganizationRolesForUser(ctx, userID, orgID)
if err != nil {
return nil, err
}

composite := &adminv1.OrganizationPermissions{}
for _, role := range roles {
composite = unionOrgRoles(composite, role)
}

return composite, nil
}

// OrganizationPermissionsForService resolves organization permissions for a service.
// A service currently gets full permissions on the org they belong to.
func (s *Service) OrganizationPermissionsForService(ctx context.Context, orgID, serviceID string) (*adminv1.OrganizationPermissions, error) {
service, err := s.DB.FindService(ctx, serviceID)
if err != nil {
return nil, err
}

// Services get full permissions on the org they belong to
if orgID == service.OrgID {
return &adminv1.OrganizationPermissions{
ReadOrg: true,
ManageOrg: true,
ReadProjects: true,
CreateProjects: true,
ManageProjects: true,
ReadOrgMembers: true,
ManageOrgMembers: true,
}, nil
}

return &adminv1.OrganizationPermissions{}, nil
}

// ProjectPermissionsForUser resolves project permissions for a user.
func (s *Service) ProjectPermissionsForUser(ctx context.Context, projectID, userID string, orgPerms *adminv1.OrganizationPermissions) (*adminv1.ProjectPermissions, error) {
// ManageProjects permission on the org gives full access to all projects in the org (only org admins have this)
if orgPerms.ManageProjects {
return &adminv1.ProjectPermissions{
ReadProject: true,
ManageProject: true,
ReadProd: true,
ReadProdStatus: true,
ManageProd: true,
ReadDev: true,
ReadDevStatus: true,
ManageDev: true,
ReadProjectMembers: true,
ManageProjectMembers: true,
}, nil
}

roles, err := s.DB.ResolveProjectRolesForUser(ctx, userID, projectID)
if err != nil {
return nil, err
}

composite := &adminv1.ProjectPermissions{}
for _, role := range roles {
composite = unionProjectRoles(composite, role)
}

return composite, nil
}

// ProjectPermissionsService resolves project permissions for a service.
// A service currently gets full permissions on all projects in the org they belong to.
func (s *Service) ProjectPermissionsForService(ctx context.Context, projectID, serviceID string, orgPerms *adminv1.OrganizationPermissions) (*adminv1.ProjectPermissions, error) {
if orgPerms.ManageProjects {
return &adminv1.ProjectPermissions{
ReadProject: true,
ManageProject: true,
ReadProd: true,
ReadProdStatus: true,
ManageProd: true,
ReadDev: true,
ReadDevStatus: true,
ManageDev: true,
ReadProjectMembers: true,
ManageProjectMembers: true,
}, nil
}

return &adminv1.ProjectPermissions{}, nil
}

func unionOrgRoles(a *adminv1.OrganizationPermissions, b *database.OrganizationRole) *adminv1.OrganizationPermissions {
return &adminv1.OrganizationPermissions{
ReadOrg: a.ReadOrg || b.ReadOrg,
ManageOrg: a.ManageOrg || b.ManageOrg,
ReadProjects: a.ReadProjects || b.ReadProjects,
CreateProjects: a.CreateProjects || b.CreateProjects,
ManageProjects: a.ManageProjects || b.ManageProjects,
ReadOrgMembers: a.ReadOrgMembers || b.ReadOrgMembers,
ManageOrgMembers: a.ManageOrgMembers || b.ManageOrgMembers,
}
}

func unionProjectRoles(a *adminv1.ProjectPermissions, b *database.ProjectRole) *adminv1.ProjectPermissions {
return &adminv1.ProjectPermissions{
ReadProject: a.ReadProject || b.ReadProject,
ManageProject: a.ManageProject || b.ManageProject,
ReadProd: a.ReadProd || b.ReadProd,
ReadProdStatus: a.ReadProdStatus || b.ReadProdStatus,
ManageProd: a.ManageProd || b.ManageProd,
ReadDev: a.ReadDev || b.ReadDev,
ReadDevStatus: a.ReadDevStatus || b.ReadDevStatus,
ManageDev: a.ManageDev || b.ManageDev,
ReadProjectMembers: a.ReadProjectMembers || b.ReadProjectMembers,
ManageProjectMembers: a.ManageProjectMembers || b.ManageProjectMembers,
}
}
141 changes: 15 additions & 126 deletions admin/server/auth/claims.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"sync"

"github.com/rilldata/rill/admin"
"github.com/rilldata/rill/admin/database"
"github.com/rilldata/rill/admin/pkg/authtoken"
adminv1 "github.com/rilldata/rill/proto/gen/rill/admin/v1"
)
Expand Down Expand Up @@ -161,13 +160,19 @@ func (c *authTokenClaims) ProjectPermissions(ctx context.Context, orgID, project
return perm
}

orgPerms := c.organizationPermissionsUnsafe(ctx, orgID)

var err error
switch c.token.Token().Type {
case authtoken.TypeUser:
perm = c.projectPermissionsUser(ctx, orgID, projectID)
perm, err = c.admin.ProjectPermissionsForUser(ctx, projectID, c.token.OwnerID(), orgPerms)
case authtoken.TypeService:
perm = c.projectPermissionsService(ctx, orgID, projectID)
perm, err = c.admin.ProjectPermissionsForService(ctx, projectID, c.token.OwnerID(), orgPerms)
default:
panic(fmt.Errorf("unexpected token type %q", c.token.Token().Type))
err = fmt.Errorf("unexpected token type %q", c.token.Token().Type)
}
if err != nil {
panic(fmt.Errorf("failed to get project permissions: %w", err))
}

c.projectPermissionsCache[projectID] = perm
Expand All @@ -182,135 +187,19 @@ func (c *authTokenClaims) organizationPermissionsUnsafe(ctx context.Context, org
return perm
}

var err error
switch c.token.Token().Type {
case authtoken.TypeUser:
perm = c.organizationPermissionsUser(ctx, orgID)
perm, err = c.admin.OrganizationPermissionsForUser(ctx, orgID, c.token.OwnerID())
case authtoken.TypeService:
perm = c.organizationPermissionsService(ctx, orgID)
perm, err = c.admin.OrganizationPermissionsForService(ctx, orgID, c.token.OwnerID())
default:
panic(fmt.Errorf("unexpected token type %q", c.token.Token().Type))
err = fmt.Errorf("unexpected token type %q", c.token.Token().Type)
}

c.orgPermissionsCache[orgID] = perm
return perm
}

// organizationPermissionsUser resolves organization permissions for a user.
func (c *authTokenClaims) organizationPermissionsUser(ctx context.Context, orgID string) *adminv1.OrganizationPermissions {
roles, err := c.admin.DB.ResolveOrganizationRolesForUser(context.Background(), c.token.OwnerID(), orgID)
if err != nil {
panic(fmt.Errorf("failed to get org permissions: %w", err))
}

composite := &adminv1.OrganizationPermissions{}
for _, role := range roles {
composite = unionOrgRoles(composite, role)
}

return composite
}

// organizationPermissionsService resolves organization permissions for a service.
// A service currently gets full permissions on the org they belong to.
func (c *authTokenClaims) organizationPermissionsService(ctx context.Context, orgID string) *adminv1.OrganizationPermissions {
service, err := c.admin.DB.FindService(ctx, c.token.OwnerID())
if err != nil {
panic(fmt.Errorf("failed to get service info: %w", err))
}

// Services get full permissions on the org they belong to
if orgID == service.OrgID {
return &adminv1.OrganizationPermissions{
ReadOrg: true,
ManageOrg: true,
ReadProjects: true,
CreateProjects: true,
ManageProjects: true,
ReadOrgMembers: true,
ManageOrgMembers: true,
}
}

return &adminv1.OrganizationPermissions{}
}

// projectPermissionsUser resolves project permissions for a user.
func (c *authTokenClaims) projectPermissionsUser(ctx context.Context, orgID, projectID string) *adminv1.ProjectPermissions {
// ManageProjects permission on the org gives full access to all projects in the org (only org admins have this)
orgPerms := c.organizationPermissionsUnsafe(ctx, orgID)
if orgPerms.ManageProjects {
return &adminv1.ProjectPermissions{
ReadProject: true,
ManageProject: true,
ReadProd: true,
ReadProdStatus: true,
ManageProd: true,
ReadDev: true,
ReadDevStatus: true,
ManageDev: true,
ReadProjectMembers: true,
ManageProjectMembers: true,
}
}

roles, err := c.admin.DB.ResolveProjectRolesForUser(ctx, c.token.OwnerID(), projectID)
if err != nil {
panic(fmt.Errorf("failed to get project permissions: %w", err))
}

composite := &adminv1.ProjectPermissions{}
for _, role := range roles {
composite = unionProjectRoles(composite, role)
}

return composite
}

// projectPermissionsService resolves project permissions for a service.
// A service currently gets full permissions on all projects in the org they belong to.
func (c *authTokenClaims) projectPermissionsService(ctx context.Context, orgID, projectID string) *adminv1.ProjectPermissions {
orgPerms := c.organizationPermissionsUnsafe(ctx, orgID)
if orgPerms.ManageProjects {
return &adminv1.ProjectPermissions{
ReadProject: true,
ManageProject: true,
ReadProd: true,
ReadProdStatus: true,
ManageProd: true,
ReadDev: true,
ReadDevStatus: true,
ManageDev: true,
ReadProjectMembers: true,
ManageProjectMembers: true,
}
}

return &adminv1.ProjectPermissions{}
}

func unionOrgRoles(a *adminv1.OrganizationPermissions, b *database.OrganizationRole) *adminv1.OrganizationPermissions {
return &adminv1.OrganizationPermissions{
ReadOrg: a.ReadOrg || b.ReadOrg,
ManageOrg: a.ManageOrg || b.ManageOrg,
ReadProjects: a.ReadProjects || b.ReadProjects,
CreateProjects: a.CreateProjects || b.CreateProjects,
ManageProjects: a.ManageProjects || b.ManageProjects,
ReadOrgMembers: a.ReadOrgMembers || b.ReadOrgMembers,
ManageOrgMembers: a.ManageOrgMembers || b.ManageOrgMembers,
}
}

func unionProjectRoles(a *adminv1.ProjectPermissions, b *database.ProjectRole) *adminv1.ProjectPermissions {
return &adminv1.ProjectPermissions{
ReadProject: a.ReadProject || b.ReadProject,
ManageProject: a.ManageProject || b.ManageProject,
ReadProd: a.ReadProd || b.ReadProd,
ReadProdStatus: a.ReadProdStatus || b.ReadProdStatus,
ManageProd: a.ManageProd || b.ManageProd,
ReadDev: a.ReadDev || b.ReadDev,
ReadDevStatus: a.ReadDevStatus || b.ReadDevStatus,
ManageDev: a.ManageDev || b.ManageDev,
ReadProjectMembers: a.ReadProjectMembers || b.ReadProjectMembers,
ManageProjectMembers: a.ManageProjectMembers || b.ManageProjectMembers,
}
c.orgPermissionsCache[orgID] = perm
return perm
}
12 changes: 11 additions & 1 deletion admin/server/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,17 @@ func (s *Server) GetDeploymentCredentials(ctx context.Context, req *adminv1.GetD
var attr map[string]any
switch forVal := req.For.(type) {
case *adminv1.GetDeploymentCredentialsRequest_UserId:
attr, err = s.jwtAttributesForUser(ctx, permissions, forVal.UserId, proj.OrganizationID)
forOrgPerms, err := s.admin.OrganizationPermissionsForUser(ctx, proj.OrganizationID, forVal.UserId)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

forProjPerms, err := s.admin.ProjectPermissionsForUser(ctx, proj.ID, forVal.UserId, forOrgPerms)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

attr, err = s.jwtAttributesForUser(ctx, forVal.UserId, proj.OrganizationID, forProjPerms)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
Expand Down
2 changes: 1 addition & 1 deletion admin/server/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func (s *Server) GetProject(ctx context.Context, req *adminv1.GetProjectRequest)

var attr map[string]any
if claims.OwnerType() == auth.OwnerTypeUser {
attr, err = s.jwtAttributesForUser(ctx, permissions, claims.OwnerID(), proj.OrganizationID)
attr, err = s.jwtAttributesForUser(ctx, claims.OwnerID(), proj.OrganizationID, permissions)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
Expand Down
10 changes: 4 additions & 6 deletions admin/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,15 +364,12 @@ func (s *Server) checkRateLimit(ctx context.Context) (context.Context, error) {
return ctx, nil
}

func (s *Server) jwtAttributesForUser(ctx context.Context, permissions *adminv1.ProjectPermissions, userID, orgID string) (map[string]any, error) {
var attr map[string]any
// Find User
func (s *Server) jwtAttributesForUser(ctx context.Context, userID, orgID string, projectPermissions *adminv1.ProjectPermissions) (map[string]any, error) {
user, err := s.admin.DB.FindUser(ctx, userID)
if err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}

// Find User groups
groups, err := s.admin.DB.FindUsergroupsForUser(ctx, user.ID, orgID)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
Expand All @@ -381,12 +378,13 @@ func (s *Server) jwtAttributesForUser(ctx context.Context, permissions *adminv1.
for i, group := range groups {
groupNames[i] = group.Name
}
attr = map[string]any{

attr := map[string]any{
"name": user.DisplayName,
"email": user.Email,
"domain": user.Email[strings.LastIndex(user.Email, "@")+1:],
"groups": groupNames,
"admin": permissions.ManageProject,
"admin": projectPermissions.ManageProject,
}

return attr, nil
Expand Down
Loading

0 comments on commit 5bec5aa

Please sign in to comment.