diff --git a/docs/databricks.md b/docs/databricks.md index 30ddcaf..e60c703 100644 --- a/docs/databricks.md +++ b/docs/databricks.md @@ -17,10 +17,6 @@ When Databricks refuses, the pane says what to check: access to the workspace and READ on the scope, or, when the workspace's IP access list refused, the network you're on. -On a scope, the right pane shows who has access to it and how much -(manage, write, read). Databricks shows that only to the scope's -managers; to others it says so. - Hide profiles you don't want in the [config](config.md): ```yaml diff --git a/internal/provider/databricks/databricks_test.go b/internal/provider/databricks/databricks_test.go index 89909c5..10caba9 100644 --- a/internal/provider/databricks/databricks_test.go +++ b/internal/provider/databricks/databricks_test.go @@ -140,15 +140,8 @@ func workspaceAPI(t *testing.T, status int) *httptest.Server { map[string]any{"key": "storage-key", "last_updated_timestamp": 1767225600000}, map[string]any{"key": "client-secret"}, }}) - case r.URL.Path == "/api/2.0/secrets/acls/list" && q.Get("scope") == "app": - write(w, 200, map[string]any{"items": []any{ - map[string]any{"principal": "users", "permission": "READ"}, - map[string]any{"principal": "admins", "permission": "MANAGE"}, - }}) case r.URL.Path == "/api/2.0/secrets/list" && q.Get("scope") == "locked": write(w, 403, map[string]any{"error_code": "PERMISSION_DENIED", "message": "User does not have READ permission on scope locked"}) - case r.URL.Path == "/api/2.0/secrets/acls/list" && q.Get("scope") == "locked": - write(w, 403, map[string]any{"error_code": "PERMISSION_DENIED", "message": "User does not have MANAGE permission"}) case r.URL.Path == "/api/2.0/secrets/get" && q.Get("scope") == "app": write(w, 200, map[string]any{"key": q.Get("key"), "value": base64.StdEncoding.EncodeToString([]byte("value-of-" + q.Get("key")))}) @@ -301,23 +294,6 @@ func TestClassify(t *testing.T) { } } -func TestScopeDetails(t *testing.T) { - p := newTestProvider(t, workspaceAPI(t, 0), nil) - src := provider.Source{ID: "prod"} - attrs, err := p.Details(context.Background(), provider.Container{Source: src, Name: "app"}) - if err != nil { - t.Fatal(err) - } - want := []provider.Attr{{Label: "access", Value: "admins · manage"}, {Value: "users · read"}} - if !reflect.DeepEqual(attrs, want) { - t.Errorf("app: %+v", attrs) - } - attrs, err = p.Details(context.Background(), provider.Container{Source: src, Name: "locked"}) - if err != nil || len(attrs) != 1 || !strings.Contains(attrs[0].Value, "managers") { - t.Errorf("locked: %+v, %v", attrs, err) - } -} - func TestDeniedSaysWhatToCheck(t *testing.T) { p := newTestProvider(t, workspaceAPI(t, 0), nil) _, err := p.Objects(context.Background(), provider.Container{Source: provider.Source{ID: "prod"}, Name: "locked"}, provider.TypeSecret) diff --git a/internal/provider/databricks/details.go b/internal/provider/databricks/details.go deleted file mode 100644 index dac37a9..0000000 --- a/internal/provider/databricks/details.go +++ /dev/null @@ -1,43 +0,0 @@ -package databricks - -import ( - "cmp" - "context" - "errors" - "slices" - "strings" - - "github.com/databricks/databricks-sdk-go/apierr" - "github.com/databricks/databricks-sdk-go/service/workspace" - - "git.kebcloud.no/keb/lazysecrets/internal/provider" -) - -// Details are who has access to a scope, and how much. Only the scope's -// managers may see that; to others it says so. -func (p *Provider) Details(ctx context.Context, c provider.Container) ([]provider.Attr, error) { - w, pr, err := p.client(c.Source.ID) - if err != nil { - return nil, err - } - acls, err := w.ListAclsAll(ctx, workspace.ListAclsRequest{Scope: c.Name}) - var api *apierr.APIError - switch { - case errors.As(err, &api) && api.StatusCode == 403: - return []provider.Attr{{Label: "access", Value: "only the scope's managers can see who has it"}}, nil - case err != nil: - return nil, classify(pr, err) - case len(acls) == 0: - return []provider.Attr{{Label: "access", Value: "workspace admins only"}}, nil - } - rank := map[workspace.AclPermission]int{workspace.AclPermissionManage: 0, workspace.AclPermissionWrite: 1, workspace.AclPermissionRead: 2} - slices.SortFunc(acls, func(a, b workspace.AclItem) int { - return cmp.Or(cmp.Compare(rank[a.Permission], rank[b.Permission]), cmp.Compare(a.Principal, b.Principal)) - }) - out := make([]provider.Attr, len(acls)) - for i, a := range acls { - out[i] = provider.Attr{Value: a.Principal + " · " + strings.ToLower(string(a.Permission))} - } - out[0].Label = "access" - return out, nil -} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 2b3ae9f..e5136d4 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -221,9 +221,8 @@ func (d *Denied) Error() string { func (d *Denied) Unwrap() error { return d.Err } // ContainerDetails is implemented by providers that can say more about a -// container than its listing does: a vault's settings, who has access to -// a scope, how a file is encrypted and what git knows of it. It may go -// over the network; ErrUnsupported means there's nothing more for c. +// container than its listing does: how a file is encrypted and what git +// knows of it. ErrUnsupported means there's nothing more for c. type ContainerDetails interface { Details(ctx context.Context, c Container) ([]Attr, error) } diff --git a/internal/ui/app/app.go b/internal/ui/app/app.go index 4604886..344da81 100644 --- a/internal/ui/app/app.go +++ b/internal/ui/app/app.go @@ -56,7 +56,7 @@ const ( slotDescribe // extra metadata for the selected object slotCopy // fetch a value straight to the clipboard slotVersions // count the selected object's versions - slotDetails // a vault's, scope's or file's details + slotDetails // a file's details slotCount ) diff --git a/internal/ui/app/fields.go b/internal/ui/app/fields.go index d06f0b6..1aa7e63 100644 --- a/internal/ui/app/fields.go +++ b/internal/ui/app/fields.go @@ -15,7 +15,7 @@ import ( // The fourth pane shows Fields for anything but a secret. On a provider's // row: its account, from what the provider knows locally. -// A vault's, scope's or file's details beyond its listing load once a +// A file's details beyond its listing load once a // session, when the cursor rests on it, and again with refresh. They're // extra: moving on cancels the load, and search never waits for them. type detailsState struct {