feat(gatekeeper): feature access resolver (#3514)
This commit is contained in:
Родитель
c40693e22e
Коммит
381c4e2a85
|
@ -84,4 +84,11 @@ extend type Workspace {
|
|||
subscription: WorkspaceSubscription @hasScope(scope: "workspace:billing")
|
||||
# this can only be created if there is an active subscription
|
||||
customerPortalUrl: String @hasScope(scope: "workspace:billing")
|
||||
hasAccessToFeature(featureName: WorkspaceFeatureName!): Boolean!
|
||||
}
|
||||
|
||||
enum WorkspaceFeatureName {
|
||||
domainBasedSecurityPolicies
|
||||
oidcSso
|
||||
workspaceDataRegionSpecificity
|
||||
}
|
||||
|
|
|
@ -4049,6 +4049,7 @@ export type Workspace = {
|
|||
domainBasedMembershipProtectionEnabled: Scalars['Boolean']['output'];
|
||||
/** Verified workspace domains */
|
||||
domains?: Maybe<Array<WorkspaceDomain>>;
|
||||
hasAccessToFeature: Scalars['Boolean']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
/** Only available to workspace owners/members */
|
||||
invitedTeam?: Maybe<Array<PendingWorkspaceCollaborator>>;
|
||||
|
@ -4068,6 +4069,11 @@ export type Workspace = {
|
|||
};
|
||||
|
||||
|
||||
export type WorkspaceHasAccessToFeatureArgs = {
|
||||
featureName: WorkspaceFeatureName;
|
||||
};
|
||||
|
||||
|
||||
export type WorkspaceInvitedTeamArgs = {
|
||||
filter?: InputMaybe<PendingWorkspaceCollaboratorsFilter>;
|
||||
};
|
||||
|
@ -4178,6 +4184,13 @@ export type WorkspaceDomainDeleteInput = {
|
|||
workspaceId: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
export enum WorkspaceFeatureName {
|
||||
DomainBasedSecurityPolicies = 'domainBasedSecurityPolicies',
|
||||
OidcSso = 'oidcSso',
|
||||
Workspace = 'workspace',
|
||||
WorkspaceDataRegionSpecificity = 'workspaceDataRegionSpecificity'
|
||||
}
|
||||
|
||||
export type WorkspaceInviteCreateInput = {
|
||||
/** Either this or userId must be filled */
|
||||
email?: InputMaybe<Scalars['String']['input']>;
|
||||
|
@ -4790,6 +4803,7 @@ export type ResolversTypes = {
|
|||
WorkspaceCreateInput: WorkspaceCreateInput;
|
||||
WorkspaceDomain: ResolverTypeWrapper<WorkspaceDomain>;
|
||||
WorkspaceDomainDeleteInput: WorkspaceDomainDeleteInput;
|
||||
WorkspaceFeatureName: WorkspaceFeatureName;
|
||||
WorkspaceInviteCreateInput: WorkspaceInviteCreateInput;
|
||||
WorkspaceInviteLookupOptions: WorkspaceInviteLookupOptions;
|
||||
WorkspaceInviteMutations: ResolverTypeWrapper<WorkspaceInviteMutationsGraphQLReturn>;
|
||||
|
@ -6463,6 +6477,7 @@ export type WorkspaceResolvers<ContextType = GraphQLContext, ParentType extends
|
|||
discoverabilityEnabled?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
|
||||
domainBasedMembershipProtectionEnabled?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
|
||||
domains?: Resolver<Maybe<Array<ResolversTypes['WorkspaceDomain']>>, ParentType, ContextType>;
|
||||
hasAccessToFeature?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<WorkspaceHasAccessToFeatureArgs, 'featureName'>>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
invitedTeam?: Resolver<Maybe<Array<ResolversTypes['PendingWorkspaceCollaborator']>>, ParentType, ContextType, Partial<WorkspaceInvitedTeamArgs>>;
|
||||
logo?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
|
|
|
@ -4030,6 +4030,7 @@ export type Workspace = {
|
|||
domainBasedMembershipProtectionEnabled: Scalars['Boolean']['output'];
|
||||
/** Verified workspace domains */
|
||||
domains?: Maybe<Array<WorkspaceDomain>>;
|
||||
hasAccessToFeature: Scalars['Boolean']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
/** Only available to workspace owners/members */
|
||||
invitedTeam?: Maybe<Array<PendingWorkspaceCollaborator>>;
|
||||
|
@ -4049,6 +4050,11 @@ export type Workspace = {
|
|||
};
|
||||
|
||||
|
||||
export type WorkspaceHasAccessToFeatureArgs = {
|
||||
featureName: WorkspaceFeatureName;
|
||||
};
|
||||
|
||||
|
||||
export type WorkspaceInvitedTeamArgs = {
|
||||
filter?: InputMaybe<PendingWorkspaceCollaboratorsFilter>;
|
||||
};
|
||||
|
@ -4159,6 +4165,13 @@ export type WorkspaceDomainDeleteInput = {
|
|||
workspaceId: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
export enum WorkspaceFeatureName {
|
||||
DomainBasedSecurityPolicies = 'domainBasedSecurityPolicies',
|
||||
OidcSso = 'oidcSso',
|
||||
Workspace = 'workspace',
|
||||
WorkspaceDataRegionSpecificity = 'workspaceDataRegionSpecificity'
|
||||
}
|
||||
|
||||
export type WorkspaceInviteCreateInput = {
|
||||
/** Either this or userId must be filled */
|
||||
email?: InputMaybe<Scalars['String']['input']>;
|
||||
|
|
|
@ -22,6 +22,7 @@ import {
|
|||
getWorkspaceSubscriptionFactory,
|
||||
saveCheckoutSessionFactory
|
||||
} from '@/modules/gatekeeper/repositories/billing'
|
||||
import { canWorkspaceAccessFeatureFactory } from '@/modules/gatekeeper/services/featureAuthorization'
|
||||
|
||||
const { FF_GATEKEEPER_MODULE_ENABLED } = getFeatureFlags()
|
||||
|
||||
|
@ -69,6 +70,21 @@ export = FF_GATEKEEPER_MODULE_ENABLED
|
|||
workspaceSlug: workspace.slug,
|
||||
customerId: workspaceSubscription.subscriptionData.customerId
|
||||
})
|
||||
},
|
||||
hasAccessToFeature: async (parent, args, ctx) => {
|
||||
await authorizeResolver(
|
||||
ctx.userId,
|
||||
parent.id,
|
||||
Roles.Workspace.Member,
|
||||
ctx.resourceAccessRules
|
||||
)
|
||||
const hasAccess = await canWorkspaceAccessFeatureFactory({
|
||||
getWorkspacePlan: getWorkspacePlanFactory({ db })
|
||||
})({
|
||||
workspaceId: parent.id,
|
||||
workspaceFeature: args.featureName
|
||||
})
|
||||
return hasAccess
|
||||
}
|
||||
},
|
||||
WorkspaceMutations: () => ({}),
|
||||
|
|
|
@ -4031,6 +4031,7 @@ export type Workspace = {
|
|||
domainBasedMembershipProtectionEnabled: Scalars['Boolean']['output'];
|
||||
/** Verified workspace domains */
|
||||
domains?: Maybe<Array<WorkspaceDomain>>;
|
||||
hasAccessToFeature: Scalars['Boolean']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
/** Only available to workspace owners/members */
|
||||
invitedTeam?: Maybe<Array<PendingWorkspaceCollaborator>>;
|
||||
|
@ -4050,6 +4051,11 @@ export type Workspace = {
|
|||
};
|
||||
|
||||
|
||||
export type WorkspaceHasAccessToFeatureArgs = {
|
||||
featureName: WorkspaceFeatureName;
|
||||
};
|
||||
|
||||
|
||||
export type WorkspaceInvitedTeamArgs = {
|
||||
filter?: InputMaybe<PendingWorkspaceCollaboratorsFilter>;
|
||||
};
|
||||
|
@ -4160,6 +4166,13 @@ export type WorkspaceDomainDeleteInput = {
|
|||
workspaceId: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
export enum WorkspaceFeatureName {
|
||||
DomainBasedSecurityPolicies = 'domainBasedSecurityPolicies',
|
||||
OidcSso = 'oidcSso',
|
||||
Workspace = 'workspace',
|
||||
WorkspaceDataRegionSpecificity = 'workspaceDataRegionSpecificity'
|
||||
}
|
||||
|
||||
export type WorkspaceInviteCreateInput = {
|
||||
/** Either this or userId must be filled */
|
||||
email?: InputMaybe<Scalars['String']['input']>;
|
||||
|
|
Загрузка…
Ссылка в новой задаче