* updating maxattempts for ldap
Этот коммит содержится в:
Ben Cooke
2025-03-12 18:22:03 -04:00
коммит произвёл GitHub
родитель 2102391672
Коммит eb967b6b6d
23 изменённых файлов: 800 добавлений и 28 удалений

Просмотреть файл

@@ -3469,6 +3469,19 @@ const AdminDefinition: AdminDefinitionType = {
),
),
},
{
type: 'number',
key: 'LdapSettings.MaximumLoginAttempts',
label: defineMessage({id: 'admin.ldap.maximumLoginAttemptsTitle', defaultMessage: 'Maximum Login Attempts:'}),
help_text: defineMessage({id: 'admin.ldap.maximumLoginAttemptsDesc', defaultMessage: 'The maximum number of login attempts before the Mattermost account is locked. You can unlock the account in system console on the users page. Setting this value lower than your LDAP maximum login attempts ensures that the users won\'t be locked out of your LDAP server because of failed login attempts in Mattermost.'}),
isDisabled: it.any(
it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.LDAP)),
it.all(
it.stateIsFalse('LdapSettings.Enable'),
it.stateIsFalse('LdapSettings.EnableSync'),
),
),
},
{
type: 'text',
key: 'LdapSettings.LdapServer',

Просмотреть файл

@@ -0,0 +1,78 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {FormattedMessage} from 'react-intl';
import {useDispatch} from 'react-redux';
import type {ServerError} from '@mattermost/types/errors';
import type {UserProfile} from '@mattermost/types/users';
import {resetFailedAttempts} from 'mattermost-redux/actions/users';
import ConfirmModalRedux from 'components/confirm_modal_redux';
type Props = {
user: UserProfile;
onError: (error: ServerError) => void;
onSuccess: () => void;
onExited: () => void;
}
export default function ConfirmResetFailedAttemptsModal({user, onSuccess, onError, onExited}: Props) {
const dispatch = useDispatch();
async function confirm() {
const {error} = await dispatch(resetFailedAttempts(user.id));
if (error) {
onError(error);
}
onSuccess();
}
const title = (
<FormattedMessage
id='confirm_reset_failed_attempts_modal.title'
defaultMessage='Reset failed login attempts for {username} and unlock account'
values={{
username: user.username,
}}
/>
);
const message = (
<FormattedMessage
id='confirm_reset_failed_attempts_modal.desc'
defaultMessage="You're about to reset the failed login attempts for {username} and unlock their account. Are you sure you want to continue?"
values={{
username: user.username,
}}
/>
);
const createGroupMembershipsButton = (
<FormattedMessage
id='confirm_reset_failed_attempts_modal.create'
defaultMessage='Yes'
/>
);
const cancelGroupMembershipsButton = (
<FormattedMessage
id='confirm_reset_failed_attempts_modal.cancel'
defaultMessage='No'
/>
);
return (
<ConfirmModalRedux
title={title}
message={message}
confirmButtonClass='btn btn-danger'
cancelButtonText={cancelGroupMembershipsButton}
confirmButtonText={createGroupMembershipsButton}
onConfirm={confirm}
onExited={onExited}
/>
);
}

Просмотреть файл

@@ -35,6 +35,7 @@ import Constants, {ModalIdentifiers} from 'utils/constants';
import type {GlobalState} from 'types/store';
import ConfirmManageUserSettingsModal from './confirm_manage_user_settings_modal';
import ConfirmResetFailedAttemptsModal from './confirm_reset_failed_attempts_modal';
import CreateGroupSyncablesMembershipsModal from './create_group_syncables_membership_modal';
import DeactivateMemberModal from './deactivate_member_modal';
import DemoteToGuestModal from './demote_to_guest_modal';
@@ -303,6 +304,24 @@ export function SystemUsersListAction({user, currentUser, tableId, rowIndex, onE
);
}, [user, updateUser, onError]);
const handleResetAttemptsClick = useCallback(() => {
function onResetAttemptsSuccess() {
updateUser({failed_attempts: 0});
}
dispatch(
openModal({
modalId: ModalIdentifiers.CONFIRM_RESET_FAILED_ATTEMPTS_MODAL,
dialogType: ConfirmResetFailedAttemptsModal,
dialogProps: {
user,
onError,
onSuccess: onResetAttemptsSuccess,
},
}),
);
}, [user, updateUser, onError]);
const disableActivationToggle = user.auth_service === Constants.LDAP_SERVICE;
const getManagedByLDAPText = (managedByLDAP: boolean) => {
@@ -314,6 +333,18 @@ export function SystemUsersListAction({user, currentUser, tableId, rowIndex, onE
} : {};
};
const showResetFailedAttempts = useCallback(() => {
if (user.failed_attempts === undefined) {
return false;
}
if (user.auth_service !== Constants.LDAP_SERVICE && user.auth_service !== '') {
return false;
}
return true;
}, [user]);
return (
<Menu.Container
menuButton={{
@@ -414,6 +445,18 @@ export function SystemUsersListAction({user, currentUser, tableId, rowIndex, onE
onClick={handleResetPasswordClick}
/>
}
{showResetFailedAttempts() && (
<Menu.Item
id={`${menuItemIdPrefix}-resetAttempts`}
labels={
<FormattedMessage
id='admin.system_users.list.actions.menu.resetAttempts'
defaultMessage='Reset login attempts'
/>
}
onClick={handleResetAttemptsClick}
/>
)}
{user.mfa_active && config.ServiceSettings?.EnableMultifactorAuthentication &&
<Menu.Item
id={`${menuItemIdPrefix}-removeMFA`}

Просмотреть файл

@@ -1335,6 +1335,8 @@
"admin.ldap.loginNameDesc": "The placeholder text that appears in the login field on the login page. Defaults to \"AD/LDAP Username\".",
"admin.ldap.loginNameEx": "E.g.: \"AD/LDAP Username\"",
"admin.ldap.loginNameTitle": "Login Field Name:",
"admin.ldap.maximumLoginAttemptsDesc": "The maximum number of login attempts before the Mattermost account is locked. You can unlock the account in system console on the users page. Setting this value lower than your LDAP maximum login attempts ensures that the users won't be locked out of your LDAP server because of failed login attempts in Mattermost.",
"admin.ldap.maximumLoginAttemptsTitle": "Maximum Login Attempts:",
"admin.ldap.maxPageSizeEx": "E.g.: \"2000\"",
"admin.ldap.maxPageSizeHelpText": "The maximum number of users the Mattermost server will request from the AD/LDAP server at one time. 0 is unlimited.",
"admin.ldap.maxPageSizeTitle": "Maximum Page Size:",
@@ -2629,6 +2631,7 @@
"admin.system_users.list.actions.menu.promoteToMember": "Promote to member",
"admin.system_users.list.actions.menu.removeMFA": "Remove MFA",
"admin.system_users.list.actions.menu.removeSessions": "Remove sessions",
"admin.system_users.list.actions.menu.resetAttempts": "Reset login attempts",
"admin.system_users.list.actions.menu.resetPassword": "Reset password",
"admin.system_users.list.actions.menu.resyncUserViaLdapGroups": "Re-sync user via LDAP groups",
"admin.system_users.list.actions.menu.switchToEmailPassword": "Switch to Email/Password",
@@ -3484,6 +3487,10 @@
"commercial_support.download_support_packet": "Download Support Packet",
"commercial_support.title": "Commercial Support",
"confirm_modal.cancel": "Cancel",
"confirm_reset_failed_attempts_modal.cancel": "No",
"confirm_reset_failed_attempts_modal.create": "Yes",
"confirm_reset_failed_attempts_modal.desc": "You're about to reset the failed login attempts for {username} and unlock their account. Are you sure you want to continue?",
"confirm_reset_failed_attempts_modal.title": "Reset failed login attempts for {username} and unlock account",
"confirm_switch_to_yearly_modal.confirm": "Confirm",
"confirm_switch_to_yearly_modal.contact_sales": "Contact Sales",
"confirm_switch_to_yearly_modal.subtitle": "Changing to the annual plan is irreversible. Are you sure you want to switch from monthly to the annual plan?",

Просмотреть файл

@@ -1070,6 +1070,24 @@ export function updateUserPassword(userId: string, currentPassword: string, newP
};
}
export function resetFailedAttempts(userId: string): ActionFuncAsync<true> {
return async (dispatch, getState) => {
try {
await Client4.resetFailedAttempts(userId);
} catch (error) {
dispatch(logError(error));
return {error};
}
const profile = getState().entities.users.profiles[userId];
if (profile) {
dispatch({type: UserTypes.RECEIVED_PROFILE, data: {...profile, failed_attempts: 0}});
}
return {data: true};
};
}
export function updateUserActive(userId: string, active: boolean): ActionFuncAsync<true> {
return async (dispatch, getState) => {
try {

Просмотреть файл

@@ -467,6 +467,7 @@ export const ModalIdentifiers = {
SECURE_CONNECTION_ACCEPT_INVITE: 'secure_connection_accept_invite',
SHARED_CHANNEL_REMOTE_INVITE: 'shared_channel_remote_invite',
SHARED_CHANNEL_REMOTE_UNINVITE: 'shared_channel_remote_uninvite',
CONFIRM_RESET_FAILED_ATTEMPTS_MODAL: 'confirm_reset_failed_attempts_modal',
USER_PROPERTY_FIELD_DELETE: 'user_property_field_delete',
};