mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-16 11:39:57 +00:00
- **Wails v3 application** (`client/ui`) with a React + TypeScript + Tailwind frontend replacing the Fyne UI: main connection view, exit-node switcher, networks/peers browser with detail panels, profile management, settings (general, network, SSH, security, troubleshooting, appearance), debug-bundle creation, and a first-run welcome flow. - **Internationalization**: go-i18n bundle with 9 locales (en, de, es, fr, hu, it, pt, ru, zh-CN) shared between the tray and the frontend. - **New system tray** implementation with per-platform theme-aware icons, including a native XEmbed host for Linux (`xembed_tray_linux.c`) and a Linux theme watcher. - **Session handling**: auth session watcher (`client/internal/auth/sessionwatch`), pending login flow, session-expiration dialog and tray notifications, and `netbird login` improvements. - **Daemon API extensions** (`daemon.proto`): status stream subscription, event stream, networks/exit-node selection endpoints, and richer full status — with probe throttling on the daemon side to protect against UI-driven request storms. - **UI preferences store** persisted per profile, autostart management via the daemon (single source of truth in HKCU on Windows). - **Build system**: Taskfile-based builds per platform (macOS, Linux, Windows), Docker cross-compilation images, MSIX/NSIS/nfpm/AppImage packaging, and a new `frontend-ui` CI workflow. Co-authored-by: Zoltan Papp <zoltan.pmail@gmail.com> Co-authored-by: Eduard Gert <kontakt@eduardgert.de> Co-authored-by: braginini <bangvalo@gmail.com> Co-authored-by: Pascal Fischer <32096965+pascal-fischer@users.noreply.github.com> Co-authored-by: riccardom <riccardomanfrin@gmail.com>
81 lines
2.2 KiB
Go
81 lines
2.2 KiB
Go
package auth
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
)
|
|
|
|
func TestIsPeerLoginExpired(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
err error
|
|
want bool
|
|
}{
|
|
{
|
|
name: "nil",
|
|
err: nil,
|
|
want: false,
|
|
},
|
|
{
|
|
name: "plain error (not a gRPC status)",
|
|
err: errors.New("network read: connection reset"),
|
|
want: false,
|
|
},
|
|
{
|
|
name: "PermissionDenied with different message",
|
|
err: status.Error(codes.PermissionDenied, "user is blocked"),
|
|
want: false,
|
|
},
|
|
{
|
|
name: "Unauthenticated with the expected phrase",
|
|
// Wrong status code — must still return false.
|
|
err: status.Error(codes.Unauthenticated, "peer login has expired, please log in once more"),
|
|
want: false,
|
|
},
|
|
{
|
|
name: "exact server message",
|
|
err: status.Error(codes.PermissionDenied, "peer login has expired, please log in once more"),
|
|
want: true,
|
|
},
|
|
{
|
|
name: "phrase as substring",
|
|
// Future-proofing: if mgm reworords but keeps the phrase,
|
|
// the friendly fallback must still kick in.
|
|
err: status.Error(codes.PermissionDenied, "session refused: peer login has expired (account=foo)"),
|
|
want: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := isPeerLoginExpired(tc.err); got != tc.want {
|
|
t.Fatalf("isPeerLoginExpired(%v) = %v, want %v", tc.err, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestErrSetupKeyOnSSOExpiredPeer(t *testing.T) {
|
|
// Sentinel must surface as PermissionDenied so the upstream
|
|
// isPermissionDenied / isAuthError checks classify it correctly
|
|
// (short-circuit retry backoff, set StatusNeedsLogin).
|
|
if !isPermissionDenied(errSetupKeyOnSSOExpiredPeer) {
|
|
t.Fatalf("errSetupKeyOnSSOExpiredPeer must be a PermissionDenied gRPC error")
|
|
}
|
|
|
|
// Message must actually mention SSO and `netbird up` so it is
|
|
// actionable for the end user. Loose substring checks keep the
|
|
// test resilient to copy edits.
|
|
s, _ := status.FromError(errSetupKeyOnSSOExpiredPeer)
|
|
msg := strings.ToLower(s.Message())
|
|
for _, want := range []string{"sso", "netbird up"} {
|
|
if !strings.Contains(msg, want) {
|
|
t.Errorf("sentinel message should contain %q, got %q", want, s.Message())
|
|
}
|
|
}
|
|
}
|