Skip to content

Commit 134bf6e

Browse files
aslilacmatifali
authored andcommitted
fix: only show valid organizations in CreateTemplateForm (#14174)
1 parent f93701f commit 134bf6e

File tree

4 files changed

+100
-2
lines changed

4 files changed

+100
-2
lines changed

site/src/components/OrganizationAutocomplete/OrganizationAutocomplete.tsx

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import {
99
useState,
1010
} from "react";
1111
import { useQuery } from "react-query";
12+
import { checkAuthorization } from "api/queries/authCheck";
1213
import { organizations } from "api/queries/organizations";
13-
import type { Organization } from "api/typesGenerated";
14+
import type { AuthorizationCheck, Organization } from "api/typesGenerated";
1415
import { Avatar } from "components/Avatar/Avatar";
1516
import { AvatarData } from "components/AvatarData/AvatarData";
1617
import { useDebouncedFunction } from "hooks/debounce";
@@ -22,6 +23,7 @@ export type OrganizationAutocompleteProps = {
2223
className?: string;
2324
size?: ComponentProps<typeof TextField>["size"];
2425
required?: boolean;
26+
check?: AuthorizationCheck;
2527
};
2628

2729
export const OrganizationAutocomplete: FC<OrganizationAutocompleteProps> = ({
@@ -31,6 +33,7 @@ export const OrganizationAutocomplete: FC<OrganizationAutocompleteProps> = ({
3133
className,
3234
size = "small",
3335
required,
36+
check,
3437
}) => {
3538
const [autoComplete, setAutoComplete] = useState<{
3639
value: string;
@@ -41,6 +44,22 @@ export const OrganizationAutocomplete: FC<OrganizationAutocompleteProps> = ({
4144
});
4245
const organizationsQuery = useQuery(organizations());
4346

47+
const permissionsQuery = useQuery(
48+
check && organizationsQuery.data
49+
? checkAuthorization({
50+
checks: Object.fromEntries(
51+
organizationsQuery.data.map((org) => [
52+
org.id,
53+
{
54+
...check,
55+
object: { ...check.object, organization_id: org.id },
56+
},
57+
]),
58+
),
59+
})
60+
: { enabled: false },
61+
);
62+
4463
const { debounced: debouncedInputOnChange } = useDebouncedFunction(
4564
(event: ChangeEvent<HTMLInputElement>) => {
4665
setAutoComplete((state) => ({
@@ -51,11 +70,20 @@ export const OrganizationAutocomplete: FC<OrganizationAutocompleteProps> = ({
5170
750,
5271
);
5372

73+
// If an authorization check was provided, filter the organizations based on
74+
// the results of that check.
75+
let options = organizationsQuery.data ?? [];
76+
if (check) {
77+
options = permissionsQuery.data
78+
? options.filter((org) => permissionsQuery.data[org.id])
79+
: [];
80+
}
81+
5482
return (
5583
<Autocomplete
5684
noOptionsText="No organizations found"
5785
className={className}
58-
options={organizationsQuery.data ?? []}
86+
options={options}
5987
loading={organizationsQuery.isLoading}
6088
value={value}
6189
data-testid="organization-autocomplete"

site/src/contexts/auth/permissions.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export const permissionsToCheck = {
4141
[checks.createTemplates]: {
4242
object: {
4343
resource_type: "template",
44+
any_org: true,
4445
},
4546
action: "update",
4647
},

site/src/pages/CreateTemplatePage/CreateTemplateForm.stories.tsx

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,40 @@ export const StarterTemplateWithOrgPicker: Story = {
6161
},
6262
};
6363

64+
const canCreateTemplate = (organizationId: string) => {
65+
return {
66+
[organizationId]: {
67+
object: {
68+
resource_type: "template",
69+
organization_id: organizationId,
70+
},
71+
action: "create",
72+
},
73+
};
74+
};
75+
6476
export const StarterTemplateWithProvisionerWarning: Story = {
6577
parameters: {
6678
queries: [
6779
{
6880
key: organizationsKey,
6981
data: [MockDefaultOrganization, MockOrganization2],
7082
},
83+
{
84+
key: [
85+
"authorization",
86+
{
87+
checks: {
88+
...canCreateTemplate(MockDefaultOrganization.id),
89+
...canCreateTemplate(MockOrganization2.id),
90+
},
91+
},
92+
],
93+
data: {
94+
[MockDefaultOrganization.id]: true,
95+
[MockOrganization2.id]: true,
96+
},
97+
},
7198
{
7299
key: getProvisionerDaemonsKey(MockOrganization2.id),
73100
data: [],
@@ -86,6 +113,44 @@ export const StarterTemplateWithProvisionerWarning: Story = {
86113
},
87114
};
88115

116+
export const StarterTemplatePermissionsCheck: Story = {
117+
parameters: {
118+
queries: [
119+
{
120+
key: organizationsKey,
121+
data: [MockDefaultOrganization, MockOrganization2],
122+
},
123+
{
124+
key: [
125+
"authorization",
126+
{
127+
checks: {
128+
...canCreateTemplate(MockDefaultOrganization.id),
129+
...canCreateTemplate(MockOrganization2.id),
130+
},
131+
},
132+
],
133+
data: {
134+
[MockDefaultOrganization.id]: true,
135+
[MockOrganization2.id]: false,
136+
},
137+
},
138+
{
139+
key: getProvisionerDaemonsKey(MockOrganization2.id),
140+
data: [],
141+
},
142+
],
143+
},
144+
args: {
145+
...StarterTemplate.args,
146+
showOrganizationPicker: true,
147+
},
148+
play: async () => {
149+
const organizationPicker = screen.getByPlaceholderText("Organization name");
150+
await userEvent.click(organizationPicker);
151+
},
152+
};
153+
89154
export const DuplicateTemplateWithVariables: Story = {
90155
args: {
91156
copiedTemplate: MockTemplate,

site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,10 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = (props) => {
267267
void form.setFieldValue("organization", newValue?.name || "");
268268
}}
269269
size="medium"
270+
check={{
271+
object: { resource_type: "template" },
272+
action: "create",
273+
}}
270274
/>
271275
</>
272276
)}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy