/* * * (c) Copyright Ascensio System Limited 2010-2018 * * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. * * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html * * You can contact Ascensio System SIA by email at sales@onlyoffice.com * * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. * * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains * relevant author attributions when distributing the software. If the display of the logo in its graphic * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" * in every copy of the program you distribute. * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. * */ using System; using System.Collections.Generic; using System.Linq; using ASC.Core.Tenants; using ASC.Core.Users; using ASC.Core.Caching; namespace ASC.Core { public class UserManager { private readonly IUserService userService; private readonly IDictionary systemUsers; public UserManager(IUserService service) { userService = service; systemUsers = Configuration.Constants.SystemAccounts.ToDictionary(a => a.ID, a => new UserInfo { ID = a.ID, LastName = a.Name }); systemUsers[Constants.LostUser.ID] = Constants.LostUser; systemUsers[Constants.OutsideUser.ID] = Constants.OutsideUser; systemUsers[Constants.NamingPoster.ID] = Constants.NamingPoster; } public void ClearCache() { if (userService is ICachedService) { ((ICachedService)userService).InvalidateCache(); } } #region Users public UserInfo[] GetUsers(Tenant tenant) { return GetUsers(tenant, EmployeeStatus.Default); } public UserInfo[] GetUsers(Tenant tenant, EmployeeStatus status) { return GetUsers(tenant, status, EmployeeType.All); } public UserInfo[] GetUsers(Tenant tenant, EmployeeStatus status, EmployeeType type) { var users = GetUsersInternal(tenant.TenantId).Where(u => (u.Status & status) == u.Status); switch (type) { case EmployeeType.User: users = users.Where(u => !u.IsVisitor(tenant)); break; case EmployeeType.Visitor: users = users.Where(u => u.IsVisitor(tenant)); break; } return users.ToArray(); } public IEnumerable GetUsers(int tenantId, bool isAdmin, EmployeeStatus? employeeStatus, List> includeGroups, List excludeGroups, EmployeeActivationStatus? activationStatus, string text, string sortBy, bool sortOrderAsc, long limit, long offset, out int total) { return userService.GetUsers(tenantId, isAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, text, sortBy, sortOrderAsc, limit, offset, out total).Values; } public DateTime GetMaxUsersLastModified(int tenantId) { return userService.GetUsers(tenantId, default) .Values .Select(g => g.LastModified) .DefaultIfEmpty() .Max(); } public string[] GetUserNames(Tenant tenant, EmployeeStatus status) { return GetUsers(tenant, status) .Select(u => u.UserName) .Where(s => !string.IsNullOrEmpty(s)) .ToArray(); } public UserInfo GetUserByUserName(int tenantId, string username) { return GetUsersInternal(tenantId) .FirstOrDefault(u => string.Compare(u.UserName, username, StringComparison.CurrentCultureIgnoreCase) == 0) ?? Constants.LostUser; } public UserInfo GetUserBySid(int tenantId, string sid) { return GetUsersInternal(tenantId) .FirstOrDefault(u => u.Sid != null && string.Compare(u.Sid, sid, StringComparison.CurrentCultureIgnoreCase) == 0) ?? Constants.LostUser; } public UserInfo GetSsoUserByNameId(int tenantId, string nameId) { return GetUsersInternal(tenantId) .FirstOrDefault(u => !string.IsNullOrEmpty(u.SsoNameId) && string.Compare(u.SsoNameId, nameId, StringComparison.CurrentCultureIgnoreCase) == 0) ?? Constants.LostUser; } public bool IsUserNameExists(Tenant tenant, string username) { return GetUserNames(tenant, EmployeeStatus.All) .Contains(username, StringComparer.CurrentCultureIgnoreCase); } public UserInfo GetUsers(Guid id) { return GetUsers(id, CoreContext.TenantManager.GetCurrentTenant().TenantId); } public UserInfo GetUsers(Guid id, int tenantId) { if (IsSystemUser(id)) return systemUsers[id]; var u = userService.GetUser(tenantId, id); return u != null && !u.Removed ? u : Constants.LostUser; } public UserInfo GetUsers(int tenant, string login, string passwordHash) { var u = userService.GetUser(tenant, login, passwordHash); return u != null && !u.Removed ? u : Constants.LostUser; } public bool UserExists(Guid id, int tenantId) { return !GetUsers(id, tenantId).Equals(Constants.LostUser); } public bool IsSystemUser(Guid id) { return systemUsers.ContainsKey(id); } public UserInfo GetUserByEmail(int tenantId, string email) { if (string.IsNullOrEmpty(email)) return Constants.LostUser; return GetUsersInternal(tenantId) .FirstOrDefault(u => string.Compare(u.Email, email, StringComparison.CurrentCultureIgnoreCase) == 0) ?? Constants.LostUser; } public UserInfo[] Search(Tenant tenant, string text, EmployeeStatus status) { return Search(tenant, text, status, Guid.Empty); } public UserInfo[] Search(Tenant tenant, string text, EmployeeStatus status, Guid groupId) { if (text == null || text.Trim() == string.Empty) return new UserInfo[0]; var words = text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (words.Length == 0) return new UserInfo[0]; var users = groupId == Guid.Empty ? GetUsers(tenant, status) : GetUsersByGroup(tenant, groupId).Where(u => (u.Status & status) == status); var findUsers = new List(); foreach (var user in users) { var properties = new string[] { user.LastName ?? string.Empty, user.FirstName ?? string.Empty, user.Title ?? string.Empty, user.Location ?? string.Empty, user.Email ?? string.Empty, }; if (IsPropertiesContainsWords(properties, words)) { findUsers.Add(user); } } return findUsers.ToArray(); } public UserInfo SaveUserInfo(Tenant tenant, UserInfo u, bool isVisitor = false) { if (IsSystemUser(u.ID)) return systemUsers[u.ID]; if (u.ID == Guid.Empty) SecurityContext.DemandPermissions(tenant, Constants.Action_AddRemoveUser); else SecurityContext.DemandPermissions(tenant, new UserSecurityProvider(u.ID), Constants.Action_EditUser); if (Constants.MaxEveryoneCount <= GetUsersByGroup(tenant, Constants.GroupEveryone.ID).Length) { throw new TenantQuotaException("Maximum number of users exceeded"); } if (u.Status == EmployeeStatus.Active) { var q = CoreContext.TenantManager.GetTenantQuota(tenant.TenantId); if (q.ActiveUsers < GetUsersByGroup(tenant, Constants.GroupUser.ID).Length) { throw new TenantQuotaException(string.Format("Exceeds the maximum active users ({0})", q.ActiveUsers)); } } var newUser = userService.SaveUser(tenant.TenantId, u); return newUser; } public void DeleteUser(Tenant tenant, Guid id) { if (IsSystemUser(id)) return; SecurityContext.DemandPermissions(tenant, Constants.Action_AddRemoveUser); if (id == tenant.OwnerId) { throw new InvalidOperationException("Can not remove tenant owner."); } userService.RemoveUser(tenant.TenantId, id); } public void SaveUserPhoto(Tenant tenant, Guid id, byte[] photo) { if (IsSystemUser(id)) return; SecurityContext.DemandPermissions(tenant, new UserSecurityProvider(id), Constants.Action_EditUser); userService.SetUserPhoto(tenant.TenantId, id, photo); } public byte[] GetUserPhoto(int tenantId, Guid id) { if (IsSystemUser(id)) return null; return userService.GetUserPhoto(tenantId, id); } public IEnumerable GetUserGroupsId(int tenantId, Guid id) { return GetUsers(id, tenantId).GetUserGroupsId(tenantId); } public GroupInfo[] GetUserGroups(Tenant tenant, Guid id) { return GetUsers(id, tenant.TenantId).GetGroups(tenant, IncludeType.Distinct, Guid.Empty); } public GroupInfo[] GetUserGroups(Tenant tenant, Guid id, Guid categoryID) { return GetUsers(id, tenant.TenantId).GetGroups(tenant, IncludeType.Distinct, categoryID); } public GroupInfo[] GetUserGroups(Tenant tenant, Guid userID, IncludeType includeType) { return GetUsers(userID, tenant.TenantId).GetGroups(tenant, includeType, null); } internal GroupInfo[] GetUserGroups(Tenant tenant, Guid userID, IncludeType includeType, Guid? categoryId) { var result = new List(); var distinctUserGroups = new List(); var refs = GetRefsInternal(tenant.TenantId); IEnumerable userRefs = null; var store = refs as UserGroupRefStore; if (store != null) { userRefs = store.GetRefsByUser(userID); } var userRefsContainsNotRemoved = userRefs != null ? userRefs.Where(r => !r.Removed && r.RefType == UserGroupRefType.Contains).ToList() : null; foreach (var g in GetGroupsInternal(tenant.TenantId).Where(g => !categoryId.HasValue || g.CategoryID == categoryId)) { if (((g.CategoryID == Constants.SysGroupCategoryId || userRefs == null) && IsUserInGroupInternal(tenant, userID, g.ID, refs)) || (userRefsContainsNotRemoved != null && userRefsContainsNotRemoved.Any(r => r.GroupId == g.ID))) { distinctUserGroups.Add(g); } } if (IncludeType.Distinct == (includeType & IncludeType.Distinct)) { result.AddRange(distinctUserGroups); } result.Sort((group1, group2) => String.Compare(group1.Name, group2.Name, StringComparison.Ordinal)); return result.ToArray(); } internal IEnumerable GetUserGroupsGuids(int tenantId, Guid userID) { var result = new List(); var refs = GetRefsInternal(tenantId); var store = refs as UserGroupRefStore; if (store != null) { var userRefs = store.GetRefsByUser(userID); if (userRefs != null) { var toAdd = userRefs.Where(r => !r.Removed && r.RefType == UserGroupRefType.Contains && !Constants.BuildinGroups.Any(g => g.ID.Equals(r.GroupId))) .Select(r => r.GroupId); result.AddRange(toAdd); } } return result; } public bool IsUserInGroup(Tenant tenant, Guid userId, Guid groupId) { return IsUserInGroupInternal(tenant, userId, groupId, GetRefsInternal(tenant.TenantId)); } public UserInfo[] GetUsersByGroup(Tenant tenant, Guid groupId, EmployeeStatus employeeStatus = EmployeeStatus.Default) { var refs = GetRefsInternal(tenant.TenantId); return GetUsers(tenant, employeeStatus).Where(u => IsUserInGroupInternal(tenant, u.ID, groupId, refs)).ToArray(); } public void AddUserIntoGroup(Tenant tenant, Guid userId, Guid groupId) { if (Constants.LostUser.ID == userId || Constants.LostGroupInfo.ID == groupId) { return; } SecurityContext.DemandPermissions(tenant, Constants.Action_EditGroups); userService.SaveUserGroupRef(tenant.TenantId, new UserGroupRef(userId, groupId, UserGroupRefType.Contains)); GetUsers(userId).ResetGroupCache(); } public void RemoveUserFromGroup(Tenant tenant, Guid userId, Guid groupId) { if (Constants.LostUser.ID == userId || Constants.LostGroupInfo.ID == groupId) return; SecurityContext.DemandPermissions(tenant, Constants.Action_EditGroups); userService.RemoveUserGroupRef(tenant.TenantId, userId, groupId, UserGroupRefType.Contains); GetUsers(userId).ResetGroupCache(); } #endregion Users #region Company public GroupInfo[] GetDepartments(int tenantId) { return GetGroups(tenantId); } public Guid GetDepartmentManager(int tenantId, Guid deparmentID) { return GetRefsInternal(tenantId) .Values .Where(r => r.RefType == UserGroupRefType.Manager && r.GroupId == deparmentID && !r.Removed) .Select(r => r.UserId) .SingleOrDefault(); } public void SetDepartmentManager(int tenantId, Guid deparmentID, Guid userID) { var managerId = GetDepartmentManager(tenantId, deparmentID); if (managerId != Guid.Empty) { userService.RemoveUserGroupRef( tenantId, managerId, deparmentID, UserGroupRefType.Manager); } if (userID != Guid.Empty) { userService.SaveUserGroupRef( tenantId, new UserGroupRef(userID, deparmentID, UserGroupRefType.Manager)); } } public UserInfo GetCompanyCEO(int tenantId) { var id = GetDepartmentManager(tenantId, Guid.Empty); return id != Guid.Empty ? GetUsers(id) : null; } public void SetCompanyCEO(int tenantId, Guid userId) { SetDepartmentManager(tenantId, Guid.Empty, userId); } #endregion Company #region Groups public GroupInfo[] GetGroups(int tenantId) { return GetGroups(tenantId, Guid.Empty); } public GroupInfo[] GetGroups(int tenantId, Guid categoryID) { return GetGroupsInternal(tenantId) .Where(g => g.CategoryID == categoryID) .ToArray(); } public GroupInfo GetGroupInfo(int tenantId, Guid groupID) { return GetGroupsInternal(tenantId) .SingleOrDefault(g => g.ID == groupID) ?? Constants.LostGroupInfo; } public GroupInfo GetGroupInfoBySid(int tenantId, string sid) { return GetGroupsInternal(tenantId) .SingleOrDefault(g => g.Sid == sid) ?? Constants.LostGroupInfo; } public DateTime GetMaxGroupsLastModified(int tenantId) { return userService.GetGroups(tenantId, default) .Values .Select(g => g.LastModified) .DefaultIfEmpty() .Max(); } public GroupInfo SaveGroupInfo(Tenant tenant, GroupInfo g) { if (Constants.LostGroupInfo.Equals(g)) return Constants.LostGroupInfo; if (Constants.BuildinGroups.Any(b => b.ID == g.ID)) return Constants.BuildinGroups.Single(b => b.ID == g.ID); SecurityContext.DemandPermissions(tenant, Constants.Action_EditGroups); var newGroup = userService.SaveGroup(tenant.TenantId, ToGroup(g)); return new GroupInfo(newGroup.CategoryId) { ID = newGroup.Id, Name = newGroup.Name, Sid = newGroup.Sid }; } public void DeleteGroup(Tenant tenant, Guid id) { if (Constants.LostGroupInfo.Equals(id)) return; if (Constants.BuildinGroups.Any(b => b.ID == id)) return; SecurityContext.DemandPermissions(tenant, Constants.Action_EditGroups); userService.RemoveGroup(tenant.TenantId, id); } #endregion Groups private bool IsPropertiesContainsWords(IEnumerable properties, IEnumerable words) { foreach (var w in words) { var find = false; foreach (var p in properties) { find = (2 <= w.Length) && (0 <= p.IndexOf(w, StringComparison.CurrentCultureIgnoreCase)); if (find) break; } if (!find) return false; } return true; } private IEnumerable GetUsersInternal(int tenantId) { return userService.GetUsers(tenantId, default) .Values .Where(u => !u.Removed); } private IEnumerable GetGroupsInternal(int tenantId) { return userService.GetGroups(tenantId, default) .Values .Where(g => !g.Removed) .Select(g => new GroupInfo(g.CategoryId) { ID = g.Id, Name = g.Name, Sid = g.Sid }) .Concat(Constants.BuildinGroups); } private IDictionary GetRefsInternal(int tenantId) { return userService.GetUserGroupRefs(tenantId, default); } private bool IsUserInGroupInternal(Tenant tenant, Guid userId, Guid groupId, IDictionary refs) { if (groupId == Constants.GroupEveryone.ID) { return true; } if (groupId == Constants.GroupAdmin.ID && (tenant.OwnerId == userId || userId == Configuration.Constants.CoreSystem.ID || userId == Constants.NamingPoster.ID)) { return true; } if (groupId == Constants.GroupVisitor.ID && userId == Constants.OutsideUser.ID) { return true; } UserGroupRef r; if (groupId == Constants.GroupUser.ID || groupId == Constants.GroupVisitor.ID) { var visitor = refs.TryGetValue(UserGroupRef.CreateKey(tenant.TenantId, userId, Constants.GroupVisitor.ID, UserGroupRefType.Contains), out r) && !r.Removed; if (groupId == Constants.GroupVisitor.ID) { return visitor; } if (groupId == Constants.GroupUser.ID) { return !visitor; } } return refs.TryGetValue(UserGroupRef.CreateKey(tenant.TenantId, userId, groupId, UserGroupRefType.Contains), out r) && !r.Removed; } private Group ToGroup(GroupInfo g) { if (g == null) return null; return new Group { Id = g.ID, Name = g.Name, ParentId = g.Parent != null ? g.Parent.ID : Guid.Empty, CategoryId = g.CategoryID, Sid = g.Sid }; } } }