diff --git a/opencti-platform/opencti-front/src/private/components/common/form/GroupField.tsx b/opencti-platform/opencti-front/src/private/components/common/form/GroupField.tsx index 2c68d4ca1690..ee55964c2319 100644 --- a/opencti-platform/opencti-front/src/private/components/common/form/GroupField.tsx +++ b/opencti-platform/opencti-front/src/private/components/common/form/GroupField.tsx @@ -25,25 +25,17 @@ const useStyles = makeStyles({ }, }); -export const searchGroupFieldQuery = graphql` - query GroupFieldSearchQuery($search: String) { - groups(orderBy: name, search: $search) { - edges { - node { - id - name - group_confidence_level { - max_confidence - } - } - } - } - } -`; - export const groupsQuery = graphql` - query GroupFieldQuery { - groups { + query GroupFieldQuery( + $orderMode: OrderingMode + $orderBy: GroupsOrdering + $filters: FilterGroup + ) { + groups( + orderMode: $orderMode + orderBy: $orderBy + filters: $filters + ) { edges { node { id @@ -95,7 +87,7 @@ const GroupField: React.FC = (props) => { if (predefinedGroups) { setGroups(predefinedGroups); } else { - fetchQuery(groupsQuery) + fetchQuery(groupsQuery, { orderBy: 'name', orderMode: 'asc' }) .toPromise() .then((data) => { const dataGroups = (data as GroupFieldQuery$data).groups?.edges ?? []; @@ -103,11 +95,13 @@ const GroupField: React.FC = (props) => { const max_confidence = n?.node.group_confidence_level ? `${t_i18n('Max Confidence Level:')} ${n.node.group_confidence_level.max_confidence}` : t_i18n('No Max Confidence Level'); - const newLabel = showConfidence + const groupLabel = n?.node.name ?? ''; + const groupLabelInList = showConfidence ? `${n?.node.name} (${max_confidence})` : n?.node.name ?? ''; return { - label: newLabel, + label: groupLabel, + labelInList: groupLabelInList, value: n?.node.id ?? '', }; }); @@ -133,12 +127,12 @@ const GroupField: React.FC = (props) => { options={groups} onInputChange={searchGroups} onChange={typeof onChange === 'function' ? onChange : null} - renderOption={(renderProps: React.HTMLAttributes, option: { color: string; label: string }) => ( + renderOption={(renderProps: React.HTMLAttributes, option: { color: string; label: string, labelInList?: string }) => (
  • -
    {option.label}
    +
    {option.labelInList ?? option.label}
  • )} classes={{ clearIndicator: classes.autoCompleteIndicator }} diff --git a/opencti-platform/opencti-front/src/private/components/settings/Users.jsx b/opencti-platform/opencti-front/src/private/components/settings/Users.jsx index 8790653a3b37..db820e85b887 100644 --- a/opencti-platform/opencti-front/src/private/components/settings/Users.jsx +++ b/opencti-platform/opencti-front/src/private/components/settings/Users.jsx @@ -1,6 +1,5 @@ import React from 'react'; import Grid from '@mui/material/Grid'; -import makeStyles from '@mui/styles/makeStyles'; import SettingsOrganizationUserCreation from './users/SettingsOrganizationUserCreation'; import EnterpriseEdition from '../common/entreprise_edition/EnterpriseEdition'; import { QueryRenderer } from '../../../relay/environment'; @@ -13,20 +12,13 @@ import useGranted, { SETTINGS_SETACCESSES, VIRTUAL_ORGANIZATION_ADMIN } from '.. import useEnterpriseEdition from '../../../utils/hooks/useEnterpriseEdition'; import { useFormatter } from '../../../components/i18n'; import Breadcrumbs from '../../../components/Breadcrumbs'; - -// Deprecated - https://mui.com/system/styles/basics/ -// Do not use it for new code. -const useStyles = makeStyles(() => ({ - container: { - margin: 0, - padding: '0 200px 50px 0', - }, -})); +import useQueryLoading from '../../../utils/hooks/useQueryLoading'; +import Loader, { LoaderVariant } from '../../../components/Loader'; +import { groupsQuery } from '../common/form/GroupField'; const LOCAL_STORAGE_KEY = 'users'; const Users = () => { - const classes = useStyles(); const { t_i18n } = useFormatter(); const { viewStorage, paginationOptions, helpers } = usePaginationLocalStorage( LOCAL_STORAGE_KEY, @@ -39,6 +31,21 @@ const Users = () => { const isSetAccess = useGranted([SETTINGS_SETACCESSES]); const isAdminOrganization = useGranted([VIRTUAL_ORGANIZATION_ADMIN]); const isEnterpriseEdition = useEnterpriseEdition(); + + const defaultAssignationFilter = { + mode: 'and', + filters: [{ key: 'default_assignation', values: [true] }], + filterGroups: [], + }; + const defaultGroupsQueryRef = useQueryLoading( + groupsQuery, + { + orderBy: 'name', + orderMode: 'asc', + filters: defaultAssignationFilter, + }, + ); + const renderLines = () => { const dataColumns = { name: { @@ -105,7 +112,13 @@ const Users = () => { }; return ( -
    +
    {isSetAccess || isEnterpriseEdition ? ( @@ -117,7 +130,11 @@ const Users = () => { /> )} - {isSetAccess && } + {isSetAccess && defaultGroupsQueryRef && ( + }> + + + )} {!isSetAccess && isAdminOrganization && isEnterpriseEdition && ( Yup.object().shape({ }), }); -const UserCreation = ({ paginationOptions }) => { +const UserCreation = ({ paginationOptions, defaultGroupsQueryRef }) => { const { settings } = useAuth(); const { t_i18n } = useFormatter(); const classes = useStyles(); const hasSetAccess = useGranted([SETTINGS_SETACCESSES]); + const { groups: defaultGroups } = usePreloadedQuery(groupsQuery, defaultGroupsQueryRef); + const onSubmit = (values, { setSubmitting, resetForm }) => { const { objectOrganization, groups, user_confidence_level, ...rest } = values; const finalValues = { @@ -123,7 +125,7 @@ const UserCreation = ({ paginationOptions }) => { password: '', confirmation: '', objectOrganization: [], - groups: [], + groups: defaultGroups.edges.map((g) => ({ value: g.node.id, label: g.node.name })), account_status: 'Active', account_lock_after_date: null, user_confidence_level: null, diff --git a/opencti-platform/opencti-graphql/src/domain/user.js b/opencti-platform/opencti-graphql/src/domain/user.js index 3a3aaa02dacc..36ef02a62dfc 100644 --- a/opencti-platform/opencti-graphql/src/domain/user.js +++ b/opencti-platform/opencti-graphql/src/domain/user.js @@ -588,23 +588,26 @@ export const addUser = async (context, user, newUser) => { })); await Promise.all(relationOrganizations.map((relation) => createRelation(context, user, relation))); // Either use the provided groups or Assign the default groups to user (SSO) - const userRelationGroups = (newUser.groups ?? []).map((group) => ({ - fromId: element.id, - toId: group, - relationship_type: RELATION_MEMBER_OF, - })); - const defaultAssignationFilter = { - mode: 'and', - filters: [{ key: 'default_assignation', values: [true] }], - filterGroups: [], - }; - const defaultGroups = await findGroups(context, user, { filters: defaultAssignationFilter }); - const defaultRelationGroups = defaultGroups.edges.map((e) => ({ - fromId: element.id, - toId: e.node.internal_id, - relationship_type: RELATION_MEMBER_OF, - })); - const relationGroups = [...userRelationGroups, ...defaultRelationGroups]; + let relationGroups = []; + if ((newUser.groups ?? []).length > 0) { + relationGroups = (newUser.groups ?? []).map((group) => ({ + fromId: element.id, + toId: group, + relationship_type: RELATION_MEMBER_OF, + })); + } else { // if no provided groups, assign the user to the default groups + const defaultAssignationFilter = { + mode: 'and', + filters: [{ key: 'default_assignation', values: [true] }], + filterGroups: [], + }; + const defaultGroups = await findGroups(context, user, { filters: defaultAssignationFilter }); + relationGroups = defaultGroups.edges.map((e) => ({ + fromId: element.id, + toId: e.node.internal_id, + relationship_type: RELATION_MEMBER_OF, + })); + } await Promise.all(relationGroups.map((relation) => createRelation(context, user, relation))); // Audit log if (isCreation) { diff --git a/opencti-platform/opencti-graphql/tests/02-integration/02-resolvers/user-test.ts b/opencti-platform/opencti-graphql/tests/02-integration/02-resolvers/user-test.ts index 461b735ca28a..42c973f34184 100644 --- a/opencti-platform/opencti-graphql/tests/02-integration/02-resolvers/user-test.ts +++ b/opencti-platform/opencti-graphql/tests/02-integration/02-resolvers/user-test.ts @@ -11,6 +11,7 @@ import { getGroupIdByName, getOrganizationIdByName, getUserIdByEmail, + GREEN_GROUP, PLATFORM_ORGANIZATION, queryAsAdmin, TEST_ORGANIZATION, @@ -142,6 +143,14 @@ const CREATE_QUERY = gql` max_confidence } } + groups { + edges { + node { + id + name + } + } + } effective_confidence_level { max_confidence source { @@ -614,6 +623,40 @@ describe('User resolver standard behavior', () => { expect(queryResult.data?.user.capabilities.length).toEqual(1); expect(queryResult.data?.user.capabilities[0].name).toEqual('KNOWLEDGE'); }); + it('Created user should not be assigned to default group if groups are specified in the new user input', async () => { + const USER_TO_CREATE = { + input: { + name: 'UserWithGroupsSpecified', + password: 'UserWithGroupsSpecified', + user_email: 'UserWithGroupsSpecified@mail.com', + groups: [GREEN_GROUP.id], + }, + }; + const userAddResult = await queryAsAdmin({ + query: CREATE_QUERY, + variables: USER_TO_CREATE, + }); + expect(userAddResult.data?.userAdd.groups.edges.length).toEqual(1); + expect(userAddResult.data?.userAdd.groups.edges[0].node.name).toEqual(GREEN_GROUP.name); + userToDeleteIds.push(userAddResult.data?.userAdd.id); + }); + it('Created user should be assigned to default group if no groups are specified in the new user input', async () => { + const USER_TO_CREATE = { + input: { + name: 'UserWithNoGroupsSpecified', + password: 'UserWithNoGroupsSpecified', + user_email: 'UserWithNoGroupsSpecified@mail.com', + groups: [], + }, + }; + const userAddResult = await queryAsAdmin({ + query: CREATE_QUERY, + variables: USER_TO_CREATE, + }); + expect(userAddResult.data?.userAdd.groups.edges.length).toEqual(1); + expect(userAddResult.data?.userAdd.groups.edges[0].node.name).toEqual('Default'); + userToDeleteIds.push(userAddResult.data?.userAdd.id); + }); it('should delete relation in user', async () => { const RELATION_DELETE_QUERY = gql` mutation UserEdit($id: ID!, $toId: StixRef!, $relationship_type: String!) {