mirror of https://github.com/databricks/cli.git
Added a warning when incorrect permissions used for `/Workspace/Shared` bundle root (#1821)
## Changes Added a warning when incorrect permissions used for `/Workspace/Shared` bundle root ## Tests Added unit test --------- Co-authored-by: Pieter Noordhuis <pieter.noordhuis@databricks.com>
This commit is contained in:
parent
c5043c3d9d
commit
0c9c90208f
|
@ -0,0 +1,56 @@
|
||||||
|
package permissions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/databricks/cli/bundle"
|
||||||
|
"github.com/databricks/cli/libs/diag"
|
||||||
|
)
|
||||||
|
|
||||||
|
type validateSharedRootPermissions struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateSharedRootPermissions() bundle.Mutator {
|
||||||
|
return &validateSharedRootPermissions{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*validateSharedRootPermissions) Name() string {
|
||||||
|
return "ValidateSharedRootPermissions"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*validateSharedRootPermissions) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
|
||||||
|
if isWorkspaceSharedRoot(b.Config.Workspace.RootPath) {
|
||||||
|
return isUsersGroupPermissionSet(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isWorkspaceSharedRoot(path string) bool {
|
||||||
|
return strings.HasPrefix(path, "/Workspace/Shared/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// isUsersGroupPermissionSet checks that top-level permissions set for bundle contain group_name: users with CAN_MANAGE permission.
|
||||||
|
func isUsersGroupPermissionSet(b *bundle.Bundle) diag.Diagnostics {
|
||||||
|
var diags diag.Diagnostics
|
||||||
|
|
||||||
|
allUsers := false
|
||||||
|
for _, p := range b.Config.Permissions {
|
||||||
|
if p.GroupName == "users" && p.Level == CAN_MANAGE {
|
||||||
|
allUsers = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !allUsers {
|
||||||
|
diags = diags.Append(diag.Diagnostic{
|
||||||
|
Severity: diag.Warning,
|
||||||
|
Summary: fmt.Sprintf("the bundle root path %s is writable by all workspace users", b.Config.Workspace.RootPath),
|
||||||
|
Detail: "The bundle is configured to use /Workspace/Shared, which will give read/write access to all users. If this is intentional, add CAN_MANAGE for 'group_name: users' permission to your bundle configuration. If the deployment should be restricted, move it to a restricted folder such as /Workspace/Users/<username or principal name>.",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return diags
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
package permissions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/databricks/cli/bundle"
|
||||||
|
"github.com/databricks/cli/bundle/config"
|
||||||
|
"github.com/databricks/cli/bundle/config/resources"
|
||||||
|
"github.com/databricks/cli/libs/diag"
|
||||||
|
"github.com/databricks/databricks-sdk-go/experimental/mocks"
|
||||||
|
"github.com/databricks/databricks-sdk-go/service/jobs"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValidateSharedRootPermissionsForShared(t *testing.T) {
|
||||||
|
b := &bundle.Bundle{
|
||||||
|
Config: config.Root{
|
||||||
|
Workspace: config.Workspace{
|
||||||
|
RootPath: "/Workspace/Shared/foo/bar",
|
||||||
|
},
|
||||||
|
Permissions: []resources.Permission{
|
||||||
|
{Level: CAN_MANAGE, GroupName: "users"},
|
||||||
|
},
|
||||||
|
Resources: config.Resources{
|
||||||
|
Jobs: map[string]*resources.Job{
|
||||||
|
"job_1": {JobSettings: &jobs.JobSettings{Name: "job_1"}},
|
||||||
|
"job_2": {JobSettings: &jobs.JobSettings{Name: "job_2"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
m := mocks.NewMockWorkspaceClient(t)
|
||||||
|
b.SetWorkpaceClient(m.WorkspaceClient)
|
||||||
|
|
||||||
|
diags := bundle.Apply(context.Background(), b, bundle.Seq(ValidateSharedRootPermissions()))
|
||||||
|
require.Empty(t, diags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateSharedRootPermissionsForSharedError(t *testing.T) {
|
||||||
|
b := &bundle.Bundle{
|
||||||
|
Config: config.Root{
|
||||||
|
Workspace: config.Workspace{
|
||||||
|
RootPath: "/Workspace/Shared/foo/bar",
|
||||||
|
},
|
||||||
|
Permissions: []resources.Permission{
|
||||||
|
{Level: CAN_MANAGE, UserName: "foo@bar.com"},
|
||||||
|
},
|
||||||
|
Resources: config.Resources{
|
||||||
|
Jobs: map[string]*resources.Job{
|
||||||
|
"job_1": {JobSettings: &jobs.JobSettings{Name: "job_1"}},
|
||||||
|
"job_2": {JobSettings: &jobs.JobSettings{Name: "job_2"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
m := mocks.NewMockWorkspaceClient(t)
|
||||||
|
b.SetWorkpaceClient(m.WorkspaceClient)
|
||||||
|
|
||||||
|
diags := bundle.Apply(context.Background(), b, bundle.Seq(ValidateSharedRootPermissions()))
|
||||||
|
require.Len(t, diags, 1)
|
||||||
|
require.Equal(t, "the bundle root path /Workspace/Shared/foo/bar is writable by all workspace users", diags[0].Summary)
|
||||||
|
require.Equal(t, diag.Warning, diags[0].Severity)
|
||||||
|
}
|
|
@ -16,6 +16,10 @@ func ApplyWorkspaceRootPermissions() bundle.Mutator {
|
||||||
return &workspaceRootPermissions{}
|
return &workspaceRootPermissions{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*workspaceRootPermissions) Name() string {
|
||||||
|
return "ApplyWorkspaceRootPermissions"
|
||||||
|
}
|
||||||
|
|
||||||
// Apply implements bundle.Mutator.
|
// Apply implements bundle.Mutator.
|
||||||
func (*workspaceRootPermissions) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
|
func (*workspaceRootPermissions) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
|
||||||
err := giveAccessForWorkspaceRoot(ctx, b)
|
err := giveAccessForWorkspaceRoot(ctx, b)
|
||||||
|
@ -26,10 +30,6 @@ func (*workspaceRootPermissions) Apply(ctx context.Context, b *bundle.Bundle) di
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*workspaceRootPermissions) Name() string {
|
|
||||||
return "ApplyWorkspaceRootPermissions"
|
|
||||||
}
|
|
||||||
|
|
||||||
func giveAccessForWorkspaceRoot(ctx context.Context, b *bundle.Bundle) error {
|
func giveAccessForWorkspaceRoot(ctx context.Context, b *bundle.Bundle) error {
|
||||||
permissions := make([]workspace.WorkspaceObjectAccessControlRequest, 0)
|
permissions := make([]workspace.WorkspaceObjectAccessControlRequest, 0)
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,6 @@ func TestApplyWorkspaceRootPermissions(t *testing.T) {
|
||||||
WorkspaceObjectType: "directories",
|
WorkspaceObjectType: "directories",
|
||||||
}).Return(nil, nil)
|
}).Return(nil, nil)
|
||||||
|
|
||||||
diags := bundle.Apply(context.Background(), b, ApplyWorkspaceRootPermissions())
|
diags := bundle.Apply(context.Background(), b, bundle.Seq(ValidateSharedRootPermissions(), ApplyWorkspaceRootPermissions()))
|
||||||
require.NoError(t, diags.Error())
|
require.Empty(t, diags)
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,8 +76,11 @@ func Initialize() bundle.Mutator {
|
||||||
|
|
||||||
mutator.TranslatePaths(),
|
mutator.TranslatePaths(),
|
||||||
trampoline.WrapperWarning(),
|
trampoline.WrapperWarning(),
|
||||||
|
|
||||||
|
permissions.ValidateSharedRootPermissions(),
|
||||||
permissions.ApplyBundlePermissions(),
|
permissions.ApplyBundlePermissions(),
|
||||||
permissions.FilterCurrentUser(),
|
permissions.FilterCurrentUser(),
|
||||||
|
|
||||||
metadata.AnnotateJobs(),
|
metadata.AnnotateJobs(),
|
||||||
metadata.AnnotatePipelines(),
|
metadata.AnnotatePipelines(),
|
||||||
terraform.Initialize(),
|
terraform.Initialize(),
|
||||||
|
|
Loading…
Reference in New Issue