Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[frontend/backend] New user shouldn't be assigned default groups if other groups are specified (#9234) #9274

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -95,19 +87,21 @@ const GroupField: React.FC<GroupFieldProps> = (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 ?? [];
const newGroups = dataGroups.map((n) => {
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 ?? '',
};
});
Expand All @@ -133,12 +127,12 @@ const GroupField: React.FC<GroupFieldProps> = (props) => {
options={groups}
onInputChange={searchGroups}
onChange={typeof onChange === 'function' ? onChange : null}
renderOption={(renderProps: React.HTMLAttributes<HTMLLIElement>, option: { color: string; label: string }) => (
renderOption={(renderProps: React.HTMLAttributes<HTMLLIElement>, option: { color: string; label: string, labelInList?: string }) => (
<li {...renderProps}>
<div className={classes.icon} style={{ color: option.color }}>
<ItemIcon type="Group" />
</div>
<div className={classes.text}>{option.label}</div>
<div className={classes.text}>{option.labelInList ?? option.label}</div>
</li>
)}
classes={{ clearIndicator: classes.autoCompleteIndicator }}
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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,
Expand All @@ -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: {
Expand Down Expand Up @@ -105,7 +112,13 @@ const Users = () => {
};

return (
<div className={classes.container} data-testid="users-settings-page">
<div
style={{
margin: 0,
padding: '0 200px 50px 0',
}}
data-testid="users-settings-page"
>
<Breadcrumbs elements={[{ label: t_i18n('Settings') }, { label: t_i18n('Security') }, { label: t_i18n('Users'), current: true }]} />
<AccessesMenu />
{isSetAccess || isEnterpriseEdition ? (
Expand All @@ -117,7 +130,11 @@ const Users = () => {
/>
</Grid>
)}
{isSetAccess && <UserCreation paginationOptions={paginationOptions} />}
{isSetAccess && defaultGroupsQueryRef && (
<React.Suspense fallback={<Loader variant={LoaderVariant.inElement} />}>
<UserCreation paginationOptions={paginationOptions} defaultGroupsQueryRef={defaultGroupsQueryRef} />
</React.Suspense>
)}
{!isSetAccess && isAdminOrganization && isEnterpriseEdition && (
<SettingsOrganizationUserCreation
paginationOptions={paginationOptions}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { Field, Form, Formik } from 'formik';
import Button from '@mui/material/Button';
import * as Yup from 'yup';
import { makeStyles } from '@mui/styles';
import { graphql } from 'react-relay';
import { graphql, usePreloadedQuery } from 'react-relay';
import Alert from '@mui/material/Alert';
import MenuItem from '@mui/material/MenuItem';
import GroupField from '../../common/form/GroupField';
import GroupField, { groupsQuery } from '../../common/form/GroupField';
import UserConfidenceLevelField from './edition/UserConfidenceLevelField';
import Drawer, { DrawerVariant } from '../../common/drawer/Drawer';
import { useFormatter } from '../../../../components/i18n';
Expand Down Expand Up @@ -65,12 +65,14 @@ const userValidation = (t) => 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 = {
Expand Down Expand Up @@ -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,
Expand Down
37 changes: 20 additions & 17 deletions opencti-platform/opencti-graphql/src/domain/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
getGroupIdByName,
getOrganizationIdByName,
getUserIdByEmail,
GREEN_GROUP,
PLATFORM_ORGANIZATION,
queryAsAdmin,
TEST_ORGANIZATION,
Expand Down Expand Up @@ -142,6 +143,14 @@ const CREATE_QUERY = gql`
max_confidence
}
}
groups {
edges {
node {
id
name
}
}
}
effective_confidence_level {
max_confidence
source {
Expand Down Expand Up @@ -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: '[email protected]',
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: '[email protected]',
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!) {
Expand Down
Loading