From 86c8ab7502a38b4de05308355fe0c847e4e78167 Mon Sep 17 00:00:00 2001 From: Sonia Appasamy Date: Thu, 9 Nov 2023 16:19:22 -0500 Subject: [PATCH] client/web: add readonly/manage toggle Updates tailscale/corp#14335 Signed-off-by: Sonia Appasamy --- client/web/package.json | 1 + client/web/qnap.go | 8 +- client/web/src/components/app.tsx | 117 +++---- .../web/src/components/exit-node-selector.tsx | 6 +- client/web/src/components/login-toggle.tsx | 149 +++++++++ .../components/views/device-details-view.tsx | 14 +- ...nagement-client-view.tsx => home-view.tsx} | 5 +- .../components/views/readonly-client-view.tsx | 75 ----- client/web/src/hooks/auth.ts | 8 +- client/web/src/hooks/node-data.ts | 1 + client/web/src/icons/eye.svg | 11 + client/web/src/icons/user.svg | 4 + client/web/src/ui/popover.tsx | 106 ++++++ client/web/src/ui/profile-pic.tsx | 21 +- client/web/synology.go | 14 +- client/web/web.go | 53 ++- client/web/web_test.go | 23 +- client/web/yarn.lock | 304 +++++++++++++++++- 18 files changed, 731 insertions(+), 189 deletions(-) create mode 100644 client/web/src/components/login-toggle.tsx rename client/web/src/components/views/{management-client-view.tsx => home-view.tsx} (96%) delete mode 100644 client/web/src/components/views/readonly-client-view.tsx create mode 100644 client/web/src/icons/eye.svg create mode 100644 client/web/src/icons/user.svg create mode 100644 client/web/src/ui/popover.tsx diff --git a/client/web/package.json b/client/web/package.json index 5c7b33add..a986265d8 100644 --- a/client/web/package.json +++ b/client/web/package.json @@ -8,6 +8,7 @@ }, "private": true, "dependencies": { + "@radix-ui/react-popover": "^1.0.6", "classnames": "^2.3.1", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/client/web/qnap.go b/client/web/qnap.go index 07145a77c..9bde64bf5 100644 --- a/client/web/qnap.go +++ b/client/web/qnap.go @@ -20,16 +20,16 @@ // authorizeQNAP authenticates the logged-in QNAP user and verifies that they // are authorized to use the web client. // If the user is not authorized to use the client, an error is returned. -func authorizeQNAP(r *http.Request) (ar authResponse, err error) { +func authorizeQNAP(r *http.Request) (authorized bool, err error) { _, resp, err := qnapAuthn(r) if err != nil { - return ar, err + return false, err } if resp.IsAdmin == 0 { - return ar, errors.New("user is not an admin") + return false, errors.New("user is not an admin") } - return authResponse{OK: true}, nil + return true, nil } type qnapAuthResponse struct { diff --git a/client/web/src/components/app.tsx b/client/web/src/components/app.tsx index 41f5e5c2e..95d19e70e 100644 --- a/client/web/src/components/app.tsx +++ b/client/web/src/components/app.tsx @@ -1,22 +1,21 @@ import cx from "classnames" import React, { useEffect } from "react" +import LoginToggle from "src/components/login-toggle" +import DeviceDetailsView from "src/components/views/device-details-view" +import HomeView from "src/components/views/home-view" import LegacyClientView from "src/components/views/legacy-client-view" import LoginClientView from "src/components/views/login-client-view" -import ManagementClientView from "src/components/views/management-client-view" -import ReadonlyClientView from "src/components/views/readonly-client-view" import useAuth, { AuthResponse } from "src/hooks/auth" -import useNodeData, { NodeData, NodeUpdate } from "src/hooks/node-data" +import useNodeData, { NodeData } from "src/hooks/node-data" import { ReactComponent as TailscaleIcon } from "src/icons/tailscale-icon.svg" -import ProfilePic from "src/ui/profile-pic" import { Link, Route, Router, Switch, useLocation } from "wouter" -import DeviceDetailsView from "./views/device-details-view" export default function App() { const { data: auth, loading: loadingAuth, newSession } = useAuth() return (
- {loadingAuth ? ( + {loadingAuth || !auth ? (
Loading...
// TODO(sonia): add a loading view ) : ( @@ -29,7 +28,7 @@ function WebClient({ auth, newSession, }: { - auth?: AuthResponse + auth: AuthResponse newSession: () => Promise }) { const { data, refreshData, updateNode } = useNodeData() @@ -37,36 +36,44 @@ function WebClient({ refreshData() }, [auth, refreshData]) - if (!data) { - return
Loading...
// TODO(sonia): add a loading view - } - - return ( + return !data ? ( +
Loading...
+ ) : data.Status === "NeedsLogin" || data.Status === "NoState" ? ( + // Client not on a tailnet, render login. + updateNode({ Reauthenticate: true })} + /> + ) : data.DebugMode !== "full" && data.DebugMode !== "login" ? ( + // Render legacy client interface. <> - {/* TODO(sonia): get rid of the conditions here once full/readonly - * views live on same components */} - {data.DebugMode === "full" && auth?.ok &&
} + + {/* TODO: add license to new client */} +