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);
break;
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);
break;
default:

View File

@ -28,7 +28,7 @@ import {
updateGroup
} from "../../../../../store/group/actions";
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 isEqual from "lodash/isEqual";
@ -110,34 +110,40 @@ class SectionBodyContent extends React.Component {
key: 0,
label: t("CustomAddEmployee", { typeUser })
},
groupMembers: group && group.members ? group.members.map(m => {
return {
key: m.id,
label: m.displayName
}
}) : [],
groupManager: group && group.manager ? {
key: group.manager.id,
label: group.manager.displayName
} : {
key: GUID_EMPTY,
label: t("CustomAddEmployee", { typeUser })
}
groupMembers:
group && group.members
? group.members.map(m => {
return {
key: m.id,
label: m.displayName
};
})
: [],
groupManager:
group && group.manager
? {
key: group.manager.id,
label: group.manager.displayName
}
: {
key: GUID_EMPTY,
label: t("CustomAddEmployee", { typeUser })
}
};
return newState;
}
};
componentDidMount() {
const { users, fetchSelectorUsers } = this.props;
if(!users || !users.length) {
if (!users || !users.length) {
fetchSelectorUsers();
}
}
componentDidUpdate(prevProps) {
//const { users, group } = this.props;
if(!isEqual(this.props, prevProps)) {
if (!isEqual(this.props, prevProps)) {
this.setState(this.mapPropsToState());
}
}
@ -163,12 +169,12 @@ class SectionBodyContent extends React.Component {
};
onHeadSelectorSelect = option => {
this.setState({
this.setState({
groupManager: {
key: option.key,
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(
options.filter(option => {
return option.label.indexOf(value) > -1;
})
);*/
};
onUsersSelectorSelect = (selectedOptions) => {
onUsersSelectorSelect = selectedOptions => {
//console.log("onSelect", selectedOptions);
//this.onUsersSelectorClick();
this.setState({
this.setState({
groupMembers: selectedOptions.map(option => {
return {
key: option.key,
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 = () => {
const { history, group, createGroup, updateGroup, resetGroup } = this.props;
const { group } = this.props;
const { groupName, groupManager, groupMembers } = this.state;
if (!groupName || !groupName.trim().length) return false;
this.setState({ inLoading: true });
(group && group.id
? updateGroup(
group.id,
groupName,
groupManager.key,
groupMembers.map(u => u.key)
)
: createGroup(groupName, groupManager.key, groupMembers.map(u => u.key))
)
.then(() => {
toastr.success("Success");
//this.setState({ inLoading: true });
history.goBack();
resetGroup();
const newGroup = {
name: groupName,
managerKey: groupManager.key,
members: groupMembers.map(u => u.key)
};
if(group && group.id)
newGroup.id = group.id;
this.save(newGroup)
.then(group => {
toastr.success(`Group '${group.name}' has been saved successfully`);
})
.catch(error => {
toastr.error(error.message);
@ -247,11 +258,11 @@ class SectionBodyContent extends React.Component {
history.goBack();
};
onSelectedItemClose = (member) => {
this.setState({
onSelectedItemClose = member => {
this.setState({
groupMembers: this.state.groupMembers.filter(g => g.key !== member.key)
});
}
};
renderModal = () => {
const { groups, modalVisible } = this.state;
@ -559,11 +570,11 @@ function mapStateToProps(state) {
settings: state.auth.settings,
group: state.group.targetGroup,
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(
mapStateToProps,
{ resetGroup, createGroup, updateGroup, fetchSelectorUsers }
{ resetGroup, createGroup, updateGroup, fetchSelectorUsers, fetchPeople, fetchGroups }
)(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 { setGroups, fetchPeopleAsync } from "../people/actions";
import { fetchGroups, fetchPeople } from "../people/actions";
import setAuthorizationToken from "../../store/services/setAuthorizationToken";
import { getFilterByLocation } from "../../helpers/converters";
import config from "../../../package.json";
@ -57,9 +57,7 @@ export async function getUserInfo(dispatch) {
dispatch(setModules(modules));
dispatch(setSettings(newSettings));
const groupResp = await api.getGroupList();
dispatch(setGroups(groupResp.data.response));
await fetchGroups(dispatch);
var re = new RegExp(`${config.homepage}((/?)$|/filter)`, "gm");
const match = window.location.pathname.match(re);
@ -67,7 +65,7 @@ export async function getUserInfo(dispatch) {
if (match && match.length > 0)
{
const newFilter = getFilterByLocation(window.location);
await fetchPeopleAsync(dispatch, newFilter);
await fetchPeople(newFilter, dispatch);
}
return dispatch(setIsLoaded(true));

View File

@ -1,5 +1,7 @@
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 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) {
return dispatch => {
api.getGroup(groupId).then(res => {
@ -36,23 +31,16 @@ export function fetchGroup(groupId) {
export function createGroup(groupName, groupManager, members) {
return (dispatch, getState) => {
const { people } = getState();
const { groups, filter } = people;
let newGroup;
const { groups } = people;
return api
.createGroup(groupName, groupManager, members)
.then(res => {
checkResponseError(res);
newGroup = res.data.response;
//dispatch(setGroup(newGroup));
return dispatch(setGroups([...groups, newGroup]));
})
.then(() => {
return fetchPeopleByFilter(dispatch, filter);
})
.then(() => {
history.goBack();
const newGroup = res.data.response;
dispatch(resetGroup());
dispatch(setGroups([...groups, newGroup]));
return Promise.resolve(newGroup);
});
};
@ -61,28 +49,19 @@ export function createGroup(groupName, groupManager, members) {
export function updateGroup(id, groupName, groupManager, members) {
return (dispatch, getState) => {
const { people } = getState();
const { groups, filter } = people;
let newGroup;
const { groups } = people;
return api
.updateGroup(id, groupName, groupManager, members)
.then(res => {
checkResponseError(res);
newGroup = res.data.response;
//dispatch(setGroup(newGroup));
history.goBack();
const newGroup = res.data.response;
dispatch(resetGroup());
const newGroups = groups.map(g =>
g.id === newGroup.id ? newGroup : g
);
return dispatch(setGroups(newGroups));
})
.then(() => {
return fetchPeopleByFilter(dispatch, filter);
})
.then(() => {
dispatch(setGroups(newGroups));
return Promise.resolve(newGroup);
});
};
@ -101,7 +80,7 @@ export function deleteGroup(id) {
})
.then(() => {
const newFilter = filter.clone(true);
return fetchPeopleByFilter(dispatch, newFilter);
return fetchPeople(newFilter, dispatch);
});
};
}

View File

@ -14,6 +14,7 @@ import {
PAGE_COUNT,
EmployeeStatus
} from "../../helpers/constants";
import { checkResponseError } from "../../helpers/utils";
export const SET_GROUPS = "SET_GROUPS";
export const SET_USERS = "SET_USERS";
@ -69,7 +70,7 @@ export function selectGroup(groupId) {
let newFilter = filter.clone();
newFilter.group = groupId;
return fetchPeopleByFilter(dispatch, newFilter);
return fetchPeople(newFilter, dispatch);
};
}
@ -147,14 +148,38 @@ export function fetchSelectorUsers() {
};
}
export function fetchPeople(filter) {
return dispatch => {
return fetchPeopleByFilter(dispatch, filter);
export function fetchGroups(dispatchFunc = null) {
return api.getGroupList()
.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) {
let filterData = (filter && filter.clone()) || Filter.getDefault();
function fetchPeopleByFilter(dispatch, filter) {
let filterData = (filter && filter.clone());
if(!filterData) {
filterData = Filter.getDefault();
filterData.employeeStatus = EmployeeStatus.Active;
}
return api.getUserList(filterData).then(res => {
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());
if(!filterData) {
@ -185,7 +210,7 @@ export async function fetchPeopleAsync(dispatch, filter = null) {
groupId: filterData.group
});
dispatch(setUsers(usersResp.data.response));
}
}*/
export function updateUserStatus(status, userIds) {
return dispatch => {
@ -224,6 +249,6 @@ export function resetFilter() {
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 { isMe } from '../auth/selectors';
import { getUserByUserName } from '../people/selectors';
import { fetchPeopleByFilter } from "../people/actions";
import { fetchPeople } from "../people/actions";
import { setCurrentUser } from "../auth/actions";
import { checkResponseError } from "../../helpers/utils";
export const SET_PROFILE = 'SET_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) {
const comment = profile.notes;
@ -67,7 +63,7 @@ export function createProfile(profile) {
result = res.data.response;
return dispatch(setProfile(result));
}).then(() => {
return fetchPeopleByFilter(dispatch, filter);
return fetchPeople(filter, dispatch);
}).then(() => {
return Promise.resolve(result);
});
@ -86,7 +82,7 @@ export function updateProfile(profile) {
result = res.data.response;
return Promise.resolve(dispatch(setProfile(result)));
}).then(() => {
return fetchPeopleByFilter(dispatch, filter);
return fetchPeople(filter, dispatch);
}).then(() => {
return Promise.resolve(result);
});

View File

@ -575,6 +575,50 @@ namespace ASC.Employee.Core.Controllers
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")]
public EmployeeWraperFull UpdateMemberContacts(string userid, UpdateMemberModel memberModel)
{

View File

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

View File

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

View File

@ -447,7 +447,7 @@ namespace ASC.Web.Studio.Core.Notify
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;