Merge pull request #1099 from ONLYOFFICE/bugfix/room-admin-permissions

Bugfix/room admin permissions
This commit is contained in:
Pavel Bannov 2022-12-06 15:22:47 +03:00 committed by GitHub
commit 7d0cc5a396
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 571 additions and 256 deletions

View File

@ -38,8 +38,9 @@ public sealed class Role : IRole
public Guid ID { get; internal set; } public Guid ID { get; internal set; }
public string Name { get; internal set; } public string Name { get; internal set; }
public string AuthenticationType => "ASC"; public string AuthenticationType => "ASC";
public bool IsAuthenticated => false; public bool IsAuthenticated => false;
public string Key => ID.ToString();
public Role(Guid id, string name) public Role(Guid id, string name)
{ {

View File

@ -26,4 +26,4 @@
namespace ASC.Common.Security.Authorizing; namespace ASC.Common.Security.Authorizing;
public interface IRole : ISubject { } public interface IRole : ISubject, IRuleData { }

View File

@ -0,0 +1,32 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL 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 details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Common.Security;
public interface IRuleData
{
public string Key { get; }
}

View File

@ -74,6 +74,12 @@ public class UserServiceCache
{ {
var key = GetUserCacheKey(userInfo.Tenant); var key = GetUserCacheKey(userInfo.Tenant);
Cache.Remove(key); Cache.Remove(key);
if (Guid.TryParse(userInfo.Id, out var userId))
{
var userKey = GetUserCacheKey(userInfo.Tenant, userId);
Cache.Remove(userKey);
}
} }
} }
private void InvalidateCache(GroupCacheItem groupCacheItem) private void InvalidateCache(GroupCacheItem groupCacheItem)

View File

@ -64,6 +64,7 @@ public class UserManager
private readonly TenantQuotaFeatureCheckerCount<CountRoomAdminFeature> _countRoomAdminChecker; private readonly TenantQuotaFeatureCheckerCount<CountRoomAdminFeature> _countRoomAdminChecker;
private readonly TenantQuotaFeatureCheckerCount<CountUserFeature> _activeUsersFeatureChecker; private readonly TenantQuotaFeatureCheckerCount<CountUserFeature> _activeUsersFeatureChecker;
private readonly Constants _constants; private readonly Constants _constants;
private readonly UserFormatter _userFormatter;
private Tenant _tenant; private Tenant _tenant;
private Tenant Tenant => _tenant ??= _tenantManager.GetCurrentTenant(); private Tenant Tenant => _tenant ??= _tenantManager.GetCurrentTenant();
@ -86,7 +87,8 @@ public class UserManager
ILogger<UserManager> log, ILogger<UserManager> log,
ICache cache, ICache cache,
TenantQuotaFeatureCheckerCount<CountRoomAdminFeature> countRoomAdrminChecker, TenantQuotaFeatureCheckerCount<CountRoomAdminFeature> countRoomAdrminChecker,
TenantQuotaFeatureCheckerCount<CountUserFeature> activeUsersFeatureChecker TenantQuotaFeatureCheckerCount<CountUserFeature> activeUsersFeatureChecker,
UserFormatter userFormatter
) )
{ {
_userService = service; _userService = service;
@ -103,6 +105,7 @@ public class UserManager
_countRoomAdminChecker = countRoomAdrminChecker; _countRoomAdminChecker = countRoomAdrminChecker;
_activeUsersFeatureChecker = activeUsersFeatureChecker; _activeUsersFeatureChecker = activeUsersFeatureChecker;
_constants = _userManagerConstants.Constants; _constants = _userManagerConstants.Constants;
_userFormatter = userFormatter;
} }
public UserManager( public UserManager(
@ -119,8 +122,9 @@ public class UserManager
ICache cache, ICache cache,
TenantQuotaFeatureCheckerCount<CountRoomAdminFeature> tenantQuotaFeatureChecker, TenantQuotaFeatureCheckerCount<CountRoomAdminFeature> tenantQuotaFeatureChecker,
TenantQuotaFeatureCheckerCount<CountUserFeature> activeUsersFeatureChecker, TenantQuotaFeatureCheckerCount<CountUserFeature> activeUsersFeatureChecker,
IHttpContextAccessor httpContextAccessor) IHttpContextAccessor httpContextAccessor,
: this(service, tenantManager, permissionContext, userManagerConstants, coreBaseSettings, coreSettings, instanceCrypto, radicaleClient, cardDavAddressbook, log, cache, tenantQuotaFeatureChecker, activeUsersFeatureChecker) UserFormatter userFormatter)
: this(service, tenantManager, permissionContext, userManagerConstants, coreBaseSettings, coreSettings, instanceCrypto, radicaleClient, cardDavAddressbook, log, cache, tenantQuotaFeatureChecker, activeUsersFeatureChecker, userFormatter)
{ {
_accessor = httpContextAccessor; _accessor = httpContextAccessor;
} }
@ -353,7 +357,8 @@ public class UserManager
return SystemUsers[u.Id]; return SystemUsers[u.Id];
} }
_permissionContext.DemandPermissions(Constants.Action_AddRemoveUser); _permissionContext.DemandPermissions(new UserSecurityProvider(u.Id, isVisitor ? EmployeeType.User : EmployeeType.RoomAdmin),
Constants.Action_AddRemoveUser);
if (!_coreBaseSettings.Personal) if (!_coreBaseSettings.Personal)
{ {
@ -631,12 +636,15 @@ public class UserManager
{ {
return; return;
} }
_permissionContext.DemandPermissions(Constants.Action_EditGroups);
var user = GetUsers(userId);
_permissionContext.DemandPermissions(new UserGroupObject(new UserAccount(user, _tenantManager.GetCurrentTenant().Id, _userFormatter), groupId),
Constants.Action_EditGroups);
_userService.SaveUserGroupRef(Tenant.Id, new UserGroupRef(userId, groupId, UserGroupRefType.Contains)); _userService.SaveUserGroupRef(Tenant.Id, new UserGroupRef(userId, groupId, UserGroupRefType.Contains));
ResetGroupCache(userId); ResetGroupCache(userId);
var user = GetUsers(userId);
if (groupId == Constants.GroupUser.ID) if (groupId == Constants.GroupUser.ID)
{ {
var tenant = _tenantManager.GetCurrentTenant(); var tenant = _tenantManager.GetCurrentTenant();
@ -658,7 +666,10 @@ public class UserManager
return; return;
} }
_permissionContext.DemandPermissions(Constants.Action_EditGroups); var user = GetUsers(userId);
_permissionContext.DemandPermissions(new UserGroupObject(new UserAccount(user, _tenantManager.GetCurrentTenant().Id, _userFormatter), groupId),
Constants.Action_EditGroups);
_userService.RemoveUserGroupRef(Tenant.Id, userId, groupId, UserGroupRefType.Contains); _userService.RemoveUserGroupRef(Tenant.Id, userId, groupId, UserGroupRefType.Contains);

View File

@ -71,4 +71,5 @@ public class GroupInfo : IRole, IRecipientsGroup
string IRecipient.Name => Name; string IRecipient.Name => Name;
public string AuthenticationType => "ASC"; public string AuthenticationType => "ASC";
public bool IsAuthenticated => false; public bool IsAuthenticated => false;
public string Key => ID.ToString();
} }

View File

@ -57,7 +57,8 @@ public class AzManager
internal AzManagerAcl GetAzManagerAcl(ISubject subject, IAction action, ISecurityObjectId objectId, ISecurityObjectProvider securityObjProvider) internal AzManagerAcl GetAzManagerAcl(ISubject subject, IAction action, ISecurityObjectId objectId, ISecurityObjectProvider securityObjProvider)
{ {
if (action.AdministratorAlwaysAllow && (Constants.DocSpaceAdmin.ID == subject.ID || _roleProvider.IsSubjectInRole(subject, Constants.DocSpaceAdmin))) if (action.AdministratorAlwaysAllow && (Constants.DocSpaceAdmin.ID == subject.ID || _roleProvider.IsSubjectInRole(subject, Constants.DocSpaceAdmin)
|| (objectId is SecurityObject obj && obj.IsMatchDefaultRules(subject, action, _roleProvider))))
{ {
return AzManagerAcl.Allow; return AzManagerAcl.Allow;
} }

View File

@ -0,0 +1,57 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL 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 details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Core.Common.Security;
public class Rule
{
public Guid ActionId { get; }
public IRuleData Data { get; }
private readonly string _key;
public Rule(Guid actionId, IRuleData data)
{
ActionId = actionId;
Data = data;
_key = actionId.ToString() + data?.Key;
}
public Rule(Guid actionId)
{
ActionId = actionId;
_key = actionId.ToString();
}
public override int GetHashCode()
{
return _key.GetHashCode();
}
public override bool Equals(object obj)
{
return obj is Rule action && action._key == _key;
}
}

View File

@ -0,0 +1,56 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL 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 details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using UserConstants = ASC.Core.Users.Constants;
using Constants = ASC.Common.Security.Authorizing.Constants;
namespace ASC.Core.Common.Security;
public static class Security
{
public static readonly Dictionary<Guid, Dictionary<Guid, HashSet<Rule>>> Rules = new Dictionary<Guid, Dictionary<Guid, HashSet<Rule>>>
{
{
Constants.RoomAdmin.ID, new Dictionary<Guid, HashSet<Rule>>()
{
{
Constants.User.ID, new HashSet<Rule>()
{
new Rule(UserConstants.Action_EditGroups.ID, Constants.User),
new Rule(UserConstants.Action_AddRemoveUser.ID),
}
},
{
Constants.RoomAdmin.ID, new HashSet<Rule>()
{
new Rule(UserConstants.Action_EditGroups.ID, Constants.User),
new Rule(UserConstants.Action_AddRemoveUser.ID),
}
}
}
}
};
}

View File

@ -0,0 +1,75 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL 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 details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Core.Common.Security;
public abstract class SecurityObject : ISecurityObject
{
public object SecurityId { get; protected set; }
public Type ObjectType { get; protected set; }
public string FullId { get; protected set; }
public bool InheritSupported { get; protected set; }
public bool ObjectRolesSupported { get; protected set; }
public virtual IEnumerable<IRole> GetObjectRoles(ISubject account, ISecurityObjectId objectId, SecurityCallContext callContext)
{
return null;
}
public virtual ISecurityObjectId InheritFrom(ISecurityObjectId objectId)
{
return null;
}
public bool IsMatchDefaultRules(ISubject subject, IAction action, IRoleProvider roleProvider)
{
var subjectRoles = roleProvider.GetRoles(subject);
var targetRoles = GetTargetRoles(roleProvider);
foreach (var subjectRole in subjectRoles)
{
if (Security.Rules.TryGetValue(subjectRole.ID, out var value))
{
foreach (var targetRole in targetRoles)
{
if (value.TryGetValue(targetRole.ID, out var value1))
{
var act = new Rule(action.ID, GetRuleData());
if (value1.Contains(act))
{
return true;
}
}
}
}
}
return false;
}
protected abstract IEnumerable<IRole> GetTargetRoles(IRoleProvider roleProvider);
protected abstract IRuleData GetRuleData();
}

View File

@ -0,0 +1,52 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL 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 details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Core.Common.Security;
public class UserGroupObject : SecurityObject
{
private ISubject User { get; set; }
private Guid GroupId { get; set; }
public UserGroupObject(ISubject user, Guid groupId)
{
SecurityId = user.ID;
User = user;
GroupId = groupId;
ObjectType = typeof(UserGroupObject);
FullId = $"{ObjectType.FullName}|{User.ID}|{GroupId}";
}
protected override IEnumerable<IRole> GetTargetRoles(IRoleProvider roleProvider)
{
return roleProvider.GetRoles(User);
}
protected override IRuleData GetRuleData()
{
return new Role(GroupId, "ruleData");
}
}

View File

@ -24,37 +24,51 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using AuthConstants = ASC.Common.Security.Authorizing.Constants;
namespace ASC.Core.Users; namespace ASC.Core.Users;
public class UserSecurityProvider : ISecurityObject public class UserSecurityProvider : SecurityObject
{ {
public Type ObjectType { get; private set; } private readonly EmployeeType _employeeType;
public object SecurityId { get; private set; }
public string FullId => AzObjectIdHelper.GetFullObjectId(this);
public UserSecurityProvider(Guid userId) public UserSecurityProvider(Guid userId)
{ {
SecurityId = userId; SecurityId = userId;
ObjectType = typeof(UserInfo); ObjectType = typeof(UserInfo);
FullId = AzObjectIdHelper.GetFullObjectId(this);
ObjectRolesSupported = true;
} }
public bool ObjectRolesSupported => true; public UserSecurityProvider(Guid userId, EmployeeType employeeType) : this(userId)
{
_employeeType = employeeType;
}
public IEnumerable<IRole> GetObjectRoles(ISubject account, ISecurityObjectId objectId, SecurityCallContext callContext) public override IEnumerable<IRole> GetObjectRoles(ISubject account, ISecurityObjectId objectId, SecurityCallContext callContext)
{ {
var roles = new List<IRole>(); var roles = new List<IRole>();
if (account.ID.Equals(objectId.SecurityId)) if (account.ID.Equals(objectId.SecurityId))
{ {
roles.Add(ASC.Common.Security.Authorizing.Constants.Self); roles.Add(AuthConstants.Self);
} }
return roles; return roles;
} }
public bool InheritSupported => false; protected override IEnumerable<IRole> GetTargetRoles(IRoleProvider roleProvider)
public ISecurityObjectId InheritFrom(ISecurityObjectId objectId)
{ {
throw new NotImplementedException(); return _employeeType switch
{
EmployeeType.DocSpaceAdmin => new[] { AuthConstants.DocSpaceAdmin },
EmployeeType.RoomAdmin => new[] { AuthConstants.RoomAdmin },
EmployeeType.User => new[] { AuthConstants.User },
_ => throw new NotImplementedException(),
};
}
protected override IRuleData GetRuleData()
{
return null;
} }
} }

View File

@ -96,6 +96,11 @@ public static class UserExtensions
return !string.IsNullOrEmpty(ui.SsoNameId); return !string.IsNullOrEmpty(ui.SsoNameId);
} }
public static EmployeeType GetUserType(this UserManager userManager, Guid id)
{
return userManager.IsDocSpaceAdmin(id) ? EmployeeType.DocSpaceAdmin : userManager.IsUser(id) ? EmployeeType.User : EmployeeType.RoomAdmin;
}
private const string _extMobPhone = "extmobphone"; private const string _extMobPhone = "extmobphone";
private const string _mobPhone = "mobphone"; private const string _mobPhone = "mobphone";
private const string _extMail = "extmail"; private const string _extMail = "extmail";

View File

@ -72,9 +72,12 @@ public abstract class PeopleControllerBase : ApiControllerBase
return user; return user;
} }
protected void UpdateContacts(IEnumerable<Contact> contacts, UserInfo user) protected void UpdateContacts(IEnumerable<Contact> contacts, UserInfo user, bool checkPermissions = true)
{ {
_permissionContext.DemandPermissions(new UserSecurityProvider(user.Id), Constants.Action_EditUser); if (checkPermissions)
{
_permissionContext.DemandPermissions(new UserSecurityProvider(user.Id), Constants.Action_EditUser);
}
if (contacts == null) if (contacts == null)
{ {

View File

@ -22,18 +22,18 @@
// //
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing // All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using ASC.Common.Log; using ASC.Common.Log;
namespace ASC.People.Api; namespace ASC.People.Api;
public class UserController : PeopleControllerBase public class UserController : PeopleControllerBase
{ {
private Tenant Tenant => _apiContext.Tenant; private Tenant Tenant => _apiContext.Tenant;
private readonly ICache _cache; private readonly ICache _cache;
private readonly TenantManager _tenantManager; private readonly TenantManager _tenantManager;
private readonly CookiesManager _cookiesManager; private readonly CookiesManager _cookiesManager;
private readonly CoreBaseSettings _coreBaseSettings; private readonly CoreBaseSettings _coreBaseSettings;
private readonly CustomNamingPeople _customNamingPeople; private readonly CustomNamingPeople _customNamingPeople;
@ -56,19 +56,19 @@ public class UserController : PeopleControllerBase
private readonly StudioNotifyService _studioNotifyService; private readonly StudioNotifyService _studioNotifyService;
private readonly MessageService _messageService; private readonly MessageService _messageService;
private readonly AuthContext _authContext; private readonly AuthContext _authContext;
private readonly SetupInfo _setupInfo; private readonly SetupInfo _setupInfo;
private readonly SettingsManager _settingsManager; private readonly SettingsManager _settingsManager;
private readonly RoomLinkService _roomLinkService; private readonly RoomLinkService _roomLinkService;
private readonly FileSecurity _fileSecurity; private readonly FileSecurity _fileSecurity;
private readonly IQuotaService _quotaService; private readonly IQuotaService _quotaService;
private readonly CountRoomAdminChecker _countRoomAdminChecker; private readonly CountRoomAdminChecker _countRoomAdminChecker;
private readonly UsersQuotaSyncOperation _usersQuotaSyncOperation; private readonly UsersQuotaSyncOperation _usersQuotaSyncOperation;
private readonly CountUserChecker _countUserChecker; private readonly CountUserChecker _countUserChecker;
private readonly UsersInRoomChecker _usersInRoomChecker; private readonly UsersInRoomChecker _usersInRoomChecker;
public UserController( public UserController(
ICache cache, ICache cache,
TenantManager tenantManager, TenantManager tenantManager,
CookiesManager cookiesManager, CookiesManager cookiesManager,
CoreBaseSettings coreBaseSettings, CoreBaseSettings coreBaseSettings,
CustomNamingPeople customNamingPeople, CustomNamingPeople customNamingPeople,
@ -96,10 +96,10 @@ public class UserController : PeopleControllerBase
PermissionContext permissionContext, PermissionContext permissionContext,
ApiContext apiContext, ApiContext apiContext,
UserPhotoManager userPhotoManager, UserPhotoManager userPhotoManager,
IHttpClientFactory httpClientFactory, IHttpClientFactory httpClientFactory,
IHttpContextAccessor httpContextAccessor, IHttpContextAccessor httpContextAccessor,
SettingsManager settingsManager, SettingsManager settingsManager,
RoomLinkService roomLinkService, RoomLinkService roomLinkService,
FileSecurity fileSecurity, FileSecurity fileSecurity,
UsersQuotaSyncOperation usersQuotaSyncOperation, UsersQuotaSyncOperation usersQuotaSyncOperation,
CountRoomAdminChecker countRoomAdminChecker, CountRoomAdminChecker countRoomAdminChecker,
@ -108,8 +108,8 @@ public class UserController : PeopleControllerBase
IQuotaService quotaService) IQuotaService quotaService)
: base(userManager, permissionContext, apiContext, userPhotoManager, httpClientFactory, httpContextAccessor) : base(userManager, permissionContext, apiContext, userPhotoManager, httpClientFactory, httpContextAccessor)
{ {
_cache = cache; _cache = cache;
_tenantManager = tenantManager; _tenantManager = tenantManager;
_cookiesManager = cookiesManager; _cookiesManager = cookiesManager;
_coreBaseSettings = coreBaseSettings; _coreBaseSettings = coreBaseSettings;
_customNamingPeople = customNamingPeople; _customNamingPeople = customNamingPeople;
@ -132,14 +132,14 @@ public class UserController : PeopleControllerBase
_studioNotifyService = studioNotifyService; _studioNotifyService = studioNotifyService;
_messageService = messageService; _messageService = messageService;
_authContext = authContext; _authContext = authContext;
_setupInfo = setupInfo; _setupInfo = setupInfo;
_settingsManager = settingsManager; _settingsManager = settingsManager;
_roomLinkService = roomLinkService; _roomLinkService = roomLinkService;
_fileSecurity = fileSecurity; _fileSecurity = fileSecurity;
_countRoomAdminChecker = countRoomAdminChecker; _countRoomAdminChecker = countRoomAdminChecker;
_countUserChecker = activeUsersChecker; _countUserChecker = activeUsersChecker;
_usersInRoomChecker = usersInRoomChecker; _usersInRoomChecker = usersInRoomChecker;
_quotaService = quotaService; _quotaService = quotaService;
_usersQuotaSyncOperation = usersQuotaSyncOperation; _usersQuotaSyncOperation = usersQuotaSyncOperation;
} }
@ -157,7 +157,7 @@ public class UserController : PeopleControllerBase
if (string.IsNullOrEmpty(inDto.Password)) if (string.IsNullOrEmpty(inDto.Password))
{ {
inDto.Password = UserManagerWrapper.GeneratePassword(); inDto.Password = UserManagerWrapper.GeneratePassword();
} }
else else
{ {
@ -185,16 +185,16 @@ public class UserController : PeopleControllerBase
UpdateContacts(inDto.Contacts, user); UpdateContacts(inDto.Contacts, user);
_cache.Insert("REWRITE_URL" + _tenantManager.GetCurrentTenant().Id, HttpContext.Request.GetUrlRewriter().ToString(), TimeSpan.FromMinutes(5)); _cache.Insert("REWRITE_URL" + _tenantManager.GetCurrentTenant().Id, HttpContext.Request.GetUrlRewriter().ToString(), TimeSpan.FromMinutes(5));
user = await _userManagerWrapper.AddUser(user, inDto.PasswordHash, false, false, inDto.IsUser, false, true, true); user = await _userManagerWrapper.AddUser(user, inDto.PasswordHash, false, false, inDto.IsUser, false, true, true);
user.ActivationStatus = EmployeeActivationStatus.Activated; user.ActivationStatus = EmployeeActivationStatus.Activated;
await UpdateDepartments(inDto.Department, user); await UpdateDepartments(inDto.Department, user);
if (inDto.Files != _userPhotoManager.GetDefaultPhotoAbsoluteWebPath()) if (inDto.Files != _userPhotoManager.GetDefaultPhotoAbsoluteWebPath())
{ {
await UpdatePhotoUrl(inDto.Files, user); await UpdatePhotoUrl(inDto.Files, user);
} }
return await _employeeFullDtoHelper.GetFull(user); return await _employeeFullDtoHelper.GetFull(user);
@ -206,29 +206,28 @@ public class UserController : PeopleControllerBase
{ {
_apiContext.AuthByClaim(); _apiContext.AuthByClaim();
_permissionContext.DemandPermissions(Constants.Action_AddRemoveUser); var options = inDto.FromInviteLink ? await _roomLinkService.GetOptionsAsync(inDto.Key, inDto.Email, inDto.Type) : null;
if (options != null && !options.IsCorrect)
var options = inDto.FromInviteLink ? await _roomLinkService.GetOptionsAsync(inDto.Key, inDto.Email, inDto.Type) : null; {
throw new SecurityException(FilesCommonResource.ErrorMessage_InvintationLink);
if (options != null && !options.IsCorrect)
{
throw new SecurityException(FilesCommonResource.ErrorMessage_InvintationLink);
} }
_permissionContext.DemandPermissions(new UserSecurityProvider(Guid.Empty, inDto.Type) ,Constants.Action_AddRemoveUser);
inDto.Type = options != null ? options.EmployeeType : inDto.Type; inDto.Type = options != null ? options.EmployeeType : inDto.Type;
var user = new UserInfo(); var user = new UserInfo();
var byEmail = options?.LinkType == LinkType.InvintationByEmail; var byEmail = options?.LinkType == LinkType.InvintationByEmail;
if (byEmail) if (byEmail)
{ {
user = _userManager.GetUserByEmail(inDto.Email); user = _userManager.GetUserByEmail(inDto.Email);
if (user == Constants.LostUser || user.ActivationStatus != EmployeeActivationStatus.Pending) if (user == Constants.LostUser || user.ActivationStatus != EmployeeActivationStatus.Pending)
{ {
throw new SecurityException(FilesCommonResource.ErrorMessage_InvintationLink); throw new SecurityException(FilesCommonResource.ErrorMessage_InvintationLink);
} }
} }
inDto.PasswordHash = (inDto.PasswordHash ?? "").Trim(); inDto.PasswordHash = (inDto.PasswordHash ?? "").Trim();
@ -238,7 +237,7 @@ public class UserController : PeopleControllerBase
if (string.IsNullOrEmpty(inDto.Password)) if (string.IsNullOrEmpty(inDto.Password))
{ {
inDto.Password = UserManagerWrapper.GeneratePassword(); inDto.Password = UserManagerWrapper.GeneratePassword();
} }
else else
{ {
@ -249,8 +248,8 @@ public class UserController : PeopleControllerBase
//Validate email //Validate email
var address = new MailAddress(inDto.Email); var address = new MailAddress(inDto.Email);
user.Email = address.Address; user.Email = address.Address;
//Set common fields //Set common fields
user.CultureName = inDto.CultureName; user.CultureName = inDto.CultureName;
user.FirstName = inDto.Firstname; user.FirstName = inDto.Firstname;
user.LastName = inDto.Lastname; user.LastName = inDto.Lastname;
@ -264,57 +263,62 @@ public class UserController : PeopleControllerBase
user.BirthDate = inDto.Birthday != null && inDto.Birthday != DateTime.MinValue ? _tenantUtil.DateTimeFromUtc(inDto.Birthday) : null; user.BirthDate = inDto.Birthday != null && inDto.Birthday != DateTime.MinValue ? _tenantUtil.DateTimeFromUtc(inDto.Birthday) : null;
user.WorkFromDate = inDto.Worksfrom != null && inDto.Worksfrom != DateTime.MinValue ? _tenantUtil.DateTimeFromUtc(inDto.Worksfrom) : DateTime.UtcNow.Date; user.WorkFromDate = inDto.Worksfrom != null && inDto.Worksfrom != DateTime.MinValue ? _tenantUtil.DateTimeFromUtc(inDto.Worksfrom) : DateTime.UtcNow.Date;
UpdateContacts(inDto.Contacts, user); UpdateContacts(inDto.Contacts, user, !inDto.FromInviteLink);
_cache.Insert("REWRITE_URL" + _tenantManager.GetCurrentTenant().Id, HttpContext.Request.GetUrlRewriter().ToString(), TimeSpan.FromMinutes(5)); _cache.Insert("REWRITE_URL" + _tenantManager.GetCurrentTenant().Id, HttpContext.Request.GetUrlRewriter().ToString(), TimeSpan.FromMinutes(5));
user = await _userManagerWrapper.AddUser(user, inDto.PasswordHash, inDto.FromInviteLink, true, inDto.Type == EmployeeType.User, inDto.FromInviteLink, true, true, byEmail, inDto.Type == EmployeeType.DocSpaceAdmin); user = await _userManagerWrapper.AddUser(user, inDto.PasswordHash, inDto.FromInviteLink, true, inDto.Type == EmployeeType.User, inDto.FromInviteLink && options.IsCorrect, true, true, byEmail, inDto.Type == EmployeeType.DocSpaceAdmin);
await UpdateDepartments(inDto.Department, user); await UpdateDepartments(inDto.Department, user);
if (inDto.Files != _userPhotoManager.GetDefaultPhotoAbsoluteWebPath()) if (inDto.Files != _userPhotoManager.GetDefaultPhotoAbsoluteWebPath())
{ {
await UpdatePhotoUrl(inDto.Files, user); await UpdatePhotoUrl(inDto.Files, user);
} }
if (options != null && options.LinkType == LinkType.InvintationToRoom) if (options != null && options.LinkType == LinkType.InvintationToRoom)
{ {
var success = int.TryParse(options.RoomId, out var id); var success = int.TryParse(options.RoomId, out var id);
if (success) if (success)
{ {
await _usersInRoomChecker.CheckAppend(); await _usersInRoomChecker.CheckAppend();
await _fileSecurity.ShareAsync(id, FileEntryType.Folder, user.Id, options.Share); await _fileSecurity.ShareAsync(id, FileEntryType.Folder, user.Id, options.Share);
} }
else else
{ {
await _usersInRoomChecker.CheckAppend(); await _usersInRoomChecker.CheckAppend();
await _fileSecurity.ShareAsync(options.RoomId, FileEntryType.Folder, user.Id, options.Share); await _fileSecurity.ShareAsync(options.RoomId, FileEntryType.Folder, user.Id, options.Share);
} }
} }
var messageAction = inDto.IsUser ? MessageAction.GuestCreated : MessageAction.UserCreated; var messageAction = inDto.IsUser ? MessageAction.GuestCreated : MessageAction.UserCreated;
_messageService.Send(messageAction, _messageTarget.Create(user.Id), user.DisplayUserName(false, _displayUserSettingsHelper)); _messageService.Send(messageAction, _messageTarget.Create(user.Id), user.DisplayUserName(false, _displayUserSettingsHelper));
return await _employeeFullDtoHelper.GetFull(user); return await _employeeFullDtoHelper.GetFull(user);
} }
[HttpPost("invite")] [HttpPost("invite")]
public async IAsyncEnumerable<EmployeeDto> InviteUsersAsync(InviteUsersRequestDto inDto) public async IAsyncEnumerable<EmployeeDto> InviteUsersAsync(InviteUsersRequestDto inDto)
{ {
foreach (var invite in inDto.Invitations) foreach (var invite in inDto.Invitations)
{ {
var user = await _userManagerWrapper.AddInvitedUserAsync(invite.Email, invite.Type); if (!_permissionContext.CheckPermissions(new UserSecurityProvider(Guid.Empty, invite.Type), Constants.Action_AddRemoveUser))
var link = _roomLinkService.GetInvitationLink(user.Email, invite.Type, _authContext.CurrentAccount.ID); {
continue;
_studioNotifyService.SendDocSpaceInvite(user.Email, link); }
_logger.Debug(link);
} var user = await _userManagerWrapper.AddInvitedUserAsync(invite.Email, invite.Type);
var link = _roomLinkService.GetInvitationLink(user.Email, invite.Type, _authContext.CurrentAccount.ID);
var users = _userManager.GetUsers().Where(u => u.ActivationStatus == EmployeeActivationStatus.Pending);
_studioNotifyService.SendDocSpaceInvite(user.Email, link);
foreach (var user in users) _logger.Debug(link);
{ }
yield return await _employeeDtoHelper.Get(user);
} var users = _userManager.GetUsers().Where(u => u.ActivationStatus == EmployeeActivationStatus.Pending);
foreach (var user in users)
{
yield return await _employeeDtoHelper.Get(user);
}
} }
[HttpPut("{userid}/password")] [HttpPut("{userid}/password")]
@ -328,12 +332,12 @@ public class UserController : PeopleControllerBase
if (!_userManager.UserExists(user)) if (!_userManager.UserExists(user))
{ {
return null; return null;
} }
if (_userManager.IsSystemUser(user.Id)) if (_userManager.IsSystemUser(user.Id))
{ {
throw new SecurityException(); throw new SecurityException();
} }
if (!string.IsNullOrEmpty(inDto.Email)) if (!string.IsNullOrEmpty(inDto.Email))
@ -468,10 +472,10 @@ public class UserController : PeopleControllerBase
list = list.Where(x => x.FirstName != null && x.FirstName.IndexOf(query, StringComparison.OrdinalIgnoreCase) > -1 || (x.LastName != null && x.LastName.IndexOf(query, StringComparison.OrdinalIgnoreCase) != -1) || list = list.Where(x => x.FirstName != null && x.FirstName.IndexOf(query, StringComparison.OrdinalIgnoreCase) > -1 || (x.LastName != null && x.LastName.IndexOf(query, StringComparison.OrdinalIgnoreCase) != -1) ||
(x.UserName != null && x.UserName.IndexOf(query, StringComparison.OrdinalIgnoreCase) != -1) || (x.Email != null && x.Email.IndexOf(query, StringComparison.OrdinalIgnoreCase) != -1) || (x.ContactsList != null && x.ContactsList.Any(y => y.IndexOf(query, StringComparison.OrdinalIgnoreCase) != -1))); (x.UserName != null && x.UserName.IndexOf(query, StringComparison.OrdinalIgnoreCase) != -1) || (x.Email != null && x.Email.IndexOf(query, StringComparison.OrdinalIgnoreCase) != -1) || (x.ContactsList != null && x.ContactsList.Any(y => y.IndexOf(query, StringComparison.OrdinalIgnoreCase) != -1)));
foreach (var item in list) foreach (var item in list)
{ {
yield return await _employeeFullDtoHelper.GetFull(item); yield return await _employeeFullDtoHelper.GetFull(item);
} }
} }
@ -480,7 +484,7 @@ public class UserController : PeopleControllerBase
{ {
return GetByStatus(EmployeeStatus.Active); return GetByStatus(EmployeeStatus.Active);
} }
[AllowNotPayment] [AllowNotPayment]
[HttpGet("email")] [HttpGet("email")]
public async Task<EmployeeFullDto> GetByEmail([FromQuery] string email) public async Task<EmployeeFullDto> GetByEmail([FromQuery] string email)
@ -497,7 +501,7 @@ public class UserController : PeopleControllerBase
} }
return await _employeeFullDtoHelper.GetFull(user); return await _employeeFullDtoHelper.GetFull(user);
} }
[AllowNotPayment] [AllowNotPayment]
[Authorize(AuthenticationSchemes = "confirm", Roles = "LinkInvite,Everyone")] [Authorize(AuthenticationSchemes = "confirm", Roles = "LinkInvite,Everyone")]
@ -506,18 +510,18 @@ public class UserController : PeopleControllerBase
{ {
if (_coreBaseSettings.Personal) if (_coreBaseSettings.Personal)
{ {
throw new MethodAccessException("Method not available"); throw new MethodAccessException("Method not available");
} }
var isInvite = _httpContextAccessor.HttpContext.User.Claims var isInvite = _httpContextAccessor.HttpContext.User.Claims
.Any(role => role.Type == ClaimTypes.Role && ConfirmTypeExtensions.TryParse(role.Value, out var confirmType) && confirmType == ConfirmType.LinkInvite); .Any(role => role.Type == ClaimTypes.Role && ConfirmTypeExtensions.TryParse(role.Value, out var confirmType) && confirmType == ConfirmType.LinkInvite);
_apiContext.AuthByClaim(); _apiContext.AuthByClaim();
var user = _userManager.GetUserByUserName(username); var user = _userManager.GetUserByUserName(username);
if (user.Id == Constants.LostUser.Id) if (user.Id == Constants.LostUser.Id)
{ {
if (Guid.TryParse(username, out var userId)) if (Guid.TryParse(username, out var userId))
{ {
user = _userManager.GetUsers(userId); user = _userManager.GetUsers(userId);
} }
@ -530,12 +534,12 @@ public class UserController : PeopleControllerBase
if (user.Id == Constants.LostUser.Id) if (user.Id == Constants.LostUser.Id)
{ {
throw new ItemNotFoundException("User not found"); throw new ItemNotFoundException("User not found");
} }
if (isInvite) if (isInvite)
{ {
return await _employeeFullDtoHelper.GetSimple(user); return await _employeeFullDtoHelper.GetSimple(user);
} }
return await _employeeFullDtoHelper.GetFull(user); return await _employeeFullDtoHelper.GetFull(user);
} }
@ -562,10 +566,10 @@ public class UserController : PeopleControllerBase
public async IAsyncEnumerable<EmployeeFullDto> GetFullByFilter(EmployeeStatus? employeeStatus, Guid? groupId, EmployeeActivationStatus? activationStatus, EmployeeType? employeeType, bool? isAdministrator, Payments? payments) public async IAsyncEnumerable<EmployeeFullDto> GetFullByFilter(EmployeeStatus? employeeStatus, Guid? groupId, EmployeeActivationStatus? activationStatus, EmployeeType? employeeType, bool? isAdministrator, Payments? payments)
{ {
var users = GetByFilter(employeeStatus, groupId, activationStatus, employeeType, isAdministrator, payments); var users = GetByFilter(employeeStatus, groupId, activationStatus, employeeType, isAdministrator, payments);
foreach (var user in users) foreach (var user in users)
{ {
yield return await _employeeFullDtoHelper.GetFull(user); yield return await _employeeFullDtoHelper.GetFull(user);
} }
} }
@ -590,19 +594,19 @@ public class UserController : PeopleControllerBase
if (_coreBaseSettings.Personal) if (_coreBaseSettings.Personal)
{ {
throw new MethodAccessException("Method not available"); throw new MethodAccessException("Method not available");
} }
var groupId = Guid.Empty; var groupId = Guid.Empty;
if ("group".Equals(_apiContext.FilterBy, StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(_apiContext.FilterValue)) if ("group".Equals(_apiContext.FilterBy, StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(_apiContext.FilterValue))
{ {
groupId = new Guid(_apiContext.FilterValue); groupId = new Guid(_apiContext.FilterValue);
} }
var users = _userManager.Search(query, EmployeeStatus.Active, groupId); var users = _userManager.Search(query, EmployeeStatus.Active, groupId);
foreach (var user in users) foreach (var user in users)
{ {
yield return await _employeeFullDtoHelper.GetFull(user); yield return await _employeeFullDtoHelper.GetFull(user);
} }
} }
@ -610,10 +614,10 @@ public class UserController : PeopleControllerBase
public async IAsyncEnumerable<EmployeeDto> GetSimpleByFilter(EmployeeStatus? employeeStatus, Guid? groupId, EmployeeActivationStatus? activationStatus, EmployeeType? employeeType, bool? isAdministrator, Payments? payments) public async IAsyncEnumerable<EmployeeDto> GetSimpleByFilter(EmployeeStatus? employeeStatus, Guid? groupId, EmployeeActivationStatus? activationStatus, EmployeeType? employeeType, bool? isAdministrator, Payments? payments)
{ {
var users = GetByFilter(employeeStatus, groupId, activationStatus, employeeType, isAdministrator, payments); var users = GetByFilter(employeeStatus, groupId, activationStatus, employeeType, isAdministrator, payments);
foreach (var user in users) foreach (var user in users)
{ {
yield return await _employeeDtoHelper.Get(user); yield return await _employeeDtoHelper.Get(user);
} }
} }
@ -644,7 +648,7 @@ public class UserController : PeopleControllerBase
foreach (var user in users) foreach (var user in users)
{ {
if (user.Status != EmployeeStatus.Terminated) if (user.Status != EmployeeStatus.Terminated)
{ {
continue; continue;
} }
@ -654,43 +658,43 @@ public class UserController : PeopleControllerBase
_queueWorkerRemove.Start(Tenant.Id, user, _securityContext.CurrentAccount.ID, false); _queueWorkerRemove.Start(Tenant.Id, user, _securityContext.CurrentAccount.ID, false);
} }
_messageService.Send(MessageAction.UsersDeleted, _messageTarget.Create(users.Select(x => x.Id)), userNames); _messageService.Send(MessageAction.UsersDeleted, _messageTarget.Create(users.Select(x => x.Id)), userNames);
foreach (var user in users) foreach (var user in users)
{ {
yield return await _employeeFullDtoHelper.GetFull(user); yield return await _employeeFullDtoHelper.GetFull(user);
} }
} }
[HttpPut("invite")] [HttpPut("invite")]
public async IAsyncEnumerable<EmployeeFullDto> ResendUserInvites(UpdateMembersRequestDto inDto) public async IAsyncEnumerable<EmployeeFullDto> ResendUserInvites(UpdateMembersRequestDto inDto)
{ {
var users = inDto.UserIds var users = inDto.UserIds
.Where(userId => !_userManager.IsSystemUser(userId)) .Where(userId => !_userManager.IsSystemUser(userId))
.Select(userId => _userManager.GetUsers(userId)) .Select(userId => _userManager.GetUsers(userId))
.ToList(); .ToList();
foreach (var user in users) foreach (var user in users)
{ {
if (user.IsActive) if (user.IsActive)
{ {
continue; continue;
} }
var viewer = _userManager.GetUsers(_securityContext.CurrentAccount.ID); var viewer = _userManager.GetUsers(_securityContext.CurrentAccount.ID);
if (viewer == null) if (viewer == null)
{ {
throw new Exception(Resource.ErrorAccessDenied); throw new Exception(Resource.ErrorAccessDenied);
} }
if (_userManager.IsDocSpaceAdmin(viewer) || viewer.Id == user.Id) if (_userManager.IsDocSpaceAdmin(viewer) || viewer.Id == user.Id)
{ {
if (user.ActivationStatus == EmployeeActivationStatus.Activated) if (user.ActivationStatus == EmployeeActivationStatus.Activated)
{ {
user.ActivationStatus = EmployeeActivationStatus.NotActivated; user.ActivationStatus = EmployeeActivationStatus.NotActivated;
} }
if (user.ActivationStatus == (EmployeeActivationStatus.AutoGenerated | EmployeeActivationStatus.Activated)) if (user.ActivationStatus == (EmployeeActivationStatus.AutoGenerated | EmployeeActivationStatus.Activated))
{ {
user.ActivationStatus = EmployeeActivationStatus.AutoGenerated; user.ActivationStatus = EmployeeActivationStatus.AutoGenerated;
} }
@ -698,31 +702,31 @@ public class UserController : PeopleControllerBase
await _userManager.UpdateUserInfoWithSyncCardDavAsync(user); await _userManager.UpdateUserInfoWithSyncCardDavAsync(user);
} }
if (user.ActivationStatus == EmployeeActivationStatus.Pending) if (user.ActivationStatus == EmployeeActivationStatus.Pending)
{ {
var type = _userManager.IsDocSpaceAdmin(user) ? EmployeeType.DocSpaceAdmin : var type = _userManager.IsDocSpaceAdmin(user) ? EmployeeType.DocSpaceAdmin :
_userManager.IsUser(user) ? EmployeeType.User : EmployeeType.RoomAdmin; _userManager.IsUser(user) ? EmployeeType.User : EmployeeType.RoomAdmin;
_studioNotifyService.SendDocSpaceInvite(user.Email, _roomLinkService.GetInvitationLink(user.Email, type, _authContext.CurrentAccount.ID)); _studioNotifyService.SendDocSpaceInvite(user.Email, _roomLinkService.GetInvitationLink(user.Email, type, _authContext.CurrentAccount.ID));
} }
else else
{ {
_studioNotifyService.SendEmailActivationInstructions(user, user.Email); _studioNotifyService.SendEmailActivationInstructions(user, user.Email);
} }
} }
_messageService.Send(MessageAction.UsersSentActivationInstructions, _messageTarget.Create(users.Select(x => x.Id)), users.Select(x => x.DisplayUserName(false, _displayUserSettingsHelper))); _messageService.Send(MessageAction.UsersSentActivationInstructions, _messageTarget.Create(users.Select(x => x.Id)), users.Select(x => x.DisplayUserName(false, _displayUserSettingsHelper)));
foreach (var user in users) foreach (var user in users)
{ {
yield return await _employeeFullDtoHelper.GetFull(user); yield return await _employeeFullDtoHelper.GetFull(user);
} }
} }
[HttpGet("theme")] [HttpGet("theme")]
public DarkThemeSettings GetTheme() public DarkThemeSettings GetTheme()
{ {
return _settingsManager.LoadForCurrentUser<DarkThemeSettings>(); return _settingsManager.LoadForCurrentUser<DarkThemeSettings>();
} }
[HttpPut("theme")] [HttpPut("theme")]
@ -740,15 +744,15 @@ public class UserController : PeopleControllerBase
[AllowNotPayment] [AllowNotPayment]
[HttpGet("@self")] [HttpGet("@self")]
public async Task<EmployeeFullDto> Self() public async Task<EmployeeFullDto> Self()
{ {
var user = _userManager.GetUser(_securityContext.CurrentAccount.ID, EmployeeFullDtoHelper.GetExpression(_apiContext)); var user = _userManager.GetUser(_securityContext.CurrentAccount.ID, EmployeeFullDtoHelper.GetExpression(_apiContext));
var result = await _employeeFullDtoHelper.GetFull(user); var result = await _employeeFullDtoHelper.GetFull(user);
result.Theme = _settingsManager.LoadForCurrentUser<DarkThemeSettings>().Theme; result.Theme = _settingsManager.LoadForCurrentUser<DarkThemeSettings>().Theme;
return result; return result;
} }
[AllowNotPayment] [AllowNotPayment]
@ -823,7 +827,7 @@ public class UserController : PeopleControllerBase
{ {
var error = _userManagerWrapper.SendUserPassword(inDto.Email); var error = _userManagerWrapper.SendUserPassword(inDto.Email);
if (!string.IsNullOrEmpty(error)) if (!string.IsNullOrEmpty(error))
{ {
_logger.ErrorPasswordRecovery(inDto.Email, error); _logger.ErrorPasswordRecovery(inDto.Email, error);
} }
@ -992,8 +996,8 @@ public class UserController : PeopleControllerBase
} }
return await _employeeFullDtoHelper.GetFull(user); return await _employeeFullDtoHelper.GetFull(user);
} }
[HttpPut("status/{status}")] [HttpPut("status/{status}")]
public async IAsyncEnumerable<EmployeeFullDto> UpdateUserStatus(EmployeeStatus status, UpdateMembersRequestDto inDto) public async IAsyncEnumerable<EmployeeFullDto> UpdateUserStatus(EmployeeStatus status, UpdateMembersRequestDto inDto)
{ {
@ -1015,13 +1019,13 @@ public class UserController : PeopleControllerBase
case EmployeeStatus.Active: case EmployeeStatus.Active:
if (user.Status == EmployeeStatus.Terminated) if (user.Status == EmployeeStatus.Terminated)
{ {
if (!_userManager.IsUser(user)) if (!_userManager.IsUser(user))
{ {
await _countRoomAdminChecker.CheckAppend(); await _countRoomAdminChecker.CheckAppend();
} }
else else
{ {
await _countUserChecker.CheckAppend(); await _countUserChecker.CheckAppend();
} }
user.Status = EmployeeStatus.Active; user.Status = EmployeeStatus.Active;
@ -1038,11 +1042,11 @@ public class UserController : PeopleControllerBase
} }
} }
_messageService.Send(MessageAction.UsersUpdatedStatus, _messageTarget.Create(users.Select(x => x.Id)), users.Select(x => x.DisplayUserName(false, _displayUserSettingsHelper))); _messageService.Send(MessageAction.UsersUpdatedStatus, _messageTarget.Create(users.Select(x => x.Id)), users.Select(x => x.DisplayUserName(false, _displayUserSettingsHelper)));
foreach (var user in users) foreach (var user in users)
{ {
yield return await _employeeFullDtoHelper.GetFull(user); yield return await _employeeFullDtoHelper.GetFull(user);
} }
} }
@ -1064,33 +1068,33 @@ public class UserController : PeopleControllerBase
switch (type) switch (type)
{ {
case EmployeeType.RoomAdmin: case EmployeeType.RoomAdmin:
await _countRoomAdminChecker.CheckAppend(); await _countRoomAdminChecker.CheckAppend();
_userManager.RemoveUserFromGroup(user.Id, Constants.GroupUser.ID); _userManager.RemoveUserFromGroup(user.Id, Constants.GroupUser.ID);
_webItemSecurityCache.ClearCache(Tenant.Id); _webItemSecurityCache.ClearCache(Tenant.Id);
break; break;
case EmployeeType.User: case EmployeeType.User:
await _countUserChecker.CheckAppend(); await _countUserChecker.CheckAppend();
await _userManager.AddUserIntoGroup(user.Id, Constants.GroupUser.ID); await _userManager.AddUserIntoGroup(user.Id, Constants.GroupUser.ID);
_webItemSecurityCache.ClearCache(Tenant.Id); _webItemSecurityCache.ClearCache(Tenant.Id);
break; break;
} }
} }
_messageService.Send(MessageAction.UsersUpdatedType, _messageTarget.Create(users.Select(x => x.Id)), users.Select(x => x.DisplayUserName(false, _displayUserSettingsHelper))); _messageService.Send(MessageAction.UsersUpdatedType, _messageTarget.Create(users.Select(x => x.Id)), users.Select(x => x.DisplayUserName(false, _displayUserSettingsHelper)));
foreach (var user in users) foreach (var user in users)
{ {
yield return await _employeeFullDtoHelper.GetFull(user); yield return await _employeeFullDtoHelper.GetFull(user);
} }
} }
[HttpGet("recalculatequota")] [HttpGet("recalculatequota")]
public void RecalculateQuota() public void RecalculateQuota()
{ {
_permissionContext.DemandPermissions(SecutiryConstants.EditPortalSettings); _permissionContext.DemandPermissions(SecutiryConstants.EditPortalSettings);
_usersQuotaSyncOperation.RecalculateQuota(_tenantManager.GetCurrentTenant()); _usersQuotaSyncOperation.RecalculateQuota(_tenantManager.GetCurrentTenant());
} }
[HttpGet("checkrecalculatequota")] [HttpGet("checkrecalculatequota")]
public TaskProgressDto CheckRecalculateQuota() public TaskProgressDto CheckRecalculateQuota()
{ {
@ -1107,31 +1111,31 @@ public class UserController : PeopleControllerBase
.ToList(); .ToList();
foreach (var user in users) foreach (var user in users)
{ {
if (inDto.Quota != -1) if (inDto.Quota != -1)
{ {
var usedSpace = Math.Max(0, var usedSpace = Math.Max(0,
_quotaService.FindUserQuotaRows( _quotaService.FindUserQuotaRows(
_tenantManager.GetCurrentTenant().Id, _tenantManager.GetCurrentTenant().Id,
user.Id user.Id
) )
.Where(r => !string.IsNullOrEmpty(r.Tag)).Sum(r => r.Counter)); .Where(r => !string.IsNullOrEmpty(r.Tag)).Sum(r => r.Counter));
var tenanSpaceQuota = _quotaService.GetTenantQuota(Tenant.Id).MaxTotalSize; var tenanSpaceQuota = _quotaService.GetTenantQuota(Tenant.Id).MaxTotalSize;
if (tenanSpaceQuota < inDto.Quota || usedSpace > inDto.Quota) if (tenanSpaceQuota < inDto.Quota || usedSpace > inDto.Quota)
{ {
continue; continue;
} }
} }
var quotaSettings = _settingsManager.Load<TenantUserQuotaSettings>(); var quotaSettings = _settingsManager.Load<TenantUserQuotaSettings>();
_settingsManager.SaveForUser(new UserQuotaSettings { UserQuota = inDto.Quota }, user); _settingsManager.SaveForUser(new UserQuotaSettings { UserQuota = inDto.Quota }, user);
yield return await _employeeFullDtoHelper.GetFull(user); yield return await _employeeFullDtoHelper.GetFull(user);
} }
} }
private async Task UpdateDepartments(IEnumerable<Guid> department, UserInfo user) private async Task UpdateDepartments(IEnumerable<Guid> department, UserInfo user)
@ -1356,7 +1360,7 @@ public class UserController : PeopleControllerBase
// lock (progressQueue.SynchRoot) // lock (progressQueue.SynchRoot)
// { // {
// var task = progressQueue.GetItems().OfType<ImportUsersTask>().FirstOrDefault(t => (int)t.Id == TenantProvider.CurrentTenantID); // var task = progressQueue.GetItems().OfType<ImportUsersTask>().FirstOrDefault(t => (int)t.Id == TenantProvider.CurrentTenantID);
//var tenant = CoreContext.TenantManager.GetCurrentTenant(); //var tenant = CoreContext.TenantManager.GetCurrentTenant();
//Cache.Insert("REWRITE_URL" + tenant.TenantId, HttpContext.Current.Request.GetUrlRewriter().ToString(), TimeSpan.FromMinutes(5)); //Cache.Insert("REWRITE_URL" + tenant.TenantId, HttpContext.Current.Request.GetUrlRewriter().ToString(), TimeSpan.FromMinutes(5));
// if (task != null && task.IsCompleted) // if (task != null && task.IsCompleted)
// { // {

View File

@ -143,9 +143,9 @@ public class PortalController : ControllerBase
[HttpGet("users/invite/{employeeType}")] [HttpGet("users/invite/{employeeType}")]
public object GeInviteLink(EmployeeType employeeType) public object GeInviteLink(EmployeeType employeeType)
{ {
if (!_webItemSecurity.IsProductAdministrator(WebItemManager.PeopleProductID, _authContext.CurrentAccount.ID)) if (!_permissionContext.CheckPermissions(new UserSecurityProvider(Guid.Empty, employeeType), ASC.Core.Users.Constants.Action_AddRemoveUser))
{ {
throw new SecurityException("Method not available"); return string.Empty;
} }
return _commonLinkUtility.GetConfirmationEmailUrl(string.Empty, ConfirmType.LinkInvite, (int)employeeType, _authContext.CurrentAccount.ID) return _commonLinkUtility.GetConfirmationEmailUrl(string.Empty, ConfirmType.LinkInvite, (int)employeeType, _authContext.CurrentAccount.ID)

View File

@ -117,12 +117,7 @@ public sealed class UserManagerWrapper
if (_userManager.GetUserByEmail(mail.Address).Id != Constants.LostUser.Id) if (_userManager.GetUserByEmail(mail.Address).Id != Constants.LostUser.Id)
{ {
throw new InvalidOperationException($"User with email {mail.Address} already exists or is invited"); throw new Exception(_customNamingPeople.Substitute<Resource>("ErrorEmailAlreadyExists"));
}
if (type is EmployeeType.RoomAdmin or EmployeeType.DocSpaceAdmin)
{
await _countManagerChecker.CheckAppend();
} }
var user = new UserInfo var user = new UserInfo
@ -135,7 +130,9 @@ public sealed class UserManagerWrapper
Status = EmployeeStatus.Active, Status = EmployeeStatus.Active,
}; };
var newUser = await _userManager.SaveUserInfo(user); user.UserName = MakeUniqueName(user);
var newUser = await _userManager.SaveUserInfo(user, type == EmployeeType.User);
var groupId = type switch var groupId = type switch
{ {
@ -226,11 +223,11 @@ public sealed class UserManagerWrapper
if (isUser) if (isUser)
{ {
await _userManager.AddUserIntoGroup(newUserInfo.Id, Constants.GroupUser.ID); await _userManager.AddUserIntoGroup(newUserInfo.Id, Constants.GroupUser.ID, true);
} }
else if (isAdmin) else if (isAdmin)
{ {
await _userManager.AddUserIntoGroup(newUserInfo.Id, Constants.GroupAdmin.ID); await _userManager.AddUserIntoGroup(newUserInfo.Id, Constants.GroupAdmin.ID, true);
} }
return newUserInfo; return newUserInfo;