mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-10 00:41:29 +02:00
Say whether an approval was refused, unanswered, or never shown
This commit is contained in:
@@ -321,11 +321,29 @@ func (a *vncApprover) Request(ctx context.Context, info vncserver.ApprovalInfo)
|
||||
Metadata: meta,
|
||||
})
|
||||
if err != nil {
|
||||
return vncserver.ApprovalDecision{}, err
|
||||
return vncserver.ApprovalDecision{}, approvalCause(err)
|
||||
}
|
||||
return vncserver.ApprovalDecision{ViewOnly: d.ViewOnly}, nil
|
||||
}
|
||||
|
||||
// approvalCause restates a broker failure as the cause the VNC server
|
||||
// classifies, so the peer that dialled learns whether the user refused or
|
||||
// never answered. The broker's denial and timeout carry nothing beyond the
|
||||
// sentinel, so those are returned as the server's own; the two unavailable
|
||||
// cases differ in a way worth keeping, so they are wrapped.
|
||||
func approvalCause(err error) error {
|
||||
switch {
|
||||
case errors.Is(err, approval.ErrDenied):
|
||||
return vncserver.ErrApprovalDenied
|
||||
case errors.Is(err, approval.ErrTimeout):
|
||||
return vncserver.ErrApprovalTimeout
|
||||
case errors.Is(err, approval.ErrNoSubscriber), errors.Is(err, approval.ErrPromptNotShown):
|
||||
return fmt.Errorf("%w: %w", vncserver.ErrApprovalUnavailable, err)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func displayPeer(info vncserver.ApprovalInfo) string {
|
||||
if info.Initiator != "" {
|
||||
return info.Initiator
|
||||
|
||||
60
client/internal/engine_vnc_test.go
Normal file
60
client/internal/engine_vnc_test.go
Normal file
@@ -0,0 +1,60 @@
|
||||
//go:build !js && !ios && !android
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal/approval"
|
||||
vncserver "github.com/netbirdio/netbird/client/vnc/server"
|
||||
)
|
||||
|
||||
// TestApprovalCause checks that each way the broker can refuse arrives at
|
||||
// the VNC server as the matching cause. Without this the server sees one
|
||||
// opaque error and tells every caller it was denied, which reads as "the
|
||||
// user said no" even when no prompt was ever answered or shown.
|
||||
func TestApprovalCause(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
in error
|
||||
want error
|
||||
}{
|
||||
{"denied", approval.ErrDenied, vncserver.ErrApprovalDenied},
|
||||
{"timeout", approval.ErrTimeout, vncserver.ErrApprovalTimeout},
|
||||
{"no_subscriber", approval.ErrNoSubscriber, vncserver.ErrApprovalUnavailable},
|
||||
{"prompt_not_shown", approval.ErrPromptNotShown, vncserver.ErrApprovalUnavailable},
|
||||
// Wrapped on the way in: the broker may add context, and the
|
||||
// classification must survive it.
|
||||
{"wrapped_timeout", fmt.Errorf("request: %w", approval.ErrTimeout), vncserver.ErrApprovalTimeout},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := approvalCause(tc.in)
|
||||
assert.ErrorIs(t, got, tc.want, "broker error %v must classify as %v", tc.in, tc.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestApprovalCausePreservesUnavailableDetail: the two unavailable causes
|
||||
// mean different things to whoever reads the daemon log ("nobody was
|
||||
// listening" versus "somebody was, and never got it"), so collapsing them
|
||||
// onto one sentinel must not discard which one occurred.
|
||||
func TestApprovalCausePreservesUnavailableDetail(t *testing.T) {
|
||||
got := approvalCause(approval.ErrPromptNotShown)
|
||||
assert.ErrorIs(t, got, vncserver.ErrApprovalUnavailable)
|
||||
assert.ErrorIs(t, got, approval.ErrPromptNotShown, "the specific cause must stay readable in the log")
|
||||
}
|
||||
|
||||
// TestApprovalCausePassesThroughUnknown keeps an error the mapping does not
|
||||
// recognise intact, so the daemon log still shows what actually happened.
|
||||
// The server rejects it as a denial either way.
|
||||
func TestApprovalCausePassesThroughUnknown(t *testing.T) {
|
||||
for _, err := range []error{context.Canceled, errors.New("something else")} {
|
||||
assert.ErrorIs(t, approvalCause(err), err, "unknown cause must pass through unchanged")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user