Files
LocalAI/pkg/addons/contract/permissions_test.go
T
Ettore Di Giacinto 15e2321d50 feat(connectors): edit the permissions of a binding on the shared connector page
Bindings saved before permissions were enforced have no capabilities, so
they deny files from senders, triggers, memory writes and talk-back, and
the owner could only fix that through the REST API.

The shared connector page now has a section for it, so every connector
add-on gets it: a switch and a description for each capability with the
sensitive ones marked, the role matrix (never a sensitive capability, never
above the ceiling), pairing with a code shown once, the approved channels
for talk-back, and a read-only view of the access check with the latest
decisions. A binding with no capability shows a notice that says why the
features are off, with an "Allow the usual" button that turns on the
non-sensitive everyday set.

The kinds endpoint returns the closed list of capabilities with a title,
a description and the sensitive and usual flags. The field is additive.

Assisted-by: Claude:claude-sonnet-5-5 go node playwright swag
2026-11-03 15:36:21 +00:00

101 lines
3.7 KiB
Go

// SPDX-License-Identifier: MIT
package contract
import (
"os"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("the roles and permissions of a connector kind", func() {
var m Manifest
BeforeEach(func() {
b, err := os.ReadFile("../../../tests/addons/fixtures/contracts/page.json")
Expect(err).NotTo(HaveOccurred())
m, err = ParseManifest(b)
Expect(err).NotTo(HaveOccurred())
m.API = VersionRange{Min: 1, Max: 2}
m.Capabilities.UI = nil
m.Capabilities.Connector = &ConnectorCapability{Kinds: []ConnectorKind{{ID: "chat", Title: "Chat", Delivery: DeliveryPull}}}
})
host := func() HostCompatibility {
h := compatible()
h.API, h.APIMin = HostAPIVersion, HostAPIMin
h.SDK = HostSDKVersion
return h
}
kind := func() *ConnectorKind { return &m.Capabilities.Connector.Kinds[0] }
It("accepts roles and known permissions", func() {
kind().Roles = []ConnectorRole{{ID: "admin", Title: "Admin", Description: "Workspace admin"}}
kind().Permissions = []string{CapProposalsConfirm, CapMemoryWrite, CapProfileWrite}
Expect(ValidateManifest(m, host())).To(Succeed())
})
It("accepts a kind that declares neither, on api 1", func() {
m.API = VersionRange{Min: 1, Max: 1}
Expect(ValidateManifest(m, host())).To(Succeed())
})
It("needs api 2", func() {
m.API = VersionRange{Min: 1, Max: 1}
kind().Permissions = []string{CapMemoryWrite}
Expect(ValidateManifest(m, host())).To(MatchError(ContainSubstring("api 2")))
})
It("refuses an unknown or repeated permission", func() {
kind().Permissions = []string{"root.everything"}
Expect(ValidateManifest(m, host())).NotTo(Succeed())
kind().Permissions = []string{CapMemoryWrite, CapMemoryWrite}
Expect(ValidateManifest(m, host())).NotTo(Succeed())
})
It("refuses too many roles or permissions, a bad role ID and a repeated role", func() {
kind().Roles = nil
for i := 0; i <= MaxConnectorRoles; i++ {
kind().Roles = append(kind().Roles, ConnectorRole{ID: "r" + string(rune('a'+i)), Title: "R"})
}
Expect(ValidateManifest(m, host())).NotTo(Succeed())
kind().Roles = []ConnectorRole{{ID: "Bad Id", Title: "R"}}
Expect(ValidateManifest(m, host())).NotTo(Succeed())
kind().Roles = []ConnectorRole{{ID: "a", Title: "A"}, {ID: "a", Title: "B"}}
Expect(ValidateManifest(m, host())).NotTo(Succeed())
kind().Roles = nil
kind().Permissions = ConnectorCapabilityNames()
Expect(ValidateManifest(m, host())).To(Succeed()) // the whole list fits
kind().Permissions = append(kind().Permissions, "x.1", "x.2")
Expect(ValidateManifest(m, host())).NotTo(Succeed())
})
It("describes every capability, and never makes a sensitive one usual", func() {
list := ConnectorCapabilities()
Expect(list).To(HaveLen(len(ConnectorCapabilityNames())))
usual := 0
for _, c := range list {
Expect(c.Title).NotTo(BeEmpty(), c.Name)
Expect(c.Description).NotTo(BeEmpty(), c.Name)
Expect(c.Sensitive).To(Equal(SensitiveCapability(c.Name)))
if c.Usual {
usual++
Expect(c.Sensitive).To(BeFalse(), c.Name)
}
}
Expect(usual).To(BeNumerically(">", 0))
Expect(capabilityText).To(HaveLen(len(capabilities)))
})
It("never grants a sensitive capability to a role", func() {
kind().Permissions = []string{CapProfileWrite, CapMemoryWrite}
Expect(kind().Grantable(CapProfileWrite)).To(BeFalse())
Expect(kind().Grantable(CapMemoryWrite)).To(BeTrue())
Expect(kind().Grantable(CapFilesUpload)).To(BeFalse(), "not listed by the kind")
})
It("keeps only declared, distinct roles of a message", func() {
kind().Roles = []ConnectorRole{{ID: "admin", Title: "Admin"}}
Expect(kind().FilterRoles([]string{"admin", "root", "admin"})).To(Equal([]string{"admin"}))
Expect((ConnectorKind{}).FilterRoles([]string{"admin"})).To(BeEmpty())
})
})