Support compact Remote setup with reader-owned access selection

This commit is contained in:
Storm Dragon committed 2026-11-06 20:07:05 -05:00
1 parent 952d3c8204
commit bf4a23645e
9 files changed
+274 -61

No files matched your search

+13 -4
View File
@@ -6,6 +6,7 @@ package tentaclewire
import (
"bytes"
"encoding/json"
"slices"
"unicode"
"unicode/utf8"
@@ -15,7 +16,8 @@ import (
// Interaction is an opt-in command result. The host runs forms outside the
// bounded RPC deadline and invokes Next with the submitted values. Poll is a
// registered command sampled once per second while the form remains open.
// A polling reply may announce a changed status or finish the form with Done.
// A polling reply may announce a changed status, replace the form, request
// reader-owned consent, or finish with a verified background lease.
type Interaction struct {
Presentation
Form *interaction.Form `json:"form,omitempty"`
@@ -34,8 +36,9 @@ type Interaction struct {
// RemoteRequest asks the reader to show its own local consent form. It never
// grants access by itself and can target only the requesting process session.
type RemoteRequest struct {
Role string `json:"role"`
Grants []string `json:"grants"`
Role string `json:"role"`
Grants []string `json:"grants"`
OptionalGrants []string `json:"optional_grants,omitempty"`
}
type Submission struct {
@@ -88,7 +91,7 @@ func DecodeInteraction(data []byte) (Interaction, error) {
return value, ErrProtocol
}
} else if value.Remote != nil {
if !ValidRoleGrants(value.Remote.Role, value.Remote.Grants) || value.Next == "" || value.Reader != nil || value.Form != nil || value.Source != "" || value.Poll != "" || value.Clipboard != nil || value.Done {
if !ValidRemoteRequest(*value.Remote) || value.Next == "" || value.Reader != nil || value.Form != nil || value.Source != "" || value.Poll != "" || value.Clipboard != nil || value.Done {
return value, ErrProtocol
}
} else if value.Reader != nil {
@@ -132,3 +135,9 @@ func validFormText(text string) bool {
}
return true
}
// Optional grants are offered only when installed; none is approved implicitly.
func ValidRemoteRequest(request RemoteRequest) bool {
combined := slices.Concat(request.Grants, request.OptionalGrants)
return ValidRoleGrants(request.Role, request.Grants) && ValidRoleGrants(request.Role, combined)
}
@@ -59,3 +59,20 @@ func TestRemoteBackgroundInteractionValidation(t *testing.T) {
}
}
}
func TestRemoteOptionalGrantsValidation(t *testing.T) {
for _, test := range []struct {
optional []string
valid bool
}{
{[]string{"keyboard-capture", "clipboard-exchange", "braille-stream"}, true},
{[]string{"keyboard-inject"}, false}, {[]string{"remote-present"}, false},
{[]string{"braille-stream", "braille-stream"}, false}, {[]string{"unknown"}, false},
} {
data, _ := json.Marshal(Interaction{Remote: &RemoteRequest{Role: "controller", Grants: []string{"remote-present"}, OptionalGrants: test.optional}, Next: "approve"})
_, err := DecodeInteraction(data)
if (err == nil) != test.valid {
t.Fatalf("optional grants %v: %v", test.optional, err)
}
}
}