DocSpace-client/common/ASC.Core.Common/Caching/CachedUserService.cs

525 lines
19 KiB
C#
Raw Normal View History

2019-05-15 14:56:09 +00:00
/*
*
* (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;
2020-02-25 08:02:13 +00:00
using System.Linq.Expressions;
2019-08-15 12:04:42 +00:00
using System.Threading;
2020-01-17 13:58:26 +00:00
2020-02-17 08:58:14 +00:00
using ASC.Common;
2019-05-15 14:56:09 +00:00
using ASC.Common.Caching;
2019-11-25 09:49:12 +00:00
using ASC.Core.Common.EF;
2019-10-31 11:28:30 +00:00
using ASC.Core.Data;
2019-05-15 14:56:09 +00:00
using ASC.Core.Tenants;
2019-10-22 16:08:37 +00:00
using ASC.Core.Users;
2020-07-17 10:52:28 +00:00
2020-02-20 08:05:10 +00:00
using Microsoft.Extensions.Options;
2019-10-22 16:08:37 +00:00
2019-05-15 14:56:09 +00:00
namespace ASC.Core.Caching
2020-10-19 15:53:15 +00:00
{
[Singletone]
2020-02-20 14:57:48 +00:00
public class UserServiceCache
2019-10-31 11:28:30 +00:00
{
public const string USERS = "users";
2019-05-15 14:56:09 +00:00
private const string GROUPS = "groups";
2019-10-31 11:28:30 +00:00
public const string REFS = "refs";
public TrustInterval TrustInterval { get; set; }
public ICache Cache { get; }
2020-08-12 09:58:08 +00:00
internal CoreBaseSettings CoreBaseSettings { get; }
internal ICacheNotify<UserInfoCacheItem> CacheUserInfoItem { get; }
internal ICacheNotify<UserPhotoCacheItem> CacheUserPhotoItem { get; }
internal ICacheNotify<GroupCacheItem> CacheGroupCacheItem { get; }
internal ICacheNotify<UserGroupRefCacheItem> CacheUserGroupRefItem { get; }
2019-10-31 11:28:30 +00:00
public UserServiceCache(
CoreBaseSettings coreBaseSettings,
ICacheNotify<UserInfoCacheItem> cacheUserInfoItem,
ICacheNotify<UserPhotoCacheItem> cacheUserPhotoItem,
ICacheNotify<GroupCacheItem> cacheGroupCacheItem,
ICacheNotify<UserGroupRefCacheItem> cacheUserGroupRefItem)
{
TrustInterval = new TrustInterval();
Cache = AscCache.Memory;
CoreBaseSettings = coreBaseSettings;
CacheUserInfoItem = cacheUserInfoItem;
CacheUserPhotoItem = cacheUserPhotoItem;
CacheGroupCacheItem = cacheGroupCacheItem;
CacheUserGroupRefItem = cacheUserGroupRefItem;
2019-10-21 12:33:02 +00:00
cacheUserInfoItem.Subscribe((u) => InvalidateCache(u), CacheNotifyAction.Any);
2019-10-31 11:28:30 +00:00
cacheUserPhotoItem.Subscribe((p) => Cache.Remove(p.Key), CacheNotifyAction.Remove);
cacheGroupCacheItem.Subscribe((g) => InvalidateCache(), CacheNotifyAction.Any);
cacheUserGroupRefItem.Subscribe((r) => UpdateUserGroupRefCache(r, true), CacheNotifyAction.Remove);
cacheUserGroupRefItem.Subscribe((r) => UpdateUserGroupRefCache(r, false), CacheNotifyAction.InsertOrUpdate);
}
2019-10-21 12:33:02 +00:00
public void InvalidateCache()
{
InvalidateCache(null);
}
2019-05-15 14:56:09 +00:00
2019-10-21 12:33:02 +00:00
private void InvalidateCache(UserInfoCacheItem userInfo)
{
if (CoreBaseSettings.Personal && userInfo != null)
{
var key = GetUserCacheKeyForPersonal(userInfo.Tenant, userInfo.ID.FromByteString());
Cache.Remove(key);
}
TrustInterval.Expire();
2019-10-31 11:28:30 +00:00
}
2019-10-21 12:33:02 +00:00
private void UpdateUserGroupRefCache(UserGroupRef r, bool remove)
{
var key = GetRefCacheKey(r.Tenant);
var refs = Cache.Get<UserGroupRefStore>(key);
if (!remove && refs != null)
{
lock (refs)
{
refs[r.CreateKey()] = r;
}
}
else
{
InvalidateCache();
}
2019-10-31 11:28:30 +00:00
}
2019-10-21 12:33:02 +00:00
public static string GetUserPhotoCacheKey(int tenant, Guid userId)
{
return tenant.ToString() + "userphoto" + userId.ToString();
}
public static string GetGroupCacheKey(int tenant)
{
return tenant.ToString() + GROUPS;
}
public static string GetRefCacheKey(int tenant)
{
return tenant.ToString() + REFS;
2019-10-31 11:28:30 +00:00
}
2019-10-21 12:33:02 +00:00
public static string GetUserCacheKey(int tenant)
{
return tenant.ToString() + USERS;
}
public static string GetUserCacheKeyForPersonal(int tenant, Guid userId)
{
return tenant.ToString() + USERS + userId;
2019-10-31 11:28:30 +00:00
}
2020-02-20 08:05:10 +00:00
}
2020-10-18 16:03:14 +00:00
[Scope]
2020-02-20 08:05:10 +00:00
class ConfigureCachedUserService : IConfigureNamedOptions<CachedUserService>
{
2020-08-12 09:58:08 +00:00
internal IOptionsSnapshot<EFUserService> Service { get; }
internal UserServiceCache UserServiceCache { get; }
internal CoreBaseSettings CoreBaseSettings { get; }
2020-02-20 08:05:10 +00:00
public ConfigureCachedUserService(
IOptionsSnapshot<EFUserService> service,
UserServiceCache userServiceCache,
CoreBaseSettings coreBaseSettings)
{
Service = service;
UserServiceCache = userServiceCache;
CoreBaseSettings = coreBaseSettings;
}
public void Configure(string name, CachedUserService options)
{
Configure(options);
options.Service = Service.Get(name);
}
public void Configure(CachedUserService options)
{
options.Service = Service.Value;
options.CoreBaseSettings = CoreBaseSettings;
options.UserServiceCache = UserServiceCache;
options.Cache = UserServiceCache.Cache;
options.CacheUserInfoItem = UserServiceCache.CacheUserInfoItem;
options.CacheUserPhotoItem = UserServiceCache.CacheUserPhotoItem;
options.CacheGroupCacheItem = UserServiceCache.CacheGroupCacheItem;
options.CacheUserGroupRefItem = UserServiceCache.CacheUserGroupRefItem;
options.TrustInterval = UserServiceCache.TrustInterval;
}
}
2020-10-18 16:03:14 +00:00
[Scope]
2020-02-20 08:05:10 +00:00
public class CachedUserService : IUserService, ICachedService
2019-10-21 12:33:02 +00:00
{
2020-02-20 08:05:10 +00:00
internal IUserService Service { get; set; }
internal ICache Cache { get; set; }
2020-02-20 08:05:10 +00:00
internal TrustInterval TrustInterval { get; set; }
2019-05-15 14:56:09 +00:00
private int getchanges;
2019-10-22 16:08:37 +00:00
private TimeSpan CacheExpiration { get; set; }
private TimeSpan DbExpiration { get; set; }
2019-10-31 11:28:30 +00:00
private TimeSpan PhotoExpiration { get; set; }
2020-02-20 08:05:10 +00:00
internal CoreBaseSettings CoreBaseSettings { get; set; }
internal UserServiceCache UserServiceCache { get; set; }
internal ICacheNotify<UserInfoCacheItem> CacheUserInfoItem { get; set; }
internal ICacheNotify<UserPhotoCacheItem> CacheUserPhotoItem { get; set; }
internal ICacheNotify<GroupCacheItem> CacheGroupCacheItem { get; set; }
internal ICacheNotify<UserGroupRefCacheItem> CacheUserGroupRefItem { get; set; }
2019-05-15 14:56:09 +00:00
2020-02-20 08:05:10 +00:00
public CachedUserService()
{
2019-05-15 14:56:09 +00:00
CacheExpiration = TimeSpan.FromMinutes(20);
DbExpiration = TimeSpan.FromMinutes(1);
2019-10-21 12:33:02 +00:00
PhotoExpiration = TimeSpan.FromMinutes(10);
2020-02-20 14:57:48 +00:00
}
public CachedUserService(
EFUserService service,
CoreBaseSettings coreBaseSettings,
UserServiceCache userServiceCache
) : this()
{
Service = service ?? throw new ArgumentNullException("service");
CoreBaseSettings = coreBaseSettings;
UserServiceCache = userServiceCache;
Cache = userServiceCache.Cache;
CacheUserInfoItem = userServiceCache.CacheUserInfoItem;
CacheUserPhotoItem = userServiceCache.CacheUserPhotoItem;
CacheGroupCacheItem = userServiceCache.CacheGroupCacheItem;
CacheUserGroupRefItem = userServiceCache.CacheUserGroupRefItem;
TrustInterval = userServiceCache.TrustInterval;
}
2019-05-15 14:56:09 +00:00
public IDictionary<Guid, UserInfo> GetUsers(int tenant, DateTime from)
{
var users = GetUsers(tenant);
lock (users)
{
2019-08-15 13:00:20 +00:00
return (from == default ? users.Values : users.Values.Where(u => u.LastModified >= from)).ToDictionary(u => u.ID);
2019-05-15 14:56:09 +00:00
}
2019-10-31 11:28:30 +00:00
}
2019-11-25 09:49:12 +00:00
public IQueryable<UserInfo> GetUsers(
int tenant,
bool isAdmin,
EmployeeStatus? employeeStatus,
List<List<Guid>> includeGroups,
List<Guid> excludeGroups,
EmployeeActivationStatus? activationStatus,
string text,
string sortBy,
bool sortOrderAsc,
long limit,
long offset,
out int total,
out int count)
2019-10-31 11:28:30 +00:00
{
2020-02-20 08:05:10 +00:00
return Service.GetUsers(tenant, isAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, text, sortBy, sortOrderAsc, limit, offset, out total, out count);
2019-10-31 11:28:30 +00:00
}
2019-05-15 14:56:09 +00:00
public UserInfo GetUser(int tenant, Guid id)
{
2019-09-19 11:34:54 +00:00
if (CoreBaseSettings.Personal)
2019-05-15 14:56:09 +00:00
{
return GetUserForPersonal(tenant, id);
}
var users = GetUsers(tenant);
lock (users)
{
2019-08-15 13:05:50 +00:00
users.TryGetValue(id, out var u);
2019-05-15 14:56:09 +00:00
return u;
}
}
/// <summary>
/// For Personal only
/// </summary>
/// <param name="tenant"></param>
/// <param name="id"></param>
/// <returns></returns>
private UserInfo GetUserForPersonal(int tenant, Guid id)
{
2019-09-19 11:34:54 +00:00
if (!CoreBaseSettings.Personal) return GetUser(tenant, id);
2019-05-15 14:56:09 +00:00
2019-10-21 12:33:02 +00:00
var key = UserServiceCache.GetUserCacheKeyForPersonal(tenant, id);
2020-02-20 08:05:10 +00:00
var user = Cache.Get<UserInfo>(key);
2019-05-15 14:56:09 +00:00
if (user == null)
{
2020-02-20 08:05:10 +00:00
user = Service.GetUser(tenant, id);
2019-05-15 14:56:09 +00:00
if (user != null)
{
2020-02-20 08:05:10 +00:00
Cache.Insert(key, user, CacheExpiration);
2019-05-15 14:56:09 +00:00
}
}
return user;
}
public UserInfo GetUserByPasswordHash(int tenant, string login, string passwordHash)
{
return Service.GetUserByPasswordHash(tenant, login, passwordHash);
2019-05-15 14:56:09 +00:00
}
public UserInfo SaveUser(int tenant, UserInfo user)
{
2020-02-20 08:05:10 +00:00
user = Service.SaveUser(tenant, user);
2019-10-21 12:33:02 +00:00
CacheUserInfoItem.Publish(new UserInfoCacheItem { ID = user.ID.ToByteString(), Tenant = tenant }, CacheNotifyAction.Any);
2019-05-15 14:56:09 +00:00
return user;
}
public void RemoveUser(int tenant, Guid id)
{
2020-02-20 08:05:10 +00:00
Service.RemoveUser(tenant, id);
2019-10-21 12:33:02 +00:00
CacheUserInfoItem.Publish(new UserInfoCacheItem { Tenant = tenant, ID = id.ToByteString() }, CacheNotifyAction.Any);
2019-05-15 14:56:09 +00:00
}
public byte[] GetUserPhoto(int tenant, Guid id)
{
2020-02-20 08:05:10 +00:00
var photo = Cache.Get<byte[]>(UserServiceCache.GetUserPhotoCacheKey(tenant, id));
2019-05-15 14:56:09 +00:00
if (photo == null)
{
2020-02-20 08:05:10 +00:00
photo = Service.GetUserPhoto(tenant, id);
Cache.Insert(UserServiceCache.GetUserPhotoCacheKey(tenant, id), photo, PhotoExpiration);
2019-05-15 14:56:09 +00:00
}
return photo;
}
public void SetUserPhoto(int tenant, Guid id, byte[] photo)
{
2020-02-20 08:05:10 +00:00
Service.SetUserPhoto(tenant, id, photo);
2019-10-21 12:33:02 +00:00
CacheUserPhotoItem.Publish(new UserPhotoCacheItem { Key = UserServiceCache.GetUserPhotoCacheKey(tenant, id) }, CacheNotifyAction.Remove);
2019-05-15 14:56:09 +00:00
}
public DateTime GetUserPasswordStamp(int tenant, Guid id)
{
return Service.GetUserPasswordStamp(tenant, id);
}
public void SetUserPasswordHash(int tenant, Guid id, string passwordHash)
{
Service.SetUserPasswordHash(tenant, id, passwordHash);
}
2019-05-15 14:56:09 +00:00
public IDictionary<Guid, Group> GetGroups(int tenant, DateTime from)
{
var groups = GetGroups(tenant);
lock (groups)
{
2019-08-15 13:00:20 +00:00
return (from == default ? groups.Values : groups.Values.Where(g => g.LastModified >= from)).ToDictionary(g => g.Id);
2019-05-15 14:56:09 +00:00
}
}
public Group GetGroup(int tenant, Guid id)
{
var groups = GetGroups(tenant);
lock (groups)
{
2019-08-15 13:05:50 +00:00
groups.TryGetValue(id, out var g);
2019-05-15 14:56:09 +00:00
return g;
}
}
public Group SaveGroup(int tenant, Group group)
{
2020-02-20 08:05:10 +00:00
group = Service.SaveGroup(tenant, group);
2019-10-21 12:33:02 +00:00
CacheGroupCacheItem.Publish(new GroupCacheItem { ID = group.Id.ToString() }, CacheNotifyAction.Any);
2019-05-15 14:56:09 +00:00
return group;
}
public void RemoveGroup(int tenant, Guid id)
{
2020-02-20 08:05:10 +00:00
Service.RemoveGroup(tenant, id);
2019-10-21 12:33:02 +00:00
CacheGroupCacheItem.Publish(new GroupCacheItem { ID = id.ToString() }, CacheNotifyAction.Any);
2019-05-15 14:56:09 +00:00
}
public IDictionary<string, UserGroupRef> GetUserGroupRefs(int tenant, DateTime from)
{
GetChangesFromDb();
2019-10-21 12:33:02 +00:00
var key = UserServiceCache.GetRefCacheKey(tenant);
2020-02-20 08:05:10 +00:00
if (!(Cache.Get<UserGroupRefStore>(key) is IDictionary<string, UserGroupRef> refs))
2019-05-15 14:56:09 +00:00
{
2020-02-20 08:05:10 +00:00
refs = Service.GetUserGroupRefs(tenant, default);
Cache.Insert(key, new UserGroupRefStore(refs), CacheExpiration);
2019-05-15 14:56:09 +00:00
}
lock (refs)
{
2019-08-15 13:00:20 +00:00
return from == default ? refs : refs.Values.Where(r => r.LastModified >= from).ToDictionary(r => r.CreateKey());
2019-05-15 14:56:09 +00:00
}
}
public UserGroupRef SaveUserGroupRef(int tenant, UserGroupRef r)
{
2020-02-20 08:05:10 +00:00
r = Service.SaveUserGroupRef(tenant, r);
2019-10-21 12:33:02 +00:00
CacheUserGroupRefItem.Publish(r, CacheNotifyAction.InsertOrUpdate);
2019-05-15 14:56:09 +00:00
return r;
}
public void RemoveUserGroupRef(int tenant, Guid userId, Guid groupId, UserGroupRefType refType)
{
2020-02-20 08:05:10 +00:00
Service.RemoveUserGroupRef(tenant, userId, groupId, refType);
2019-05-15 14:56:09 +00:00
var r = new UserGroupRef(userId, groupId, refType) { Tenant = tenant };
2019-10-21 12:33:02 +00:00
CacheUserGroupRefItem.Publish(r, CacheNotifyAction.Remove);
2019-05-15 14:56:09 +00:00
}
private IDictionary<Guid, UserInfo> GetUsers(int tenant)
{
GetChangesFromDb();
2019-10-21 12:33:02 +00:00
var key = UserServiceCache.GetUserCacheKey(tenant);
2020-02-20 08:05:10 +00:00
var users = Cache.Get<IDictionary<Guid, UserInfo>>(key);
2019-05-15 14:56:09 +00:00
if (users == null)
{
2020-02-20 08:05:10 +00:00
users = Service.GetUsers(tenant, default);
2019-05-15 14:56:09 +00:00
2020-02-20 08:05:10 +00:00
Cache.Insert(key, users, CacheExpiration);
2019-05-15 14:56:09 +00:00
}
return users;
}
private IDictionary<Guid, Group> GetGroups(int tenant)
{
GetChangesFromDb();
2019-10-21 12:33:02 +00:00
var key = UserServiceCache.GetGroupCacheKey(tenant);
2020-02-20 08:05:10 +00:00
var groups = Cache.Get<IDictionary<Guid, Group>>(key);
2019-05-15 14:56:09 +00:00
if (groups == null)
{
2020-02-20 08:05:10 +00:00
groups = Service.GetGroups(tenant, default);
Cache.Insert(key, groups, CacheExpiration);
2019-05-15 14:56:09 +00:00
}
return groups;
}
private void GetChangesFromDb()
{
2020-02-20 08:05:10 +00:00
if (!TrustInterval.Expired)
2019-05-15 14:56:09 +00:00
{
return;
}
if (Interlocked.CompareExchange(ref getchanges, 1, 0) == 0)
{
try
{
2020-02-20 08:05:10 +00:00
if (!TrustInterval.Expired)
2019-05-15 14:56:09 +00:00
{
return;
}
2020-02-20 08:05:10 +00:00
var starttime = TrustInterval.StartTime;
2019-08-15 13:00:20 +00:00
if (starttime != default)
2019-05-15 14:56:09 +00:00
{
var correction = TimeSpan.FromTicks(DbExpiration.Ticks * 3);
2020-02-20 08:05:10 +00:00
starttime = TrustInterval.StartTime.Subtract(correction);
2019-05-15 14:56:09 +00:00
}
2020-02-20 08:05:10 +00:00
TrustInterval.Start(DbExpiration);
2019-05-15 14:56:09 +00:00
//get and merge changes in cached tenants
2020-02-20 08:05:10 +00:00
foreach (var tenantGroup in Service.GetUsers(Tenant.DEFAULT_TENANT, starttime).Values.GroupBy(u => u.Tenant))
2019-05-15 14:56:09 +00:00
{
2020-02-20 08:05:10 +00:00
var users = Cache.Get<IDictionary<Guid, UserInfo>>(UserServiceCache.GetUserCacheKey(tenantGroup.Key));
2019-05-15 14:56:09 +00:00
if (users != null)
{
lock (users)
{
foreach (var u in tenantGroup)
{
users[u.ID] = u;
}
}
}
}
2020-02-20 08:05:10 +00:00
foreach (var tenantGroup in Service.GetGroups(Tenant.DEFAULT_TENANT, starttime).Values.GroupBy(g => g.Tenant))
2019-05-15 14:56:09 +00:00
{
2020-02-20 08:05:10 +00:00
var groups = Cache.Get<IDictionary<Guid, Group>>(UserServiceCache.GetGroupCacheKey(tenantGroup.Key));
2019-05-15 14:56:09 +00:00
if (groups != null)
{
lock (groups)
{
foreach (var g in tenantGroup)
{
groups[g.Id] = g;
}
}
}
}
2020-02-20 08:05:10 +00:00
foreach (var tenantGroup in Service.GetUserGroupRefs(Tenant.DEFAULT_TENANT, starttime).Values.GroupBy(r => r.Tenant))
2019-05-15 14:56:09 +00:00
{
2020-02-20 08:05:10 +00:00
var refs = Cache.Get<UserGroupRefStore>(UserServiceCache.GetRefCacheKey(tenantGroup.Key));
2019-05-15 14:56:09 +00:00
if (refs != null)
{
lock (refs)
{
foreach (var r in tenantGroup)
{
refs[r.CreateKey()] = r;
}
}
}
}
}
finally
{
Volatile.Write(ref getchanges, 0);
}
}
}
2019-10-31 11:28:30 +00:00
public void InvalidateCache()
{
UserServiceCache.InvalidateCache();
2020-02-25 08:02:13 +00:00
}
public UserInfo GetUser(int tenant, Guid id, Expression<Func<User, UserInfo>> exp)
{
return Service.GetUser(tenant, id, exp);
}
2019-05-15 14:56:09 +00:00
[Serializable]
class UserPhoto
{
public string Key { get; set; }
}
2019-10-31 11:28:30 +00:00
}
2019-05-15 14:56:09 +00:00
}