This commit is contained in:
Nikita Gopienko 2019-10-04 11:15:14 +03:00
commit 180767c213
11 changed files with 179 additions and 120 deletions

View File

@ -173,6 +173,17 @@ namespace ASC.Security.Cryptography
checkKeyResult = ValidateEmailKey(Email + Type + (string.IsNullOrEmpty(hash) ? string.Empty : Hasher.Base64Hash(hash)) + UiD, Key, ValidInterval); checkKeyResult = ValidateEmailKey(Email + Type + (string.IsNullOrEmpty(hash) ? string.Empty : Hasher.Base64Hash(hash)) + UiD, Key, ValidInterval);
break; break;
case ConfirmType.Activation: case ConfirmType.Activation:
checkKeyResult = ValidateEmailKey(Email + Type + UiD, Key, ValidInterval);
break;
case ConfirmType.ProfileRemove:
// validate UiD
if (P == 1)
{
var user = CoreContext.UserManager.GetUsers(UiD.GetValueOrDefault());
if (user == null || user.Status == EmployeeStatus.Terminated || SecurityContext.IsAuthenticated && SecurityContext.CurrentAccount.ID != UiD)
return ValidationResult.Invalid;
}
checkKeyResult = ValidateEmailKey(Email + Type + UiD, Key, ValidInterval); checkKeyResult = ValidateEmailKey(Email + Type + UiD, Key, ValidInterval);
break; break;
default: default:

View File

@ -28,7 +28,7 @@ import {
updateGroup updateGroup
} from "../../../../../store/group/actions"; } from "../../../../../store/group/actions";
import styled from "styled-components"; import styled from "styled-components";
import { fetchSelectorUsers } from "../../../../../store/people/actions"; import { fetchSelectorUsers, fetchPeople, fetchGroups } from "../../../../../store/people/actions";
import { GUID_EMPTY } from "../../../../../helpers/constants"; import { GUID_EMPTY } from "../../../../../helpers/constants";
import isEqual from "lodash/isEqual"; import isEqual from "lodash/isEqual";
@ -110,34 +110,40 @@ class SectionBodyContent extends React.Component {
key: 0, key: 0,
label: t("CustomAddEmployee", { typeUser }) label: t("CustomAddEmployee", { typeUser })
}, },
groupMembers: group && group.members ? group.members.map(m => { groupMembers:
return { group && group.members
key: m.id, ? group.members.map(m => {
label: m.displayName return {
} key: m.id,
}) : [], label: m.displayName
groupManager: group && group.manager ? { };
key: group.manager.id, })
label: group.manager.displayName : [],
} : { groupManager:
key: GUID_EMPTY, group && group.manager
label: t("CustomAddEmployee", { typeUser }) ? {
} key: group.manager.id,
label: group.manager.displayName
}
: {
key: GUID_EMPTY,
label: t("CustomAddEmployee", { typeUser })
}
}; };
return newState; return newState;
} };
componentDidMount() { componentDidMount() {
const { users, fetchSelectorUsers } = this.props; const { users, fetchSelectorUsers } = this.props;
if(!users || !users.length) { if (!users || !users.length) {
fetchSelectorUsers(); fetchSelectorUsers();
} }
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
//const { users, group } = this.props; //const { users, group } = this.props;
if(!isEqual(this.props, prevProps)) { if (!isEqual(this.props, prevProps)) {
this.setState(this.mapPropsToState()); this.setState(this.mapPropsToState());
} }
} }
@ -163,12 +169,12 @@ class SectionBodyContent extends React.Component {
}; };
onHeadSelectorSelect = option => { onHeadSelectorSelect = option => {
this.setState({ this.setState({
groupManager: { groupManager: {
key: option.key, key: option.key,
label: option.label label: option.label
}, },
isHeaderSelectorOpen: !this.state.isHeaderSelectorOpen isHeaderSelectorOpen: !this.state.isHeaderSelectorOpen
}); });
}; };
@ -178,24 +184,24 @@ class SectionBodyContent extends React.Component {
}); });
}; };
onUsersSelectorSearch = (value) => { onUsersSelectorSearch = value => {
/*setOptions( /*setOptions(
options.filter(option => { options.filter(option => {
return option.label.indexOf(value) > -1; return option.label.indexOf(value) > -1;
}) })
);*/ );*/
}; };
onUsersSelectorSelect = (selectedOptions) => { onUsersSelectorSelect = selectedOptions => {
//console.log("onSelect", selectedOptions); //console.log("onSelect", selectedOptions);
//this.onUsersSelectorClick(); //this.onUsersSelectorClick();
this.setState({ this.setState({
groupMembers: selectedOptions.map(option => { groupMembers: selectedOptions.map(option => {
return { return {
key: option.key, key: option.key,
label: option.label label: option.label
}; };
}), }),
isUsersSelectorOpen: !this.state.isUsersSelectorOpen isUsersSelectorOpen: !this.state.isUsersSelectorOpen
}); });
}; };
@ -211,28 +217,33 @@ class SectionBodyContent extends React.Component {
}); });
}; };
save = (group) => {
const { createGroup, updateGroup } = this.props;
return group.id
? updateGroup(group.id, group.name, group.managerKey, group.members)
: createGroup(group.name, group.managerKey, group.members);
};
onSave = () => { onSave = () => {
const { history, group, createGroup, updateGroup, resetGroup } = this.props; const { group } = this.props;
const { groupName, groupManager, groupMembers } = this.state; const { groupName, groupManager, groupMembers } = this.state;
if (!groupName || !groupName.trim().length) return false; if (!groupName || !groupName.trim().length) return false;
this.setState({ inLoading: true }); this.setState({ inLoading: true });
(group && group.id const newGroup = {
? updateGroup( name: groupName,
group.id, managerKey: groupManager.key,
groupName, members: groupMembers.map(u => u.key)
groupManager.key, };
groupMembers.map(u => u.key)
) if(group && group.id)
: createGroup(groupName, groupManager.key, groupMembers.map(u => u.key)) newGroup.id = group.id;
)
.then(() => { this.save(newGroup)
toastr.success("Success"); .then(group => {
//this.setState({ inLoading: true }); toastr.success(`Group '${group.name}' has been saved successfully`);
history.goBack();
resetGroup();
}) })
.catch(error => { .catch(error => {
toastr.error(error.message); toastr.error(error.message);
@ -247,11 +258,11 @@ class SectionBodyContent extends React.Component {
history.goBack(); history.goBack();
}; };
onSelectedItemClose = (member) => { onSelectedItemClose = member => {
this.setState({ this.setState({
groupMembers: this.state.groupMembers.filter(g => g.key !== member.key) groupMembers: this.state.groupMembers.filter(g => g.key !== member.key)
}); });
} };
renderModal = () => { renderModal = () => {
const { groups, modalVisible } = this.state; const { groups, modalVisible } = this.state;
@ -559,11 +570,11 @@ function mapStateToProps(state) {
settings: state.auth.settings, settings: state.auth.settings,
group: state.group.targetGroup, group: state.group.targetGroup,
groups: convertGroups(state.people.groups), groups: convertGroups(state.people.groups),
users: convertUsers(state.people.selector.users) //TODO: replace to api requests with search users: convertUsers(state.people.selector.users), //TODO: replace to api requests with search
}; };
} }
export default connect( export default connect(
mapStateToProps, mapStateToProps,
{ resetGroup, createGroup, updateGroup, fetchSelectorUsers } { resetGroup, createGroup, updateGroup, fetchSelectorUsers, fetchPeople, fetchGroups }
)(withRouter(withTranslation()(SectionBodyContent))); )(withRouter(withTranslation()(SectionBodyContent)));

View File

@ -0,0 +1,6 @@
export function checkResponseError(res) {
if (res && res.data && res.data.error) {
console.error(res.data.error);
throw new Error(res.data.error.message);
}
}

View File

@ -1,5 +1,5 @@
import * as api from "../services/api"; import * as api from "../services/api";
import { setGroups, fetchPeopleAsync } from "../people/actions"; import { fetchGroups, fetchPeople } from "../people/actions";
import setAuthorizationToken from "../../store/services/setAuthorizationToken"; import setAuthorizationToken from "../../store/services/setAuthorizationToken";
import { getFilterByLocation } from "../../helpers/converters"; import { getFilterByLocation } from "../../helpers/converters";
import config from "../../../package.json"; import config from "../../../package.json";
@ -57,9 +57,7 @@ export async function getUserInfo(dispatch) {
dispatch(setModules(modules)); dispatch(setModules(modules));
dispatch(setSettings(newSettings)); dispatch(setSettings(newSettings));
const groupResp = await api.getGroupList(); await fetchGroups(dispatch);
dispatch(setGroups(groupResp.data.response));
var re = new RegExp(`${config.homepage}((/?)$|/filter)`, "gm"); var re = new RegExp(`${config.homepage}((/?)$|/filter)`, "gm");
const match = window.location.pathname.match(re); const match = window.location.pathname.match(re);
@ -67,7 +65,7 @@ export async function getUserInfo(dispatch) {
if (match && match.length > 0) if (match && match.length > 0)
{ {
const newFilter = getFilterByLocation(window.location); const newFilter = getFilterByLocation(window.location);
await fetchPeopleAsync(dispatch, newFilter); await fetchPeople(newFilter, dispatch);
} }
return dispatch(setIsLoaded(true)); return dispatch(setIsLoaded(true));

View File

@ -1,5 +1,7 @@
import * as api from "../../store/services/api"; import * as api from "../../store/services/api";
import { setGroups, fetchPeopleByFilter } from "../people/actions"; import { setGroups, fetchPeople, fetchGroups } from "../people/actions";
import { checkResponseError } from "../../helpers/utils";
import history from "../../history";
export const SET_GROUP = "SET_GROUP"; export const SET_GROUP = "SET_GROUP";
export const CLEAN_GROUP = "CLEAN_GROUP"; export const CLEAN_GROUP = "CLEAN_GROUP";
@ -17,13 +19,6 @@ export function resetGroup() {
}; };
} }
export function checkResponseError(res) {
if (res && res.data && res.data.error) {
console.error(res.data.error);
throw new Error(res.data.error.message);
}
}
export function fetchGroup(groupId) { export function fetchGroup(groupId) {
return dispatch => { return dispatch => {
api.getGroup(groupId).then(res => { api.getGroup(groupId).then(res => {
@ -36,23 +31,16 @@ export function fetchGroup(groupId) {
export function createGroup(groupName, groupManager, members) { export function createGroup(groupName, groupManager, members) {
return (dispatch, getState) => { return (dispatch, getState) => {
const { people } = getState(); const { people } = getState();
const { groups, filter } = people; const { groups } = people;
let newGroup;
return api return api
.createGroup(groupName, groupManager, members) .createGroup(groupName, groupManager, members)
.then(res => { .then(res => {
checkResponseError(res); checkResponseError(res);
newGroup = res.data.response; history.goBack();
const newGroup = res.data.response;
//dispatch(setGroup(newGroup)); dispatch(resetGroup());
return dispatch(setGroups([...groups, newGroup])); dispatch(setGroups([...groups, newGroup]));
})
.then(() => {
return fetchPeopleByFilter(dispatch, filter);
})
.then(() => {
return Promise.resolve(newGroup); return Promise.resolve(newGroup);
}); });
}; };
@ -61,28 +49,19 @@ export function createGroup(groupName, groupManager, members) {
export function updateGroup(id, groupName, groupManager, members) { export function updateGroup(id, groupName, groupManager, members) {
return (dispatch, getState) => { return (dispatch, getState) => {
const { people } = getState(); const { people } = getState();
const { groups, filter } = people; const { groups } = people;
let newGroup;
return api return api
.updateGroup(id, groupName, groupManager, members) .updateGroup(id, groupName, groupManager, members)
.then(res => { .then(res => {
checkResponseError(res); checkResponseError(res);
newGroup = res.data.response; history.goBack();
const newGroup = res.data.response;
//dispatch(setGroup(newGroup)); dispatch(resetGroup());
const newGroups = groups.map(g => const newGroups = groups.map(g =>
g.id === newGroup.id ? newGroup : g g.id === newGroup.id ? newGroup : g
); );
dispatch(setGroups(newGroups));
return dispatch(setGroups(newGroups));
})
.then(() => {
return fetchPeopleByFilter(dispatch, filter);
})
.then(() => {
return Promise.resolve(newGroup); return Promise.resolve(newGroup);
}); });
}; };
@ -101,7 +80,7 @@ export function deleteGroup(id) {
}) })
.then(() => { .then(() => {
const newFilter = filter.clone(true); const newFilter = filter.clone(true);
return fetchPeopleByFilter(dispatch, newFilter); return fetchPeople(newFilter, dispatch);
}); });
}; };
} }

View File

@ -14,6 +14,7 @@ import {
PAGE_COUNT, PAGE_COUNT,
EmployeeStatus EmployeeStatus
} from "../../helpers/constants"; } from "../../helpers/constants";
import { checkResponseError } from "../../helpers/utils";
export const SET_GROUPS = "SET_GROUPS"; export const SET_GROUPS = "SET_GROUPS";
export const SET_USERS = "SET_USERS"; export const SET_USERS = "SET_USERS";
@ -69,7 +70,7 @@ export function selectGroup(groupId) {
let newFilter = filter.clone(); let newFilter = filter.clone();
newFilter.group = groupId; newFilter.group = groupId;
return fetchPeopleByFilter(dispatch, newFilter); return fetchPeople(newFilter, dispatch);
}; };
} }
@ -147,14 +148,38 @@ export function fetchSelectorUsers() {
}; };
} }
export function fetchPeople(filter) { export function fetchGroups(dispatchFunc = null) {
return dispatch => { return api.getGroupList()
return fetchPeopleByFilter(dispatch, filter); .then(res => {
checkResponseError(res);
return dispatchFunc
? dispatchFunc(setGroups(res.data.response))
: Promise.resolve(dispatch => dispatch(setGroups(res.data.response)));
});
}
export function fetchPeople(filter, dispatchFunc = null) {
return dispatchFunc ? fetchPeopleByFilter(dispatchFunc, filter)
: (dispatch, getState) => {
if(filter) {
return fetchPeopleByFilter(dispatch, filter);
}
else {
const {people} = getState();
const {filter} = people;
return fetchPeopleByFilter(dispatch, filter);
}
}; };
} }
export function fetchPeopleByFilter(dispatch, filter) { function fetchPeopleByFilter(dispatch, filter) {
let filterData = (filter && filter.clone()) || Filter.getDefault(); let filterData = (filter && filter.clone());
if(!filterData) {
filterData = Filter.getDefault();
filterData.employeeStatus = EmployeeStatus.Active;
}
return api.getUserList(filterData).then(res => { return api.getUserList(filterData).then(res => {
filterData.total = res.data.total; filterData.total = res.data.total;
@ -167,7 +192,7 @@ export function fetchPeopleByFilter(dispatch, filter) {
}); });
} }
export async function fetchPeopleAsync(dispatch, filter = null) { /*export async function fetchPeopleAsync(dispatch, filter = null) {
let filterData = (filter && filter.clone()); let filterData = (filter && filter.clone());
if(!filterData) { if(!filterData) {
@ -185,7 +210,7 @@ export async function fetchPeopleAsync(dispatch, filter = null) {
groupId: filterData.group groupId: filterData.group
}); });
dispatch(setUsers(usersResp.data.response)); dispatch(setUsers(usersResp.data.response));
} }*/
export function updateUserStatus(status, userIds) { export function updateUserStatus(status, userIds) {
return dispatch => { return dispatch => {
@ -224,6 +249,6 @@ export function resetFilter() {
const newFilter = filter.clone(true); const newFilter = filter.clone(true);
return fetchPeopleByFilter(dispatch, newFilter); return fetchPeople(newFilter, dispatch);
}; };
} }

View File

@ -1,8 +1,9 @@
import * as api from "../../store/services/api"; import * as api from "../../store/services/api";
import { isMe } from '../auth/selectors'; import { isMe } from '../auth/selectors';
import { getUserByUserName } from '../people/selectors'; import { getUserByUserName } from '../people/selectors';
import { fetchPeopleByFilter } from "../people/actions"; import { fetchPeople } from "../people/actions";
import { setCurrentUser } from "../auth/actions"; import { setCurrentUser } from "../auth/actions";
import { checkResponseError } from "../../helpers/utils";
export const SET_PROFILE = 'SET_PROFILE'; export const SET_PROFILE = 'SET_PROFILE';
export const CLEAN_PROFILE = 'CLEAN_PROFILE'; export const CLEAN_PROFILE = 'CLEAN_PROFILE';
@ -20,12 +21,7 @@ export function resetProfile() {
}; };
}; };
export function checkResponseError(res) {
if (res && res.data && res.data.error) {
console.error(res.data.error);
throw new Error(res.data.error.message);
}
}
export function employeeWrapperToMemberModel(profile) { export function employeeWrapperToMemberModel(profile) {
const comment = profile.notes; const comment = profile.notes;
@ -67,7 +63,7 @@ export function createProfile(profile) {
result = res.data.response; result = res.data.response;
return dispatch(setProfile(result)); return dispatch(setProfile(result));
}).then(() => { }).then(() => {
return fetchPeopleByFilter(dispatch, filter); return fetchPeople(filter, dispatch);
}).then(() => { }).then(() => {
return Promise.resolve(result); return Promise.resolve(result);
}); });
@ -86,7 +82,7 @@ export function updateProfile(profile) {
result = res.data.response; result = res.data.response;
return Promise.resolve(dispatch(setProfile(result))); return Promise.resolve(dispatch(setProfile(result)));
}).then(() => { }).then(() => {
return fetchPeopleByFilter(dispatch, filter); return fetchPeople(filter, dispatch);
}).then(() => { }).then(() => {
return Promise.resolve(result); return Promise.resolve(result);
}); });

View File

@ -575,6 +575,50 @@ namespace ASC.Employee.Core.Controllers
return new EmployeeWraperFull(user, ApiContext); return new EmployeeWraperFull(user, ApiContext);
} }
[Delete("@self")]
[Authorize(AuthenticationSchemes = "confirm", Roles = "ProfileRemove")]
public EmployeeWraperFull DeleteProfile()
{
ApiContext.AuthByClaim();
if (CoreContext.UserManager.IsSystemUser(SecurityContext.CurrentAccount.ID))
throw new SecurityException();
var user = GetUserInfo(SecurityContext.CurrentAccount.ID.ToString());
if (!CoreContext.UserManager.UserExists(user))
throw new Exception(Resource.ErrorUserNotFound);
if(user.IsLDAP())
throw new SecurityException();
_ = SecurityContext.AuthenticateMe(ASC.Core.Configuration.Constants.CoreSystem);
user.Status = EmployeeStatus.Terminated;
CoreContext.UserManager.SaveUserInfo(Tenant, user);
var userName = user.DisplayUserName(false);
MessageService.Send(MessageAction.UsersUpdatedStatus, MessageTarget.Create(user.ID), userName);
HttpContext.ResetUserCookie(Tenant.TenantId, user.ID);
MessageService.Send(MessageAction.CookieSettingsUpdated);
if (CoreContext.Configuration.Personal)
{
UserPhotoManager.RemovePhoto(Tenant, user.ID);
CoreContext.UserManager.DeleteUser(Tenant, user.ID);
MessageService.Send(MessageAction.UserDeleted, MessageTarget.Create(user.ID), userName);
}
else
{
//StudioNotifyService.Instance.SendMsgProfileHasDeletedItself(user);
//StudioNotifyService.SendMsgProfileDeletion(Tenant.TenantId, user);
}
return new EmployeeWraperFull(user, ApiContext);
}
[Update("{userid}/contacts")] [Update("{userid}/contacts")]
public EmployeeWraperFull UpdateMemberContacts(string userid, UpdateMemberModel memberModel) public EmployeeWraperFull UpdateMemberContacts(string userid, UpdateMemberModel memberModel)
{ {

View File

@ -5,8 +5,8 @@ import styled from 'styled-components';
import { welcomePageTitle } from './../../../../helpers/customNames'; import { welcomePageTitle } from './../../../../helpers/customNames';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { withTranslation } from 'react-i18next'; import { withTranslation } from 'react-i18next';
import { deleteUser, updateUserStatus } from './../../../../store/services/api' import { deleteSelf } from './../../../../store/services/api';
import { EmployeeStatus } from './../../../../helpers/constants'; import setAuthorizationToken from './../../../../store/services/setAuthorizationToken';
const ProfileRemoveContainer = styled.div` const ProfileRemoveContainer = styled.div`
display: flex; display: flex;
@ -40,17 +40,13 @@ class ProfileRemove extends React.PureComponent {
onDeleteProfile = (e) => { onDeleteProfile = (e) => {
this.setState({ isLoading: true }, function () { this.setState({ isLoading: true }, function () {
const { linkData } = this.props; const { linkData } = this.props;
deleteSelf(linkData.confirmHeader)
updateUserStatus(EmployeeStatus.Disabled, [linkData.uid], linkData.confirmHeader)
.then((res) => {
console.log('success update status', res)
return deleteUser(linkData.uid);
})
.then((res) => { .then((res) => {
this.setState({ this.setState({
isLoading: false, isLoading: false,
isProfileDeleted: true isProfileDeleted: true
}); });
setAuthorizationToken();
console.log('success delete', res) console.log('success delete', res)
}) })
.catch((e) => { .catch((e) => {

View File

@ -85,20 +85,13 @@ export function checkConfirmLink(data) {
: axios.post(`${API_URL}/authentication/confirm.json`, data); : axios.post(`${API_URL}/authentication/confirm.json`, data);
} }
export function deleteUser(userId) { export function deleteSelf(key) {
return IS_FAKE return IS_FAKE
? fakeApi.deleteUser(userId) ? fakeApi.deleteUser(key)
: axios.delete(`${API_URL}/people/${userId}.json`); : axios.delete(`${API_URL}/people/@self`, {
}
export function updateUserStatus(status, userIds, key) {
return IS_FAKE
? fakeApi.updateUserStatus(status, userIds)
: axios.put(`${API_URL}/people/status/${status}`, { userIds }, {
headers: { confirm: key } headers: { confirm: key }
}); });
} }
export function sendInstructionsToChangePassword(email) { export function sendInstructionsToChangePassword(email) {
return IS_FAKE return IS_FAKE
? fakeApi.sendInstructionsToChangePassword() ? fakeApi.sendInstructionsToChangePassword()

View File

@ -447,7 +447,7 @@ namespace ASC.Web.Studio.Core.Notify
public void SendMsgProfileDeletion(int tenantId, UserInfo user) public void SendMsgProfileDeletion(int tenantId, UserInfo user)
{ {
var confirmationUrl = CommonLinkUtility.GetConfirmationUrl(tenantId, user.Email, ConfirmType.ProfileRemove, null, SecurityContext.CurrentAccount.ID); var confirmationUrl = CommonLinkUtility.GetConfirmationUrl(tenantId, user.Email, ConfirmType.ProfileRemove, SecurityContext.CurrentAccount.ID, SecurityContext.CurrentAccount.ID);
static string greenButtonText() => CoreContext.Configuration.Personal ? WebstudioNotifyPatternResource.ButtonConfirmTermination : WebstudioNotifyPatternResource.ButtonRemoveProfile; static string greenButtonText() => CoreContext.Configuration.Personal ? WebstudioNotifyPatternResource.ButtonConfirmTermination : WebstudioNotifyPatternResource.ButtonRemoveProfile;