More permissions

This commit is contained in:
andrewwallacespeckle
2025-06-05 12:47:01 +02:00
parent 742865370a
commit eb325ccfd8
5 changed files with 73 additions and 41 deletions
@@ -18,16 +18,17 @@
Lets users discover the workspace if they sign up with a matching email.
</p>
</div>
<div>
<div
v-tippy="
!isWorkspaceAdmin
? 'You must be a workspace admin'
: !hasWorkspaceDomains
? 'Your workspace must have at least one verified domain'
: undefined
"
>
<FormSwitch
v-model="isDomainDiscoverabilityEnabled"
v-tippy="
!isWorkspaceAdmin
? 'You must be a workspace admin'
: !hasWorkspaceDomains
? 'Your workspace must have at least one verified domain'
: undefined
"
name="domain-discoverability"
:disabled="!hasWorkspaceDomains || !isWorkspaceAdmin"
:show-label="false"
@@ -112,10 +113,6 @@ graphql(`
discoverabilityEnabled
discoverabilityAutoJoinEnabled
domainBasedMembershipProtectionEnabled
hasAccessToDomainBasedSecurityPolicies: hasAccessToFeature(
featureName: domainBasedSecurityPolicies
)
hasAccessToSSO: hasAccessToFeature(featureName: oidcSso)
}
`)
@@ -154,6 +151,13 @@ const isDomainDiscoverabilityEnabled = computed({
}).catch(convertThrowIntoFetchResult)
if (result?.data) {
triggerNotification({
type: ToastNotificationType.Success,
title: 'Workspace discoverability updated',
description: `Workspace discoverability has been ${
newVal ? 'enabled' : 'disabled'
}`
})
mixpanel.track('Workspace Discoverability Toggled', {
value: newVal,
// eslint-disable-next-line camelcase
@@ -13,7 +13,11 @@
Only allow users with verified domains to join the workspace
</p>
</div>
<div key="tooltipText" v-tippy="switchDisabled ? tooltipText : undefined">
<div
v-if="props.workspace?.hasAccessToDomainBasedSecurityPolicies"
key="tooltipText"
v-tippy="switchDisabled ? tooltipText : undefined"
>
<!-- Never disable switch when domain protection is enabled to
allow expired workspaces ability to downgrade-->
<FormSwitch
@@ -23,6 +27,23 @@
name="domain-protection"
/>
</div>
<div
v-else
v-tippy="
props.workspace?.role !== Roles.Workspace.Admin
? 'You must be a workspace admin'
: undefined
"
>
<FormButton
:to="settingsWorkspaceRoutes.billing.route(workspace.slug)"
size="sm"
color="outline"
:disabled="props.workspace?.role !== Roles.Workspace.Admin"
>
Upgrade to Business
</FormButton>
</div>
</div>
</section>
</template>
@@ -34,10 +55,12 @@ import { graphql } from '~/lib/common/generated/gql'
import type { SettingsWorkspacesSecurityDomainProtection_WorkspaceFragment } from '~/lib/common/generated/gql/graphql'
import { useMixpanel } from '~/lib/core/composables/mp'
import { workspaceUpdateDomainProtectionMutation } from '~/lib/workspaces/graphql/mutations'
import { settingsWorkspaceRoutes } from '~/lib/common/helpers/route'
graphql(`
fragment SettingsWorkspacesSecurityDomainProtection_Workspace on Workspace {
id
slug
role
domainBasedMembershipProtectionEnabled
hasAccessToDomainBasedSecurityPolicies: hasAccessToFeature(
@@ -57,6 +80,7 @@ const mixpanel = useMixpanel()
const { mutate: updateDomainProtection } = useMutation(
workspaceUpdateDomainProtectionMutation
)
const { triggerNotification } = useGlobalToast()
const hasWorkspaceDomains = computed(() => (props.workspace?.domains?.length || 0) > 0)
@@ -73,6 +97,11 @@ const isDomainProtectionEnabled = computed({
}).catch(convertThrowIntoFetchResult)
if (result?.data) {
triggerNotification({
type: ToastNotificationType.Success,
title: 'Domain protection updated',
description: `Domain protection has been ${newVal ? 'enabled' : 'disabled'}`
})
mixpanel.track('Workspace Domain Protection Toggled', {
value: newVal,
// eslint-disable-next-line camelcase
@@ -85,7 +114,6 @@ const isDomainProtectionEnabled = computed({
const switchDisabled = computed(() => {
if (props.workspace?.role !== Roles.Workspace.Admin) return true
if (isDomainProtectionEnabled.value) return false
if (!props.workspace?.hasAccessToDomainBasedSecurityPolicies) return true
if (!hasWorkspaceDomains.value) return true
return false
})
@@ -94,8 +122,6 @@ const tooltipText = computed(() => {
if (props.workspace?.role !== Roles.Workspace.Admin)
return 'You must be a workspace admin'
if (isDomainProtectionEnabled.value) return undefined
if (!props.workspace?.hasAccessToDomainBasedSecurityPolicies)
return 'Business plan required'
if (!hasWorkspaceDomains.value)
return 'Your workspace must have at least one verified domain'
return undefined
@@ -18,11 +18,20 @@
</div>
<div>
<!-- Case 1: User doesn't have SSO access/plan -->
<div v-if="!workspace.hasAccessToSSO">
<p class="text-body-xs text-foreground-2 mb-2">
SSO requires a Business subscription
</p>
<FormButton :to="settingsWorkspaceRoutes.billing.route(workspace.slug)">
<div
v-if="!workspace.hasAccessToSSO"
v-tippy="
props.workspace?.role !== Roles.Workspace.Admin
? 'You must be a workspace admin'
: undefined
"
>
<FormButton
:to="settingsWorkspaceRoutes.billing.route(workspace.slug)"
size="sm"
color="outline"
:disabled="props.workspace?.role !== Roles.Workspace.Admin"
>
Upgrade to Business
</FormButton>
</div>
@@ -63,13 +72,6 @@
</div>
<template v-else>
<CommonCard
v-if="!workspace.hasAccessToSSO && workspace.sso?.provider?.id"
class="bg-foundation"
>
SSO access requires an active Business subscription.
</CommonCard>
<!-- Existing Provider Configuration -->
<div v-if="provider" class="p-4 border border-outline-3 rounded-lg">
<div v-if="!isEditing" class="flex items-center justify-between">
@@ -133,9 +133,9 @@ type Documents = {
"\n query WorkspaceAvailableEditorSeats($slug: String!) {\n workspaceBySlug(slug: $slug) {\n id\n seats {\n editors {\n available\n }\n }\n }\n }\n": typeof types.WorkspaceAvailableEditorSeatsDocument,
"\n fragment SettingsWorkspacesRegionsSelect_ServerRegionItem on ServerRegionItem {\n id\n key\n name\n description\n }\n": typeof types.SettingsWorkspacesRegionsSelect_ServerRegionItemFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDefaultSeat_Workspace on Workspace {\n id\n slug\n defaultSeatType\n discoverabilityAutoJoinEnabled\n role\n }\n": typeof types.SettingsWorkspacesSecurityDefaultSeat_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDiscoverability_Workspace on Workspace {\n id\n slug\n role\n domains {\n id\n domain\n }\n sso {\n provider {\n id\n }\n }\n discoverabilityEnabled\n discoverabilityAutoJoinEnabled\n domainBasedMembershipProtectionEnabled\n hasAccessToDomainBasedSecurityPolicies: hasAccessToFeature(\n featureName: domainBasedSecurityPolicies\n )\n hasAccessToSSO: hasAccessToFeature(featureName: oidcSso)\n }\n": typeof types.SettingsWorkspacesSecurityDiscoverability_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDiscoverability_Workspace on Workspace {\n id\n slug\n role\n domains {\n id\n domain\n }\n sso {\n provider {\n id\n }\n }\n discoverabilityEnabled\n discoverabilityAutoJoinEnabled\n domainBasedMembershipProtectionEnabled\n }\n": typeof types.SettingsWorkspacesSecurityDiscoverability_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDomainManagement_Workspace on Workspace {\n id\n role\n discoverabilityEnabled\n domainBasedMembershipProtectionEnabled\n hasAccessToDomainBasedSecurityPolicies: hasAccessToFeature(\n featureName: domainBasedSecurityPolicies\n )\n hasAccessToSSO: hasAccessToFeature(featureName: oidcSso)\n domains {\n id\n domain\n }\n }\n": typeof types.SettingsWorkspacesSecurityDomainManagement_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDomainProtection_Workspace on Workspace {\n id\n role\n domainBasedMembershipProtectionEnabled\n hasAccessToDomainBasedSecurityPolicies: hasAccessToFeature(\n featureName: domainBasedSecurityPolicies\n )\n domains {\n id\n }\n }\n": typeof types.SettingsWorkspacesSecurityDomainProtection_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDomainProtection_Workspace on Workspace {\n id\n slug\n role\n domainBasedMembershipProtectionEnabled\n hasAccessToDomainBasedSecurityPolicies: hasAccessToFeature(\n featureName: domainBasedSecurityPolicies\n )\n domains {\n id\n }\n }\n": typeof types.SettingsWorkspacesSecurityDomainProtection_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain on WorkspaceDomain {\n id\n domain\n }\n": typeof types.SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomainFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDomainRemoveDialog_Workspace on Workspace {\n id\n domains {\n ...SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain\n }\n }\n": typeof types.SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesSecuritySsoWrapper_Workspace on Workspace {\n id\n role\n slug\n sso {\n provider {\n id\n name\n clientId\n issuerUrl\n }\n }\n hasAccessToSSO: hasAccessToFeature(featureName: oidcSso)\n }\n": typeof types.SettingsWorkspacesSecuritySsoWrapper_WorkspaceFragmentDoc,
@@ -578,9 +578,9 @@ const documents: Documents = {
"\n query WorkspaceAvailableEditorSeats($slug: String!) {\n workspaceBySlug(slug: $slug) {\n id\n seats {\n editors {\n available\n }\n }\n }\n }\n": types.WorkspaceAvailableEditorSeatsDocument,
"\n fragment SettingsWorkspacesRegionsSelect_ServerRegionItem on ServerRegionItem {\n id\n key\n name\n description\n }\n": types.SettingsWorkspacesRegionsSelect_ServerRegionItemFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDefaultSeat_Workspace on Workspace {\n id\n slug\n defaultSeatType\n discoverabilityAutoJoinEnabled\n role\n }\n": types.SettingsWorkspacesSecurityDefaultSeat_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDiscoverability_Workspace on Workspace {\n id\n slug\n role\n domains {\n id\n domain\n }\n sso {\n provider {\n id\n }\n }\n discoverabilityEnabled\n discoverabilityAutoJoinEnabled\n domainBasedMembershipProtectionEnabled\n hasAccessToDomainBasedSecurityPolicies: hasAccessToFeature(\n featureName: domainBasedSecurityPolicies\n )\n hasAccessToSSO: hasAccessToFeature(featureName: oidcSso)\n }\n": types.SettingsWorkspacesSecurityDiscoverability_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDiscoverability_Workspace on Workspace {\n id\n slug\n role\n domains {\n id\n domain\n }\n sso {\n provider {\n id\n }\n }\n discoverabilityEnabled\n discoverabilityAutoJoinEnabled\n domainBasedMembershipProtectionEnabled\n }\n": types.SettingsWorkspacesSecurityDiscoverability_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDomainManagement_Workspace on Workspace {\n id\n role\n discoverabilityEnabled\n domainBasedMembershipProtectionEnabled\n hasAccessToDomainBasedSecurityPolicies: hasAccessToFeature(\n featureName: domainBasedSecurityPolicies\n )\n hasAccessToSSO: hasAccessToFeature(featureName: oidcSso)\n domains {\n id\n domain\n }\n }\n": types.SettingsWorkspacesSecurityDomainManagement_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDomainProtection_Workspace on Workspace {\n id\n role\n domainBasedMembershipProtectionEnabled\n hasAccessToDomainBasedSecurityPolicies: hasAccessToFeature(\n featureName: domainBasedSecurityPolicies\n )\n domains {\n id\n }\n }\n": types.SettingsWorkspacesSecurityDomainProtection_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDomainProtection_Workspace on Workspace {\n id\n slug\n role\n domainBasedMembershipProtectionEnabled\n hasAccessToDomainBasedSecurityPolicies: hasAccessToFeature(\n featureName: domainBasedSecurityPolicies\n )\n domains {\n id\n }\n }\n": types.SettingsWorkspacesSecurityDomainProtection_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain on WorkspaceDomain {\n id\n domain\n }\n": types.SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomainFragmentDoc,
"\n fragment SettingsWorkspacesSecurityDomainRemoveDialog_Workspace on Workspace {\n id\n domains {\n ...SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain\n }\n }\n": types.SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceFragmentDoc,
"\n fragment SettingsWorkspacesSecuritySsoWrapper_Workspace on Workspace {\n id\n role\n slug\n sso {\n provider {\n id\n name\n clientId\n issuerUrl\n }\n }\n hasAccessToSSO: hasAccessToFeature(featureName: oidcSso)\n }\n": types.SettingsWorkspacesSecuritySsoWrapper_WorkspaceFragmentDoc,
@@ -1397,7 +1397,7 @@ export function graphql(source: "\n fragment SettingsWorkspacesSecurityDefaultS
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment SettingsWorkspacesSecurityDiscoverability_Workspace on Workspace {\n id\n slug\n role\n domains {\n id\n domain\n }\n sso {\n provider {\n id\n }\n }\n discoverabilityEnabled\n discoverabilityAutoJoinEnabled\n domainBasedMembershipProtectionEnabled\n hasAccessToDomainBasedSecurityPolicies: hasAccessToFeature(\n featureName: domainBasedSecurityPolicies\n )\n hasAccessToSSO: hasAccessToFeature(featureName: oidcSso)\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesSecurityDiscoverability_Workspace on Workspace {\n id\n slug\n role\n domains {\n id\n domain\n }\n sso {\n provider {\n id\n }\n }\n discoverabilityEnabled\n discoverabilityAutoJoinEnabled\n domainBasedMembershipProtectionEnabled\n hasAccessToDomainBasedSecurityPolicies: hasAccessToFeature(\n featureName: domainBasedSecurityPolicies\n )\n hasAccessToSSO: hasAccessToFeature(featureName: oidcSso)\n }\n"];
export function graphql(source: "\n fragment SettingsWorkspacesSecurityDiscoverability_Workspace on Workspace {\n id\n slug\n role\n domains {\n id\n domain\n }\n sso {\n provider {\n id\n }\n }\n discoverabilityEnabled\n discoverabilityAutoJoinEnabled\n domainBasedMembershipProtectionEnabled\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesSecurityDiscoverability_Workspace on Workspace {\n id\n slug\n role\n domains {\n id\n domain\n }\n sso {\n provider {\n id\n }\n }\n discoverabilityEnabled\n discoverabilityAutoJoinEnabled\n domainBasedMembershipProtectionEnabled\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -1405,7 +1405,7 @@ export function graphql(source: "\n fragment SettingsWorkspacesSecurityDomainMa
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment SettingsWorkspacesSecurityDomainProtection_Workspace on Workspace {\n id\n role\n domainBasedMembershipProtectionEnabled\n hasAccessToDomainBasedSecurityPolicies: hasAccessToFeature(\n featureName: domainBasedSecurityPolicies\n )\n domains {\n id\n }\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesSecurityDomainProtection_Workspace on Workspace {\n id\n role\n domainBasedMembershipProtectionEnabled\n hasAccessToDomainBasedSecurityPolicies: hasAccessToFeature(\n featureName: domainBasedSecurityPolicies\n )\n domains {\n id\n }\n }\n"];
export function graphql(source: "\n fragment SettingsWorkspacesSecurityDomainProtection_Workspace on Workspace {\n id\n slug\n role\n domainBasedMembershipProtectionEnabled\n hasAccessToDomainBasedSecurityPolicies: hasAccessToFeature(\n featureName: domainBasedSecurityPolicies\n )\n domains {\n id\n }\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesSecurityDomainProtection_Workspace on Workspace {\n id\n slug\n role\n domainBasedMembershipProtectionEnabled\n hasAccessToDomainBasedSecurityPolicies: hasAccessToFeature(\n featureName: domainBasedSecurityPolicies\n )\n domains {\n id\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
File diff suppressed because one or more lines are too long