From 9c8009752359ea10f7ede15366499d602647bd89 Mon Sep 17 00:00:00 2001 From: adrians5j Date: Tue, 1 Oct 2024 10:54:51 +0200 Subject: [PATCH 01/19] wip: create teams/roles plugins --- .../src/createSecurity/createGroupsMethods.ts | 73 +++++++++++--- .../src/createSecurity/createTeamsMethods.ts | 74 ++++++++++++--- .../api-security/src/graphql/group.gql.ts | 1 + packages/api-security/src/graphql/team.gql.ts | 1 + packages/api-security/src/index.ts | 14 ++- .../src/plugins/SecurityRolePlugin.ts | 44 +++++++++ .../src/plugins/SecurityTeamPlugin.ts | 44 +++++++++ packages/api-security/src/types.ts | 37 ++++---- .../src/types.ts | 2 + .../src/ui/views/Groups/GroupsDataList.tsx | 4 +- .../src/ui/views/Groups/GroupsForm.tsx | 74 +++++++++------ .../src/ui/views/Groups/graphql.ts | 1 + .../src/ui/views/Teams/TeamsDataList.tsx | 4 +- .../src/ui/views/Teams/TeamsForm.tsx | 94 +++++++++++++------ .../src/ui/views/Teams/graphql.ts | 1 + 15 files changed, 351 insertions(+), 117 deletions(-) create mode 100644 packages/api-security/src/plugins/SecurityRolePlugin.ts create mode 100644 packages/api-security/src/plugins/SecurityTeamPlugin.ts diff --git a/packages/api-security/src/createSecurity/createGroupsMethods.ts b/packages/api-security/src/createSecurity/createGroupsMethods.ts index 4151c2c8cc2..1fd38be6c13 100644 --- a/packages/api-security/src/createSecurity/createGroupsMethods.ts +++ b/packages/api-security/src/createSecurity/createGroupsMethods.ts @@ -24,7 +24,8 @@ import { GroupInput, PermissionsTenantLink, ListGroupsParams, - Security + Security, + SecurityRole } from "~/types"; import NotAuthorizedError from "../NotAuthorizedError"; import { SecurityConfig } from "~/types"; @@ -149,9 +150,10 @@ async function updateTenantLinks( } export const createGroupsMethods = ({ - getTenant: initialGetTenant, - storageOperations -}: SecurityConfig) => { + getTenant: initialGetTenant, + storageOperations, + listPluginRoles + }: SecurityConfig) => { const getTenant = () => { const tenant = initialGetTenant(); if (!tenant) { @@ -159,6 +161,41 @@ export const createGroupsMethods = ({ } return tenant; }; + + const listRolesFromPlugins = (params?: ListGroupsParams): SecurityRole[] => { + if (!listPluginRoles) { + return []; + } + + const groups = listPluginRoles().map(plugin => { + return plugin.securityRole; + }); + + const where = params?.where; + if (!where) { + return groups; + } + + return groups.filter(group => { + if (where.id_in) { + return where.id_in.includes(group.id); + } + return group; + }); + }; + + const getRoleFromPlugins = (params: GetGroupParams) => { + const { where } = params; + const allGroupsFromPlugins = listRolesFromPlugins(); + return allGroupsFromPlugins.find(role => { + if (where.id) { + return role.id === where.id; + } + + return role.slug === where.slug; + }); + }; + return { onGroupBeforeCreate: createTopic("security.onGroupBeforeCreate"), onGroupAfterCreate: createTopic("security.onGroupAfterCreate"), @@ -174,9 +211,14 @@ export const createGroupsMethods = ({ let group: Group | null = null; try { - group = await storageOperations.getGroup({ - where: { ...where, tenant: where.tenant || getTenant() } - }); + const groupFromPlugins = getRoleFromPlugins({ where }); + if (groupFromPlugins) { + group = groupFromPlugins; + } else { + group = await storageOperations.getGroup({ + where: { ...where, tenant: where.tenant || getTenant() } + }); + } } catch (ex) { throw new WebinyError( ex.message || "Could not get group.", @@ -193,17 +235,20 @@ export const createGroupsMethods = ({ async listGroups(this: Security, { where }: ListGroupsParams = {}) { await checkPermission(this); try { - return await storageOperations.listGroups({ + const databaseGroups = await storageOperations.listGroups({ where: { ...where, tenant: getTenant() }, sort: ["createdOn_ASC"] }); + + const pluginGroups = listRolesFromPlugins({ where }); + return [...pluginGroups, ...databaseGroups]; } catch (ex) { throw new WebinyError( - ex.message || "Could not list groups.", - ex.code || "LIST_GROUPS_ERROR" + ex.message || "Could not list security groups.", + ex.code || "LIST_SECURITY_GROUP_ERROR" ); } }, @@ -239,10 +284,10 @@ export const createGroupsMethods = ({ createdOn: new Date().toISOString(), createdBy: identity ? { - id: identity.id, - displayName: identity.displayName, - type: identity.type - } + id: identity.id, + displayName: identity.displayName, + type: identity.type + } : null }; diff --git a/packages/api-security/src/createSecurity/createTeamsMethods.ts b/packages/api-security/src/createSecurity/createTeamsMethods.ts index a82b2fc2d94..c9771a7ec6e 100644 --- a/packages/api-security/src/createSecurity/createTeamsMethods.ts +++ b/packages/api-security/src/createSecurity/createTeamsMethods.ts @@ -20,7 +20,7 @@ import { TeamInput, PermissionsTenantLink, Security, - ListGroupsParams + ListTeamsParams } from "~/types"; import NotAuthorizedError from "../NotAuthorizedError"; import { SecurityConfig } from "~/types"; @@ -112,9 +112,10 @@ async function updateTenantLinks( } export const createTeamsMethods = ({ - getTenant: initialGetTenant, - storageOperations -}: SecurityConfig) => { + getTenant: initialGetTenant, + storageOperations, + listPluginTeams + }: SecurityConfig) => { const getTenant = () => { const tenant = initialGetTenant(); if (!tenant) { @@ -122,6 +123,40 @@ export const createTeamsMethods = ({ } return tenant; }; + + const listTeamsFromPlugins = (params?: ListTeamsParams) => { + if (!listPluginTeams) { + return []; + } + const teams = listPluginTeams().map(plugin => { + return plugin.securityTeam; + }); + + const where = params?.where; + if (!where) { + return teams; + } + + return teams.filter(team => { + if (where.id_in) { + return where.id_in.includes(team.id); + } + return team; + }); + }; + + const getTeamFromPlugins = (params: GetTeamParams) => { + const { where } = params; + const allTeamsFromPlugins = listTeamsFromPlugins(); + return allTeamsFromPlugins.find(team => { + if (where.id) { + return team.id === where.id; + } + + return team.slug === where.slug; + }); + }; + return { onTeamBeforeCreate: createTopic("security.onTeamBeforeCreate"), onTeamAfterCreate: createTopic("security.onTeamAfterCreate"), @@ -137,9 +172,15 @@ export const createTeamsMethods = ({ let team: Team | null = null; try { - team = await storageOperations.getTeam({ - where: { ...where, tenant: where.tenant || getTenant() } - }); + const teamFromPlugins = getTeamFromPlugins({ where }); + + if (teamFromPlugins) { + team = teamFromPlugins; + } else { + team = await storageOperations.getTeam({ + where: { ...where, tenant: where.tenant || getTenant() } + }); + } } catch (ex) { throw new WebinyError( ex.message || "Could not get team.", @@ -153,20 +194,23 @@ export const createTeamsMethods = ({ return team; }, - async listTeams(this: Security, { where }: ListGroupsParams = {}) { + async listTeams(this: Security, { where }: ListTeamsParams = {}) { await checkPermission(this); try { - return await storageOperations.listTeams({ + const databaseGroups = await storageOperations.listTeams({ where: { ...where, tenant: getTenant() }, sort: ["createdOn_ASC"] }); + + const teamsFromPlugins = listTeamsFromPlugins({ where }); + return [...teamsFromPlugins, ...databaseGroups]; } catch (ex) { throw new WebinyError( - ex.message || "Could not list API keys.", - ex.code || "LIST_API_KEY_ERROR" + ex.message || "Could not list teams.", + ex.code || "LIST_TEAM_ERROR" ); } }, @@ -202,10 +246,10 @@ export const createTeamsMethods = ({ createdOn: new Date().toISOString(), createdBy: identity ? { - id: identity.id, - displayName: identity.displayName, - type: identity.type - } + id: identity.id, + displayName: identity.displayName, + type: identity.type + } : null }; diff --git a/packages/api-security/src/graphql/group.gql.ts b/packages/api-security/src/graphql/group.gql.ts index a745cf6f657..e0aed27a919 100644 --- a/packages/api-security/src/graphql/group.gql.ts +++ b/packages/api-security/src/graphql/group.gql.ts @@ -17,6 +17,7 @@ export default new GraphQLSchemaPlugin({ description: String permissions: [JSON] system: Boolean! + plugin: Boolean } input SecurityGroupCreateInput { diff --git a/packages/api-security/src/graphql/team.gql.ts b/packages/api-security/src/graphql/team.gql.ts index f34e61e4a37..d1235b138a2 100644 --- a/packages/api-security/src/graphql/team.gql.ts +++ b/packages/api-security/src/graphql/team.gql.ts @@ -17,6 +17,7 @@ export default new GraphQLSchemaPlugin({ description: String groups: [SecurityGroup] system: Boolean! + plugin: Boolean } input SecurityTeamCreateInput { diff --git a/packages/api-security/src/index.ts b/packages/api-security/src/index.ts index dd25368f09d..f8448f288ad 100644 --- a/packages/api-security/src/index.ts +++ b/packages/api-security/src/index.ts @@ -42,7 +42,9 @@ export const createSecurityContext = ({ storageOperations }: SecurityConfig) => const tenant = context.tenancy.getCurrentTenant(); return tenant ? tenant.id : undefined; }, - storageOperations + storageOperations, + listPluginRoles: () => context.plugins.byType("security-role"), + listPluginTeams: () => context.plugins.byType("security-team") }); attachGroupInstaller(context.security); @@ -70,10 +72,18 @@ export const createSecurityContext = ({ storageOperations }: SecurityConfig) => export const createSecurityGraphQL = (config: MultiTenancyGraphQLConfig = {}) => { return new ContextPlugin(context => { - context.plugins.register(graphqlPlugins({ teams: context.wcp.canUseTeams() })); + const license = context.wcp.getProjectLicense(); + context.plugins.register( + graphqlPlugins({ + teams: license?.package?.features?.advancedAccessControlLayer?.options?.teams + }) + ); if (context.tenancy.isMultiTenant()) { applyMultiTenancyGraphQLPlugins(config, context); } }); }; + +export { createSecurityRolePlugin } from "./plugins/SecurityRolePlugin"; +export { createSecurityTeamPlugin } from "./plugins/SecurityTeamPlugin"; diff --git a/packages/api-security/src/plugins/SecurityRolePlugin.ts b/packages/api-security/src/plugins/SecurityRolePlugin.ts new file mode 100644 index 00000000000..e5332aa9403 --- /dev/null +++ b/packages/api-security/src/plugins/SecurityRolePlugin.ts @@ -0,0 +1,44 @@ +import { Plugin } from "@webiny/plugins"; +import {SecurityPermission, SecurityRole} from "~/types"; + +export interface SecurityRolePluginParams { + id: string; + name: string; + slug?: string; + description?: string; + permissions?: SecurityPermission[] + tenant?: string; +} + +export class SecurityRolePlugin extends Plugin { + public static override readonly type: string = "security-role"; + public readonly securityRole: SecurityRole; + + constructor(params: SecurityRolePluginParams) { + super(); + + const { id, name, slug = id, description = "", permissions = [], tenant = null } = params; + + this.securityRole = { + id, + name, + slug, + description, + permissions, + tenant, + + // Internal properties. + system: false, + plugin: true, + createdBy: null, + createdOn: null, + webinyVersion: null + }; + } +} + +export const createSecurityRolePlugin = ( + securityRole: SecurityRolePluginParams +): SecurityRolePlugin => { + return new SecurityRolePlugin(securityRole); +}; diff --git a/packages/api-security/src/plugins/SecurityTeamPlugin.ts b/packages/api-security/src/plugins/SecurityTeamPlugin.ts new file mode 100644 index 00000000000..36e97229d3d --- /dev/null +++ b/packages/api-security/src/plugins/SecurityTeamPlugin.ts @@ -0,0 +1,44 @@ +import { Plugin } from "@webiny/plugins"; +import { SecurityTeam } from "~/types"; + +export interface SecurityTeamPluginParams { + id: string; + name: string; + slug?: string; + description?: string; + roles?: string[]; + tenant?: string; +} + +export class SecurityTeamPlugin extends Plugin { + public static override readonly type: string = "security-team"; + public readonly securityTeam: SecurityTeam; + + constructor(params: SecurityTeamPluginParams) { + super(); + + const { id, name, slug = id, description = "", roles = [], tenant = null } = params; + + this.securityTeam = { + id, + name, + slug, + description, + groups: roles, + tenant, + + // Internal properties. + system: false, + plugin: true, + createdBy: null, + createdOn: null, + webinyVersion: null + }; + } +} + +export const createSecurityTeamPlugin = ( + securityTeam: SecurityTeamPluginParams +): SecurityTeamPlugin => { + return new SecurityTeamPlugin(securityTeam); +}; diff --git a/packages/api-security/src/types.ts b/packages/api-security/src/types.ts index 8ff40c9817d..884beb907fd 100644 --- a/packages/api-security/src/types.ts +++ b/packages/api-security/src/types.ts @@ -5,6 +5,8 @@ import { Topic } from "@webiny/pubsub/types"; import { GetTenant } from "~/createSecurity"; import { ProjectPackageFeatures } from "@webiny/wcp/types"; import { TenancyContext } from "@webiny/api-tenancy/types"; +import { SecurityRolePlugin } from "~/plugins/SecurityRolePlugin"; +import { SecurityTeamPlugin } from "~/plugins/SecurityTeamPlugin"; // Backwards compatibility - START export type SecurityIdentity = Identity; @@ -34,6 +36,8 @@ export interface SecurityConfig { advancedAccessControlLayer?: ProjectPackageFeatures["advancedAccessControlLayer"]; getTenant: GetTenant; storageOperations: SecurityStorageOperations; + listPluginRoles?: () => Array>; + listPluginTeams?: () => Array>; } export interface ErrorEvent extends InstallEvent { @@ -280,8 +284,8 @@ export interface CreatedBy { } export interface Group { - tenant: string; - createdOn: string; + tenant: string | null; + createdOn: string | null; createdBy: CreatedBy | null; id: string; name: string; @@ -289,9 +293,13 @@ export interface Group { description: string; system: boolean; permissions: SecurityPermission[]; - webinyVersion: string; + webinyVersion: string | null; + plugin?: boolean } +export type SecurityRole = Group; +export type SecurityTeam = Team; + export type GroupInput = Pick & { system?: boolean; }; @@ -303,7 +311,6 @@ export interface GetGroupParams { export interface ListGroupsParams { where?: { id_in?: string[]; - slug_in?: string[]; }; sort?: string[]; } @@ -326,16 +333,17 @@ export interface DeleteGroupParams { } export interface Team { - tenant: string; - createdOn: string; + tenant: string | null; + createdOn: string | null; createdBy: CreatedBy | null; id: string; name: string; slug: string; description: string; system: boolean; + plugin?: boolean groups: string[]; - webinyVersion: string; + webinyVersion: string | null; } export type TeamInput = Pick & { @@ -349,7 +357,6 @@ export interface GetTeamParams { export interface ListTeamsParams { where?: { id_in?: string[]; - slug_in?: string[]; }; sort?: string[]; } @@ -436,19 +443,9 @@ export interface TenantLink { webinyVersion: string; } -export interface PermissionsTenantLinkGroup { - id: string; - permissions: SecurityPermission[]; -} - -export interface PermissionsTenantLinkTeam { - id: string; - groups: Array<{ id: string; permissions: SecurityPermission[] }>; -} - export type PermissionsTenantLink = TenantLink<{ - groups: PermissionsTenantLinkGroup[]; - teams: PermissionsTenantLinkTeam[]; + groups: Array<{ id: string; permissions: SecurityPermission[] }>; + teams: Array<{ id: string; groups: Array<{ id: string; permissions: SecurityPermission[] }> }>; }>; export interface ApiKey { diff --git a/packages/app-security-access-management/src/types.ts b/packages/app-security-access-management/src/types.ts index b985c2bf68c..a0dde4323fc 100644 --- a/packages/app-security-access-management/src/types.ts +++ b/packages/app-security-access-management/src/types.ts @@ -6,6 +6,7 @@ export interface Group { description: string; slug: string; system?: boolean; + plugin: boolean; permissions: SecurityPermission[]; createdOn: string; } @@ -16,6 +17,7 @@ export interface Team { description: string; slug: string; system?: boolean; + plugin?: boolean; createdOn: string; } diff --git a/packages/app-security-access-management/src/ui/views/Groups/GroupsDataList.tsx b/packages/app-security-access-management/src/ui/views/Groups/GroupsDataList.tsx index 6d93cff94d8..b0e6cf37ddd 100644 --- a/packages/app-security-access-management/src/ui/views/Groups/GroupsDataList.tsx +++ b/packages/app-security-access-management/src/ui/views/Groups/GroupsDataList.tsx @@ -178,7 +178,7 @@ export const GroupsDataList = () => { - {!item.system ? ( + {!item.system && !item.plugin ? ( deleteItem(item)} data-testid={"default-data-list.delete"} @@ -186,7 +186,7 @@ export const GroupsDataList = () => { ) : ( {t`You can't delete this group.`}} + content={{t`Role is registered via a plugin.`}} > diff --git a/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx b/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx index 19c983b7d9d..88cfb3a433f 100644 --- a/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx +++ b/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx @@ -94,22 +94,22 @@ export const GroupsForm = () => { const isUpdate = formData.createdOn; const [operation, args] = isUpdate ? [ - update, - { - variables: { - id: formData.id, - data: pick(formData, ["name", "description", "permissions"]) - } - } - ] + update, + { + variables: { + id: formData.id, + data: pick(formData, ["name", "description", "permissions"]) + } + } + ] : [ - create, - { - variables: { - data: pick(formData, ["name", "slug", "description", "permissions"]) - } - } - ]; + create, + { + variables: { + data: pick(formData, ["name", "slug", "description", "permissions"]) + } + } + ]; const response = await operation(args); @@ -126,7 +126,9 @@ export const GroupsForm = () => { const data: Group = loading ? {} : get(getQuery, "data.security.group.data", {}); - const systemGroup = data.slug === "full-access"; + const systemGroup = data.slug === "full-access" || data.system; + const pluginGroup = data.plugin; + const canModifyGroup = !systemGroup && !pluginGroup; const showEmptyView = !newGroup && !loading && isEmpty(data); // Render "No content" selected view. @@ -155,6 +157,26 @@ export const GroupsForm = () => { {loading && } + {systemGroup && ( + + + + This is a protected system role and you can't + modify its permissions. + + + + )} + {pluginGroup && ( + + + + This role is registered via an extension. It cannot be + modified via the Admin Area. + + + + )} { > @@ -174,7 +196,7 @@ export const GroupsForm = () => { validators={validation.create("required,minLength:3")} > @@ -190,23 +212,13 @@ export const GroupsForm = () => { - {systemGroup && ( - - - - This is a protected system role and you can't - modify its permissions. - - - - )} - {!systemGroup && ( + {canModifyGroup && ( {t`Permissions`} @@ -240,7 +252,7 @@ export const GroupsForm = () => { )} - {systemGroup ? null : ( + {canModifyGroup && ( { - {!item.system ? ( + {!item.system && !item.plugin ? ( deleteItem(item)} data-testid={"default-data-list.delete"} @@ -186,7 +186,7 @@ export const TeamsDataList = () => { ) : ( {t`You can't delete this team.`}} + content={{t`Team registered via an extension.`}} > diff --git a/packages/app-security-access-management/src/ui/views/Teams/TeamsForm.tsx b/packages/app-security-access-management/src/ui/views/Teams/TeamsForm.tsx index 86f08e26f87..e551952395d 100644 --- a/packages/app-security-access-management/src/ui/views/Teams/TeamsForm.tsx +++ b/packages/app-security-access-management/src/ui/views/Teams/TeamsForm.tsx @@ -22,8 +22,9 @@ import { CREATE_TEAM, LIST_TEAMS, READ_TEAM, UPDATE_TEAM } from "./graphql"; import isEmpty from "lodash/isEmpty"; import EmptyView from "@webiny/app-admin/components/EmptyView"; import { ReactComponent as AddIcon } from "@webiny/app-admin/assets/icons/add-18px.svg"; -import { GroupsMultiAutocomplete } from "~/components/GroupsMultiAutocomplete"; +import { GroupsMultiAutoComplete } from "~/components/GroupsMultiAutocomplete"; import { Team } from "~/types"; +import { Alert } from "@webiny/ui/Alert"; const t = i18n.ns("app-security/admin/teams/form"); @@ -74,22 +75,22 @@ export const TeamsForm = () => { const isUpdate = formData.createdOn; const [operation, args] = isUpdate ? [ - update, - { - variables: { - id: formData.id, - data: pick(formData, ["name", "description", "groups"]) - } - } - ] + update, + { + variables: { + id: formData.id, + data: pick(formData, ["name", "description", "groups"]) + } + } + ] : [ - create, - { - variables: { - data: pick(formData, ["name", "slug", "description", "groups"]) - } - } - ]; + create, + { + variables: { + data: pick(formData, ["name", "slug", "description", "groups"]) + } + } + ]; const response = await operation(args); @@ -106,7 +107,12 @@ export const TeamsForm = () => { const data = loading ? {} : get(getQuery, "data.security.team.data", {}); + const systemTeam = data.system; + const pluginTeam = data.plugin; + const canModifyTeam = !systemTeam && !pluginTeam; + const showEmptyView = !newTeam && !loading && isEmpty(data); + // Render "No content" selected view. if (showEmptyView) { return ( @@ -133,6 +139,26 @@ export const TeamsForm = () => { {loading && } + {systemTeam && ( + + + + This is a protected system team and you can't + modify its permissions. + + + + )} + {pluginTeam && ( + + + + This team is registered via an extension. It cannot be + modified via the Admin Area. + + + + )} { validators={validation.create("required,minLength:3")} > @@ -151,7 +178,7 @@ export const TeamsForm = () => { validators={validation.create("required,minLength:3")} > @@ -165,6 +192,7 @@ export const TeamsForm = () => { validators={validation.create("maxLength:500")} > { + - @@ -183,19 +213,21 @@ export const TeamsForm = () => { - - - history.push("/access-management/teams")} - >{t`Cancel`} - { - form.submit(ev); - }} - >{t`Save team`} - - + {canModifyTeam && ( + + + history.push("/access-management/teams")} + >{t`Cancel`} + { + form.submit(ev); + }} + >{t`Save team`} + + + )} ); }} diff --git a/packages/app-security-access-management/src/ui/views/Teams/graphql.ts b/packages/app-security-access-management/src/ui/views/Teams/graphql.ts index a150f50d616..3cae8997b13 100644 --- a/packages/app-security-access-management/src/ui/views/Teams/graphql.ts +++ b/packages/app-security-access-management/src/ui/views/Teams/graphql.ts @@ -12,6 +12,7 @@ const fields = ` name } system + plugin createdOn `; From 6f018e38462691fe97556dca1f90a63911ea35d7 Mon Sep 17 00:00:00 2001 From: adrians5j Date: Tue, 1 Oct 2024 14:32:17 +0200 Subject: [PATCH 02/19] wip: go through merge changes again --- packages/api-security/src/types.ts | 16 ++++++++++++++-- .../src/ui/views/Teams/TeamsForm.tsx | 6 ++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/api-security/src/types.ts b/packages/api-security/src/types.ts index 884beb907fd..a944d14052e 100644 --- a/packages/api-security/src/types.ts +++ b/packages/api-security/src/types.ts @@ -311,6 +311,7 @@ export interface GetGroupParams { export interface ListGroupsParams { where?: { id_in?: string[]; + slug_in?: string[]; }; sort?: string[]; } @@ -357,6 +358,7 @@ export interface GetTeamParams { export interface ListTeamsParams { where?: { id_in?: string[]; + slug_in?: string[]; }; sort?: string[]; } @@ -443,9 +445,19 @@ export interface TenantLink { webinyVersion: string; } -export type PermissionsTenantLink = TenantLink<{ +export interface PermissionsTenantLinkGroup { + id: string; + permissions: SecurityPermission[]; +} + +export interface PermissionsTenantLinkTeam { + id: string; groups: Array<{ id: string; permissions: SecurityPermission[] }>; - teams: Array<{ id: string; groups: Array<{ id: string; permissions: SecurityPermission[] }> }>; +} + +export type PermissionsTenantLink = TenantLink<{ + groups: PermissionsTenantLinkGroup[]; + teams: PermissionsTenantLinkTeam[]; }>; export interface ApiKey { diff --git a/packages/app-security-access-management/src/ui/views/Teams/TeamsForm.tsx b/packages/app-security-access-management/src/ui/views/Teams/TeamsForm.tsx index e551952395d..7ef40050cfd 100644 --- a/packages/app-security-access-management/src/ui/views/Teams/TeamsForm.tsx +++ b/packages/app-security-access-management/src/ui/views/Teams/TeamsForm.tsx @@ -22,7 +22,7 @@ import { CREATE_TEAM, LIST_TEAMS, READ_TEAM, UPDATE_TEAM } from "./graphql"; import isEmpty from "lodash/isEmpty"; import EmptyView from "@webiny/app-admin/components/EmptyView"; import { ReactComponent as AddIcon } from "@webiny/app-admin/assets/icons/add-18px.svg"; -import { GroupsMultiAutoComplete } from "~/components/GroupsMultiAutocomplete"; +import { GroupsMultiAutocomplete } from "~/components/GroupsMultiAutocomplete"; import { Team } from "~/types"; import { Alert } from "@webiny/ui/Alert"; @@ -112,7 +112,6 @@ export const TeamsForm = () => { const canModifyTeam = !systemTeam && !pluginTeam; const showEmptyView = !newTeam && !loading && isEmpty(data); - // Render "No content" selected view. if (showEmptyView) { return ( @@ -200,11 +199,10 @@ export const TeamsForm = () => { - - Date: Tue, 1 Oct 2024 14:33:34 +0200 Subject: [PATCH 03/19] chore: run prettier --- .../src/createSecurity/createGroupsMethods.ts | 16 +++++----- .../src/createSecurity/createTeamsMethods.ts | 16 +++++----- .../src/plugins/SecurityRolePlugin.ts | 4 +-- packages/api-security/src/types.ts | 4 +-- .../src/ui/views/Groups/GroupsDataList.tsx | 4 ++- .../src/ui/views/Groups/GroupsForm.tsx | 30 +++++++++---------- .../src/ui/views/Teams/TeamsDataList.tsx | 4 ++- .../src/ui/views/Teams/TeamsForm.tsx | 30 +++++++++---------- 8 files changed, 56 insertions(+), 52 deletions(-) diff --git a/packages/api-security/src/createSecurity/createGroupsMethods.ts b/packages/api-security/src/createSecurity/createGroupsMethods.ts index 1fd38be6c13..a94c46c4ff6 100644 --- a/packages/api-security/src/createSecurity/createGroupsMethods.ts +++ b/packages/api-security/src/createSecurity/createGroupsMethods.ts @@ -150,10 +150,10 @@ async function updateTenantLinks( } export const createGroupsMethods = ({ - getTenant: initialGetTenant, - storageOperations, - listPluginRoles - }: SecurityConfig) => { + getTenant: initialGetTenant, + storageOperations, + listPluginRoles +}: SecurityConfig) => { const getTenant = () => { const tenant = initialGetTenant(); if (!tenant) { @@ -284,10 +284,10 @@ export const createGroupsMethods = ({ createdOn: new Date().toISOString(), createdBy: identity ? { - id: identity.id, - displayName: identity.displayName, - type: identity.type - } + id: identity.id, + displayName: identity.displayName, + type: identity.type + } : null }; diff --git a/packages/api-security/src/createSecurity/createTeamsMethods.ts b/packages/api-security/src/createSecurity/createTeamsMethods.ts index c9771a7ec6e..b8a2c58463c 100644 --- a/packages/api-security/src/createSecurity/createTeamsMethods.ts +++ b/packages/api-security/src/createSecurity/createTeamsMethods.ts @@ -112,10 +112,10 @@ async function updateTenantLinks( } export const createTeamsMethods = ({ - getTenant: initialGetTenant, - storageOperations, - listPluginTeams - }: SecurityConfig) => { + getTenant: initialGetTenant, + storageOperations, + listPluginTeams +}: SecurityConfig) => { const getTenant = () => { const tenant = initialGetTenant(); if (!tenant) { @@ -246,10 +246,10 @@ export const createTeamsMethods = ({ createdOn: new Date().toISOString(), createdBy: identity ? { - id: identity.id, - displayName: identity.displayName, - type: identity.type - } + id: identity.id, + displayName: identity.displayName, + type: identity.type + } : null }; diff --git a/packages/api-security/src/plugins/SecurityRolePlugin.ts b/packages/api-security/src/plugins/SecurityRolePlugin.ts index e5332aa9403..8a05a6a0ed6 100644 --- a/packages/api-security/src/plugins/SecurityRolePlugin.ts +++ b/packages/api-security/src/plugins/SecurityRolePlugin.ts @@ -1,12 +1,12 @@ import { Plugin } from "@webiny/plugins"; -import {SecurityPermission, SecurityRole} from "~/types"; +import { SecurityPermission, SecurityRole } from "~/types"; export interface SecurityRolePluginParams { id: string; name: string; slug?: string; description?: string; - permissions?: SecurityPermission[] + permissions?: SecurityPermission[]; tenant?: string; } diff --git a/packages/api-security/src/types.ts b/packages/api-security/src/types.ts index a944d14052e..d9c83bf276c 100644 --- a/packages/api-security/src/types.ts +++ b/packages/api-security/src/types.ts @@ -294,7 +294,7 @@ export interface Group { system: boolean; permissions: SecurityPermission[]; webinyVersion: string | null; - plugin?: boolean + plugin?: boolean; } export type SecurityRole = Group; @@ -342,7 +342,7 @@ export interface Team { slug: string; description: string; system: boolean; - plugin?: boolean + plugin?: boolean; groups: string[]; webinyVersion: string | null; } diff --git a/packages/app-security-access-management/src/ui/views/Groups/GroupsDataList.tsx b/packages/app-security-access-management/src/ui/views/Groups/GroupsDataList.tsx index b0e6cf37ddd..c3708b94767 100644 --- a/packages/app-security-access-management/src/ui/views/Groups/GroupsDataList.tsx +++ b/packages/app-security-access-management/src/ui/views/Groups/GroupsDataList.tsx @@ -186,7 +186,9 @@ export const GroupsDataList = () => { ) : ( {t`Role is registered via a plugin.`}} + content={ + {t`Role is registered via a plugin.`} + } > diff --git a/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx b/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx index 88cfb3a433f..394ce1fea6d 100644 --- a/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx +++ b/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx @@ -94,22 +94,22 @@ export const GroupsForm = () => { const isUpdate = formData.createdOn; const [operation, args] = isUpdate ? [ - update, - { - variables: { - id: formData.id, - data: pick(formData, ["name", "description", "permissions"]) - } - } - ] + update, + { + variables: { + id: formData.id, + data: pick(formData, ["name", "description", "permissions"]) + } + } + ] : [ - create, - { - variables: { - data: pick(formData, ["name", "slug", "description", "permissions"]) - } - } - ]; + create, + { + variables: { + data: pick(formData, ["name", "slug", "description", "permissions"]) + } + } + ]; const response = await operation(args); diff --git a/packages/app-security-access-management/src/ui/views/Teams/TeamsDataList.tsx b/packages/app-security-access-management/src/ui/views/Teams/TeamsDataList.tsx index 62baf80ff23..4277af1caaf 100644 --- a/packages/app-security-access-management/src/ui/views/Teams/TeamsDataList.tsx +++ b/packages/app-security-access-management/src/ui/views/Teams/TeamsDataList.tsx @@ -186,7 +186,9 @@ export const TeamsDataList = () => { ) : ( {t`Team registered via an extension.`}} + content={ + {t`Team registered via an extension.`} + } > diff --git a/packages/app-security-access-management/src/ui/views/Teams/TeamsForm.tsx b/packages/app-security-access-management/src/ui/views/Teams/TeamsForm.tsx index 7ef40050cfd..d4e5567206b 100644 --- a/packages/app-security-access-management/src/ui/views/Teams/TeamsForm.tsx +++ b/packages/app-security-access-management/src/ui/views/Teams/TeamsForm.tsx @@ -75,22 +75,22 @@ export const TeamsForm = () => { const isUpdate = formData.createdOn; const [operation, args] = isUpdate ? [ - update, - { - variables: { - id: formData.id, - data: pick(formData, ["name", "description", "groups"]) - } - } - ] + update, + { + variables: { + id: formData.id, + data: pick(formData, ["name", "description", "groups"]) + } + } + ] : [ - create, - { - variables: { - data: pick(formData, ["name", "slug", "description", "groups"]) - } - } - ]; + create, + { + variables: { + data: pick(formData, ["name", "slug", "description", "groups"]) + } + } + ]; const response = await operation(args); From 30b99bbf8de277ceda7189d6103a107a1db49325 Mon Sep 17 00:00:00 2001 From: adrians5j Date: Tue, 1 Oct 2024 14:44:02 +0200 Subject: [PATCH 04/19] wip: go through merge changes again --- .../src/createSecurity/createGroupsMethods.ts | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/packages/api-security/src/createSecurity/createGroupsMethods.ts b/packages/api-security/src/createSecurity/createGroupsMethods.ts index a94c46c4ff6..80380d0d3d2 100644 --- a/packages/api-security/src/createSecurity/createGroupsMethods.ts +++ b/packages/api-security/src/createSecurity/createGroupsMethods.ts @@ -162,7 +162,7 @@ export const createGroupsMethods = ({ return tenant; }; - const listRolesFromPlugins = (params?: ListGroupsParams): SecurityRole[] => { + const listGroupsFromPlugins = (params?: ListGroupsParams): SecurityRole[] => { if (!listPluginRoles) { return []; } @@ -184,9 +184,9 @@ export const createGroupsMethods = ({ }); }; - const getRoleFromPlugins = (params: GetGroupParams) => { + const getGroupFromPlugins = (params: GetGroupParams) => { const { where } = params; - const allGroupsFromPlugins = listRolesFromPlugins(); + const allGroupsFromPlugins = listGroupsFromPlugins(); return allGroupsFromPlugins.find(role => { if (where.id) { return role.id === where.id; @@ -211,13 +211,12 @@ export const createGroupsMethods = ({ let group: Group | null = null; try { - const groupFromPlugins = getRoleFromPlugins({ where }); + const finalWhere = { ...where, tenant: where.tenant || getTenant() }; + const groupFromPlugins = getGroupFromPlugins({ where: finalWhere }); if (groupFromPlugins) { group = groupFromPlugins; } else { - group = await storageOperations.getGroup({ - where: { ...where, tenant: where.tenant || getTenant() } - }); + group = await storageOperations.getGroup({ where: finalWhere }); } } catch (ex) { throw new WebinyError( @@ -235,16 +234,19 @@ export const createGroupsMethods = ({ async listGroups(this: Security, { where }: ListGroupsParams = {}) { await checkPermission(this); try { - const databaseGroups = await storageOperations.listGroups({ - where: { - ...where, - tenant: getTenant() - }, + const finalWhere = { ...where, tenant: getTenant() }; + + const groupsFromDatabase = await storageOperations.listGroups({ + where: finalWhere, sort: ["createdOn_ASC"] }); - const pluginGroups = listRolesFromPlugins({ where }); - return [...pluginGroups, ...databaseGroups]; + const groupsFromPlugins = listGroupsFromPlugins({ where }); + + // We don't have to do any extra sorting because, as we can see above, `createdOn_ASC` is + // hardcoded, and groups coming from plugins don't have `createdOn`, meaning they should + // always be at the top of the list. + return [...groupsFromPlugins, ...groupsFromDatabase]; } catch (ex) { throw new WebinyError( ex.message || "Could not list security groups.", From 4b3065766c91bafc6378f7c036417ad1d2322396 Mon Sep 17 00:00:00 2001 From: adrians5j Date: Tue, 1 Oct 2024 15:52:56 +0200 Subject: [PATCH 05/19] wip: go through merge changes again --- .../src/createSecurity/createGroupsMethods.ts | 63 +++++++---------- .../src/createSecurity/createTeamsMethods.ts | 70 ++++++++----------- .../groupsTeamsPlugins/getGroupFromPlugins.ts | 25 +++++++ .../groupsTeamsPlugins/getTeamFromPlugins.ts | 25 +++++++ .../listGroupsFromPlugins.ts | 45 ++++++++++++ .../listTeamsFromPlugins.ts | 45 ++++++++++++ packages/api-security/src/index.ts | 4 +- packages/api-security/src/types.ts | 4 +- 8 files changed, 199 insertions(+), 82 deletions(-) create mode 100644 packages/api-security/src/createSecurity/groupsTeamsPlugins/getGroupFromPlugins.ts create mode 100644 packages/api-security/src/createSecurity/groupsTeamsPlugins/getTeamFromPlugins.ts create mode 100644 packages/api-security/src/createSecurity/groupsTeamsPlugins/listGroupsFromPlugins.ts create mode 100644 packages/api-security/src/createSecurity/groupsTeamsPlugins/listTeamsFromPlugins.ts diff --git a/packages/api-security/src/createSecurity/createGroupsMethods.ts b/packages/api-security/src/createSecurity/createGroupsMethods.ts index 80380d0d3d2..e603526a6c6 100644 --- a/packages/api-security/src/createSecurity/createGroupsMethods.ts +++ b/packages/api-security/src/createSecurity/createGroupsMethods.ts @@ -24,11 +24,18 @@ import { GroupInput, PermissionsTenantLink, ListGroupsParams, - Security, - SecurityRole + Security } from "~/types"; import NotAuthorizedError from "../NotAuthorizedError"; import { SecurityConfig } from "~/types"; +import { + listGroupsFromPlugins as baseListGroupsFromPlugins, + type ListGroupsFromPluginsParams +} from "./groupsTeamsPlugins/listGroupsFromPlugins"; +import { + getGroupFromPlugins as baseGetGroupFromPlugins, + type GetGroupFromPluginsParams +} from "./groupsTeamsPlugins/getGroupFromPlugins"; const CreateDataModel = withFields({ tenant: string({ validation: validation.create("required") }), @@ -152,7 +159,7 @@ async function updateTenantLinks( export const createGroupsMethods = ({ getTenant: initialGetTenant, storageOperations, - listPluginRoles + listGroupsFromPluginsCallback }: SecurityConfig) => { const getTenant = () => { const tenant = initialGetTenant(); @@ -162,37 +169,16 @@ export const createGroupsMethods = ({ return tenant; }; - const listGroupsFromPlugins = (params?: ListGroupsParams): SecurityRole[] => { - if (!listPluginRoles) { - return []; - } - - const groups = listPluginRoles().map(plugin => { - return plugin.securityRole; - }); - - const where = params?.where; - if (!where) { - return groups; - } - - return groups.filter(group => { - if (where.id_in) { - return where.id_in.includes(group.id); - } - return group; + const listGroupsFromPlugins = (params: Pick): Group[] => { + return baseListGroupsFromPlugins({ + ...params, + listGroupsFromPluginsCallback }); }; - - const getGroupFromPlugins = (params: GetGroupParams) => { - const { where } = params; - const allGroupsFromPlugins = listGroupsFromPlugins(); - return allGroupsFromPlugins.find(role => { - if (where.id) { - return role.id === where.id; - } - - return role.slug === where.slug; + const getGroupFromPlugins = (params: Pick): Group => { + return baseGetGroupFromPlugins({ + ...params, + listGroupsFromPluginsCallback }); }; @@ -211,12 +197,13 @@ export const createGroupsMethods = ({ let group: Group | null = null; try { - const finalWhere = { ...where, tenant: where.tenant || getTenant() }; - const groupFromPlugins = getGroupFromPlugins({ where: finalWhere }); + const whereWithTenant = { ...where, tenant: where.tenant || getTenant() }; + const groupFromPlugins = getGroupFromPlugins({ where: whereWithTenant }); + if (groupFromPlugins) { group = groupFromPlugins; } else { - group = await storageOperations.getGroup({ where: finalWhere }); + group = await storageOperations.getGroup({ where: whereWithTenant }); } } catch (ex) { throw new WebinyError( @@ -234,14 +221,14 @@ export const createGroupsMethods = ({ async listGroups(this: Security, { where }: ListGroupsParams = {}) { await checkPermission(this); try { - const finalWhere = { ...where, tenant: getTenant() }; + const whereWithTenant = { ...where, tenant: getTenant() }; const groupsFromDatabase = await storageOperations.listGroups({ - where: finalWhere, + where: whereWithTenant, sort: ["createdOn_ASC"] }); - const groupsFromPlugins = listGroupsFromPlugins({ where }); + const groupsFromPlugins = listGroupsFromPlugins({ where: whereWithTenant }); // We don't have to do any extra sorting because, as we can see above, `createdOn_ASC` is // hardcoded, and groups coming from plugins don't have `createdOn`, meaning they should diff --git a/packages/api-security/src/createSecurity/createTeamsMethods.ts b/packages/api-security/src/createSecurity/createTeamsMethods.ts index b8a2c58463c..c333b47e802 100644 --- a/packages/api-security/src/createSecurity/createTeamsMethods.ts +++ b/packages/api-security/src/createSecurity/createTeamsMethods.ts @@ -24,6 +24,14 @@ import { } from "~/types"; import NotAuthorizedError from "../NotAuthorizedError"; import { SecurityConfig } from "~/types"; +import { + listTeamsFromPlugins as baseListTeamsFromPlugins, + type ListTeamsFromPluginsParams +} from "./groupsTeamsPlugins/listTeamsFromPlugins"; +import { + getTeamFromPlugins as baseGetTeamFromPlugins, + type GetTeamFromPluginsParams +} from "./groupsTeamsPlugins/getTeamFromPlugins"; const CreateDataModel = withFields({ tenant: string({ validation: validation.create("required") }), @@ -114,7 +122,7 @@ async function updateTenantLinks( export const createTeamsMethods = ({ getTenant: initialGetTenant, storageOperations, - listPluginTeams + listTeamsFromPluginsCallback }: SecurityConfig) => { const getTenant = () => { const tenant = initialGetTenant(); @@ -124,36 +132,16 @@ export const createTeamsMethods = ({ return tenant; }; - const listTeamsFromPlugins = (params?: ListTeamsParams) => { - if (!listPluginTeams) { - return []; - } - const teams = listPluginTeams().map(plugin => { - return plugin.securityTeam; - }); - - const where = params?.where; - if (!where) { - return teams; - } - - return teams.filter(team => { - if (where.id_in) { - return where.id_in.includes(team.id); - } - return team; + const listTeamsFromPlugins = (params: Pick): Team[] => { + return baseListTeamsFromPlugins({ + ...params, + listTeamsFromPluginsCallback }); }; - - const getTeamFromPlugins = (params: GetTeamParams) => { - const { where } = params; - const allTeamsFromPlugins = listTeamsFromPlugins(); - return allTeamsFromPlugins.find(team => { - if (where.id) { - return team.id === where.id; - } - - return team.slug === where.slug; + const getTeamFromPlugins = (params: Pick): Team => { + return baseGetTeamFromPlugins({ + ...params, + listTeamsFromPluginsCallback }); }; @@ -172,14 +160,13 @@ export const createTeamsMethods = ({ let team: Team | null = null; try { - const teamFromPlugins = getTeamFromPlugins({ where }); + const whereWithTenant = { ...where, tenant: where.tenant || getTenant() }; + const teamFromPlugins = getTeamFromPlugins({ where: whereWithTenant }); if (teamFromPlugins) { team = teamFromPlugins; } else { - team = await storageOperations.getTeam({ - where: { ...where, tenant: where.tenant || getTenant() } - }); + team = await storageOperations.getTeam({ where: whereWithTenant }); } } catch (ex) { throw new WebinyError( @@ -197,16 +184,19 @@ export const createTeamsMethods = ({ async listTeams(this: Security, { where }: ListTeamsParams = {}) { await checkPermission(this); try { - const databaseGroups = await storageOperations.listTeams({ - where: { - ...where, - tenant: getTenant() - }, + const whereWithTenant = { ...where, tenant: getTenant() }; + + const teamsFromDatabase = await storageOperations.listTeams({ + where: whereWithTenant, sort: ["createdOn_ASC"] }); - const teamsFromPlugins = listTeamsFromPlugins({ where }); - return [...teamsFromPlugins, ...databaseGroups]; + const teamsFromPlugins = listTeamsFromPlugins({ where: whereWithTenant }); + + // We don't have to do any extra sorting because, as we can see above, `createdOn_ASC` is + // hardcoded, and teams coming from plugins don't have `createdOn`, meaning they should + // always be at the top of the list. + return [...teamsFromPlugins, ...teamsFromDatabase]; } catch (ex) { throw new WebinyError( ex.message || "Could not list teams.", diff --git a/packages/api-security/src/createSecurity/groupsTeamsPlugins/getGroupFromPlugins.ts b/packages/api-security/src/createSecurity/groupsTeamsPlugins/getGroupFromPlugins.ts new file mode 100644 index 00000000000..dc62286a1cb --- /dev/null +++ b/packages/api-security/src/createSecurity/groupsTeamsPlugins/getGroupFromPlugins.ts @@ -0,0 +1,25 @@ +import { SecurityConfig } from "~/types"; +import { listGroupsFromPlugins } from "./listGroupsFromPlugins"; + +export interface GetGroupFromPluginsParams { + listGroupsFromPluginsCallback?: SecurityConfig["listGroupsFromPluginsCallback"]; + where: { + tenant: string; + id?: string; + slug?: string; + }; +} + +export const getGroupFromPlugins = (params: GetGroupFromPluginsParams) => { + const { listGroupsFromPluginsCallback, where } = params; + const [group] = listGroupsFromPlugins({ + listGroupsFromPluginsCallback, + where: { + tenant: where.tenant, + id_in: where.id ? [where.id] : undefined, + slug_in: where.slug ? [where.slug] : undefined + } + }); + + return group; +}; diff --git a/packages/api-security/src/createSecurity/groupsTeamsPlugins/getTeamFromPlugins.ts b/packages/api-security/src/createSecurity/groupsTeamsPlugins/getTeamFromPlugins.ts new file mode 100644 index 00000000000..3c68537f023 --- /dev/null +++ b/packages/api-security/src/createSecurity/groupsTeamsPlugins/getTeamFromPlugins.ts @@ -0,0 +1,25 @@ +import { SecurityConfig } from "~/types"; +import { listTeamsFromPlugins } from "./listTeamsFromPlugins"; + +export interface GetTeamFromPluginsParams { + listTeamsFromPluginsCallback?: SecurityConfig["listTeamsFromPluginsCallback"]; + where: { + tenant: string; + id?: string; + slug?: string; + }; +} + +export const getTeamFromPlugins = (params: GetTeamFromPluginsParams) => { + const { listTeamsFromPluginsCallback, where } = params; + const [team] = listTeamsFromPlugins({ + listTeamsFromPluginsCallback, + where: { + tenant: where.tenant, + id_in: where.id ? [where.id] : undefined, + slug_in: where.slug ? [where.slug] : undefined + } + }); + + return team; +}; diff --git a/packages/api-security/src/createSecurity/groupsTeamsPlugins/listGroupsFromPlugins.ts b/packages/api-security/src/createSecurity/groupsTeamsPlugins/listGroupsFromPlugins.ts new file mode 100644 index 00000000000..d17cca4ce03 --- /dev/null +++ b/packages/api-security/src/createSecurity/groupsTeamsPlugins/listGroupsFromPlugins.ts @@ -0,0 +1,45 @@ +import { SecurityConfig } from "~/types"; + +export interface ListGroupsFromPluginsParams { + listGroupsFromPluginsCallback?: SecurityConfig["listGroupsFromPluginsCallback"]; + where: { + tenant: string; + id_in?: string[]; + slug_in?: string[]; + }; +} + +export const listGroupsFromPlugins = (params: ListGroupsFromPluginsParams) => { + const { listGroupsFromPluginsCallback, where } = params; + if (!listGroupsFromPluginsCallback) { + return []; + } + + const groups = listGroupsFromPluginsCallback().map(plugin => { + return plugin.securityRole; + }); + + return groups.filter(group => { + // First we ensure the group belongs to the correct tenant. + if (group.tenant) { + if (group.tenant !== where.tenant) { + return false; + } + } + + const { id_in, slug_in } = where; + if (id_in) { + if (!id_in.includes(group.id)) { + return false; + } + } + + if (slug_in) { + if (!slug_in.includes(group.id)) { + return false; + } + } + + return group; + }); +}; diff --git a/packages/api-security/src/createSecurity/groupsTeamsPlugins/listTeamsFromPlugins.ts b/packages/api-security/src/createSecurity/groupsTeamsPlugins/listTeamsFromPlugins.ts new file mode 100644 index 00000000000..866ab6c63dd --- /dev/null +++ b/packages/api-security/src/createSecurity/groupsTeamsPlugins/listTeamsFromPlugins.ts @@ -0,0 +1,45 @@ +import { SecurityConfig } from "~/types"; + +export interface ListTeamsFromPluginsParams { + listTeamsFromPluginsCallback?: SecurityConfig["listTeamsFromPluginsCallback"]; + where: { + tenant: string; + id_in?: string[]; + slug_in?: string[]; + }; +} + +export const listTeamsFromPlugins = (params: ListTeamsFromPluginsParams) => { + const { listTeamsFromPluginsCallback, where } = params; + if (!listTeamsFromPluginsCallback) { + return []; + } + + const teams = listTeamsFromPluginsCallback().map(plugin => { + return plugin.securityRole; + }); + + return teams.filter(team => { + // First we ensure the team belongs to the correct tenant. + if (team.tenant) { + if (team.tenant !== where.tenant) { + return false; + } + } + + const { id_in, slug_in } = where; + if (id_in) { + if (!id_in.includes(team.id)) { + return false; + } + } + + if (slug_in) { + if (!slug_in.includes(team.id)) { + return false; + } + } + + return team; + }); +}; diff --git a/packages/api-security/src/index.ts b/packages/api-security/src/index.ts index f8448f288ad..30fe27d1c1f 100644 --- a/packages/api-security/src/index.ts +++ b/packages/api-security/src/index.ts @@ -43,8 +43,8 @@ export const createSecurityContext = ({ storageOperations }: SecurityConfig) => return tenant ? tenant.id : undefined; }, storageOperations, - listPluginRoles: () => context.plugins.byType("security-role"), - listPluginTeams: () => context.plugins.byType("security-team") + listGroupsFromPluginsCallback: () => context.plugins.byType("security-role"), + listTeamsFromPluginsCallback: () => context.plugins.byType("security-team") }); attachGroupInstaller(context.security); diff --git a/packages/api-security/src/types.ts b/packages/api-security/src/types.ts index d9c83bf276c..ad7149d5f40 100644 --- a/packages/api-security/src/types.ts +++ b/packages/api-security/src/types.ts @@ -36,8 +36,8 @@ export interface SecurityConfig { advancedAccessControlLayer?: ProjectPackageFeatures["advancedAccessControlLayer"]; getTenant: GetTenant; storageOperations: SecurityStorageOperations; - listPluginRoles?: () => Array>; - listPluginTeams?: () => Array>; + listGroupsFromPluginsCallback?: () => Array>; + listTeamsFromPluginsCallback?: () => Array>; } export interface ErrorEvent extends InstallEvent { From 772f6221b3d268f7746a5f1c404ca992e51ce273 Mon Sep 17 00:00:00 2001 From: adrians5j Date: Tue, 1 Oct 2024 15:57:11 +0200 Subject: [PATCH 06/19] wip: go through merge changes again --- packages/api-security/src/index.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/api-security/src/index.ts b/packages/api-security/src/index.ts index 30fe27d1c1f..47c114b2360 100644 --- a/packages/api-security/src/index.ts +++ b/packages/api-security/src/index.ts @@ -72,12 +72,7 @@ export const createSecurityContext = ({ storageOperations }: SecurityConfig) => export const createSecurityGraphQL = (config: MultiTenancyGraphQLConfig = {}) => { return new ContextPlugin(context => { - const license = context.wcp.getProjectLicense(); - context.plugins.register( - graphqlPlugins({ - teams: license?.package?.features?.advancedAccessControlLayer?.options?.teams - }) - ); + context.plugins.register(graphqlPlugins({ teams: context.wcp.canUseTeams() })); if (context.tenancy.isMultiTenant()) { applyMultiTenancyGraphQLPlugins(config, context); From b2aa5e15f5ab8f438bc18a7b54224239f0ba761d Mon Sep 17 00:00:00 2001 From: adrians5j Date: Tue, 1 Oct 2024 16:03:12 +0200 Subject: [PATCH 07/19] wip: improve UI --- .../src/ui/views/Groups/GroupsDataList.tsx | 19 ++++++---- .../src/ui/views/Teams/TeamsDataList.tsx | 36 ++++++++++--------- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/packages/app-security-access-management/src/ui/views/Groups/GroupsDataList.tsx b/packages/app-security-access-management/src/ui/views/Groups/GroupsDataList.tsx index c3708b94767..6d4ce515a44 100644 --- a/packages/app-security-access-management/src/ui/views/Groups/GroupsDataList.tsx +++ b/packages/app-security-access-management/src/ui/views/Groups/GroupsDataList.tsx @@ -53,6 +53,7 @@ export interface GroupsDataListProps { // TODO @ts-refactor delete and go up the tree and sort it out [key: string]: any; } + export const GroupsDataList = () => { const [filter, setFilter] = useState(""); const [sort, setSort] = useState(SORTERS[0].sorter); @@ -178,20 +179,24 @@ export const GroupsDataList = () => { - {!item.system && !item.plugin ? ( - deleteItem(item)} - data-testid={"default-data-list.delete"} - /> - ) : ( + {item.system || item.plugin ? ( {t`Role is registered via a plugin.`} + + {item.system + ? t`Cannot delete system roles.` + : t`Cannot delete roles registered via extensions.`} + } > + ) : ( + deleteItem(item)} + data-testid={"default-data-list.delete"} + /> )} diff --git a/packages/app-security-access-management/src/ui/views/Teams/TeamsDataList.tsx b/packages/app-security-access-management/src/ui/views/Teams/TeamsDataList.tsx index 4277af1caaf..f52a5e559f2 100644 --- a/packages/app-security-access-management/src/ui/views/Teams/TeamsDataList.tsx +++ b/packages/app-security-access-management/src/ui/views/Teams/TeamsDataList.tsx @@ -177,23 +177,25 @@ export const TeamsDataList = () => { - - {!item.system && !item.plugin ? ( - deleteItem(item)} - data-testid={"default-data-list.delete"} - /> - ) : ( - {t`Team registered via an extension.`} - } - > - - - )} - + {item.system || item.plugin ? ( + + {item.system + ? t`Cannot delete system teams.` + : t`Cannot delete teams created via extensions.`} + + } + > + + + ) : ( + deleteItem(item)} + data-testid={"default-data-list.delete"} + /> + )} ))} From 8ee0304c58922bf52d330d5dde0fd79105ba991e Mon Sep 17 00:00:00 2001 From: adrians5j Date: Tue, 1 Oct 2024 16:04:38 +0200 Subject: [PATCH 08/19] wip: improve UI --- packages/app-security-access-management/src/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/app-security-access-management/src/types.ts b/packages/app-security-access-management/src/types.ts index a0dde4323fc..52a9b8c1071 100644 --- a/packages/app-security-access-management/src/types.ts +++ b/packages/app-security-access-management/src/types.ts @@ -6,7 +6,7 @@ export interface Group { description: string; slug: string; system?: boolean; - plugin: boolean; + plugin: boolean | null; permissions: SecurityPermission[]; createdOn: string; } @@ -17,7 +17,7 @@ export interface Team { description: string; slug: string; system?: boolean; - plugin?: boolean; + plugin: boolean | null; createdOn: string; } From 40ae9c23d8f6ead920a4d4e16ba27e2bf5bbf4fd Mon Sep 17 00:00:00 2001 From: adrians5j Date: Tue, 1 Oct 2024 16:11:53 +0200 Subject: [PATCH 09/19] wip: improve types --- packages/api-security/src/types.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/api-security/src/types.ts b/packages/api-security/src/types.ts index ad7149d5f40..b071679ac5b 100644 --- a/packages/api-security/src/types.ts +++ b/packages/api-security/src/types.ts @@ -284,16 +284,24 @@ export interface CreatedBy { } export interface Group { + // Groups defined via plugins might not have `tenant` specified (meaning they are global). tenant: string | null; + + // Groups defined via plugins don't have `createdOn` and `createdBy` specified. createdOn: string | null; createdBy: CreatedBy | null; + id: string; name: string; slug: string; description: string; system: boolean; permissions: SecurityPermission[]; + + // Groups defined via plugins don't have `webinyVersion` specified. webinyVersion: string | null; + + // Set to `true` when a group is defined via a plugin. plugin?: boolean; } @@ -334,17 +342,25 @@ export interface DeleteGroupParams { } export interface Team { + // Teams defined via plugins might not have `tenant` specified (meaning they are global). tenant: string | null; + + // Teams defined via plugins don't have `createdOn` and `createdBy` specified. createdOn: string | null; createdBy: CreatedBy | null; + id: string; name: string; slug: string; description: string; system: boolean; - plugin?: boolean; groups: string[]; + + // Teams defined via plugins don't have `webinyVersion` specified. webinyVersion: string | null; + + // Set to `true` when a group is defined via a plugin. + plugin?: boolean; } export type TeamInput = Pick & { From cba22bae59a2ddc7142c7c5a5b43e01b09cf14ff Mon Sep 17 00:00:00 2001 From: adrians5j Date: Tue, 1 Oct 2024 16:34:35 +0200 Subject: [PATCH 10/19] fix: update exports --- packages/api-serverless-cms/src/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/api-serverless-cms/src/index.ts b/packages/api-serverless-cms/src/index.ts index f1d6e92782d..d9f4d039427 100644 --- a/packages/api-serverless-cms/src/index.ts +++ b/packages/api-serverless-cms/src/index.ts @@ -15,6 +15,7 @@ import { createGraphQLSchemaPlugin as baseCreateGraphQLSchemaPlugin, GraphQLSchemaPluginConfig } from "@webiny/handler-graphql"; +import { createSecurityRolePlugin, createSecurityTeamPlugin } from "@webiny/api-security"; export interface Context extends ClientContext, @@ -42,4 +43,6 @@ export const createGraphQLSchemaPlugin = ( return baseCreateGraphQLSchemaPlugin(config); }; +export { createSecurityRolePlugin, createSecurityTeamPlugin }; + export * from "./tenancy/InstallTenant"; From 1e736430f015e8dae58647f39ca4366cef1bae03 Mon Sep 17 00:00:00 2001 From: adrians5j Date: Tue, 1 Oct 2024 16:43:13 +0200 Subject: [PATCH 11/19] fix: remove `copyPermissionsButton` feature flag and make the featue available to all users --- .../src/ui/views/ApiKeys/ApiKeyForm.tsx | 30 +++++++------------ .../src/ui/views/Groups/GroupsForm.tsx | 30 +++++++------------ packages/feature-flags/src/index.ts | 1 - webiny.project.ts | 1 - 4 files changed, 22 insertions(+), 40 deletions(-) diff --git a/packages/app-security-access-management/src/ui/views/ApiKeys/ApiKeyForm.tsx b/packages/app-security-access-management/src/ui/views/ApiKeys/ApiKeyForm.tsx index 223ae9ff9d7..4052929e98d 100644 --- a/packages/app-security-access-management/src/ui/views/ApiKeys/ApiKeyForm.tsx +++ b/packages/app-security-access-management/src/ui/views/ApiKeys/ApiKeyForm.tsx @@ -212,25 +212,17 @@ export const ApiKeyForm = () => { {t`Permissions`} - {featureFlags.copyPermissionsButton && ( - - } - onClick={() => { - navigator.clipboard.writeText( - JSON.stringify( - data.permissions, - null, - 2 - ) - ); - showSnackbar( - "JSON data copied to clipboard." - ); - }} - /> - - )} + + } + onClick={() => { + navigator.clipboard.writeText( + JSON.stringify(data.permissions, null, 2) + ); + showSnackbar("JSON data copied to clipboard."); + }} + /> + diff --git a/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx b/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx index 394ce1fea6d..a6c37412e39 100644 --- a/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx +++ b/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx @@ -222,25 +222,17 @@ export const GroupsForm = () => { {t`Permissions`} - {featureFlags.copyPermissionsButton && ( - - } - onClick={() => { - navigator.clipboard.writeText( - JSON.stringify( - data.permissions, - null, - 2 - ) - ); - showSnackbar( - "JSON data copied to clipboard." - ); - }} - /> - - )} + + } + onClick={() => { + navigator.clipboard.writeText( + JSON.stringify(data.permissions, null, 2) + ); + showSnackbar("JSON data copied to clipboard."); + }} + /> + diff --git a/packages/feature-flags/src/index.ts b/packages/feature-flags/src/index.ts index e7258787f83..6b323751509 100644 --- a/packages/feature-flags/src/index.ts +++ b/packages/feature-flags/src/index.ts @@ -1,5 +1,4 @@ export type FeatureFlags> = { - copyPermissionsButton?: boolean; experimentalAdminOmniSearch?: boolean; allowCmsLegacyRichTextInput?: boolean; newWatchCommand?: boolean; diff --git a/webiny.project.ts b/webiny.project.ts index 865fe882f10..bf70fe8b1e6 100644 --- a/webiny.project.ts +++ b/webiny.project.ts @@ -53,7 +53,6 @@ export default { }, featureFlags: { - copyPermissionsButton: true, experimentalAdminOmniSearch: true, newWatchCommand: true } From bc2ef80d421cff6a0b93a883020cef3860030a8d Mon Sep 17 00:00:00 2001 From: adrians5j Date: Wed, 2 Oct 2024 06:25:01 +0200 Subject: [PATCH 12/19] chore: run prettier --- .../src/ui/views/Teams/TeamsDataList.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/app-security-access-management/src/ui/views/Teams/TeamsDataList.tsx b/packages/app-security-access-management/src/ui/views/Teams/TeamsDataList.tsx index f52a5e559f2..147ebf0fe38 100644 --- a/packages/app-security-access-management/src/ui/views/Teams/TeamsDataList.tsx +++ b/packages/app-security-access-management/src/ui/views/Teams/TeamsDataList.tsx @@ -53,6 +53,7 @@ export interface TeamsDataListProps { // TODO @ts-refactor delete and go up the tree and sort it out [key: string]: any; } + export const TeamsDataList = () => { const [filter, setFilter] = useState(""); const [sort, setSort] = useState(SORTERS[0].sorter); @@ -182,10 +183,10 @@ export const TeamsDataList = () => { placement={"bottom"} content={ - {item.system - ? t`Cannot delete system teams.` - : t`Cannot delete teams created via extensions.`} - + {item.system + ? t`Cannot delete system teams.` + : t`Cannot delete teams created via extensions.`} + } > From 56ee9f2b7317c0c2ba3284887a993480af478402 Mon Sep 17 00:00:00 2001 From: adrians5j Date: Wed, 2 Oct 2024 09:11:57 +0200 Subject: [PATCH 13/19] test: improve groups/teams-related tests --- .../api-security/__tests__/graphql/teams.ts | 75 ++++++ .../api-security/__tests__/groups.test.ts | 137 ++++++++-- .../__tests__/mocks/securityTeam.ts | 16 ++ packages/api-security/__tests__/teams.test.ts | 235 ++++++++++++++++++ .../api-security/__tests__/useGqlHandler.ts | 30 +++ .../src/createSecurity/createGroupsMethods.ts | 35 ++- .../src/createSecurity/createTeamsMethods.ts | 35 ++- .../listTeamsFromPlugins.ts | 2 +- .../wcp/src/testing/createTestWcpLicense.ts | 47 ++++ 9 files changed, 589 insertions(+), 23 deletions(-) create mode 100644 packages/api-security/__tests__/graphql/teams.ts create mode 100644 packages/api-security/__tests__/mocks/securityTeam.ts create mode 100644 packages/api-security/__tests__/teams.test.ts create mode 100644 packages/wcp/src/testing/createTestWcpLicense.ts diff --git a/packages/api-security/__tests__/graphql/teams.ts b/packages/api-security/__tests__/graphql/teams.ts new file mode 100644 index 00000000000..c3a00d69812 --- /dev/null +++ b/packages/api-security/__tests__/graphql/teams.ts @@ -0,0 +1,75 @@ +const DATA_FIELD = (extra = "") => /* GraphQL */ ` + { + name + description + slug + groups { + id + name + } + ${extra} + } +`; + +const ERROR_FIELD = /* GraphQL */ ` + { + code + data + message + } +`; + +export const CREATE_SECURITY_TEAM = /* GraphQL */ ` + mutation CreateTeam($data: SecurityTeamCreateInput!) { + security { + createTeam(data: $data) { + data ${DATA_FIELD("id")} + error ${ERROR_FIELD} + } + } + } +`; + +export const UPDATE_SECURITY_TEAM = /* GraphQL */ ` + mutation UpdateTeam($id: ID!, $data: SecurityTeamUpdateInput!) { + security { + updateTeam(id: $id, data: $data) { + data ${DATA_FIELD()} + error ${ERROR_FIELD} + } + } + } +`; + +export const DELETE_SECURITY_TEAM = /* GraphQL */ ` + mutation DeleteTeam($id: ID!) { + security { + deleteTeam(id: $id) { + data + error ${ERROR_FIELD} + } + } + } +`; + +export const LIST_SECURITY_TEAMS = /* GraphQL */ ` + query ListTeams { + security { + listTeams { + data ${DATA_FIELD()} + error ${ERROR_FIELD} + } + } + } +`; + +export const GET_SECURITY_TEAM = /* GraphQL */ ` + query GetTeam($id: ID!) { + security { + getTeam(where: { id: $id }) { + data ${DATA_FIELD()} + error ${ERROR_FIELD} + } + } + } +`; diff --git a/packages/api-security/__tests__/groups.test.ts b/packages/api-security/__tests__/groups.test.ts index e6c27df4534..6ff14a5e7eb 100644 --- a/packages/api-security/__tests__/groups.test.ts +++ b/packages/api-security/__tests__/groups.test.ts @@ -1,8 +1,24 @@ import useGqlHandler from "./useGqlHandler"; import mocks from "./mocks/securityGroup"; +import { createSecurityRolePlugin } from "~/plugins/SecurityRolePlugin"; describe("Security Group CRUD Test", () => { - const { install, securityGroup } = useGqlHandler(); + const { install, securityGroup } = useGqlHandler({ + plugins: [ + createSecurityRolePlugin({ + id: "test-role-1", + name: "Test Role 1", + description: "1st test role defined via an extension.", + permissions: [{ name: "cms.*" }] + }), + createSecurityRolePlugin({ + id: "test-role-2", + name: "Test Role 2", + description: "2nd test role defined via an extension.", + permissions: [{ name: "pb.*" }] + }) + ] + }); beforeEach(async () => { await install.install(); @@ -23,19 +39,67 @@ describe("Security Group CRUD Test", () => { // Let's check whether both of the group exists const [listResponse] = await securityGroup.list(); - expect(listResponse.data.security.listGroups).toEqual( - expect.objectContaining({ - data: expect.arrayContaining([ - { - name: expect.any(String), - description: expect.any(String), - slug: expect.stringMatching(/anonymous|full-access|group-a|group-b/), - permissions: expect.any(Array) - } - ]), - error: null - }) - ); + expect(listResponse.data.security.listGroups).toEqual({ + error: null, + data: [ + { + name: "Test Role 1", + description: "1st test role defined via an extension.", + slug: "test-role-1", + permissions: [ + { + name: "cms.*" + } + ] + }, + { + name: "Test Role 2", + description: "2nd test role defined via an extension.", + slug: "test-role-2", + permissions: [ + { + name: "pb.*" + } + ] + }, + { + name: "Full Access", + description: "Grants full access to all apps.", + slug: "full-access", + permissions: [ + { + name: "*" + } + ] + }, + { + name: "Anonymous", + description: "Permissions for anonymous users (public access).", + slug: "anonymous", + permissions: [] + }, + { + name: "Group-A", + description: "A: Dolor odit et quia animi ipsum nostrum nesciunt.", + slug: "group-a", + permissions: [ + { + name: "security.*" + } + ] + }, + { + name: "Group-B", + description: "B: Dolor odit et quia animi ipsum nostrum nesciunt.", + slug: "group-b", + permissions: [ + { + name: "security.*" + } + ] + } + ] + }); // Let's update the "groupB" name const updatedName = "Group B - updated"; @@ -131,4 +195,49 @@ describe("Security Group CRUD Test", () => { } }); }); + + test("should not allow update of a group created via a plugin", async () => { + // Creating a group with same "slug" should not be allowed + const [response] = await securityGroup.update({ + id: "test-role-1", + data: { + name: "Test Role 1 - updated" + } + }); + + expect(response).toEqual({ + data: { + security: { + updateGroup: { + data: null, + error: { + code: "CANNOT_UPDATE_PLUGIN_GROUPS", + data: null, + message: "Cannot update groups created via plugins." + } + } + } + } + }); + }); + + test("should not allow deletion of a group created via a plugin", async () => { + // Creating a group with same "slug" should not be allowed + const [response] = await securityGroup.delete({ id: "" }); + + expect(response).toEqual({ + data: { + security: { + deleteGroup: { + data: null, + error: { + code: "CANNOT_DELETE_PLUGIN_GROUPS", + data: null, + message: "Cannot delete groups created via plugins." + } + } + } + } + }); + }); }); diff --git a/packages/api-security/__tests__/mocks/securityTeam.ts b/packages/api-security/__tests__/mocks/securityTeam.ts new file mode 100644 index 00000000000..9358b2632ab --- /dev/null +++ b/packages/api-security/__tests__/mocks/securityTeam.ts @@ -0,0 +1,16 @@ +const mocks = { + teamA: { + name: "Team-A", + slug: "team-a", + description: "A: Dolor odit et quia animi ipsum nostrum nesciunt.", + groups: [] + }, + teamB: { + name: "Team-B", + slug: "team-b", + description: "B: Dolor odit et quia animi ipsum nostrum nesciunt.", + groups: [] + } +}; + +export default mocks; diff --git a/packages/api-security/__tests__/teams.test.ts b/packages/api-security/__tests__/teams.test.ts new file mode 100644 index 00000000000..0e2dfdb06c8 --- /dev/null +++ b/packages/api-security/__tests__/teams.test.ts @@ -0,0 +1,235 @@ +import useGqlHandler from "./useGqlHandler"; +import mocks from "./mocks/securityTeam"; +import { createSecurityRolePlugin } from "~/plugins/SecurityRolePlugin"; +import { createSecurityTeamPlugin } from "~/plugins/SecurityTeamPlugin"; +import { createTestWcpLicense } from "@webiny/wcp/testing/createTestWcpLicense"; + +describe("Security Team CRUD Test", () => { + const { install, securityTeam } = useGqlHandler({ + wcpLicense: createTestWcpLicense(), + plugins: [ + createSecurityRolePlugin({ + id: "test-team-1", + name: "Test Team 1", + description: "1st test team defined via an extension.", + permissions: [{ name: "cms.*" }] + }), + createSecurityRolePlugin({ + id: "test-team-2", + name: "Test Team 2", + description: "2nd test team defined via an extension.", + permissions: [{ name: "pb.*" }] + }), + createSecurityTeamPlugin({ + id: "test-team-2", + name: "Test Team 2", + description: "2nd test team defined via an extension.", + roles: ["test-team-1"] + }), + createSecurityTeamPlugin({ + id: "test-team-1", + name: "Test Team 1", + description: "1st test team defined via an extension.", + roles: ["test-team-2"] + }) + ] + }); + + beforeEach(async () => { + await install.install(); + }); + + test("should able to create, read, update and delete `Security Teams`", async () => { + const [responseA] = await securityTeam.create({ data: mocks.teamA }); + + // Let's create two teams. + const teamA = responseA.data.security.createTeam.data; + expect(teamA).toEqual({ id: teamA.id, ...mocks.teamA }); + + const [responseB] = await securityTeam.create({ data: mocks.teamB }); + + const teamB = responseB.data.security.createTeam.data; + expect(teamB).toEqual({ id: teamB.id, ...mocks.teamB }); + + // Let's check whether both of the team exists + const [listResponse] = await securityTeam.list(); + + expect(listResponse.data.security.listTeams).toEqual({ + data: [ + { + name: "Test Team 2", + description: "2nd test team defined via an extension.", + slug: "test-team-2", + groups: [ + { + id: "test-team-1", + name: "Test Team 1" + } + ] + }, + { + name: "Test Team 1", + description: "1st test team defined via an extension.", + slug: "test-team-1", + groups: [ + { + id: "test-team-2", + name: "Test Team 2" + } + ] + }, + { + name: "Team-A", + description: "A: Dolor odit et quia animi ipsum nostrum nesciunt.", + slug: "team-a", + groups: [] + }, + { + name: "Team-B", + description: "B: Dolor odit et quia animi ipsum nostrum nesciunt.", + slug: "team-b", + groups: [] + } + ], + error: null + }); + + // Let's update the "teamB" name + const updatedName = "Team B - updated"; + const [updateB] = await securityTeam.update({ + id: teamB.id, + data: { + name: updatedName + } + }); + + expect(updateB).toEqual({ + data: { + security: { + updateTeam: { + data: { + ...mocks.teamB, + name: updatedName + }, + error: null + } + } + } + }); + + // Let's delete "teamB" + const [deleteB] = await securityTeam.delete({ + id: teamB.id + }); + + expect(deleteB).toEqual({ + data: { + security: { + deleteTeam: { + data: true, + error: null + } + } + } + }); + + // Should not contain "teamB" + const [getB] = await securityTeam.get({ id: teamB.id }); + + expect(getB).toMatchObject({ + data: { + security: { + getTeam: { + data: null, + error: { + code: "NOT_FOUND", + data: null + } + } + } + } + }); + + // Should contain "teamA" by slug + const [getA] = await securityTeam.get({ id: teamA.id }); + + expect(getA).toEqual({ + data: { + security: { + getTeam: { + data: mocks.teamA, + error: null + } + } + } + }); + }); + + test('should not allow creating a team with same "slug"', async () => { + // Creating a team + await securityTeam.create({ data: mocks.teamA }); + + // Creating a team with same "slug" should not be allowed + const [response] = await securityTeam.create({ data: mocks.teamA }); + + expect(response).toEqual({ + data: { + security: { + createTeam: { + data: null, + error: { + code: "TEAM_EXISTS", + message: `Team with slug "${mocks.teamA.slug}" already exists.`, + data: null + } + } + } + } + }); + }); + + test("should not allow update of a team created via a plugin", async () => { + // Creating a team with same "slug" should not be allowed + const [response] = await securityTeam.update({ + id: "test-team-1", + data: { + name: "Test Team 1 - updated" + } + }); + + expect(response).toEqual({ + data: { + security: { + updateTeam: { + data: null, + error: { + code: "CANNOT_UPDATE_PLUGIN_TEAMS", + data: null, + message: "Cannot update teams created via plugins." + } + } + } + } + }); + }); + + test("should not allow deletion of a team created via a plugin", async () => { + // Creating a team with same "slug" should not be allowed + const [response] = await securityTeam.delete({ id: "" }); + + expect(response).toEqual({ + data: { + security: { + deleteTeam: { + data: null, + error: { + code: "CANNOT_DELETE_PLUGIN_TEAMS", + data: null, + message: "Cannot delete teams created via plugins." + } + } + } + } + }); + }); +}); diff --git a/packages/api-security/__tests__/useGqlHandler.ts b/packages/api-security/__tests__/useGqlHandler.ts index 157aaaf5dc5..2bf8f363e28 100644 --- a/packages/api-security/__tests__/useGqlHandler.ts +++ b/packages/api-security/__tests__/useGqlHandler.ts @@ -14,6 +14,14 @@ import { UPDATE_SECURITY_GROUP } from "./graphql/groups"; +import { + CREATE_SECURITY_TEAM, + DELETE_SECURITY_TEAM, + GET_SECURITY_TEAM, + LIST_SECURITY_TEAMS, + UPDATE_SECURITY_TEAM +} from "./graphql/teams"; + import { CREATE_API_KEY, DELETE_API_KEY, @@ -32,9 +40,12 @@ import { TenancyStorageOperations } from "@webiny/api-tenancy/types"; import { getStorageOps } from "@webiny/project-utils/testing/environment"; import { SecurityStorageOperations } from "~/types"; import { APIGatewayEvent, LambdaContext } from "@webiny/handler-aws/types"; +import { DecryptedWcpProjectLicense } from "@webiny/wcp/types"; +import { createWcpContext } from "@webiny/api-wcp"; type UseGqlHandlerParams = { plugins?: PluginCollection; + wcpLicense?: DecryptedWcpProjectLicense; }; export default (opts: UseGqlHandlerParams = {}) => { @@ -48,6 +59,7 @@ export default (opts: UseGqlHandlerParams = {}) => { const handler = createHandler({ plugins: [ graphqlHandlerPlugins(), + createWcpContext({ testProjectLicense: opts.wcpLicense }), createTenancyContext({ storageOperations: tenancyStorage.storageOperations }), @@ -101,6 +113,23 @@ export default (opts: UseGqlHandlerParams = {}) => { return invoke({ body: { query: GET_SECURITY_GROUP, variables } }); } }; + const securityTeam = { + async create(variables = {}) { + return invoke({ body: { query: CREATE_SECURITY_TEAM, variables } }); + }, + async update(variables = {}) { + return invoke({ body: { query: UPDATE_SECURITY_TEAM, variables } }); + }, + async delete(variables = {}) { + return invoke({ body: { query: DELETE_SECURITY_TEAM, variables } }); + }, + async list(variables = {}, headers = {}) { + return invoke({ body: { query: LIST_SECURITY_TEAMS, variables }, headers }); + }, + async get(variables = {}) { + return invoke({ body: { query: GET_SECURITY_TEAM, variables } }); + } + }; const securityApiKeys = { async list(variables = {}) { @@ -144,6 +173,7 @@ export default (opts: UseGqlHandlerParams = {}) => { invoke, securityIdentity, securityGroup, + securityTeam, securityApiKeys, install }; diff --git a/packages/api-security/src/createSecurity/createGroupsMethods.ts b/packages/api-security/src/createSecurity/createGroupsMethods.ts index e603526a6c6..bece5efb560 100644 --- a/packages/api-security/src/createSecurity/createGroupsMethods.ts +++ b/packages/api-security/src/createSecurity/createGroupsMethods.ts @@ -303,13 +303,30 @@ export const createGroupsMethods = ({ const model = await new UpdateDataModel().populate(input); await model.validate(); - const original = await storageOperations.getGroup({ + const original = await this.getGroup({ where: { tenant: getTenant(), id } }); if (!original) { throw new NotFoundError(`Group "${id}" was not found!`); } + // We can't proceed with the update if one of the following is true: + // 1. The group is system group. + // 2. The group is created via a plugin. + if (original.system) { + throw new WebinyError( + `Cannot update system groups.`, + "CANNOT_UPDATE_SYSTEM_GROUPS" + ); + } + + if (original.plugin) { + throw new WebinyError( + `Cannot update groups created via plugins.`, + "CANNOT_UPDATE_PLUGIN_GROUPS" + ); + } + const data = await model.toJSON({ onlyDirty: true }); const permissionsChanged = !deepEqual(data.permissions, original.permissions); @@ -341,17 +358,16 @@ export const createGroupsMethods = ({ async deleteGroup(this: Security, id: string): Promise { await checkPermission(this); - const group = await storageOperations.getGroup({ where: { tenant: getTenant(), id } }); + const group = await this.getGroup({ where: { tenant: getTenant(), id } }); if (!group) { throw new NotFoundError(`Group "${id}" was not found!`); } // We can't proceed with the deletion if one of the following is true: // 1. The group is system group. - // 2. The group is being used by one or more tenant links. - // 3. The group is being used by one or more teams. - - // 1. Is system group? + // 2. The group is created via a plugin. + // 3. The group is being used by one or more tenant links. + // 4. The group is being used by one or more teams. if (group.system) { throw new WebinyError( `Cannot delete system groups.`, @@ -359,6 +375,13 @@ export const createGroupsMethods = ({ ); } + if (group.plugin) { + throw new WebinyError( + `Cannot delete groups created via plugins.`, + "CANNOT_DELETE_PLUGIN_GROUPS" + ); + } + // 2. Is being used by one or more tenant links? const usagesInTenantLinks = await storageOperations .listTenantLinksByType({ diff --git a/packages/api-security/src/createSecurity/createTeamsMethods.ts b/packages/api-security/src/createSecurity/createTeamsMethods.ts index c333b47e802..42e1ea82072 100644 --- a/packages/api-security/src/createSecurity/createTeamsMethods.ts +++ b/packages/api-security/src/createSecurity/createTeamsMethods.ts @@ -266,13 +266,28 @@ export const createTeamsMethods = ({ const model = await new UpdateDataModel().populate(input); await model.validate(); - const original = await storageOperations.getTeam({ + const original = await this.getTeam({ where: { tenant: getTenant(), id } }); + if (!original) { throw new NotFoundError(`Team "${id}" was not found!`); } + // We can't proceed with the update if one of the following is true: + // 1. The group is system group. + // 2. The group is created via a plugin. + if (original.system) { + throw new WebinyError(`Cannot update system teams.`, "CANNOT_UPDATE_SYSTEM_TEAMS"); + } + + if (original.plugin) { + throw new WebinyError( + `Cannot update teams created via plugins.`, + "CANNOT_UPDATE_PLUGIN_TEAMS" + ); + } + const data = await model.toJSON({ onlyDirty: true }); const groupsChanged = !deepEqual(data.groups, original.groups); @@ -304,11 +319,27 @@ export const createTeamsMethods = ({ async deleteTeam(this: Security, id: string): Promise { await checkPermission(this); - const team = await storageOperations.getTeam({ where: { tenant: getTenant(), id } }); + const team = await this.getTeam({ where: { tenant: getTenant(), id } }); if (!team) { throw new NotFoundError(`Team "${id}" was not found!`); } + // We can't proceed with the deletion if one of the following is true: + // 1. The group is system group. + // 2. The group is created via a plugin. + // 3. The group is being used by one or more tenant links. + // 4. The group is being used by one or more teams. + if (team.system) { + throw new WebinyError(`Cannot delete system teams.`, "CANNOT_DELETE_SYSTEM_TEAMS"); + } + + if (team.plugin) { + throw new WebinyError( + `Cannot delete teams created via plugins.`, + "CANNOT_DELETE_PLUGIN_TEAMS" + ); + } + const usagesInTenantLinks = await storageOperations .listTenantLinksByType({ tenant: getTenant(), diff --git a/packages/api-security/src/createSecurity/groupsTeamsPlugins/listTeamsFromPlugins.ts b/packages/api-security/src/createSecurity/groupsTeamsPlugins/listTeamsFromPlugins.ts index 866ab6c63dd..df7ca982c33 100644 --- a/packages/api-security/src/createSecurity/groupsTeamsPlugins/listTeamsFromPlugins.ts +++ b/packages/api-security/src/createSecurity/groupsTeamsPlugins/listTeamsFromPlugins.ts @@ -16,7 +16,7 @@ export const listTeamsFromPlugins = (params: ListTeamsFromPluginsParams) => { } const teams = listTeamsFromPluginsCallback().map(plugin => { - return plugin.securityRole; + return plugin.securityTeam; }); return teams.filter(team => { diff --git a/packages/wcp/src/testing/createTestWcpLicense.ts b/packages/wcp/src/testing/createTestWcpLicense.ts new file mode 100644 index 00000000000..97bbe7695dd --- /dev/null +++ b/packages/wcp/src/testing/createTestWcpLicense.ts @@ -0,0 +1,47 @@ +import { + DecryptedWcpProjectLicense, + MT_OPTIONS_MAX_COUNT_TYPE, + PROJECT_PACKAGE_FEATURE_NAME +} from "~/types"; + +export const createTestWcpLicense = (): DecryptedWcpProjectLicense => { + return { + orgId: "org-id", + projectId: "project-id", + package: { + features: { + [PROJECT_PACKAGE_FEATURE_NAME.AACL]: { + enabled: true, + options: { + teams: true, + folderLevelPermissions: true, + privateFiles: true + } + }, + [PROJECT_PACKAGE_FEATURE_NAME.MT]: { + enabled: true, + options: { + maxCount: { + type: MT_OPTIONS_MAX_COUNT_TYPE.SEAT_BASED + } + } + }, + [PROJECT_PACKAGE_FEATURE_NAME.APW]: { + enabled: false + }, + [PROJECT_PACKAGE_FEATURE_NAME.AUDIT_LOGS]: { + enabled: false + }, + [PROJECT_PACKAGE_FEATURE_NAME.RECORD_LOCKING]: { + enabled: false + }, + [PROJECT_PACKAGE_FEATURE_NAME.SEATS]: { + enabled: true, + options: { + maxCount: 100 + } + } + } + } + }; +}; From 4c31a46d376215c5335834cff285f132c0946800 Mon Sep 17 00:00:00 2001 From: adrians5j Date: Wed, 2 Oct 2024 09:18:43 +0200 Subject: [PATCH 14/19] chore: eslint --- .../src/ui/views/ApiKeys/ApiKeyForm.tsx | 1 - .../src/ui/views/Groups/GroupsForm.tsx | 1 - .../src/ui/views/Teams/TeamsDataList.tsx | 1 - 3 files changed, 3 deletions(-) diff --git a/packages/app-security-access-management/src/ui/views/ApiKeys/ApiKeyForm.tsx b/packages/app-security-access-management/src/ui/views/ApiKeys/ApiKeyForm.tsx index 4052929e98d..68378d9ba2f 100644 --- a/packages/app-security-access-management/src/ui/views/ApiKeys/ApiKeyForm.tsx +++ b/packages/app-security-access-management/src/ui/views/ApiKeys/ApiKeyForm.tsx @@ -35,7 +35,6 @@ import { ReactComponent as CopyIcon } from "@material-design-icons/svg/outlined/ import styled from "@emotion/styled"; import { ApiKey } from "~/types"; import { Tooltip } from "@webiny/ui/Tooltip"; -import { featureFlags } from "@webiny/feature-flags"; const t = i18n.ns("app-security-admin-users/admin/api-keys/form"); diff --git a/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx b/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx index a6c37412e39..92dcfd24530 100644 --- a/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx +++ b/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx @@ -28,7 +28,6 @@ import EmptyView from "@webiny/app-admin/components/EmptyView"; import { ReactComponent as AddIcon } from "@webiny/app-admin/assets/icons/add-18px.svg"; import { Tooltip } from "@webiny/ui/Tooltip"; import { ReactComponent as CopyIcon } from "@material-design-icons/svg/outlined/content_copy.svg"; -import { featureFlags } from "@webiny/feature-flags"; import { Group } from "~/types"; const t = i18n.ns("app-security/admin/roles/form"); diff --git a/packages/app-security-access-management/src/ui/views/Teams/TeamsDataList.tsx b/packages/app-security-access-management/src/ui/views/Teams/TeamsDataList.tsx index 147ebf0fe38..32bdb4bf1cf 100644 --- a/packages/app-security-access-management/src/ui/views/Teams/TeamsDataList.tsx +++ b/packages/app-security-access-management/src/ui/views/Teams/TeamsDataList.tsx @@ -8,7 +8,6 @@ import { ListItemText, ListItemTextSecondary, ListItemMeta, - ListActions, DataListModalOverlayAction, DataListModalOverlay } from "@webiny/ui/List"; From 2f78454677ef5601dee8fbbd025aec06770bee37 Mon Sep 17 00:00:00 2001 From: adrians5j Date: Wed, 2 Oct 2024 09:34:13 +0200 Subject: [PATCH 15/19] fix: update dependencies --- packages/app-security-access-management/package.json | 1 - packages/app-security-access-management/tsconfig.build.json | 1 - packages/app-security-access-management/tsconfig.json | 3 --- yarn.lock | 1 - 4 files changed, 6 deletions(-) diff --git a/packages/app-security-access-management/package.json b/packages/app-security-access-management/package.json index 6f2c63aff49..283c76f54c0 100644 --- a/packages/app-security-access-management/package.json +++ b/packages/app-security-access-management/package.json @@ -19,7 +19,6 @@ "@webiny/app": "0.0.0", "@webiny/app-admin": "0.0.0", "@webiny/app-security": "0.0.0", - "@webiny/feature-flags": "0.0.0", "@webiny/form": "0.0.0", "@webiny/plugins": "0.0.0", "@webiny/react-router": "0.0.0", diff --git a/packages/app-security-access-management/tsconfig.build.json b/packages/app-security-access-management/tsconfig.build.json index 01c5d0dfa46..f14235ad0f4 100644 --- a/packages/app-security-access-management/tsconfig.build.json +++ b/packages/app-security-access-management/tsconfig.build.json @@ -5,7 +5,6 @@ { "path": "../app/tsconfig.build.json" }, { "path": "../app-admin/tsconfig.build.json" }, { "path": "../app-security/tsconfig.build.json" }, - { "path": "../feature-flags/tsconfig.build.json" }, { "path": "../form/tsconfig.build.json" }, { "path": "../plugins/tsconfig.build.json" }, { "path": "../react-router/tsconfig.build.json" }, diff --git a/packages/app-security-access-management/tsconfig.json b/packages/app-security-access-management/tsconfig.json index 6c336f09a13..03fc0bb2e5f 100644 --- a/packages/app-security-access-management/tsconfig.json +++ b/packages/app-security-access-management/tsconfig.json @@ -5,7 +5,6 @@ { "path": "../app" }, { "path": "../app-admin" }, { "path": "../app-security" }, - { "path": "../feature-flags" }, { "path": "../form" }, { "path": "../plugins" }, { "path": "../react-router" }, @@ -25,8 +24,6 @@ "@webiny/app-admin": ["../app-admin/src"], "@webiny/app-security/*": ["../app-security/src/*"], "@webiny/app-security": ["../app-security/src"], - "@webiny/feature-flags/*": ["../feature-flags/src/*"], - "@webiny/feature-flags": ["../feature-flags/src"], "@webiny/form/*": ["../form/src/*"], "@webiny/form": ["../form/src"], "@webiny/plugins/*": ["../plugins/src/*"], diff --git a/yarn.lock b/yarn.lock index 5fb5d5b33be..44d5ee0bfff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16462,7 +16462,6 @@ __metadata: "@webiny/app-admin": 0.0.0 "@webiny/app-security": 0.0.0 "@webiny/cli": 0.0.0 - "@webiny/feature-flags": 0.0.0 "@webiny/form": 0.0.0 "@webiny/plugins": 0.0.0 "@webiny/project-utils": 0.0.0 From 0e31870904e0965be6249f2689018617efd09bb6 Mon Sep 17 00:00:00 2001 From: adrians5j Date: Wed, 2 Oct 2024 10:06:44 +0200 Subject: [PATCH 16/19] fix: remove `createTestWcpLicense` clones --- .../__tests__/utils/createTestWcpLicense.ts | 47 ------------------- .../__tests__/utils/useGraphQlHandler.ts | 2 +- .../__tests__/utils/createTestWcpLicense.ts | 44 ----------------- .../__tests__/utils/useGraphQlHandler.ts | 2 +- .../__tests__/utils/createTestWcpLicense.ts | 47 ------------------- .../__tests__/utils/useGraphQlHandler.ts | 2 +- 6 files changed, 3 insertions(+), 141 deletions(-) delete mode 100644 packages/api-aco/__tests__/utils/createTestWcpLicense.ts delete mode 100644 packages/api-headless-cms-aco/__tests__/utils/createTestWcpLicense.ts delete mode 100644 packages/api-page-builder-aco/__tests__/utils/createTestWcpLicense.ts diff --git a/packages/api-aco/__tests__/utils/createTestWcpLicense.ts b/packages/api-aco/__tests__/utils/createTestWcpLicense.ts deleted file mode 100644 index 51eb5f98b2c..00000000000 --- a/packages/api-aco/__tests__/utils/createTestWcpLicense.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { - DecryptedWcpProjectLicense, - MT_OPTIONS_MAX_COUNT_TYPE, - PROJECT_PACKAGE_FEATURE_NAME -} from "@webiny/wcp/types"; - -export const createTestWcpLicense = (): DecryptedWcpProjectLicense => { - return { - orgId: "org-id", - projectId: "project-id", - package: { - features: { - [PROJECT_PACKAGE_FEATURE_NAME.AACL]: { - enabled: true, - options: { - teams: true, - folderLevelPermissions: true, - privateFiles: true - } - }, - [PROJECT_PACKAGE_FEATURE_NAME.MT]: { - enabled: true, - options: { - maxCount: { - type: MT_OPTIONS_MAX_COUNT_TYPE.SEAT_BASED - } - } - }, - [PROJECT_PACKAGE_FEATURE_NAME.APW]: { - enabled: false - }, - [PROJECT_PACKAGE_FEATURE_NAME.AUDIT_LOGS]: { - enabled: false - }, - [PROJECT_PACKAGE_FEATURE_NAME.RECORD_LOCKING]: { - enabled: false - }, - [PROJECT_PACKAGE_FEATURE_NAME.SEATS]: { - enabled: true, - options: { - maxCount: 100 - } - } - } - } - }; -}; diff --git a/packages/api-aco/__tests__/utils/useGraphQlHandler.ts b/packages/api-aco/__tests__/utils/useGraphQlHandler.ts index b28b3f4f865..4e06eec15d9 100644 --- a/packages/api-aco/__tests__/utils/useGraphQlHandler.ts +++ b/packages/api-aco/__tests__/utils/useGraphQlHandler.ts @@ -66,7 +66,7 @@ import { FileManagerStorageOperations } from "@webiny/api-file-manager/types"; import { DecryptedWcpProjectLicense } from "@webiny/wcp/types"; import createAdminUsersApp from "@webiny/api-admin-users"; import { createWcpContext } from "@webiny/api-wcp"; -import { createTestWcpLicense } from "~tests/utils/createTestWcpLicense"; +import { createTestWcpLicense } from "@webiny/wcp/testing/createTestWcpLicense"; import { AdminUsersStorageOperations } from "@webiny/api-admin-users/types"; export interface UseGQLHandlerParams { diff --git a/packages/api-headless-cms-aco/__tests__/utils/createTestWcpLicense.ts b/packages/api-headless-cms-aco/__tests__/utils/createTestWcpLicense.ts deleted file mode 100644 index 0c92c342cff..00000000000 --- a/packages/api-headless-cms-aco/__tests__/utils/createTestWcpLicense.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { - DecryptedWcpProjectLicense, - MT_OPTIONS_MAX_COUNT_TYPE, - PROJECT_PACKAGE_FEATURE_NAME -} from "@webiny/wcp/types"; - -export const createTestWcpLicense = (): DecryptedWcpProjectLicense => { - return { - orgId: "org-id", - projectId: "project-id", - package: { - features: { - [PROJECT_PACKAGE_FEATURE_NAME.AACL]: { - enabled: true, - options: { - teams: true, - folderLevelPermissions: true, - privateFiles: true - } - }, - [PROJECT_PACKAGE_FEATURE_NAME.MT]: { - enabled: true, - options: { - maxCount: { - type: MT_OPTIONS_MAX_COUNT_TYPE.SEAT_BASED - } - } - }, - [PROJECT_PACKAGE_FEATURE_NAME.APW]: { - enabled: false - }, - [PROJECT_PACKAGE_FEATURE_NAME.AUDIT_LOGS]: { - enabled: false - }, - [PROJECT_PACKAGE_FEATURE_NAME.SEATS]: { - enabled: true, - options: { - maxCount: 100 - } - } - } - } - }; -}; diff --git a/packages/api-headless-cms-aco/__tests__/utils/useGraphQlHandler.ts b/packages/api-headless-cms-aco/__tests__/utils/useGraphQlHandler.ts index 92e37b01c76..bb9ccc619a5 100644 --- a/packages/api-headless-cms-aco/__tests__/utils/useGraphQlHandler.ts +++ b/packages/api-headless-cms-aco/__tests__/utils/useGraphQlHandler.ts @@ -18,7 +18,7 @@ import { getIntrospectionQuery } from "graphql"; import { APIGatewayEvent, LambdaContext } from "@webiny/handler-aws/types"; import { DecryptedWcpProjectLicense } from "@webiny/wcp/types"; import createAdminUsersApp from "@webiny/api-admin-users"; -import { createTestWcpLicense } from "~tests/utils/createTestWcpLicense"; +import { createTestWcpLicense } from "@webiny/wcp/testing/createTestWcpLicense"; import { createWcpContext } from "@webiny/api-wcp"; import { AdminUsersStorageOperations } from "@webiny/api-admin-users/types"; import { until } from "@webiny/project-utils/testing/helpers/until"; diff --git a/packages/api-page-builder-aco/__tests__/utils/createTestWcpLicense.ts b/packages/api-page-builder-aco/__tests__/utils/createTestWcpLicense.ts deleted file mode 100644 index 51eb5f98b2c..00000000000 --- a/packages/api-page-builder-aco/__tests__/utils/createTestWcpLicense.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { - DecryptedWcpProjectLicense, - MT_OPTIONS_MAX_COUNT_TYPE, - PROJECT_PACKAGE_FEATURE_NAME -} from "@webiny/wcp/types"; - -export const createTestWcpLicense = (): DecryptedWcpProjectLicense => { - return { - orgId: "org-id", - projectId: "project-id", - package: { - features: { - [PROJECT_PACKAGE_FEATURE_NAME.AACL]: { - enabled: true, - options: { - teams: true, - folderLevelPermissions: true, - privateFiles: true - } - }, - [PROJECT_PACKAGE_FEATURE_NAME.MT]: { - enabled: true, - options: { - maxCount: { - type: MT_OPTIONS_MAX_COUNT_TYPE.SEAT_BASED - } - } - }, - [PROJECT_PACKAGE_FEATURE_NAME.APW]: { - enabled: false - }, - [PROJECT_PACKAGE_FEATURE_NAME.AUDIT_LOGS]: { - enabled: false - }, - [PROJECT_PACKAGE_FEATURE_NAME.RECORD_LOCKING]: { - enabled: false - }, - [PROJECT_PACKAGE_FEATURE_NAME.SEATS]: { - enabled: true, - options: { - maxCount: 100 - } - } - } - } - }; -}; diff --git a/packages/api-page-builder-aco/__tests__/utils/useGraphQlHandler.ts b/packages/api-page-builder-aco/__tests__/utils/useGraphQlHandler.ts index 15d4eb3704e..e382b3df79c 100644 --- a/packages/api-page-builder-aco/__tests__/utils/useGraphQlHandler.ts +++ b/packages/api-page-builder-aco/__tests__/utils/useGraphQlHandler.ts @@ -41,7 +41,7 @@ import { getIntrospectionQuery } from "graphql"; import { APIGatewayEvent, LambdaContext } from "@webiny/handler-aws/types"; import { DecryptedWcpProjectLicense } from "@webiny/wcp/types"; import createAdminUsersApp from "@webiny/api-admin-users"; -import { createTestWcpLicense } from "~tests/utils/createTestWcpLicense"; +import { createTestWcpLicense } from "@webiny/wcp/testing/createTestWcpLicense"; import { createWcpContext } from "@webiny/api-wcp"; import { AdminUsersStorageOperations } from "@webiny/api-admin-users/types"; From 0482d4c21f0b8d16316ef583b30e684cadb65df3 Mon Sep 17 00:00:00 2001 From: adrians5j Date: Wed, 2 Oct 2024 10:51:35 +0200 Subject: [PATCH 17/19] fix: change label to "Copy permissions as JSON" --- .../src/ui/views/ApiKeys/ApiKeyForm.tsx | 5 ++++- .../src/ui/views/Groups/GroupsForm.tsx | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/app-security-access-management/src/ui/views/ApiKeys/ApiKeyForm.tsx b/packages/app-security-access-management/src/ui/views/ApiKeys/ApiKeyForm.tsx index 68378d9ba2f..912ba562289 100644 --- a/packages/app-security-access-management/src/ui/views/ApiKeys/ApiKeyForm.tsx +++ b/packages/app-security-access-management/src/ui/views/ApiKeys/ApiKeyForm.tsx @@ -211,7 +211,10 @@ export const ApiKeyForm = () => { {t`Permissions`} - + } onClick={() => { diff --git a/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx b/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx index 92dcfd24530..fb03e8f8b7b 100644 --- a/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx +++ b/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx @@ -221,7 +221,10 @@ export const GroupsForm = () => { {t`Permissions`} - + } onClick={() => { From 588a8aafb7fd5dd9fb92c160bbf20fbea9d7864b Mon Sep 17 00:00:00 2001 From: adrians5j Date: Thu, 3 Oct 2024 13:06:59 +0200 Subject: [PATCH 18/19] fix: apply fixes per review --- .../src/createSecurity/createGroupsMethods.ts | 19 ++++++++++++------- .../src/createSecurity/createTeamsMethods.ts | 16 +++++++++------- .../groupsTeamsPlugins/getGroupFromPlugins.ts | 10 +++++----- .../groupsTeamsPlugins/getTeamFromPlugins.ts | 10 +++++----- .../listGroupsFromPlugins.ts | 12 +++++------- .../listTeamsFromPlugins.ts | 12 +++++------- packages/api-security/src/index.ts | 12 ++++++++++-- packages/api-security/src/types.ts | 6 ++---- .../src/ui/views/Groups/GroupsForm.tsx | 4 ++-- .../src/ui/views/Teams/TeamsForm.tsx | 4 ++-- 10 files changed, 57 insertions(+), 48 deletions(-) diff --git a/packages/api-security/src/createSecurity/createGroupsMethods.ts b/packages/api-security/src/createSecurity/createGroupsMethods.ts index bece5efb560..94ea4c08818 100644 --- a/packages/api-security/src/createSecurity/createGroupsMethods.ts +++ b/packages/api-security/src/createSecurity/createGroupsMethods.ts @@ -159,7 +159,7 @@ async function updateTenantLinks( export const createGroupsMethods = ({ getTenant: initialGetTenant, storageOperations, - listGroupsFromPluginsCallback + groupsProvider }: SecurityConfig) => { const getTenant = () => { const tenant = initialGetTenant(); @@ -169,16 +169,21 @@ export const createGroupsMethods = ({ return tenant; }; - const listGroupsFromPlugins = (params: Pick): Group[] => { + const listGroupsFromPlugins = ( + params: Pick + ): Promise => { return baseListGroupsFromPlugins({ ...params, - listGroupsFromPluginsCallback + groupsProvider }); }; - const getGroupFromPlugins = (params: Pick): Group => { + + const getGroupFromPlugins = ( + params: Pick + ): Promise => { return baseGetGroupFromPlugins({ ...params, - listGroupsFromPluginsCallback + groupsProvider }); }; @@ -198,7 +203,7 @@ export const createGroupsMethods = ({ let group: Group | null = null; try { const whereWithTenant = { ...where, tenant: where.tenant || getTenant() }; - const groupFromPlugins = getGroupFromPlugins({ where: whereWithTenant }); + const groupFromPlugins = await getGroupFromPlugins({ where: whereWithTenant }); if (groupFromPlugins) { group = groupFromPlugins; @@ -228,7 +233,7 @@ export const createGroupsMethods = ({ sort: ["createdOn_ASC"] }); - const groupsFromPlugins = listGroupsFromPlugins({ where: whereWithTenant }); + const groupsFromPlugins = await listGroupsFromPlugins({ where: whereWithTenant }); // We don't have to do any extra sorting because, as we can see above, `createdOn_ASC` is // hardcoded, and groups coming from plugins don't have `createdOn`, meaning they should diff --git a/packages/api-security/src/createSecurity/createTeamsMethods.ts b/packages/api-security/src/createSecurity/createTeamsMethods.ts index 42e1ea82072..ca305329ffe 100644 --- a/packages/api-security/src/createSecurity/createTeamsMethods.ts +++ b/packages/api-security/src/createSecurity/createTeamsMethods.ts @@ -122,7 +122,7 @@ async function updateTenantLinks( export const createTeamsMethods = ({ getTenant: initialGetTenant, storageOperations, - listTeamsFromPluginsCallback + teamsProvider }: SecurityConfig) => { const getTenant = () => { const tenant = initialGetTenant(); @@ -132,16 +132,18 @@ export const createTeamsMethods = ({ return tenant; }; - const listTeamsFromPlugins = (params: Pick): Team[] => { + const listTeamsFromPlugins = ( + params: Pick + ): Promise => { return baseListTeamsFromPlugins({ ...params, - listTeamsFromPluginsCallback + teamsProvider }); }; - const getTeamFromPlugins = (params: Pick): Team => { + const getTeamFromPlugins = (params: Pick): Promise => { return baseGetTeamFromPlugins({ ...params, - listTeamsFromPluginsCallback + teamsProvider }); }; @@ -161,7 +163,7 @@ export const createTeamsMethods = ({ let team: Team | null = null; try { const whereWithTenant = { ...where, tenant: where.tenant || getTenant() }; - const teamFromPlugins = getTeamFromPlugins({ where: whereWithTenant }); + const teamFromPlugins = await getTeamFromPlugins({ where: whereWithTenant }); if (teamFromPlugins) { team = teamFromPlugins; @@ -191,7 +193,7 @@ export const createTeamsMethods = ({ sort: ["createdOn_ASC"] }); - const teamsFromPlugins = listTeamsFromPlugins({ where: whereWithTenant }); + const teamsFromPlugins = await listTeamsFromPlugins({ where: whereWithTenant }); // We don't have to do any extra sorting because, as we can see above, `createdOn_ASC` is // hardcoded, and teams coming from plugins don't have `createdOn`, meaning they should diff --git a/packages/api-security/src/createSecurity/groupsTeamsPlugins/getGroupFromPlugins.ts b/packages/api-security/src/createSecurity/groupsTeamsPlugins/getGroupFromPlugins.ts index dc62286a1cb..cd02fdf902b 100644 --- a/packages/api-security/src/createSecurity/groupsTeamsPlugins/getGroupFromPlugins.ts +++ b/packages/api-security/src/createSecurity/groupsTeamsPlugins/getGroupFromPlugins.ts @@ -2,7 +2,7 @@ import { SecurityConfig } from "~/types"; import { listGroupsFromPlugins } from "./listGroupsFromPlugins"; export interface GetGroupFromPluginsParams { - listGroupsFromPluginsCallback?: SecurityConfig["listGroupsFromPluginsCallback"]; + groupsProvider?: SecurityConfig["groupsProvider"]; where: { tenant: string; id?: string; @@ -10,10 +10,10 @@ export interface GetGroupFromPluginsParams { }; } -export const getGroupFromPlugins = (params: GetGroupFromPluginsParams) => { - const { listGroupsFromPluginsCallback, where } = params; - const [group] = listGroupsFromPlugins({ - listGroupsFromPluginsCallback, +export const getGroupFromPlugins = async (params: GetGroupFromPluginsParams) => { + const { groupsProvider, where } = params; + const [group] = await listGroupsFromPlugins({ + groupsProvider, where: { tenant: where.tenant, id_in: where.id ? [where.id] : undefined, diff --git a/packages/api-security/src/createSecurity/groupsTeamsPlugins/getTeamFromPlugins.ts b/packages/api-security/src/createSecurity/groupsTeamsPlugins/getTeamFromPlugins.ts index 3c68537f023..6abc2aa55b9 100644 --- a/packages/api-security/src/createSecurity/groupsTeamsPlugins/getTeamFromPlugins.ts +++ b/packages/api-security/src/createSecurity/groupsTeamsPlugins/getTeamFromPlugins.ts @@ -2,7 +2,7 @@ import { SecurityConfig } from "~/types"; import { listTeamsFromPlugins } from "./listTeamsFromPlugins"; export interface GetTeamFromPluginsParams { - listTeamsFromPluginsCallback?: SecurityConfig["listTeamsFromPluginsCallback"]; + teamsProvider?: SecurityConfig["teamsProvider"]; where: { tenant: string; id?: string; @@ -10,10 +10,10 @@ export interface GetTeamFromPluginsParams { }; } -export const getTeamFromPlugins = (params: GetTeamFromPluginsParams) => { - const { listTeamsFromPluginsCallback, where } = params; - const [team] = listTeamsFromPlugins({ - listTeamsFromPluginsCallback, +export const getTeamFromPlugins = async (params: GetTeamFromPluginsParams) => { + const { teamsProvider, where } = params; + const [team] = await listTeamsFromPlugins({ + teamsProvider, where: { tenant: where.tenant, id_in: where.id ? [where.id] : undefined, diff --git a/packages/api-security/src/createSecurity/groupsTeamsPlugins/listGroupsFromPlugins.ts b/packages/api-security/src/createSecurity/groupsTeamsPlugins/listGroupsFromPlugins.ts index d17cca4ce03..8e45513458f 100644 --- a/packages/api-security/src/createSecurity/groupsTeamsPlugins/listGroupsFromPlugins.ts +++ b/packages/api-security/src/createSecurity/groupsTeamsPlugins/listGroupsFromPlugins.ts @@ -1,7 +1,7 @@ import { SecurityConfig } from "~/types"; export interface ListGroupsFromPluginsParams { - listGroupsFromPluginsCallback?: SecurityConfig["listGroupsFromPluginsCallback"]; + groupsProvider?: SecurityConfig["groupsProvider"]; where: { tenant: string; id_in?: string[]; @@ -9,15 +9,13 @@ export interface ListGroupsFromPluginsParams { }; } -export const listGroupsFromPlugins = (params: ListGroupsFromPluginsParams) => { - const { listGroupsFromPluginsCallback, where } = params; - if (!listGroupsFromPluginsCallback) { +export const listGroupsFromPlugins = async (params: ListGroupsFromPluginsParams) => { + const { groupsProvider, where } = params; + if (!groupsProvider) { return []; } - const groups = listGroupsFromPluginsCallback().map(plugin => { - return plugin.securityRole; - }); + const groups = await groupsProvider(); return groups.filter(group => { // First we ensure the group belongs to the correct tenant. diff --git a/packages/api-security/src/createSecurity/groupsTeamsPlugins/listTeamsFromPlugins.ts b/packages/api-security/src/createSecurity/groupsTeamsPlugins/listTeamsFromPlugins.ts index df7ca982c33..f15101cd307 100644 --- a/packages/api-security/src/createSecurity/groupsTeamsPlugins/listTeamsFromPlugins.ts +++ b/packages/api-security/src/createSecurity/groupsTeamsPlugins/listTeamsFromPlugins.ts @@ -1,7 +1,7 @@ import { SecurityConfig } from "~/types"; export interface ListTeamsFromPluginsParams { - listTeamsFromPluginsCallback?: SecurityConfig["listTeamsFromPluginsCallback"]; + teamsProvider?: SecurityConfig["teamsProvider"]; where: { tenant: string; id_in?: string[]; @@ -9,15 +9,13 @@ export interface ListTeamsFromPluginsParams { }; } -export const listTeamsFromPlugins = (params: ListTeamsFromPluginsParams) => { - const { listTeamsFromPluginsCallback, where } = params; - if (!listTeamsFromPluginsCallback) { +export const listTeamsFromPlugins = async (params: ListTeamsFromPluginsParams) => { + const { teamsProvider, where } = params; + if (!teamsProvider) { return []; } - const teams = listTeamsFromPluginsCallback().map(plugin => { - return plugin.securityTeam; - }); + const teams = await teamsProvider(); return teams.filter(team => { // First we ensure the team belongs to the correct tenant. diff --git a/packages/api-security/src/index.ts b/packages/api-security/src/index.ts index 47c114b2360..1365fe0c882 100644 --- a/packages/api-security/src/index.ts +++ b/packages/api-security/src/index.ts @@ -16,6 +16,8 @@ import { MultiTenancyAppConfig, MultiTenancyGraphQLConfig } from "~/enterprise/multiTenancy"; +import { SecurityRolePlugin } from "~/plugins/SecurityRolePlugin"; +import { SecurityTeamPlugin } from "~/plugins/SecurityTeamPlugin"; export { default as NotAuthorizedResponse } from "./NotAuthorizedResponse"; export { default as NotAuthorizedError } from "./NotAuthorizedError"; @@ -43,8 +45,14 @@ export const createSecurityContext = ({ storageOperations }: SecurityConfig) => return tenant ? tenant.id : undefined; }, storageOperations, - listGroupsFromPluginsCallback: () => context.plugins.byType("security-role"), - listTeamsFromPluginsCallback: () => context.plugins.byType("security-team") + groupsProvider: async () => + context.plugins + .byType(SecurityRolePlugin.type) + .map(plugin => plugin.securityRole), + teamsProvider: async () => + context.plugins + .byType(SecurityTeamPlugin.type) + .map(plugin => plugin.securityTeam) }); attachGroupInstaller(context.security); diff --git a/packages/api-security/src/types.ts b/packages/api-security/src/types.ts index b071679ac5b..83a9d1bfe91 100644 --- a/packages/api-security/src/types.ts +++ b/packages/api-security/src/types.ts @@ -5,8 +5,6 @@ import { Topic } from "@webiny/pubsub/types"; import { GetTenant } from "~/createSecurity"; import { ProjectPackageFeatures } from "@webiny/wcp/types"; import { TenancyContext } from "@webiny/api-tenancy/types"; -import { SecurityRolePlugin } from "~/plugins/SecurityRolePlugin"; -import { SecurityTeamPlugin } from "~/plugins/SecurityTeamPlugin"; // Backwards compatibility - START export type SecurityIdentity = Identity; @@ -36,8 +34,8 @@ export interface SecurityConfig { advancedAccessControlLayer?: ProjectPackageFeatures["advancedAccessControlLayer"]; getTenant: GetTenant; storageOperations: SecurityStorageOperations; - listGroupsFromPluginsCallback?: () => Array>; - listTeamsFromPluginsCallback?: () => Array>; + groupsProvider?: () => Promise; + teamsProvider?: () => Promise; } export interface ErrorEvent extends InstallEvent { diff --git a/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx b/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx index fb03e8f8b7b..68dfbd9d86b 100644 --- a/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx +++ b/packages/app-security-access-management/src/ui/views/Groups/GroupsForm.tsx @@ -170,8 +170,8 @@ export const GroupsForm = () => { - This role is registered via an extension. It cannot be - modified via the Admin Area. + This role is registered via an extension, and cannot be + modified. diff --git a/packages/app-security-access-management/src/ui/views/Teams/TeamsForm.tsx b/packages/app-security-access-management/src/ui/views/Teams/TeamsForm.tsx index d4e5567206b..605206e46a2 100644 --- a/packages/app-security-access-management/src/ui/views/Teams/TeamsForm.tsx +++ b/packages/app-security-access-management/src/ui/views/Teams/TeamsForm.tsx @@ -152,8 +152,8 @@ export const TeamsForm = () => { - This team is registered via an extension. It cannot be - modified via the Admin Area. + This team is registered via an extension, and cannot be + modified. From 22035f1edf88d9bbb5c51f5faff7928ee85a61e9 Mon Sep 17 00:00:00 2001 From: adrians5j Date: Fri, 4 Oct 2024 14:15:55 +0200 Subject: [PATCH 19/19] fix: rename files --- .../src/createSecurity/createGroupsMethods.ts | 8 ++++---- .../api-security/src/createSecurity/createTeamsMethods.ts | 8 ++++---- .../{getGroupFromPlugins.ts => getGroupFromProvider.ts} | 6 +++--- .../{getTeamFromPlugins.ts => getTeamFromProvider.ts} | 6 +++--- ...listGroupsFromPlugins.ts => listGroupsFromProvider.ts} | 2 +- .../{listTeamsFromPlugins.ts => listTeamsFromProvider.ts} | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) rename packages/api-security/src/createSecurity/groupsTeamsPlugins/{getGroupFromPlugins.ts => getGroupFromProvider.ts} (71%) rename packages/api-security/src/createSecurity/groupsTeamsPlugins/{getTeamFromPlugins.ts => getTeamFromProvider.ts} (72%) rename packages/api-security/src/createSecurity/groupsTeamsPlugins/{listGroupsFromPlugins.ts => listGroupsFromProvider.ts} (91%) rename packages/api-security/src/createSecurity/groupsTeamsPlugins/{listTeamsFromPlugins.ts => listTeamsFromProvider.ts} (91%) diff --git a/packages/api-security/src/createSecurity/createGroupsMethods.ts b/packages/api-security/src/createSecurity/createGroupsMethods.ts index 94ea4c08818..d86b8017600 100644 --- a/packages/api-security/src/createSecurity/createGroupsMethods.ts +++ b/packages/api-security/src/createSecurity/createGroupsMethods.ts @@ -29,13 +29,13 @@ import { import NotAuthorizedError from "../NotAuthorizedError"; import { SecurityConfig } from "~/types"; import { - listGroupsFromPlugins as baseListGroupsFromPlugins, + listGroupsFromProvider as baseListGroupsFromPlugins, type ListGroupsFromPluginsParams -} from "./groupsTeamsPlugins/listGroupsFromPlugins"; +} from "./groupsTeamsPlugins/listGroupsFromProvider"; import { - getGroupFromPlugins as baseGetGroupFromPlugins, + getGroupFromProvider as baseGetGroupFromPlugins, type GetGroupFromPluginsParams -} from "./groupsTeamsPlugins/getGroupFromPlugins"; +} from "./groupsTeamsPlugins/getGroupFromProvider"; const CreateDataModel = withFields({ tenant: string({ validation: validation.create("required") }), diff --git a/packages/api-security/src/createSecurity/createTeamsMethods.ts b/packages/api-security/src/createSecurity/createTeamsMethods.ts index ca305329ffe..b45794c4d80 100644 --- a/packages/api-security/src/createSecurity/createTeamsMethods.ts +++ b/packages/api-security/src/createSecurity/createTeamsMethods.ts @@ -25,13 +25,13 @@ import { import NotAuthorizedError from "../NotAuthorizedError"; import { SecurityConfig } from "~/types"; import { - listTeamsFromPlugins as baseListTeamsFromPlugins, + listTeamsFromProvider as baseListTeamsFromPlugins, type ListTeamsFromPluginsParams -} from "./groupsTeamsPlugins/listTeamsFromPlugins"; +} from "./groupsTeamsPlugins/listTeamsFromProvider"; import { - getTeamFromPlugins as baseGetTeamFromPlugins, + getTeamFromProvider as baseGetTeamFromPlugins, type GetTeamFromPluginsParams -} from "./groupsTeamsPlugins/getTeamFromPlugins"; +} from "./groupsTeamsPlugins/getTeamFromProvider"; const CreateDataModel = withFields({ tenant: string({ validation: validation.create("required") }), diff --git a/packages/api-security/src/createSecurity/groupsTeamsPlugins/getGroupFromPlugins.ts b/packages/api-security/src/createSecurity/groupsTeamsPlugins/getGroupFromProvider.ts similarity index 71% rename from packages/api-security/src/createSecurity/groupsTeamsPlugins/getGroupFromPlugins.ts rename to packages/api-security/src/createSecurity/groupsTeamsPlugins/getGroupFromProvider.ts index cd02fdf902b..c3d4b56161f 100644 --- a/packages/api-security/src/createSecurity/groupsTeamsPlugins/getGroupFromPlugins.ts +++ b/packages/api-security/src/createSecurity/groupsTeamsPlugins/getGroupFromProvider.ts @@ -1,5 +1,5 @@ import { SecurityConfig } from "~/types"; -import { listGroupsFromPlugins } from "./listGroupsFromPlugins"; +import { listGroupsFromProvider } from "./listGroupsFromProvider"; export interface GetGroupFromPluginsParams { groupsProvider?: SecurityConfig["groupsProvider"]; @@ -10,9 +10,9 @@ export interface GetGroupFromPluginsParams { }; } -export const getGroupFromPlugins = async (params: GetGroupFromPluginsParams) => { +export const getGroupFromProvider = async (params: GetGroupFromPluginsParams) => { const { groupsProvider, where } = params; - const [group] = await listGroupsFromPlugins({ + const [group] = await listGroupsFromProvider({ groupsProvider, where: { tenant: where.tenant, diff --git a/packages/api-security/src/createSecurity/groupsTeamsPlugins/getTeamFromPlugins.ts b/packages/api-security/src/createSecurity/groupsTeamsPlugins/getTeamFromProvider.ts similarity index 72% rename from packages/api-security/src/createSecurity/groupsTeamsPlugins/getTeamFromPlugins.ts rename to packages/api-security/src/createSecurity/groupsTeamsPlugins/getTeamFromProvider.ts index 6abc2aa55b9..8e9e78cd065 100644 --- a/packages/api-security/src/createSecurity/groupsTeamsPlugins/getTeamFromPlugins.ts +++ b/packages/api-security/src/createSecurity/groupsTeamsPlugins/getTeamFromProvider.ts @@ -1,5 +1,5 @@ import { SecurityConfig } from "~/types"; -import { listTeamsFromPlugins } from "./listTeamsFromPlugins"; +import { listTeamsFromProvider } from "./listTeamsFromProvider"; export interface GetTeamFromPluginsParams { teamsProvider?: SecurityConfig["teamsProvider"]; @@ -10,9 +10,9 @@ export interface GetTeamFromPluginsParams { }; } -export const getTeamFromPlugins = async (params: GetTeamFromPluginsParams) => { +export const getTeamFromProvider = async (params: GetTeamFromPluginsParams) => { const { teamsProvider, where } = params; - const [team] = await listTeamsFromPlugins({ + const [team] = await listTeamsFromProvider({ teamsProvider, where: { tenant: where.tenant, diff --git a/packages/api-security/src/createSecurity/groupsTeamsPlugins/listGroupsFromPlugins.ts b/packages/api-security/src/createSecurity/groupsTeamsPlugins/listGroupsFromProvider.ts similarity index 91% rename from packages/api-security/src/createSecurity/groupsTeamsPlugins/listGroupsFromPlugins.ts rename to packages/api-security/src/createSecurity/groupsTeamsPlugins/listGroupsFromProvider.ts index 8e45513458f..ca0f9aa81a9 100644 --- a/packages/api-security/src/createSecurity/groupsTeamsPlugins/listGroupsFromPlugins.ts +++ b/packages/api-security/src/createSecurity/groupsTeamsPlugins/listGroupsFromProvider.ts @@ -9,7 +9,7 @@ export interface ListGroupsFromPluginsParams { }; } -export const listGroupsFromPlugins = async (params: ListGroupsFromPluginsParams) => { +export const listGroupsFromProvider = async (params: ListGroupsFromPluginsParams) => { const { groupsProvider, where } = params; if (!groupsProvider) { return []; diff --git a/packages/api-security/src/createSecurity/groupsTeamsPlugins/listTeamsFromPlugins.ts b/packages/api-security/src/createSecurity/groupsTeamsPlugins/listTeamsFromProvider.ts similarity index 91% rename from packages/api-security/src/createSecurity/groupsTeamsPlugins/listTeamsFromPlugins.ts rename to packages/api-security/src/createSecurity/groupsTeamsPlugins/listTeamsFromProvider.ts index f15101cd307..6da6112c204 100644 --- a/packages/api-security/src/createSecurity/groupsTeamsPlugins/listTeamsFromPlugins.ts +++ b/packages/api-security/src/createSecurity/groupsTeamsPlugins/listTeamsFromProvider.ts @@ -9,7 +9,7 @@ export interface ListTeamsFromPluginsParams { }; } -export const listTeamsFromPlugins = async (params: ListTeamsFromPluginsParams) => { +export const listTeamsFromProvider = async (params: ListTeamsFromPluginsParams) => { const { teamsProvider, where } = params; if (!teamsProvider) { return [];