Merge branch 'release/v1.0.0' of https://github.com/ONLYOFFICE/DocSpace into release/v1.0.0

This commit is contained in:
Maria Sukhova 2023-03-23 19:05:56 +03:00
commit fc2810ca97
22 changed files with 492 additions and 377 deletions

View File

@ -1,157 +0,0 @@
// (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 ValidationResult = ASC.Security.Cryptography.EmailValidationKeyProvider.ValidationResult;
namespace ASC.Api.Core.Security;
[Scope]
public class DocSpaceLinkHelper
{
private readonly IDbContextFactory<MessagesContext> _dbContextFactory;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly MessageService _messageService;
private readonly MessageTarget _messageTarget;
private readonly Signature _signature;
private readonly EmailValidationKeyProvider _emailValidationKeyProvider;
private readonly UserManager _userManager;
private readonly AuthManager _authManager;
public TimeSpan ExpirationInterval => _emailValidationKeyProvider.ValidEmailKeyInterval;
public TimeSpan ExpirationVisitInterval => _emailValidationKeyProvider.ValidVisitLinkInterval;
public DocSpaceLinkHelper(
IHttpContextAccessor httpContextAccessor,
MessageTarget messageTarget,
MessageService messageService,
Signature signature,
IDbContextFactory<MessagesContext> dbContextFactory,
EmailValidationKeyProvider emailValidationKeyProvider,
UserManager userManager,
AuthManager authManager)
{
_httpContextAccessor = httpContextAccessor;
_messageTarget = messageTarget;
_messageService = messageService;
_dbContextFactory = dbContextFactory;
_signature = signature;
_emailValidationKeyProvider = emailValidationKeyProvider;
_userManager = userManager;
_authManager = authManager;
}
public string MakeKey(Guid linkId)
{
return _signature.Create(linkId);
}
public string MakeKey(string email, EmployeeType employeeType)
{
return email + ConfirmType.LinkInvite.ToStringFast() + employeeType.ToStringFast();
}
public Guid Parse(string key)
{
return _signature.Read<Guid>(key);
}
public ValidationResult Validate(string key, string email, EmployeeType employeeType)
{
return string.IsNullOrEmpty(email) ? ValidateRoomExternalLink(key) : ValidateEmailLink(email, key, employeeType);
}
public ValidationResult ValidateEmailLink(string email, string key, EmployeeType employeeType)
{
var result = _emailValidationKeyProvider.ValidateEmailKey(MakeKey(email, employeeType), key, ExpirationInterval);
if (result == ValidationResult.Ok)
{
var user = _userManager.GetUserByEmail(email);
if (user == ASC.Core.Users.Constants.LostUser || _authManager.GetUserPasswordStamp(user.Id) != DateTime.MinValue)
{
return ValidationResult.Invalid;
}
if (!CanUsed(email, key, ExpirationVisitInterval))
{
return ValidationResult.Expired;
}
}
return result;
}
public ValidationResult ValidateRoomExternalLink(string key)
{
var payload = Parse(key);
return payload == default ? ValidationResult.Invalid : ValidationResult.Ok;
}
public ValidationResult ValidateExtarnalLink(string key, EmployeeType employeeType)
{
return _emailValidationKeyProvider.ValidateEmailKey(ConfirmType.LinkInvite.ToStringFast() + (int)employeeType, key);
}
private bool CanUsed(string email, string key, TimeSpan interval)
{
var message = GetLinkInfo(email, key);
if (message == null)
{
SaveVisitLinkInfo(email, key);
return true;
}
return message.Date + interval > DateTime.UtcNow;
}
private AuditEvent GetLinkInfo(string email, string key)
{
using var context = _dbContextFactory.CreateDbContext();
var target = _messageTarget.Create(email);
var description = JsonConvert.SerializeObject(new[] { key },
new JsonSerializerSettings
{
DateTimeZoneHandling = DateTimeZoneHandling.Utc
});
var message = context.AuditEvents.Where(a => a.Target == target.ToString() &&
a.DescriptionRaw == description).FirstOrDefault();
return message;
}
private void SaveVisitLinkInfo(string email, string key)
{
var headers = _httpContextAccessor?.HttpContext?.Request?.Headers;
var target = _messageTarget.Create(email);
_messageService.Send(headers, MessageAction.RoomInviteLinkUsed, target, key);
}
}

View File

@ -38,7 +38,7 @@ public class EmailValidationKeyModelHelper
private readonly AuthContext _authContext;
private readonly UserManager _userManager;
private readonly AuthManager _authentication;
private readonly DocSpaceLinkHelper _docSpaceLinkHelper;
private readonly InvitationLinkHelper _invitationLinkHelper;
private readonly AuditEventsRepository _auditEventsRepository;
private readonly TenantUtil _tenantUtil;
private readonly MessageTarget _messageTarget;
@ -49,7 +49,7 @@ public class EmailValidationKeyModelHelper
AuthContext authContext,
UserManager userManager,
AuthManager authentication,
DocSpaceLinkHelper docSpaceLinkHelper,
InvitationLinkHelper invitationLinkHelper,
AuditEventsRepository auditEventsRepository,
TenantUtil tenantUtil,
MessageTarget messageTarget)
@ -59,7 +59,7 @@ public class EmailValidationKeyModelHelper
_authContext = authContext;
_userManager = userManager;
_authentication = authentication;
_docSpaceLinkHelper = docSpaceLinkHelper;
_invitationLinkHelper = invitationLinkHelper;
_auditEventsRepository = auditEventsRepository;
_tenantUtil = tenantUtil;
_messageTarget = messageTarget;
@ -110,13 +110,7 @@ public class EmailValidationKeyModelHelper
break;
case ConfirmType.LinkInvite:
checkKeyResult = string.IsNullOrEmpty(email) ? _docSpaceLinkHelper.ValidateRoomExternalLink(key)
: _docSpaceLinkHelper.ValidateEmailLink(email, key, emplType ?? default);
if (checkKeyResult == ValidationResult.Invalid)
{
checkKeyResult = _provider.ValidateEmailKey(type.ToString() + (int)(emplType ?? default), key, _provider.ValidEmailKeyInterval);
}
checkKeyResult = _invitationLinkHelper.Validate(key, email, emplType ?? default).Result;
break;
case ConfirmType.PortalOwnerChange:

View File

@ -0,0 +1,186 @@
// (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 Constants = ASC.Core.Users.Constants;
using ValidationResult = ASC.Security.Cryptography.EmailValidationKeyProvider.ValidationResult;
namespace ASC.Api.Core.Security;
[Scope]
public class InvitationLinkHelper
{
private readonly IDbContextFactory<MessagesContext> _dbContextFactory;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly MessageService _messageService;
private readonly MessageTarget _messageTarget;
private readonly Signature _signature;
private readonly EmailValidationKeyProvider _emailValidationKeyProvider;
private readonly UserManager _userManager;
private readonly AuthManager _authManager;
public TimeSpan IndividualLinkExpirationInterval => _emailValidationKeyProvider.ValidEmailKeyInterval;
public InvitationLinkHelper(
IHttpContextAccessor httpContextAccessor,
MessageTarget messageTarget,
MessageService messageService,
Signature signature,
IDbContextFactory<MessagesContext> dbContextFactory,
EmailValidationKeyProvider emailValidationKeyProvider,
UserManager userManager,
AuthManager authManager)
{
_httpContextAccessor = httpContextAccessor;
_messageTarget = messageTarget;
_messageService = messageService;
_dbContextFactory = dbContextFactory;
_signature = signature;
_emailValidationKeyProvider = emailValidationKeyProvider;
_userManager = userManager;
_authManager = authManager;
}
public string MakeIndividualLinkKey(Guid linkId)
{
return _signature.Create(linkId);
}
public async Task<LinkValidationResult> ValidateAsync(string key, string email, EmployeeType employeeType)
{
var validationResult = new LinkValidationResult { Result = ValidationResult.Invalid };
var (commonWithRoomLinkResult, linkId) = ValidateCommonWithRoomLink(key);
if (commonWithRoomLinkResult != ValidationResult.Invalid)
{
validationResult.Result = commonWithRoomLinkResult;
validationResult.LinkType = InvitationLinkType.CommonWithRoom;
validationResult.LinkId = linkId;
return validationResult;
}
var commonLinkResult = _emailValidationKeyProvider.ValidateEmailKey(ConfirmType.LinkInvite.ToStringFast() + (int)employeeType,
key, _emailValidationKeyProvider.ValidEmailKeyInterval);
if (commonLinkResult != ValidationResult.Invalid)
{
validationResult.Result = commonLinkResult;
validationResult.LinkType = InvitationLinkType.Common;
return validationResult;
}
if (string.IsNullOrEmpty(email))
{
return validationResult;
}
var individualLinkResult = await ValidateIndividualLink(email, key, employeeType);
validationResult.Result = individualLinkResult;
validationResult.LinkType = InvitationLinkType.Individual;
return validationResult;
}
public LinkValidationResult Validate(string key, string email, EmployeeType employeeType)
{
return ValidateAsync(key, email, employeeType).Result;
}
private async Task<ValidationResult> ValidateIndividualLink(string email, string key, EmployeeType employeeType)
{
var result = _emailValidationKeyProvider.ValidateEmailKey(email + ConfirmType.LinkInvite.ToStringFast() + employeeType.ToStringFast(),
key, IndividualLinkExpirationInterval);
if (result != ValidationResult.Ok)
{
return result;
}
var user = _userManager.GetUserByEmail(email);
if (user.Equals(Constants.LostUser) || _authManager.GetUserPasswordStamp(user.Id) != DateTime.MinValue)
{
return ValidationResult.Invalid;
}
var visitMessage = await GetLinkVisitMessageAsync(email, key);
if (visitMessage == null)
{
SaveLinkVisitMessage(email, key);
}
else if (visitMessage.Date + _emailValidationKeyProvider.ValidVisitLinkInterval < DateTime.UtcNow)
{
return ValidationResult.Expired;
}
return result;
}
private (ValidationResult, Guid) ValidateCommonWithRoomLink(string key)
{
var linkId = _signature.Read<Guid>(key);
return linkId == default ? (ValidationResult.Invalid, default) : (ValidationResult.Ok, linkId);
}
private async Task<AuditEvent> GetLinkVisitMessageAsync(string email, string key)
{
using var context = _dbContextFactory.CreateDbContext();
var target = _messageTarget.Create(email);
var description = JsonConvert.SerializeObject(new[] { key });
var message = await context.AuditEvents.FirstOrDefaultAsync(a => a.Target == target.ToString() && a.DescriptionRaw == description);
return message;
}
private void SaveLinkVisitMessage(string email, string key)
{
var headers = _httpContextAccessor?.HttpContext?.Request.Headers;
var target = _messageTarget.Create(email);
_messageService.Send(headers, MessageAction.RoomInviteLinkUsed, target, key);
}
}
public enum InvitationLinkType
{
Common,
CommonWithRoom,
Individual
}
public class LinkValidationResult
{
public ValidationResult Result { get; set; }
public InvitationLinkType LinkType { get; set; }
public Guid LinkId { get; set; }
}

View File

@ -329,6 +329,11 @@ public class PermissionContext
return CheckPermissions(securityObject, null, actions);
}
public bool CheckPermissions(IAccount account, ISecurityObject securityObject, params IAction[] actions)
{
return PermissionResolver.Check(account, securityObject, null, actions);
}
public bool CheckPermissions(ISecurityObjectId objectId, ISecurityObjectProvider securityObjProvider, params IAction[] actions)
{
return PermissionResolver.Check(AuthContext.CurrentAccount, objectId, securityObjProvider, actions);

View File

@ -66,7 +66,7 @@
},
"files": {
"thirdparty": {
"enable": [ "box", "dropboxv2", "docusign", "google", "onedrive", "sharepoint", "nextcloud", "owncloud", "webdav", "kdrive", "yandex" ]
"enable": [ "box", "dropboxv2", "docusign", "google", "onedrive", "sharepoint", "nextcloud", "owncloud", "webdav", "kdrive" ]
},
"docservice": {
"coauthor-docs": [ ".pptx", ".ppsx", ".xlsx", ".csv", ".docx", ".docxf", ".oform", ".txt" ],

View File

@ -3,5 +3,6 @@
"HideInput": "Hide",
"Ready": "Done",
"UploadAndConvert": "Upload and convert files",
"Uploads": "Uploads"
"Uploads": "Uploads",
"CancelUpload": "The upload has been interrupted. Some files have not been uploaded."
}

View File

@ -3,5 +3,6 @@
"HideInput": "Скрыть ввод",
"Ready": "Готово",
"UploadAndConvert": "Загрузки и конвертация",
"Uploads": "Загрузки"
"Uploads": "Загрузки",
"CancelUpload": "Загрузка была прервана. Не удалось загрузить часть файлов."
}

View File

@ -61,10 +61,10 @@ const ChangeUserTypeEvent = ({
const onChangeUserType = () => {
onClosePanel();
updateUserType(toType, userIDs, peopleFilter, fromType)
.then(() => {
.then((users) => {
toastr.success(t("SuccessChangeUserType"));
successCallback && successCallback();
successCallback && successCallback(users);
})
.catch((err) => {
toastr.error(

View File

@ -67,6 +67,10 @@ class UploadPanelComponent extends React.Component {
this.onClose();
};
onCancelUpload = () => {
this.props.cancelUpload(this.props.t);
};
render() {
//console.log("UploadPanel render");
const {
@ -75,7 +79,6 @@ class UploadPanelComponent extends React.Component {
/* sharingPanelVisible, */ uploaded,
converted,
uploadDataFiles,
cancelUpload,
cancelConversion,
isUploading,
isUploadingAndConversion,
@ -126,7 +129,9 @@ class UploadPanelComponent extends React.Component {
iconName={ButtonCancelReactSvgUrl}
// color={theme.filesPanels.upload.color}
isClickable
onClick={uploaded ? cancelConversion : cancelUpload}
onClick={
uploaded ? cancelConversion : this.onCancelUpload
}
/>
)}
</div>

View File

@ -18,6 +18,8 @@ const Accounts = ({
isAdmin,
changeUserType,
canChangeUserType,
setSelection,
getPeopleListItem,
}) => {
const [statusLabel, setStatusLabel] = React.useState("");
const [isLoading, setIsLoading] = React.useState(false);
@ -104,7 +106,16 @@ const Accounts = ({
setIsLoading(false);
};
const onSuccess = () => {
const onSuccess = (users) => {
if (users) {
const items = [];
users.map((u) => items.push(getPeopleListItem(u)));
if (items.length === 1) {
setSelection(getPeopleListItem(items[0]));
} else {
setSelection(items);
}
}
setIsLoading(false);
};
@ -219,6 +230,8 @@ export default inject(({ auth, peopleStore, accessRightsStore }) => {
const { changeType: changeUserType, usersStore } = peopleStore;
const { canChangeUserType } = accessRightsStore;
const { setSelection } = auth.infoPanelStore;
return {
isOwner,
isAdmin,
@ -226,6 +239,8 @@ export default inject(({ auth, peopleStore, accessRightsStore }) => {
selfId,
canChangeUserType,
loading: usersStore.operationRunning,
getPeopleListItem: usersStore.getPeopleListItem,
setSelection,
};
})(
withTranslation([

View File

@ -216,6 +216,7 @@ export const StyledInfo = styled.div`
padding-left: 0;
font-size: 12px !important;
line-height: 16px !important;
max-width: 100%;
}
}

View File

@ -288,7 +288,7 @@ class FilesStore {
if (this.selectedFolderStore.id !== file.folderId) {
const movedToIndex = this.getFolderIndex(file.folderId);
if (movedToIndex) this.folders[movedToIndex].filesCount++;
if (movedToIndex > -1) this.folders[movedToIndex].filesCount++;
return;
}
@ -327,7 +327,7 @@ class FilesStore {
if (this.selectedFolderStore.id !== folder.parentId) {
const movedToIndex = this.getFolderIndex(folder.parentId);
if (movedToIndex) this.folders[movedToIndex].foldersCount++;
if (movedToIndex > -1) this.folders[movedToIndex].foldersCount++;
}
if (

View File

@ -164,7 +164,7 @@ class UploadDataStore {
return this.files.filter((f) => f.uniqueId === id);
};
cancelUpload = () => {
cancelUpload = (t) => {
let newFiles = [];
for (let i = 0; i < this.files.length; i++) {
@ -190,6 +190,8 @@ class UploadDataStore {
if (newUploadData.files.length === 0) this.setUploadPanelVisible(false);
this.setUploadData(newUploadData);
this.uploadedFilesHistory = newHistory;
toastr.info(t("CancelUpload"));
};
cancelConversion = () => {

View File

@ -122,13 +122,17 @@ class UsersStore {
toType = EmployeeType.User;
}
let users = null;
try {
await api.people.updateUserType(toType, userIds);
users = await api.people.updateUserType(toType, userIds);
} catch (e) {
throw new Error(e);
}
await this.getUsersList(filter);
return users;
};
updateProfileInUsers = async (updatedProfile) => {

View File

@ -83,8 +83,8 @@ public class FileStorageService<T> //: IFileStorageService
private readonly FileShareParamsHelper _fileShareParamsHelper;
private readonly EncryptionLoginProvider _encryptionLoginProvider;
private readonly CountRoomChecker _countRoomChecker;
private readonly RoomLinkService _roomLinkService;
private readonly DocSpaceLinkHelper _docSpaceLinkHelper;
private readonly InvitationLinkService _invitationLinkService;
private readonly InvitationLinkHelper _invitationLinkHelper;
private readonly StudioNotifyService _studioNotifyService;
public FileStorageService(
Global global,
@ -138,8 +138,8 @@ public class FileStorageService<T> //: IFileStorageService
FileShareParamsHelper fileShareParamsHelper,
EncryptionLoginProvider encryptionLoginProvider,
CountRoomChecker countRoomChecker,
RoomLinkService roomLinkService,
DocSpaceLinkHelper docSpaceLinkHelper,
InvitationLinkService invitationLinkService,
InvitationLinkHelper invitationLinkHelper,
StudioNotifyService studioNotifyService)
{
_global = global;
@ -193,8 +193,8 @@ public class FileStorageService<T> //: IFileStorageService
_fileShareParamsHelper = fileShareParamsHelper;
_encryptionLoginProvider = encryptionLoginProvider;
_countRoomChecker = countRoomChecker;
_roomLinkService = roomLinkService;
_docSpaceLinkHelper = docSpaceLinkHelper;
_invitationLinkService = invitationLinkService;
_invitationLinkHelper = invitationLinkHelper;
_studioNotifyService = studioNotifyService;
}
@ -2677,7 +2677,7 @@ public class FileStorageService<T> //: IFileStorageService
var aces = new List<AceWrapper>
{
new AceWrapper
new()
{
Access = share,
Id = linkId,
@ -2685,7 +2685,7 @@ public class FileStorageService<T> //: IFileStorageService
FileShareOptions = new FileShareOptions
{
Title = title,
ExpirationDate = DateTime.UtcNow.Add(_docSpaceLinkHelper.ExpirationInterval)
ExpirationDate = DateTime.UtcNow.Add(_invitationLinkHelper.IndividualLinkExpirationInterval)
}
}
};
@ -3316,7 +3316,7 @@ public class FileStorageService<T> //: IFileStorageService
continue;
}
var link = _roomLinkService.GetInvitationLink(user.Email, share.Access, _authContext.CurrentAccount.ID);
var link = _invitationLinkService.GetInvitationLink(user.Email, share.Access, _authContext.CurrentAccount.ID);
_studioNotifyService.SendEmailRoomInvite(user.Email, room.Title, link);
}
}

View File

@ -0,0 +1,198 @@
// (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 ASC.Core.Billing;
namespace ASC.Files.Core.VirtualRooms;
[Scope]
public class InvitationLinkService
{
private readonly CommonLinkUtility _commonLinkUtility;
private readonly IDaoFactory _daoFactory;
private readonly InvitationLinkHelper _invitationLinkHelper;
private readonly ITariffService _tariffService;
private readonly TenantManager _tenantManager;
private readonly AuthManager _authManager;
private readonly PermissionContext _permissionContext;
private readonly CountPaidUserChecker _countPaidUserChecker;
public InvitationLinkService(
CommonLinkUtility commonLinkUtility,
IDaoFactory daoFactory,
InvitationLinkHelper invitationLinkHelper,
ITariffService tariffService,
TenantManager tenantManager,
AuthManager authManager,
PermissionContext permissionContext,
CountPaidUserChecker countPaidUserChecker)
{
_commonLinkUtility = commonLinkUtility;
_daoFactory = daoFactory;
_invitationLinkHelper = invitationLinkHelper;
_tariffService = tariffService;
_tenantManager = tenantManager;
_authManager = authManager;
_permissionContext = permissionContext;
_countPaidUserChecker = countPaidUserChecker;
}
public string GetInvitationLink(Guid linkId, Guid createdBy)
{
var key = _invitationLinkHelper.MakeIndividualLinkKey(linkId);
return _commonLinkUtility.GetConfirmationUrl(key, ConfirmType.LinkInvite, createdBy) + "&toRoom=true";
}
public string GetInvitationLink(string email, FileShare share, Guid createdBy)
{
var type = FileSecurity.GetTypeByShare(share);
var link = _commonLinkUtility.GetConfirmationEmailUrl(email, ConfirmType.LinkInvite, type, createdBy)
+ $"&emplType={type:d}&toRoom=true";
return link;
}
public string GetInvitationLink(string email, EmployeeType employeeType, Guid createdBy)
{
var link = _commonLinkUtility.GetConfirmationEmailUrl(email, ConfirmType.LinkInvite, employeeType, createdBy)
+ $"&emplType={employeeType:d}";
return link;
}
public async Task<InvitationLinkData> GetProcessedLinkDataAsync(string key, string email)
{
return await GetProcessedLinkDataAsync(key, email, EmployeeType.All);
}
public async Task<InvitationLinkData> GetProcessedLinkDataAsync(string key, string email, EmployeeType employeeType, Guid userId = default)
{
Tenant tenant;
var linkData = new InvitationLinkData { Result = EmailValidationKeyProvider.ValidationResult.Invalid };
try
{
tenant = _tenantManager.GetCurrentTenant();
}
catch (Exception)
{
return linkData;
}
if (_tariffService.GetTariff(tenant.Id).State > TariffState.Paid)
{
return new InvitationLinkData { Result = EmailValidationKeyProvider.ValidationResult.Invalid };
}
if (userId != default)
{
var account = _authManager.GetAccountByID(tenant.Id, userId);
if (!_permissionContext.CheckPermissions(account, new UserSecurityProvider(employeeType), Constants.Action_AddRemoveUser))
{
return linkData;
}
}
var validationResult = await _invitationLinkHelper.ValidateAsync(key, email, employeeType);
linkData.Result = validationResult.Result;
linkData.LinkType = validationResult.LinkType;
linkData.EmployeeType = employeeType;
if (validationResult.LinkId == default)
{
if (!await CheckQuota(linkData.LinkType, employeeType))
{
linkData.Result = EmailValidationKeyProvider.ValidationResult.Invalid;
}
return linkData;
}
var record = await GetLinkRecordAsync(validationResult.LinkId);
if (record?.FileShareOptions == null)
{
linkData.Result = EmailValidationKeyProvider.ValidationResult.Invalid;
return linkData;
}
linkData.Result = record.FileShareOptions.ExpirationDate > DateTime.UtcNow ?
EmailValidationKeyProvider.ValidationResult.Ok : EmailValidationKeyProvider.ValidationResult.Expired;
linkData.Share = record.Share;
linkData.RoomId = record.EntryId.ToString();
linkData.EmployeeType = FileSecurity.GetTypeByShare(record.Share);
if (!await CheckQuota(linkData.LinkType, linkData.EmployeeType))
{
linkData.Result = EmailValidationKeyProvider.ValidationResult.Invalid;
}
return linkData;
}
private async Task<FileShareRecord> GetLinkRecordAsync(Guid linkId)
{
var securityDao = _daoFactory.GetSecurityDao<int>();
var share = await securityDao.GetSharesAsync(new[] { linkId })
.FirstOrDefaultAsync(s => s.SubjectType == SubjectType.InvitationLink);
return share;
}
private async Task<bool> CheckQuota(InvitationLinkType linkType, EmployeeType employeeType)
{
if (linkType == InvitationLinkType.Individual ||
employeeType is not (EmployeeType.DocSpaceAdmin or EmployeeType.RoomAdmin or EmployeeType.Collaborator))
{
return true;
}
try
{
await _countPaidUserChecker.CheckAppend();
}
catch (TenantQuotaException)
{
return false;
}
return true;
}
}
public class InvitationLinkData
{
public string RoomId { get; set; }
public FileShare Share { get; set; }
public InvitationLinkType LinkType { get; set; }
public EmployeeType EmployeeType { get; set; }
public EmailValidationKeyProvider.ValidationResult Result { get; set; }
public bool IsCorrect => Result == EmailValidationKeyProvider.ValidationResult.Ok;
}

View File

@ -1,139 +0,0 @@
// (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.Files.Core.VirtualRooms;
[Scope]
public class RoomLinkService
{
private readonly CommonLinkUtility _commonLinkUtility;
private readonly IDaoFactory _daoFactory;
private readonly DocSpaceLinkHelper _docSpaceLinksHelper;
public RoomLinkService(CommonLinkUtility commonLinkUtility, IDaoFactory daoFactory, DocSpaceLinkHelper docSpaceLinksHelper)
{
_commonLinkUtility = commonLinkUtility;
_daoFactory = daoFactory;
_docSpaceLinksHelper = docSpaceLinksHelper;
}
public string GetInvitationLink(Guid linkId, Guid createdBy)
{
var key = _docSpaceLinksHelper.MakeKey(linkId);
return _commonLinkUtility.GetConfirmationUrl(key, ConfirmType.LinkInvite, createdBy) + "&toRoom=true";
}
public string GetInvitationLink(string email, FileShare share, Guid createdBy)
{
var type = FileSecurity.GetTypeByShare(share);
var link = _commonLinkUtility.GetConfirmationEmailUrl(email, ConfirmType.LinkInvite, type, createdBy)
+ $"&emplType={type:d}&toRoom=true";
return link;
}
public string GetInvitationLink(string email, EmployeeType employeeType, Guid createdBy)
{
var link = _commonLinkUtility.GetConfirmationEmailUrl(email, ConfirmType.LinkInvite, employeeType, createdBy)
+ $"&emplType={employeeType:d}";
return link;
}
public async Task<LinkOptions> GetOptionsAsync(string key, string email)
{
return await GetOptionsAsync(key, email, EmployeeType.All);
}
public async Task<LinkOptions> GetOptionsAsync(string key, string email, EmployeeType employeeType)
{
var options = new LinkOptions();
var payload = _docSpaceLinksHelper.Parse(key);
if (payload != default)
{
options.LinkType = LinkType.InvitationToRoom;
var record = await GetRecordAsync(payload);
if (record == null)
{
return options;
}
options.IsCorrect = true;
options.RoomId = record.EntryId.ToString();
options.Share = record.Share;
options.Id = record.Subject;
options.EmployeeType = FileSecurity.GetTypeByShare(record.Share);
}
else if (_docSpaceLinksHelper.ValidateEmailLink(email, key, employeeType) == EmailValidationKeyProvider.ValidationResult.Ok)
{
options.IsCorrect = true;
options.LinkType = LinkType.InvitationByEmail;
options.EmployeeType = employeeType;
}
else if (_docSpaceLinksHelper.ValidateExtarnalLink(key, employeeType) == EmailValidationKeyProvider.ValidationResult.Ok)
{
options.LinkType = LinkType.DefaultInvitation;
options.IsCorrect = true;
options.EmployeeType = employeeType;
}
return options;
}
private async Task<FileShareRecord> GetRecordAsync(Guid key)
{
var securityDao = _daoFactory.GetSecurityDao<int>();
var share = await securityDao.GetSharesAsync(new[] { key })
.Where(s => s.SubjectType == SubjectType.InvitationLink)
.FirstOrDefaultAsync();
return share;
}
}
public class LinkOptions
{
public Guid Id { get; set; }
public string RoomId { get; set; }
public FileShare Share { get; set; }
public LinkType LinkType { get; set; }
public EmployeeType EmployeeType { get; set; }
public bool IsCorrect { get; set; }
}
[EnumExtensions]
public enum LinkType
{
DefaultInvitation,
InvitationByEmail,
InvitationToRoom,
}

View File

@ -126,6 +126,9 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
var folder = await FolderDao.GetFolderAsync(folderId);
var isRoom = DocSpaceHelper.IsRoom(folder.FolderType);
var canDelete = await FilesSecurity.CanDeleteAsync(folder);
checkPermissions = isRoom ? !canDelete : checkPermissions;
T canCalculate = default;
if (folder == null)
{
@ -136,7 +139,7 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
{
this[Err] = FilesCommonResource.ErrorMassage_SecurityException_DeleteFolder;
}
else if (!_ignoreException && checkPermissions && !await FilesSecurity.CanDeleteAsync(folder))
else if (!_ignoreException && checkPermissions && !canDelete)
{
canCalculate = FolderDao.CanCalculateSubitems(folderId) ? default : folderId;
@ -144,8 +147,6 @@ class FileDeleteOperation<T> : FileOperation<FileDeleteOperationData<T>, T>
}
else
{
checkPermissions = isRoom ? false : checkPermissions;
canCalculate = FolderDao.CanCalculateSubitems(folderId) ? default : folderId;
await fileMarker.RemoveMarkAsNewForAllAsync(folder);

View File

@ -42,7 +42,7 @@ public class FileSharingAceHelper<T>
private readonly FileSharingHelper _fileSharingHelper;
private readonly FileTrackerHelper _fileTracker;
private readonly FilesSettingsHelper _filesSettingsHelper;
private readonly RoomLinkService _roomLinkService;
private readonly InvitationLinkService _invitationLinkService;
private readonly StudioNotifyService _studioNotifyService;
private readonly UsersInRoomChecker _usersInRoomChecker;
private readonly UserManagerWrapper _userManagerWrapper;
@ -62,7 +62,7 @@ public class FileSharingAceHelper<T>
FileSharingHelper fileSharingHelper,
FileTrackerHelper fileTracker,
FilesSettingsHelper filesSettingsHelper,
RoomLinkService roomLinkService,
InvitationLinkService invitationLinkService,
StudioNotifyService studioNotifyService,
ILoggerProvider loggerProvider,
UsersInRoomChecker usersInRoomChecker,
@ -81,7 +81,7 @@ public class FileSharingAceHelper<T>
_fileSharingHelper = fileSharingHelper;
_fileTracker = fileTracker;
_filesSettingsHelper = filesSettingsHelper;
_roomLinkService = roomLinkService;
_invitationLinkService = invitationLinkService;
_studioNotifyService = studioNotifyService;
_usersInRoomChecker = usersInRoomChecker;
_logger = loggerProvider.CreateLogger("ASC.Files");
@ -226,7 +226,7 @@ public class FileSharingAceHelper<T>
if (emailInvite)
{
var link = _roomLinkService.GetInvitationLink(w.Email, share, _authContext.CurrentAccount.ID);
var link = _invitationLinkService.GetInvitationLink(w.Email, share, _authContext.CurrentAccount.ID);
_studioNotifyService.SendEmailRoomInvite(w.Email, entry.Title, link);
_logger.Debug(link);
}
@ -431,7 +431,7 @@ public class FileSharing
private readonly IDaoFactory _daoFactory;
private readonly FileSharingHelper _fileSharingHelper;
private readonly FilesSettingsHelper _filesSettingsHelper;
private readonly RoomLinkService _roomLinkService;
private readonly InvitationLinkService _invitationLinkService;
public FileSharing(
Global global,
@ -444,7 +444,7 @@ public class FileSharing
IDaoFactory daoFactory,
FileSharingHelper fileSharingHelper,
FilesSettingsHelper filesSettingsHelper,
RoomLinkService roomLinkService)
InvitationLinkService invitationLinkService)
{
_global = global;
_fileSecurity = fileSecurity;
@ -456,7 +456,7 @@ public class FileSharing
_fileSharingHelper = fileSharingHelper;
_filesSettingsHelper = filesSettingsHelper;
_logger = logger;
_roomLinkService = roomLinkService;
_invitationLinkService = invitationLinkService;
}
public Task<bool> CanSetAccessAsync<T>(FileEntry<T> entry)
@ -545,7 +545,7 @@ public class FileSharing
if (isRoom && r.IsLink)
{
w.Link = _roomLinkService.GetInvitationLink(r.Subject, r.Owner);
w.Link = _invitationLinkService.GetInvitationLink(r.Subject, _authContext.CurrentAccount.ID);
w.SubjectGroup = true;
w.CanEditAccess = false;
}
@ -568,7 +568,7 @@ public class FileSharing
var w = new AceWrapper
{
Id = id,
Link = _roomLinkService.GetInvitationLink(id, _authContext.CurrentAccount.ID),
Link = _invitationLinkService.GetInvitationLink(id, _authContext.CurrentAccount.ID),
SubjectGroup = true,
Access = FileShare.Read,
Owner = false

View File

@ -591,7 +591,7 @@ public class VirtualRoomsCommonController : ApiControllerBase
private readonly RoomLogoManager _roomLogoManager;
private readonly SetupInfo _setupInfo;
private readonly FileSizeComment _fileSizeComment;
private readonly RoomLinkService _roomLinkService;
private readonly InvitationLinkService _invitationLinkService;
private readonly AuthContext _authContext;
public VirtualRoomsCommonController(
@ -607,7 +607,7 @@ public class VirtualRoomsCommonController : ApiControllerBase
FileSizeComment fileSizeComment,
FolderDtoHelper folderDtoHelper,
FileDtoHelper fileDtoHelper,
RoomLinkService roomLinkService,
InvitationLinkService invitationLinkService,
AuthContext authContext) : base(folderDtoHelper, fileDtoHelper)
{
_fileStorageServiceInt = fileStorageServiceInt;
@ -620,7 +620,7 @@ public class VirtualRoomsCommonController : ApiControllerBase
_roomLogoManager = roomLogoManager;
_setupInfo = setupInfo;
_fileSizeComment = fileSizeComment;
_roomLinkService = roomLinkService;
_invitationLinkService = invitationLinkService;
_authContext = authContext;
}
@ -846,18 +846,18 @@ public class VirtualRoomsCommonController : ApiControllerBase
[HttpPost("rooms/accept")]
public async Task SetSecurityByLink(AcceptInvitationDto inDto)
{
var options = await _roomLinkService.GetOptionsAsync(inDto.Key, null);
var linkData = await _invitationLinkService.GetProcessedLinkDataAsync(inDto.Key, null);
if (!options.IsCorrect)
if (!linkData.IsCorrect)
{
throw new SecurityException(FilesCommonResource.ErrorMessage_InvintationLink);
}
var aces = new List<AceWrapper>
{
new AceWrapper
new()
{
Access = options.Share,
Access = linkData.Share,
Id = _authContext.CurrentAccount.ID
}
};
@ -867,7 +867,7 @@ public class VirtualRoomsCommonController : ApiControllerBase
InvitationLink = true
};
if (int.TryParse(options.RoomId, out var id))
if (int.TryParse(linkData.RoomId, out var id))
{
var aceCollection = new AceCollection<int>
{
@ -885,7 +885,7 @@ public class VirtualRoomsCommonController : ApiControllerBase
{
Aces = aces,
Files = Array.Empty<string>(),
Folders = new[] { options.RoomId },
Folders = new[] { linkData.RoomId },
AdvancedSettings = settings
};

View File

@ -24,6 +24,7 @@
// 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 ASC.Api.Core.Security;
using ASC.Common.Log;
namespace ASC.People.Api;
@ -58,7 +59,7 @@ public class UserController : PeopleControllerBase
private readonly AuthContext _authContext;
private readonly SetupInfo _setupInfo;
private readonly SettingsManager _settingsManager;
private readonly RoomLinkService _roomLinkService;
private readonly InvitationLinkService _invitationLinkService;
private readonly FileSecurity _fileSecurity;
private readonly IQuotaService _quotaService;
private readonly CountPaidUserChecker _countPaidUserChecker;
@ -99,7 +100,7 @@ public class UserController : PeopleControllerBase
IHttpClientFactory httpClientFactory,
IHttpContextAccessor httpContextAccessor,
SettingsManager settingsManager,
RoomLinkService roomLinkService,
InvitationLinkService invitationLinkService,
FileSecurity fileSecurity,
UsersQuotaSyncOperation usersQuotaSyncOperation,
CountPaidUserChecker countPaidUserChecker,
@ -134,7 +135,7 @@ public class UserController : PeopleControllerBase
_authContext = authContext;
_setupInfo = setupInfo;
_settingsManager = settingsManager;
_roomLinkService = roomLinkService;
_invitationLinkService = invitationLinkService;
_fileSecurity = fileSecurity;
_countPaidUserChecker = countPaidUserChecker;
_countUserChecker = activeUsersChecker;
@ -207,26 +208,26 @@ public class UserController : PeopleControllerBase
{
_apiContext.AuthByClaim();
var options = inDto.FromInviteLink ? await _roomLinkService.GetOptionsAsync(inDto.Key, inDto.Email, inDto.Type) : null;
if (options is { IsCorrect: false })
var linkData = inDto.FromInviteLink ? await _invitationLinkService.GetProcessedLinkDataAsync(inDto.Key, inDto.Email, inDto.Type) : null;
if (linkData is { IsCorrect: false })
{
throw new SecurityException(FilesCommonResource.ErrorMessage_InvintationLink);
}
if (options != null)
{
_permissionContext.DemandPermissions(new UserSecurityProvider(Guid.Empty, options.EmployeeType) ,Constants.Action_AddRemoveUser);
if (linkData != null)
{
_permissionContext.DemandPermissions(new UserSecurityProvider(Guid.Empty, linkData.EmployeeType) ,Constants.Action_AddRemoveUser);
}
else
{
_permissionContext.DemandPermissions(Constants.Action_AddRemoveUser);
}
inDto.Type = options?.EmployeeType ?? inDto.Type;
inDto.Type = linkData?.EmployeeType ?? inDto.Type;
var user = new UserInfo();
var byEmail = options?.LinkType == LinkType.InvitationByEmail;
var byEmail = linkData?.LinkType == InvitationLinkType.Individual;
if (byEmail)
{
@ -276,7 +277,7 @@ public class UserController : PeopleControllerBase
_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,
inDto.FromInviteLink && options is { IsCorrect: true }, true, true, byEmail);
inDto.FromInviteLink && linkData is { IsCorrect: true }, true, true, byEmail);
await UpdateDepartments(inDto.Department, user);
@ -285,19 +286,19 @@ public class UserController : PeopleControllerBase
await UpdatePhotoUrl(inDto.Files, user);
}
if (options is { LinkType: LinkType.InvitationToRoom })
if (linkData is { LinkType: InvitationLinkType.CommonWithRoom })
{
var success = int.TryParse(options.RoomId, out var id);
var success = int.TryParse(linkData.RoomId, out var id);
if (success)
{
await _usersInRoomChecker.CheckAppend();
await _fileSecurity.ShareAsync(id, FileEntryType.Folder, user.Id, options.Share);
await _fileSecurity.ShareAsync(id, FileEntryType.Folder, user.Id, linkData.Share);
}
else
{
await _usersInRoomChecker.CheckAppend();
await _fileSecurity.ShareAsync(options.RoomId, FileEntryType.Folder, user.Id, options.Share);
await _fileSecurity.ShareAsync(linkData.RoomId, FileEntryType.Folder, user.Id, linkData.Share);
}
}
@ -324,7 +325,7 @@ public class UserController : PeopleControllerBase
}
var user = await _userManagerWrapper.AddInvitedUserAsync(invite.Email, invite.Type);
var link = _roomLinkService.GetInvitationLink(user.Email, invite.Type, _authContext.CurrentAccount.ID);
var link = _invitationLinkService.GetInvitationLink(user.Email, invite.Type, _authContext.CurrentAccount.ID);
_studioNotifyService.SendDocSpaceInvite(user.Email, link);
_logger.Debug(link);
@ -742,7 +743,7 @@ public class UserController : PeopleControllerBase
continue;
}
var link = _roomLinkService.GetInvitationLink(user.Email, type, _authContext.CurrentAccount.ID);
var link = _invitationLinkService.GetInvitationLink(user.Email, type, _authContext.CurrentAccount.ID);
_studioNotifyService.SendDocSpaceInvite(user.Email, link);
}
else

View File

@ -70,7 +70,7 @@ public class AuthenticationController : ControllerBase
private readonly EmailValidationKeyProvider _emailValidationKeyProvider;
private readonly BruteForceLoginManager _bruteForceLoginManager;
private readonly ILogger<AuthenticationController> _logger;
private readonly RoomLinkService _roomLinkService;
private readonly InvitationLinkService _invitationLinkService;
public AuthenticationController(
UserManager userManager,
@ -108,7 +108,7 @@ public class AuthenticationController : ControllerBase
TfaAppAuthSettingsHelper tfaAppAuthSettingsHelper,
EmailValidationKeyProvider emailValidationKeyProvider,
ILogger<AuthenticationController> logger,
RoomLinkService roomLinkService)
InvitationLinkService invitationLinkService)
{
_userManager = userManager;
_tenantManager = tenantManager;
@ -145,7 +145,7 @@ public class AuthenticationController : ControllerBase
_tfaAppAuthSettingsHelper = tfaAppAuthSettingsHelper;
_emailValidationKeyProvider = emailValidationKeyProvider;
_logger = logger;
_roomLinkService = roomLinkService;
_invitationLinkService = invitationLinkService;
}
[AllowNotPayment]
@ -321,17 +321,14 @@ public class AuthenticationController : ControllerBase
[HttpPost("confirm")]
public async Task<ValidationResult> CheckConfirm(EmailValidationKeyModel inDto)
{
if (inDto.Type == ConfirmType.LinkInvite)
if (inDto.Type != ConfirmType.LinkInvite)
{
var options = await _roomLinkService.GetOptionsAsync(inDto.Key, inDto.Email, inDto.EmplType ?? default);
if (options.LinkType == LinkType.InvitationToRoom && !options.IsCorrect)
{
return ValidationResult.Invalid;
}
return _emailValidationKeyModelHelper.Validate(inDto);
}
return _emailValidationKeyModelHelper.Validate(inDto);
var linkData = await _invitationLinkService.GetProcessedLinkDataAsync(inDto.Key, inDto.Email, inDto.EmplType ?? default, inDto.UiD ?? default);
return linkData.Result;
}
[AllowNotPayment]