Browse Source

Merge branch 'feat/124385-126902-make-granted-group-ref-dynamic' into granted-groups-tests

Futa Arai 2 năm trước cách đây
mục cha
commit
2b4639dc25

+ 5 - 3
apps/app/src/components/Admin/UserGroup/UserGroupForm.tsx

@@ -68,9 +68,11 @@ export const UserGroupForm: FC<Props> = (props: Props) => {
       <fieldset>
         <h2 className="admin-setting-header">{t('user_group_management.basic_info')}</h2>
         {isExternalGroup
-        && <div className='mb-3'>
-          <small className="text-muted">{t('external_user_group.only_description_edit_allowed')}</small>
-        </div>}
+        && (
+          <div className="mb-3">
+            <small className="text-muted">{t('external_user_group.only_description_edit_allowed')}</small>
+          </div>
+        )}
         {
           userGroup?.createdAt != null && (
             <div className="form-group row">

+ 7 - 5
apps/app/src/components/Admin/UserGroup/UserGroupModal.tsx

@@ -96,11 +96,13 @@ export const UserGroupModal: FC<Props> = (props: Props) => {
               {t('Description')}
             </label>
             <textarea className="form-control" name="description" value={currentDescription} onChange={onChangeDescriptionHandler} />
-            {isExternalGroup && <p className="form-text text-muted">
-              <small>
-                {t('external_user_group.description_form_detail')}
-              </small>
-            </p>}
+            {isExternalGroup && (
+              <p className="form-text text-muted">
+                <small>
+                  {t('external_user_group.description_form_detail')}
+                </small>
+              </p>
+            )}
           </div>
 
           {/* TODO 90732: Add a drop-down to show selectable parents */}

+ 5 - 3
apps/app/src/components/Admin/UserGroup/UserGroupTable.tsx

@@ -209,9 +209,11 @@ export const UserGroupTable: FC<Props> = ({
                             <i className="icon-fw icon-note"></i> {t('Edit')}
                           </button>
                           {onRemove != null
-                          && <button className="dropdown-item" type="button" role="button" onClick={onClickRemove} data-user-group-id={group._id}>
-                            <i className="icon-fw fa fa-chain-broken"></i> {t('admin:user_group_management.remove_child_group')}
-                          </button>}
+                          && (
+                            <button className="dropdown-item" type="button" role="button" onClick={onClickRemove} data-user-group-id={group._id}>
+                              <i className="icon-fw fa fa-chain-broken"></i> {t('admin:user_group_management.remove_child_group')}
+                            </button>
+                          )}
                           <button className="dropdown-item" type="button" role="button" onClick={onClickDelete} data-user-group-id={group._id}>
                             <i className="icon-fw icon-fire text-danger"></i> {t('Delete')}
                           </button>

+ 9 - 6
apps/app/src/components/Admin/UserGroupDetail/UserGroupDetailPage.tsx

@@ -365,7 +365,8 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
                   <Link href={{
                     pathname: `/admin/user-group-detail/${ancestorUserGroup._id}`,
                     query: { isExternalGroup: 'true' },
-                  }}>
+                  }}
+                  >
                     {ancestorUserGroup.name}
                   </Link>
                 ) }
@@ -408,11 +409,13 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
       />
 
       <h2 className="admin-setting-header mt-4">{t('user_group_management.child_group_list')}</h2>
-      {!isExternalGroup && <UserGroupDropdown
-        selectableUserGroups={selectableChildUserGroups}
-        onClickAddExistingUserGroupButton={onClickAddExistingUserGroupButtonHandler}
-        onClickCreateUserGroupButton={showCreateModal}
-      />}
+      {!isExternalGroup && (
+        <UserGroupDropdown
+          selectableUserGroups={selectableChildUserGroups}
+          onClickAddExistingUserGroupButton={onClickAddExistingUserGroupButtonHandler}
+          onClickCreateUserGroupButton={showCreateModal}
+        />
+      )}
 
       <UserGroupModal
         userGroup={selectedUserGroup}

+ 32 - 28
apps/app/src/components/Admin/UserGroupDetail/UserGroupUserTable.tsx

@@ -45,43 +45,47 @@ export const UserGroupUserTable = (props: Props): JSX.Element => {
               <td>{relatedUser.name}</td>
               <td>{relatedUser.createdAt ? dateFnsFormat(new Date(relatedUser.createdAt), 'yyyy-MM-dd') : ''}</td>
               <td>{relatedUser.lastLoginAt ? dateFnsFormat(new Date(relatedUser.lastLoginAt), 'yyyy-MM-dd HH:mm:ss') : ''}</td>
-              {!props.isExternalGroup && <td>
-                <div className="btn-group admin-user-menu">
-                  <button
-                    type="button"
-                    id={`admin-group-menu-button-${relatedUser._id}`}
-                    className="btn btn-outline-secondary btn-sm dropdown-toggle"
-                    data-toggle="dropdown"
-                  >
-                    <i className="icon-settings"></i>
-                  </button>
-                  <div className="dropdown-menu" role="menu" aria-labelledby={`admin-group-menu-button-${relatedUser._id}`}>
+              {!props.isExternalGroup && (
+                <td>
+                  <div className="btn-group admin-user-menu">
                     <button
-                      className="dropdown-item"
                       type="button"
-                      onClick={() => props.onClickRemoveUserBtn(relatedUser.username)}
+                      id={`admin-group-menu-button-${relatedUser._id}`}
+                      className="btn btn-outline-secondary btn-sm dropdown-toggle"
+                      data-toggle="dropdown"
                     >
-                      <i className="icon-fw icon-user-unfollow"></i> {t('admin:user_group_management.remove_from_group')}
+                      <i className="icon-settings"></i>
                     </button>
+                    <div className="dropdown-menu" role="menu" aria-labelledby={`admin-group-menu-button-${relatedUser._id}`}>
+                      <button
+                        className="dropdown-item"
+                        type="button"
+                        onClick={() => props.onClickRemoveUserBtn(relatedUser.username)}
+                      >
+                        <i className="icon-fw icon-user-unfollow"></i> {t('admin:user_group_management.remove_from_group')}
+                      </button>
+                    </div>
                   </div>
-                </div>
-              </td>}
+                </td>
+              )}
             </tr>
           );
         })}
 
-        {!props.isExternalGroup && <tr>
-          <td></td>
-          <td className="text-center">
-            <button className="btn btn-outline-secondary" type="button" onClick={props.onClickPlusBtn}>
-              <i className="ti ti-plus"></i>
-            </button>
-          </td>
-          <td></td>
-          <td></td>
-          <td></td>
-          <td></td>
-        </tr>}
+        {!props.isExternalGroup && (
+          <tr>
+            <td></td>
+            <td className="text-center">
+              <button className="btn btn-outline-secondary" type="button" onClick={props.onClickPlusBtn}>
+                <i className="ti ti-plus"></i>
+              </button>
+            </td>
+            <td></td>
+            <td></td>
+            <td></td>
+            <td></td>
+          </tr>
+        )}
 
       </tbody>
     </table>

+ 4 - 2
apps/app/src/components/PageAlert/PageGrantAlert.tsx

@@ -33,9 +33,11 @@ export const PageGrantAlert = (): JSX.Element => {
       if (pageData.grant === 5) {
         return (
           <>
-            <i className="icon-fw icon-organization"></i><strong>{
+            <i className="icon-fw icon-organization"></i>
+            <strong>{
               isPopulated(pageData.grantedGroups[0].item) && pageData.grantedGroups[0].item.name
-            }</strong>
+            }
+            </strong>
           </>
         );
       }

+ 45 - 43
apps/app/src/features/external-user-group/client/components/ExternalUserGroup/ExternalUserGroupManagement.tsx

@@ -125,47 +125,49 @@ export const ExternalGroupManagement: FC = () => {
     };
   }, []);
 
-  return <>
-    <h2 className="border-bottom">{t('external_user_group.management')}</h2>
-    <UserGroupTable
-      headerLabel={t('admin:user_group_management.group_list')}
-      userGroups={externalUserGroups}
-      childUserGroups={childExternalUserGroups}
-      isAclEnabled={isAclEnabled ?? false}
-      onEdit={showUpdateModal}
-      onDelete={showDeleteModal}
-      userGroupRelations={externalUserGroupRelations}
-      isExternalGroup
-    />
-
-    <UserGroupModal
-      userGroup={selectedExternalUserGroup}
-      buttonLabel={t('Update')}
-      onClickSubmit={updateExternalUserGroup}
-      isShow={isUpdateModalShown}
-      onHide={hideUpdateModal}
-      isExternalGroup
-    />
-
-    <UserGroupDeleteModal
-      userGroups={externalUserGroups}
-      deleteUserGroup={selectedExternalUserGroup}
-      onDelete={deleteExternalUserGroupById}
-      isShow={isDeleteModalShown}
-      onHide={hideDeleteModal}
-    />
-
-    <CustomNav
-      activeTab={activeTab}
-      navTabMapping={navTabMapping}
-      onNavSelected={switchActiveTab}
-      hideBorderBottom
-      breakpointToSwitchDropdownDown="md"
-    />
-    <TabContent activeTab={activeTab} className="p-5">
-      <TabPane tabId="ldap">
-        {activeComponents.has('ldap') && <LdapGroupManagement />}
-      </TabPane>
-    </TabContent>
-  </>;
+  return (
+    <>
+      <h2 className="border-bottom">{t('external_user_group.management')}</h2>
+      <UserGroupTable
+        headerLabel={t('admin:user_group_management.group_list')}
+        userGroups={externalUserGroups}
+        childUserGroups={childExternalUserGroups}
+        isAclEnabled={isAclEnabled ?? false}
+        onEdit={showUpdateModal}
+        onDelete={showDeleteModal}
+        userGroupRelations={externalUserGroupRelations}
+        isExternalGroup
+      />
+
+      <UserGroupModal
+        userGroup={selectedExternalUserGroup}
+        buttonLabel={t('Update')}
+        onClickSubmit={updateExternalUserGroup}
+        isShow={isUpdateModalShown}
+        onHide={hideUpdateModal}
+        isExternalGroup
+      />
+
+      <UserGroupDeleteModal
+        userGroups={externalUserGroups}
+        deleteUserGroup={selectedExternalUserGroup}
+        onDelete={deleteExternalUserGroupById}
+        isShow={isDeleteModalShown}
+        onHide={hideDeleteModal}
+      />
+
+      <CustomNav
+        activeTab={activeTab}
+        navTabMapping={navTabMapping}
+        onNavSelected={switchActiveTab}
+        hideBorderBottom
+        breakpointToSwitchDropdownDown="md"
+      />
+      <TabContent activeTab={activeTab} className="p-5">
+        <TabPane tabId="ldap">
+          {activeComponents.has('ldap') && <LdapGroupManagement />}
+        </TabPane>
+      </TabContent>
+    </>
+  );
 };

+ 27 - 23
apps/app/src/features/external-user-group/client/components/ExternalUserGroup/LdapGroupManagement.tsx

@@ -44,28 +44,32 @@ export const LdapGroupManagement: FC = () => {
     }
   }, [t, isUserBind]);
 
-  return <>
-    <LdapGroupSyncSettingsForm />
-    <h3 className="border-bottom mb-3">{t('external_user_group.execute_sync')}</h3>
-    <form onSubmit={onSyncBtnClick}>
-      {isUserBind && <div className="row form-group">
-        <label htmlFor="ldapGroupSyncPassword" className="text-left text-md-right col-md-3 col-form-label">{t('external_user_group.ldap.password')}</label>
-        <div className="col-md-6">
-          <input
-            className="form-control"
-            type="password"
-            name="password"
-            id="ldapGroupSyncPassword"
-          />
-          <p className="form-text text-muted">
-            <small>{t('external_user_group.ldap.password_detail')}</small>
-          </p>
+  return (
+    <>
+      <LdapGroupSyncSettingsForm />
+      <h3 className="border-bottom mb-3">{t('external_user_group.execute_sync')}</h3>
+      <form onSubmit={onSyncBtnClick}>
+        {isUserBind && (
+          <div className="row form-group">
+            <label htmlFor="ldapGroupSyncPassword" className="text-left text-md-right col-md-3 col-form-label">{t('external_user_group.ldap.password')}</label>
+            <div className="col-md-6">
+              <input
+                className="form-control"
+                type="password"
+                name="password"
+                id="ldapGroupSyncPassword"
+              />
+              <p className="form-text text-muted">
+                <small>{t('external_user_group.ldap.password_detail')}</small>
+              </p>
+            </div>
+          </div>
+        )}
+        <div className="row">
+          <div className="col-md-3"></div>
+          <div className="col-md-6"><button className="btn btn-primary" type="submit">{t('external_user_group.sync')}</button></div>
         </div>
-      </div>}
-      <div className="row">
-        <div className="col-md-3"></div>
-        <div className="col-md-6"><button className="btn btn-primary" type="submit">{t('external_user_group.sync')}</button></div>
-      </div>
-    </form>
-  </>;
+      </form>
+    </>
+  );
 };

+ 188 - 183
apps/app/src/features/external-user-group/client/components/ExternalUserGroup/LdapGroupSyncSettingsForm.tsx

@@ -42,201 +42,206 @@ export const LdapGroupSyncSettingsForm: FC = () => {
     }
   }, [formValues, t]);
 
-  return <>
-    <h3 className="border-bottom mb-3">{t('external_user_group.ldap.group_sync_settings')}</h3>
-    <form onSubmit={submitHandler}>
-      <div className="row form-group">
-        <label
-          htmlFor="ldapGroupSearchBase"
-          className="text-left text-md-right col-md-3 col-form-label">
-          {t('external_user_group.ldap.group_search_base_DN')}
-        </label>
-        <div className="col-md-6">
-          <input
-            className="form-control"
-            type="text"
-            name="ldapGroupSearchBase"
-            id="ldapGroupSearchBase"
-            value={formValues.ldapGroupSearchBase}
-            onChange={e => setFormValues({ ...formValues, ldapGroupSearchBase: e.target.value })}
-          />
-          <p className="form-text text-muted">
-            <small>{t('external_user_group.ldap.group_search_base_dn_detail')}</small>
-          </p>
-        </div>
-      </div>
-      <div className="row form-group">
-        <label htmlFor="ldapGroupMembershipAttribute" className="text-left text-md-right col-md-3 col-form-label">
-          {t('external_user_group.ldap.membership_attribute')}
-        </label>
-        <div className="col-md-6">
-          <input
-            className="form-control"
-            required
-            type="text"
-            name="ldapGroupMembershipAttribute"
-            id="ldapGroupMembershipAttribute"
-            value={formValues.ldapGroupMembershipAttribute}
-            onChange={e => setFormValues({ ...formValues, ldapGroupMembershipAttribute: e.target.value })}
-          />
-          <p className="form-text text-muted">
-            <small>
-              {t('external_user_group.ldap.membership_attribute_detail')} <br />
-              e.g.) <code>member</code>, <code>memberUid</code>
-            </small>
-          </p>
-        </div>
-      </div>
-      <div className="row form-group">
-        <label htmlFor="ldapGroupMembershipAttributeType" className="text-left text-md-right col-md-3 col-form-label">
-          {t('external_user_group.ldap.membership_attribute_type')}
-        </label>
-        <div className="col-md-6">
-          <select
-            className="form-control"
-            required
-            name="ldapGroupMembershipAttributeType"
-            id="ldapGroupMembershipAttributeType"
-            value={formValues.ldapGroupMembershipAttributeType}
-            onChange={(e) => {
-              if (e.target.value === LdapGroupMembershipAttributeType.dn || e.target.value === LdapGroupMembershipAttributeType.uid) {
-                setFormValues({ ...formValues, ldapGroupMembershipAttributeType: e.target.value });
-              }
-            }}>
-            <option value="DN">DN</option>
-            <option value="UID">UID</option>
-          </select>
-          <p className="form-text text-muted">
-            <small>
-              {t('external_user_group.ldap.membership_attribute_type_detail')}
-            </small>
-          </p>
-        </div>
-      </div>
-      <div className="row form-group">
-        <label htmlFor="ldapGroupChildGroupAttribute" className="text-left text-md-right col-md-3 col-form-label">
-          {t('external_user_group.ldap.child_group_attribute')}
-        </label>
-        <div className="col-md-6">
-          <input
-            className="form-control"
-            required
-            type="text"
-            name="ldapGroupChildGroupAttribute"
-            id="ldapGroupChildGroupAttribute"
-            value={formValues.ldapGroupChildGroupAttribute}
-            onChange={e => setFormValues({ ...formValues, ldapGroupChildGroupAttribute: e.target.value })}/>
-          <p className="form-text text-muted">
-            <small>
-              {t('external_user_group.ldap.child_group_attribute_detail')}<br />
-            e.g.) <code>member</code>
-            </small>
-          </p>
+  return (
+    <>
+      <h3 className="border-bottom mb-3">{t('external_user_group.ldap.group_sync_settings')}</h3>
+      <form onSubmit={submitHandler}>
+        <div className="row form-group">
+          <label
+            htmlFor="ldapGroupSearchBase"
+            className="text-left text-md-right col-md-3 col-form-label"
+          >
+            {t('external_user_group.ldap.group_search_base_DN')}
+          </label>
+          <div className="col-md-6">
+            <input
+              className="form-control"
+              type="text"
+              name="ldapGroupSearchBase"
+              id="ldapGroupSearchBase"
+              value={formValues.ldapGroupSearchBase}
+              onChange={e => setFormValues({ ...formValues, ldapGroupSearchBase: e.target.value })}
+            />
+            <p className="form-text text-muted">
+              <small>{t('external_user_group.ldap.group_search_base_dn_detail')}</small>
+            </p>
+          </div>
         </div>
-      </div>
-      <div className="row form-group">
-        <label
-          className="text-left text-md-right col-md-3 col-form-label"
-        >
-          {/* {t('external_user_group.ldap.auto_generate_user_on_sync')} */}
-        </label>
-        <div className="col-md-6">
-          <div className="custom-control custom-checkbox custom-checkbox-info">
+        <div className="row form-group">
+          <label htmlFor="ldapGroupMembershipAttribute" className="text-left text-md-right col-md-3 col-form-label">
+            {t('external_user_group.ldap.membership_attribute')}
+          </label>
+          <div className="col-md-6">
             <input
-              type="checkbox"
-              className="custom-control-input"
-              name="autoGenerateUserOnLdapGroupSync"
-              id="autoGenerateUserOnLdapGroupSync"
-              checked={formValues.autoGenerateUserOnLdapGroupSync}
-              onChange={() => setFormValues({ ...formValues, autoGenerateUserOnLdapGroupSync: !formValues.autoGenerateUserOnLdapGroupSync })}
+              className="form-control"
+              required
+              type="text"
+              name="ldapGroupMembershipAttribute"
+              id="ldapGroupMembershipAttribute"
+              value={formValues.ldapGroupMembershipAttribute}
+              onChange={e => setFormValues({ ...formValues, ldapGroupMembershipAttribute: e.target.value })}
             />
-            <label
-              className="custom-control-label"
-              htmlFor="autoGenerateUserOnLdapGroupSync"
+            <p className="form-text text-muted">
+              <small>
+                {t('external_user_group.ldap.membership_attribute_detail')} <br />
+                e.g.) <code>member</code>, <code>memberUid</code>
+              </small>
+            </p>
+          </div>
+        </div>
+        <div className="row form-group">
+          <label htmlFor="ldapGroupMembershipAttributeType" className="text-left text-md-right col-md-3 col-form-label">
+            {t('external_user_group.ldap.membership_attribute_type')}
+          </label>
+          <div className="col-md-6">
+            <select
+              className="form-control"
+              required
+              name="ldapGroupMembershipAttributeType"
+              id="ldapGroupMembershipAttributeType"
+              value={formValues.ldapGroupMembershipAttributeType}
+              onChange={(e) => {
+                if (e.target.value === LdapGroupMembershipAttributeType.dn || e.target.value === LdapGroupMembershipAttributeType.uid) {
+                  setFormValues({ ...formValues, ldapGroupMembershipAttributeType: e.target.value });
+                }
+              }}
             >
-              {t('external_user_group.ldap.auto_generate_user_on_sync')}
-            </label>
+              <option value="DN">DN</option>
+              <option value="UID">UID</option>
+            </select>
+            <p className="form-text text-muted">
+              <small>
+                {t('external_user_group.ldap.membership_attribute_type_detail')}
+              </small>
+            </p>
           </div>
         </div>
-      </div>
-      <div className="row form-group">
-        <label
-          className="text-left text-md-right col-md-3 col-form-label"
-        >
-          {/* {t('external_user_group.ldap.preserve_deleted_ldap_groups')} */}
-        </label>
-        <div className="col-md-6">
-          <div className="custom-control custom-checkbox custom-checkbox-info">
+        <div className="row form-group">
+          <label htmlFor="ldapGroupChildGroupAttribute" className="text-left text-md-right col-md-3 col-form-label">
+            {t('external_user_group.ldap.child_group_attribute')}
+          </label>
+          <div className="col-md-6">
             <input
-              type="checkbox"
-              className="custom-control-input"
-              name="preserveDeletedLdapGroups"
-              id="preserveDeletedLdapGroups"
-              checked={formValues.preserveDeletedLdapGroups}
-              onChange={() => setFormValues({ ...formValues, preserveDeletedLdapGroups: !formValues.preserveDeletedLdapGroups })}
+              className="form-control"
+              required
+              type="text"
+              name="ldapGroupChildGroupAttribute"
+              id="ldapGroupChildGroupAttribute"
+              value={formValues.ldapGroupChildGroupAttribute}
+              onChange={e => setFormValues({ ...formValues, ldapGroupChildGroupAttribute: e.target.value })}
             />
-            <label
-              className="custom-control-label"
-              htmlFor="preserveDeletedLdapGroups"
-            >
-              {t('external_user_group.ldap.preserve_deleted_ldap_groups')}
-            </label>
+            <p className="form-text text-muted">
+              <small>
+                {t('external_user_group.ldap.child_group_attribute_detail')}<br />
+                e.g.) <code>member</code>
+              </small>
+            </p>
+          </div>
+        </div>
+        <div className="row form-group">
+          <label
+            className="text-left text-md-right col-md-3 col-form-label"
+          >
+            {/* {t('external_user_group.ldap.auto_generate_user_on_sync')} */}
+          </label>
+          <div className="col-md-6">
+            <div className="custom-control custom-checkbox custom-checkbox-info">
+              <input
+                type="checkbox"
+                className="custom-control-input"
+                name="autoGenerateUserOnLdapGroupSync"
+                id="autoGenerateUserOnLdapGroupSync"
+                checked={formValues.autoGenerateUserOnLdapGroupSync}
+                onChange={() => setFormValues({ ...formValues, autoGenerateUserOnLdapGroupSync: !formValues.autoGenerateUserOnLdapGroupSync })}
+              />
+              <label
+                className="custom-control-label"
+                htmlFor="autoGenerateUserOnLdapGroupSync"
+              >
+                {t('external_user_group.ldap.auto_generate_user_on_sync')}
+              </label>
+            </div>
           </div>
         </div>
-      </div>
-      <div className="px-5">
-        <h4 className="border-bottom mb-3">Attribute Mapping ({t('optional')})</h4>
-      </div>
-      <div className="row form-group">
-        <label htmlFor="ldapGroupNameAttribute" className="text-left text-md-right col-md-3 col-form-label">{t('Name')}</label>
-        <div className="col-md-6">
-          <input
-            className="form-control"
-            type="text"
-            name="ldapGroupNameAttribute"
-            id="ldapGroupNameAttribute"
-            value={formValues.ldapGroupNameAttribute}
-            onChange={e => setFormValues({ ...formValues, ldapGroupNameAttribute: e.target.value })}
-            placeholder="Default: cn"
-          />
-          <p className="form-text text-muted">
-            <small>
-              {t('external_user_group.ldap.name_mapper_detail')}
-            </small>
-          </p>
+        <div className="row form-group">
+          <label
+            className="text-left text-md-right col-md-3 col-form-label"
+          >
+            {/* {t('external_user_group.ldap.preserve_deleted_ldap_groups')} */}
+          </label>
+          <div className="col-md-6">
+            <div className="custom-control custom-checkbox custom-checkbox-info">
+              <input
+                type="checkbox"
+                className="custom-control-input"
+                name="preserveDeletedLdapGroups"
+                id="preserveDeletedLdapGroups"
+                checked={formValues.preserveDeletedLdapGroups}
+                onChange={() => setFormValues({ ...formValues, preserveDeletedLdapGroups: !formValues.preserveDeletedLdapGroups })}
+              />
+              <label
+                className="custom-control-label"
+                htmlFor="preserveDeletedLdapGroups"
+              >
+                {t('external_user_group.ldap.preserve_deleted_ldap_groups')}
+              </label>
+            </div>
+          </div>
         </div>
-      </div>
-      <div className="row form-group">
-        <label htmlFor="ldapGroupDescriptionAttribute" className="text-left text-md-right col-md-3 col-form-label">
-          {t('Description')}
-        </label>
-        <div className="col-md-6">
-          <input
-            className="form-control"
-            type="text"
-            name="ldapGroupDescriptionAttribute"
-            id="ldapGroupDescriptionAttribute"
-            value={formValues.ldapGroupDescriptionAttribute || ''}
-            onChange={e => setFormValues({ ...formValues, ldapGroupDescriptionAttribute: e.target.value })}
-          />
-          <p className="form-text text-muted">
-            <small>
-              {t('external_user_group.ldap.description_mapper_detail')}
-            </small>
-          </p>
+        <div className="px-5">
+          <h4 className="border-bottom mb-3">Attribute Mapping ({t('optional')})</h4>
+        </div>
+        <div className="row form-group">
+          <label htmlFor="ldapGroupNameAttribute" className="text-left text-md-right col-md-3 col-form-label">{t('Name')}</label>
+          <div className="col-md-6">
+            <input
+              className="form-control"
+              type="text"
+              name="ldapGroupNameAttribute"
+              id="ldapGroupNameAttribute"
+              value={formValues.ldapGroupNameAttribute}
+              onChange={e => setFormValues({ ...formValues, ldapGroupNameAttribute: e.target.value })}
+              placeholder="Default: cn"
+            />
+            <p className="form-text text-muted">
+              <small>
+                {t('external_user_group.ldap.name_mapper_detail')}
+              </small>
+            </p>
+          </div>
+        </div>
+        <div className="row form-group">
+          <label htmlFor="ldapGroupDescriptionAttribute" className="text-left text-md-right col-md-3 col-form-label">
+            {t('Description')}
+          </label>
+          <div className="col-md-6">
+            <input
+              className="form-control"
+              type="text"
+              name="ldapGroupDescriptionAttribute"
+              id="ldapGroupDescriptionAttribute"
+              value={formValues.ldapGroupDescriptionAttribute || ''}
+              onChange={e => setFormValues({ ...formValues, ldapGroupDescriptionAttribute: e.target.value })}
+            />
+            <p className="form-text text-muted">
+              <small>
+                {t('external_user_group.ldap.description_mapper_detail')}
+              </small>
+            </p>
+          </div>
         </div>
-      </div>
 
-      <div className="row my-3">
-        <div className="offset-3 col-5">
-          <button
-            type="submit"
-            className="btn btn-primary"
-          >
-            {t('Update')}
-          </button>
+        <div className="row my-3">
+          <div className="offset-3 col-5">
+            <button
+              type="submit"
+              className="btn btn-primary"
+            >
+              {t('Update')}
+            </button>
+          </div>
         </div>
-      </div>
-    </form>
-  </>;
+      </form>
+    </>
+  );
 };