Skip to content

Commit cdd79f3

Browse files
Add permission check for Insights page and don't show it in the menu for non-owners (#20593)
* Add permission check for Insights page and don't show it in the menu for non-owners Tool: gitpod/catfood.gitpod.cloud * Make necessary permission clear Co-authored-by: Gero Posmyk-Leinemann <[email protected]> Tool: gitpod/catfood.gitpod.cloud * Bring more disjunction to your life Tool: gitpod/catfood.gitpod.cloud --------- Co-authored-by: Gero Posmyk-Leinemann <[email protected]>
1 parent 10c49da commit cdd79f3

File tree

2 files changed

+29
-13
lines changed

2 files changed

+29
-13
lines changed

Diff for: components/dashboard/src/Insights.tsx

+18-4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { DownloadIcon } from "lucide-react";
2727
import { Button } from "@podkit/buttons/Button";
2828
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from "@podkit/dropdown/DropDown";
2929
import { useInstallationConfiguration } from "./data/installation/installation-config-query";
30+
import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
3031

3132
export const Insights = () => {
3233
const toDate = useMemo(() => Timestamp.fromDate(new Date()), []);
@@ -49,6 +50,9 @@ export const Insights = () => {
4950
const grouped = Object.groupBy(sessions, (ws) => ws.workspace?.id ?? "unknown");
5051
const [page, setPage] = useState(0);
5152

53+
const isLackingPermissions =
54+
errorMessage instanceof ApplicationError && errorMessage.code === ErrorCodes.PERMISSION_DENIED;
55+
5256
return (
5357
<>
5458
<Header title="Insights" subtitle="Insights into workspace sessions in your organization" />
@@ -59,7 +63,7 @@ export const Insights = () => {
5963
"md:flex-row md:items-center md:space-x-4 md:space-y-0",
6064
)}
6165
>
62-
<DownloadUsage to={toDate} />
66+
<DownloadUsage to={toDate} disabled={isLackingPermissions} />
6367
</div>
6468

6569
<div
@@ -71,7 +75,16 @@ export const Insights = () => {
7175

7276
{errorMessage && (
7377
<Alert type="error" className="mt-4">
74-
{errorMessage instanceof Error ? errorMessage.message : "An error occurred."}
78+
{isLackingPermissions ? (
79+
<>
80+
You don't have <span className="font-medium">Owner</span> permissions to access this
81+
organization's insights.
82+
</>
83+
) : errorMessage instanceof Error ? (
84+
errorMessage.message
85+
) : (
86+
"An error occurred."
87+
)}
7588
</Alert>
7689
)}
7790

@@ -151,8 +164,9 @@ export const Insights = () => {
151164

152165
type DownloadUsageProps = {
153166
to: Timestamp;
167+
disabled?: boolean;
154168
};
155-
export const DownloadUsage = ({ to }: DownloadUsageProps) => {
169+
export const DownloadUsage = ({ to, disabled }: DownloadUsageProps) => {
156170
const { data: org } = useCurrentOrg();
157171
const { toast } = useToast();
158172
// When we start the download, we disable the button for a short time
@@ -184,7 +198,7 @@ export const DownloadUsage = ({ to }: DownloadUsageProps) => {
184198
return (
185199
<DropdownMenu>
186200
<DropdownMenuTrigger asChild>
187-
<Button variant="secondary" className="gap-1" disabled={downloadDisabled}>
201+
<Button variant="secondary" className="gap-1" disabled={disabled || downloadDisabled}>
188202
<DownloadIcon strokeWidth={3} className="w-4" />
189203
<span>Export as CSV</span>
190204
</Button>

Diff for: components/dashboard/src/menu/OrganizationSelector.tsx

+11-9
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export default function OrganizationSelector() {
2323
const orgs = useOrganizations();
2424
const currentOrg = useCurrentOrg();
2525
const members = useListOrganizationMembers().data ?? [];
26-
const owner = useIsOwner();
26+
const isOwner = useIsOwner();
2727
const hasMemberPermission = useHasRolePermission(OrganizationRole.MEMBER);
2828
const { data: billingMode } = useOrgBillingMode();
2929
const getOrgURL = useGetOrgURL();
@@ -78,13 +78,15 @@ export default function OrganizationSelector() {
7878
link: "/members",
7979
});
8080
if (isDedicated) {
81-
linkEntries.push({
82-
title: "Insights",
83-
customContent: <LinkEntry>Insights</LinkEntry>,
84-
active: false,
85-
separator: false,
86-
link: "/insights",
87-
});
81+
if (isOwner) {
82+
linkEntries.push({
83+
title: "Insights",
84+
customContent: <LinkEntry>Insights</LinkEntry>,
85+
active: false,
86+
separator: false,
87+
link: "/insights",
88+
});
89+
}
8890
} else {
8991
linkEntries.push({
9092
title: "Usage",
@@ -95,7 +97,7 @@ export default function OrganizationSelector() {
9597
});
9698
}
9799
// Show billing if user is an owner of current org
98-
if (owner) {
100+
if (isOwner) {
99101
if (billingMode?.mode === "usage-based") {
100102
linkEntries.push({
101103
title: "Billing",

0 commit comments

Comments
 (0)