Skip to content

Commit

Permalink
Add read access to linting policy; Closes #5861
Browse files Browse the repository at this point in the history
  • Loading branch information
jdolle committed Jan 2, 2025
1 parent 8483bd5 commit 6ae3d60
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 133 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ export class SchemaPolicyProvider {

async getOrganizationPolicy(selector: OrganizationSelector) {
await this.session.assertPerformAction({
action: 'schemaLinting:modifyOrganizationRules',
action: 'organization:describe',
organizationId: selector.organizationId,
params: {
organizationId: selector.organizationId,
Expand All @@ -194,7 +194,7 @@ export class SchemaPolicyProvider {

async getOrganizationPolicyForProject(selector: ProjectSelector) {
await this.session.assertPerformAction({
action: 'schemaLinting:modifyProjectRules',
action: 'project:describe',
organizationId: selector.organizationId,
params: {
organizationId: selector.organizationId,
Expand All @@ -207,7 +207,7 @@ export class SchemaPolicyProvider {

async getProjectPolicy(selector: ProjectSelector) {
await this.session.assertPerformAction({
action: 'schemaLinting:modifyProjectRules',
action: 'project:describe',
organizationId: selector.organizationId,
params: {
organizationId: selector.organizationId,
Expand Down
18 changes: 8 additions & 10 deletions packages/web/app/src/components/layouts/organization.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,16 +157,14 @@ export function OrganizationLayout({
</Link>
</TabsTrigger>
)}
{currentOrganization.viewerCanModifySchemaPolicy && (
<TabsTrigger variant="menu" value={Page.Policy} asChild>
<Link
to="/$organizationSlug/view/policy"
params={{ organizationSlug: currentOrganization.slug }}
>
Policy
</Link>
</TabsTrigger>
)}
<TabsTrigger variant="menu" value={Page.Policy} asChild>
<Link
to="/$organizationSlug/view/policy"
params={{ organizationSlug: currentOrganization.slug }}
>
Policy
</Link>
</TabsTrigger>
{currentOrganization.viewerCanAccessSettings && (
<TabsTrigger variant="menu" value={Page.Settings} asChild>
<Link
Expand Down
26 changes: 11 additions & 15 deletions packages/web/app/src/components/layouts/project.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,21 +151,17 @@ export function ProjectLayout({
</Link>
</TabsTrigger>
)}
{currentProject.viewerCanModifySchemaPolicy && (
<>
<TabsTrigger variant="menu" value={Page.Policy} asChild>
<Link
to="/$organizationSlug/$projectSlug/view/policy"
params={{
organizationSlug: props.organizationSlug,
projectSlug: props.projectSlug,
}}
>
Policy
</Link>
</TabsTrigger>
</>
)}
<TabsTrigger variant="menu" value={Page.Policy} asChild>
<Link
to="/$organizationSlug/$projectSlug/view/policy"
params={{
organizationSlug: props.organizationSlug,
projectSlug: props.projectSlug,
}}
>
Policy
</Link>
</TabsTrigger>
{currentProject.viewerCanModifySettings && (
<TabsTrigger variant="menu" value={Page.Settings} asChild>
<Link
Expand Down
2 changes: 2 additions & 0 deletions packages/web/app/src/components/policy/policy-list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ function extractBaseSchema(configJsonSchema: JSONSchema | null | undefined): JSO
}

export function PolicyListItem(props: {
disabled?: boolean;
ruleInfo: FragmentType<typeof PolicyListItem_RuleInfoFragment>;
overridingParentRule: boolean;
}): ReactElement {
Expand All @@ -55,6 +56,7 @@ export function PolicyListItem(props: {
id={ruleInfo.id}
value={ruleInfo.id}
checked={enabled}
disabled={props.disabled}
onCheckedChange={newState => toggleRuleState(newState as boolean)}
/>
</div>
Expand Down
40 changes: 22 additions & 18 deletions packages/web/app/src/components/policy/policy-settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function PolicySettingsListForm({
saving?: boolean;
rulesInParent?: string[];
error?: string;
onSave: (values: SchemaPolicyInput, allowOverrides: boolean) => Promise<void>;
onSave?: (values: SchemaPolicyInput, allowOverrides: boolean) => Promise<void>;
availableRules: AvailableRulesList;
currentState?: PolicySettings_SchemaPolicyFragmentFragment | null;
children?: (form: FormikProps<PolicyFormValues>) => ReactElement;
Expand All @@ -74,7 +74,7 @@ function PolicySettingsListForm({
})),
};

void onSave(asInput, values.allowOverrides).then(() => formikHelpers.resetForm());
void onSave?.(asInput, values.allowOverrides).then(() => formikHelpers.resetForm());
},
);
const validationSchema = useMemo(() => buildValidationSchema(availableRules), [availableRules]);
Expand Down Expand Up @@ -112,7 +112,7 @@ function PolicySettingsListForm({
{props.dirty ? <p className="pr-2 text-sm text-gray-500">Unsaved changes</p> : null}

<Button
disabled={!props.dirty || saving || !props.isValid}
disabled={!props.dirty || saving || !props.isValid || !onSave}
type="submit"
variant="default"
onClick={() => props.submitForm()}
Expand All @@ -130,6 +130,7 @@ function PolicySettingsListForm({
<div className="grid grid-cols-1 divide-y divide-gray-800">
{availableRules.map(availableRule => (
<PolicyListItem
disabled={!onSave}
overridingParentRule={rulesInParent?.includes(availableRule.id) ?? false}
key={availableRule.id}
ruleInfo={availableRule}
Expand All @@ -153,7 +154,7 @@ export function PolicySettings({
saving?: boolean;
rulesInParent?: string[];
currentState?: null | FragmentType<typeof PolicySettings_SchemaPolicyFragment>;
onSave: (values: SchemaPolicyInput, allowOverrides: boolean) => Promise<void>;
onSave?: (values: SchemaPolicyInput, allowOverrides: boolean) => Promise<void>;
error?: string;
children?: (form: FormikProps<PolicyFormValues>) => ReactElement;
}): ReactElement {
Expand All @@ -164,19 +165,22 @@ export function PolicySettings({
const activePolicy = useFragment(PolicySettings_SchemaPolicyFragment, currentState);

return (
<DataWrapper query={availableRules} organizationSlug={null}>
{query => (
<PolicySettingsListForm
saving={saving}
rulesInParent={rulesInParent}
currentState={activePolicy}
onSave={onSave}
error={error}
availableRules={query.data.schemaPolicyRules}
>
{children}
</PolicySettingsListForm>
)}
</DataWrapper>
<>
{!onSave && 'You have read only access to this policy.'}
<DataWrapper query={availableRules} organizationSlug={null}>
{query => (
<PolicySettingsListForm
saving={saving}
rulesInParent={rulesInParent}
currentState={activePolicy}
onSave={onSave}
error={error}
availableRules={query.data.schemaPolicyRules}
>
{children}
</PolicySettingsListForm>
)}
</DataWrapper>
</>
);
}
78 changes: 34 additions & 44 deletions packages/web/app/src/pages/organization-policy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { QueryError } from '@/components/ui/query-error';
import { useToast } from '@/components/ui/use-toast';
import { graphql } from '@/gql';
import { RegistryModel } from '@/gql/graphql';
import { useRedirect } from '@/lib/access/common';

const OrganizationPolicyPageQuery = graphql(`
query OrganizationPolicyPageQuery($selector: OrganizationSelectorInput!) {
Expand Down Expand Up @@ -83,23 +82,6 @@ function PolicyPageContent(props: { organizationSlug: string }) {
p => p.registryModel === RegistryModel.Legacy,
);

useRedirect({
canAccess: currentOrganization?.viewerCanModifySchemaPolicy === true,
redirectTo: router => {
void router.navigate({
to: '/$organizationSlug',
params: {
organizationSlug: props.organizationSlug,
},
});
},
entity: currentOrganization,
});

if (currentOrganization?.viewerCanModifySchemaPolicy === false) {
return null;
}

if (query.error) {
return <QueryError organizationSlug={props.organizationSlug} error={query.error} />;
}
Expand Down Expand Up @@ -169,33 +151,40 @@ function PolicyPageContent(props: { organizationSlug: string }) {
mutation.error?.message ||
mutation.data?.updateSchemaPolicyForOrganization.error?.message
}
onSave={async (newPolicy, allowOverrides) => {
await mutate({
selector: {
organizationSlug: props.organizationSlug,
},
policy: newPolicy,
allowOverrides,
})
.then(result => {
if (result.data?.updateSchemaPolicyForOrganization.error || result.error) {
toast({
variant: 'destructive',
title: 'Error',
description:
result.data?.updateSchemaPolicyForOrganization.error?.message ||
result.error?.message,
});
} else {
toast({
variant: 'default',
title: 'Success',
description: 'Policy updated successfully',
});
onSave={
currentOrganization.viewerCanModifySchemaPolicy
? async (newPolicy, allowOverrides) => {
await mutate({
selector: {
organizationSlug: props.organizationSlug,
},
policy: newPolicy,
allowOverrides,
})
.then(result => {
if (
result.data?.updateSchemaPolicyForOrganization.error ||
result.error
) {
toast({
variant: 'destructive',
title: 'Error',
description:
result.data?.updateSchemaPolicyForOrganization.error?.message ||
result.error?.message,
});
} else {
toast({
variant: 'default',
title: 'Success',
description: 'Policy updated successfully',
});
}
})
.catch();
}
})
.catch();
}}
: undefined
}
currentState={currentOrganization.schemaPolicy}
>
{form => (
Expand All @@ -205,6 +194,7 @@ function PolicyPageContent(props: { organizationSlug: string }) {
checked={form.values.allowOverrides}
value="allowOverrides"
onCheckedChange={newValue => form.setFieldValue('allowOverrides', newValue)}
disabled={!currentOrganization.viewerCanModifySchemaPolicy}
/>
<label
htmlFor="allowOverrides"
Expand Down
72 changes: 29 additions & 43 deletions packages/web/app/src/pages/project-policy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,24 +83,6 @@ function ProjectPolicyContent(props: { organizationSlug: string; projectSlug: st
const currentOrganization = query.data?.organization?.organization;
const currentProject = query.data?.project;

useRedirect({
canAccess: currentProject?.viewerCanModifySchemaPolicy === true,
redirectTo: router => {
void router.navigate({
to: '/$organizationSlug/$projectSlug',
params: {
organizationSlug: props.organizationSlug,
projectSlug: props.projectSlug,
},
});
},
entity: currentProject,
});

if (currentProject?.viewerCanModifySchemaPolicy === false) {
return null;
}

if (query.error) {
return (
<QueryError
Expand Down Expand Up @@ -164,31 +146,35 @@ function ProjectPolicyContent(props: { organizationSlug: string; projectSlug: st
mutation.error?.message ||
mutation.data?.updateSchemaPolicyForProject.error?.message
}
onSave={async newPolicy => {
await mutate({
selector: {
organizationSlug: props.organizationSlug,
projectSlug: props.projectSlug,
},
policy: newPolicy,
}).then(result => {
if (result.error || result.data?.updateSchemaPolicyForProject.error) {
toast({
variant: 'destructive',
title: 'Error',
description:
result.error?.message ||
result.data?.updateSchemaPolicyForProject.error?.message,
});
} else {
toast({
variant: 'default',
title: 'Success',
description: 'Policy updated successfully',
});
}
});
}}
onSave={
currentProject?.viewerCanModifySchemaPolicy
? async newPolicy => {
await mutate({
selector: {
organizationSlug: props.organizationSlug,
projectSlug: props.projectSlug,
},
policy: newPolicy,
}).then(result => {
if (result.error || result.data?.updateSchemaPolicyForProject.error) {
toast({
variant: 'destructive',
title: 'Error',
description:
result.error?.message ||
result.data?.updateSchemaPolicyForProject.error?.message,
});
} else {
toast({
variant: 'default',
title: 'Success',
description: 'Policy updated successfully',
});
}
});
}
: undefined
}
currentState={currentProject.schemaPolicy}
/>
) : (
Expand Down

0 comments on commit 6ae3d60

Please sign in to comment.