diff --git a/WebsitePanel/Lib/References/Microsoft/Microsoft.CobaltCore.dll b/WebsitePanel/Lib/References/Microsoft/Microsoft.CobaltCore.dll new file mode 100644 index 00000000..3ef8a60d Binary files /dev/null and b/WebsitePanel/Lib/References/Microsoft/Microsoft.CobaltCore.dll differ diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/Entities/OfficeOnlineCollection.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/Entities/OfficeOnlineCollection.cs index 4ecb0c7f..47e614ed 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/Entities/OfficeOnlineCollection.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/Entities/OfficeOnlineCollection.cs @@ -13,11 +13,13 @@ namespace WebsitePanel.WebDav.Core.Config.Entities { IsEnabled = ConfigSection.OfficeOnline.IsEnabled; Url = ConfigSection.OfficeOnline.Url; + CobaltFileTtl = ConfigSection.OfficeOnline.CobaltFileTtl; _officeExtensions = ConfigSection.OfficeOnline.Cast().ToList(); } public bool IsEnabled { get; private set; } public string Url { get; private set; } + public int CobaltFileTtl { get; private set; } public IEnumerator GetEnumerator() { diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebConfigSections/OfficeOnlineElement.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebConfigSections/OfficeOnlineElement.cs index 1f836eb3..4df1d52a 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebConfigSections/OfficeOnlineElement.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebConfigSections/OfficeOnlineElement.cs @@ -5,7 +5,8 @@ namespace WebsitePanel.WebDavPortal.WebConfigSections public class OfficeOnlineElement : ConfigurationElement { private const string ExtensionKey = "extension"; - private const string OwaOpenerKey = "owaOpener"; + private const string OwaViewKey = "OwaView"; + private const string OwaEditorKey = "OwaEditor"; [ConfigurationProperty(ExtensionKey, IsKey = true, IsRequired = true)] public string Extension @@ -14,11 +15,18 @@ namespace WebsitePanel.WebDavPortal.WebConfigSections set { this[ExtensionKey] = value; } } - [ConfigurationProperty(OwaOpenerKey, IsKey = true, IsRequired = true)] - public string OwaOpener + [ConfigurationProperty(OwaViewKey, IsKey = true, IsRequired = true)] + public string OwaView { - get { return this[OwaOpenerKey].ToString(); } - set { this[OwaOpenerKey] = value; } + get { return this[OwaViewKey].ToString(); } + set { this[OwaViewKey] = value; } + } + + [ConfigurationProperty(OwaEditorKey, IsKey = true, IsRequired = true)] + public string OwaEditor + { + get { return this[OwaEditorKey].ToString(); } + set { this[OwaEditorKey] = value; } } } } \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebConfigSections/OfficeOnlineElementCollection.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebConfigSections/OfficeOnlineElementCollection.cs index c937e18d..03f570c5 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebConfigSections/OfficeOnlineElementCollection.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Config/WebConfigSections/OfficeOnlineElementCollection.cs @@ -8,6 +8,7 @@ namespace WebsitePanel.WebDavPortal.WebConfigSections { private const string UrlKey = "url"; private const string IsEnabledKey = "isEnabled"; + private const string CobaltFileTtlKey = "cobaltFileTtl"; [ConfigurationProperty(UrlKey, IsKey = true, IsRequired = true)] public string Url @@ -23,6 +24,13 @@ namespace WebsitePanel.WebDavPortal.WebConfigSections set { this[IsEnabledKey] = value; } } + [ConfigurationProperty(CobaltFileTtlKey, IsKey = true, IsRequired = true)] + public int CobaltFileTtl + { + get { return int.Parse(this[CobaltFileTtlKey].ToString()); } + set { this[CobaltFileTtlKey] = value; } + } + protected override ConfigurationElement CreateNewElement() { return new OfficeOnlineElement(); diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Entities/Owa/CheckFileInfo.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Entities/Owa/CheckFileInfo.cs index e4283ac9..bac8e2d5 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Entities/Owa/CheckFileInfo.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Entities/Owa/CheckFileInfo.cs @@ -13,6 +13,24 @@ namespace WebsitePanel.WebDav.Core.Entities.Owa public long Size { get; set; } [DataMember] public string Version { get; set; } + [DataMember] + public bool SupportsCoauth { get; set; } + [DataMember] + public bool SupportsCobalt { get; set; } + [DataMember] + public bool SupportsFolders { get; set; } + [DataMember] + public bool SupportsLocks { get; set; } + [DataMember] + public bool SupportsScenarioLinks { get; set; } + [DataMember] + public bool SupportsSecureStore { get; set; } + [DataMember] + public bool SupportsUpdate { get; set; } + [DataMember] + public bool UserCanWrite { get; set; } + //[DataMember] + //public bool ReadOnly { get; set; } //[DataMember] //public string SHA256 { get; set; } @@ -85,20 +103,7 @@ namespace WebsitePanel.WebDav.Core.Entities.Owa //[DataMember] //public string SignoutUrl { get; set; } - //[DataMember] - //public bool SupportsCoauth { get; set; } - //[DataMember] - //public bool SupportsCobalt { get; set; } - //[DataMember] - //public bool SupportsFolders { get; set; } - //[DataMember] - //public bool SupportsLocks { get; set; } - //[DataMember] - //public bool SupportsScenarioLinks { get; set; } - //[DataMember] - //public bool SupportsSecureStore { get; set; } - //[DataMember] - //public bool SupportsUpdate { get; set; } + //[DataMember] //public string TenantId { get; set; } //[DataMember] diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Managers/IWebDavManager.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Managers/IWebDavManager.cs index f6611a66..cfa6a8b5 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Managers/IWebDavManager.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Managers/IWebDavManager.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.IO; using System.Web; using WebsitePanel.WebDav.Core.Client; @@ -10,6 +11,8 @@ namespace WebsitePanel.WebDav.Core.Interfaces.Managers bool IsFile(string path); byte[] GetFileBytes(string path); void UploadFile(string path, HttpPostedFileBase file); + void UploadFile(string path, byte[] bytes); + void UploadFile(string path, Stream stream); IResource GetResource(string path); string GetFileUrl(string path); void DeleteResource(string path); diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Owa/ICobaltManager.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Owa/ICobaltManager.cs new file mode 100644 index 00000000..c50c129f --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Owa/ICobaltManager.cs @@ -0,0 +1,10 @@ +using System.IO; +using Cobalt; + +namespace WebsitePanel.WebDav.Core.Interfaces.Owa +{ + public interface ICobaltManager + { + Atom ProcessRequest(int accessTokenId, Stream requestStream); + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Owa/IWopiFileManager.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Owa/IWopiFileManager.cs new file mode 100644 index 00000000..4dd65f51 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Owa/IWopiFileManager.cs @@ -0,0 +1,12 @@ +using Cobalt; + +namespace WebsitePanel.WebDav.Core.Interfaces.Owa +{ + public interface IWopiFileManager + { + CobaltFile Create(int accessTokenId); + CobaltFile Get(int accessTokenId); + bool Add(int accessTokenId, CobaltFile file); + bool Delete(int accessTokenId); + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Storages/IKeyValueStorage.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Storages/IKeyValueStorage.cs new file mode 100644 index 00000000..a92a0568 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Storages/IKeyValueStorage.cs @@ -0,0 +1,9 @@ +namespace WebsitePanel.WebDav.Core.Interfaces.Storages +{ + public interface IKeyValueStorage + { + TV Get(string id); + bool Add(string id, TV value); + bool Delete(string id); + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Storages/ITtlStorage.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Storages/ITtlStorage.cs new file mode 100644 index 00000000..f41dfa9f --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Interfaces/Storages/ITtlStorage.cs @@ -0,0 +1,9 @@ +using WebsitePanel.Ecommerce.EnterpriseServer; + +namespace WebsitePanel.WebDav.Core.Interfaces.Storages +{ + public interface ITtlStorage : IKeyValueStorage + { + void SetTtl(string id, TV value); + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Managers/WebDavManager.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Managers/WebDavManager.cs index 9575f2d2..4dca416b 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Managers/WebDavManager.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Managers/WebDavManager.cs @@ -143,6 +143,36 @@ namespace WebsitePanel.WebDav.Core.Managers resource.Upload(bytes); } + public void UploadFile(string path, byte[] bytes) + { + var resource = new WebDavResource(); + + var fileUrl = new Uri(WebDavAppConfigManager.Instance.WebdavRoot) + .Append(WspContext.User.OrganizationId) + .Append(path); + + resource.SetHref(fileUrl); + resource.SetCredentials(new NetworkCredential(WspContext.User.Login, _cryptography.Decrypt(WspContext.User.EncryptedPassword))); + + resource.Upload(bytes); + } + + public void UploadFile(string path, Stream stream) + { + var resource = new WebDavResource(); + + var fileUrl = new Uri(WebDavAppConfigManager.Instance.WebdavRoot) + .Append(WspContext.User.OrganizationId) + .Append(path); + + resource.SetHref(fileUrl); + resource.SetCredentials(new NetworkCredential(WspContext.User.Login, _cryptography.Decrypt(WspContext.User.EncryptedPassword))); + + var bytes = ReadFully(stream); + + resource.Upload(bytes); + } + public void DeleteResource(string path) { path = RemoveLeadingFromPath(path, "office365"); diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/CobaltFileManager.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/CobaltFileManager.cs new file mode 100644 index 00000000..e8627682 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/CobaltFileManager.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Runtime.Caching; +using Cobalt; +using WebsitePanel.WebDav.Core.Client; +using WebsitePanel.WebDav.Core.Config; +using WebsitePanel.WebDav.Core.Interfaces.Managers; +using WebsitePanel.WebDav.Core.Interfaces.Owa; +using WebsitePanel.WebDav.Core.Interfaces.Storages; + +namespace WebsitePanel.WebDav.Core.Owa +{ + public class CobaltFileManager : IWopiFileManager + { + private readonly IWebDavManager _webDavManager; + private readonly IAccessTokenManager _tokenManager; + private readonly ITtlStorage _storage; + + public CobaltFileManager(IWebDavManager webDavManager, IAccessTokenManager tokenManager, ITtlStorage storage) + { + _webDavManager = webDavManager; + + _tokenManager = tokenManager; + _storage = storage; + } + + public CobaltFile Create(int accessTokenId) + { + var disposal = new DisposalEscrow(accessTokenId.ToString(CultureInfo.InvariantCulture)); + + var content = new CobaltFilePartitionConfig + { + IsNewFile = true, + HostBlobStore = new TemporaryHostBlobStore(new TemporaryHostBlobStore.Config(), disposal, accessTokenId + @".Content"), + cellSchemaIsGenericFda = true, + CellStorageConfig = new CellStorageConfig(), + Schema = CobaltFilePartition.Schema.ShreddedCobalt, + PartitionId = FilePartitionId.Content + }; + + var coauth = new CobaltFilePartitionConfig + { + IsNewFile = true, + HostBlobStore = new TemporaryHostBlobStore(new TemporaryHostBlobStore.Config(), disposal, accessTokenId + @".CoauthMetadata"), + cellSchemaIsGenericFda = false, + CellStorageConfig = new CellStorageConfig(), + Schema = CobaltFilePartition.Schema.ShreddedCobalt, + PartitionId = FilePartitionId.CoauthMetadata + }; + + var wacupdate = new CobaltFilePartitionConfig + { + IsNewFile = true, + HostBlobStore = new TemporaryHostBlobStore(new TemporaryHostBlobStore.Config(), disposal, accessTokenId + @".WordWacUpdate"), + cellSchemaIsGenericFda = false, + CellStorageConfig = new CellStorageConfig(), + Schema = CobaltFilePartition.Schema.ShreddedCobalt, + PartitionId = FilePartitionId.WordWacUpdate + }; + + var partitionConfs = new Dictionary + { + {FilePartitionId.Content, content}, + {FilePartitionId.WordWacUpdate, wacupdate}, + {FilePartitionId.CoauthMetadata, coauth} + }; + + var cobaltFile = new CobaltFile(disposal, partitionConfs, new CobaltHostLockingStore(), null); + + var token = _tokenManager.GetToken(accessTokenId); + + var fileBytes = _webDavManager.GetFileBytes(token.FilePath); + + var atom = new AtomFromByteArray(fileBytes); + + Cobalt.Metrics o1; + cobaltFile.GetCobaltFilePartition(FilePartitionId.Content).SetStream(RootId.Default.Value, atom, out o1); + cobaltFile.GetCobaltFilePartition(FilePartitionId.Content).GetStream(RootId.Default.Value).Flush(); + + Add(accessTokenId, cobaltFile); + + return cobaltFile; + } + + public CobaltFile Get(int accessTokenId) + { + return _storage.Get(GetFileKey(accessTokenId)); + } + + public bool Add(int accessTokenId, CobaltFile file) + { + return _storage.Add(GetFileKey(accessTokenId), file); + } + + public bool Delete(int accessTokenId) + { + return _storage.Delete(GetFileKey(accessTokenId)); + } + + private string GetFileKey(int accessTokenId) + { + return string.Format("{0}", accessTokenId); + } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/CobaltHostLockingStore.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/CobaltHostLockingStore.cs new file mode 100644 index 00000000..e1f20d14 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/CobaltHostLockingStore.cs @@ -0,0 +1,245 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using Cobalt; +using WebsitePanel.WebDav.Core; + +namespace WebsitePanel.WebDav.Core.Owa +{ + public class CobaltHostLockingStore : HostLockingStore + { + public override WhoAmIRequest.OutputType HandleWhoAmI(WhoAmIRequest.InputType input) + { + WhoAmIRequest.OutputType result = new WhoAmIRequest.OutputType(); + result.UserIsAnonymous = WspContext.User == null; + if (WspContext.User != null) + { + result.UserEmailAddress = WspContext.User.Login; + result.UserLogin = WspContext.User.Login; + result.UserName = WspContext.User.DisplayName; + } + + return result; + } + + public override ServerTimeRequest.OutputType HandleServerTime(ServerTimeRequest.InputType input) + { + ServerTimeRequest.OutputType result = new ServerTimeRequest.OutputType(); + result.ServerTime = DateTime.UtcNow; + + return result; + } + + public override LockAndCheckOutStatusRequest.OutputType HandleLockAndCheckOutStatus(LockAndCheckOutStatusRequest.InputType input) + { + LockAndCheckOutStatusRequest.OutputType result = new LockAndCheckOutStatusRequest.OutputType(); + result.LockType = 1U; + result.CheckOutType = 0U; + + return result; + } + + public override GetExclusiveLockRequest.OutputType HandleGetExclusiveLock(GetExclusiveLockRequest.InputType input) + { + GetExclusiveLockRequest.OutputType result = new GetExclusiveLockRequest.OutputType(); + + return result; + } + + public override RefreshExclusiveLockRequest.OutputType HandleRefreshExclusiveLock(RefreshExclusiveLockRequest.InputType input) + { + RefreshExclusiveLockRequest.OutputType result = new RefreshExclusiveLockRequest.OutputType(); + + return result; + } + + public override CheckExclusiveLockAvailabilityRequest.OutputType HandleCheckExclusiveLockAvailability(CheckExclusiveLockAvailabilityRequest.InputType input) + { + CheckExclusiveLockAvailabilityRequest.OutputType result = new CheckExclusiveLockAvailabilityRequest.OutputType(); + + return result; + } + + public override ConvertExclusiveLockToSchemaLockRequest.OutputType HandleConvertExclusiveLockToSchemaLock(ConvertExclusiveLockToSchemaLockRequest.InputType input, int protocolMajorVersion, int protocolMinorVersion) + { + ConvertExclusiveLockToSchemaLockRequest.OutputType result = new ConvertExclusiveLockToSchemaLockRequest.OutputType(); + + return result; + } + + public override ConvertExclusiveLockWithCoauthTransitionRequest.OutputType HandleConvertExclusiveLockWithCoauthTransition(ConvertExclusiveLockWithCoauthTransitionRequest.InputType input, int protocolMajorVersion, int protocolMinorVersion) + { + ConvertExclusiveLockWithCoauthTransitionRequest.OutputType result = new ConvertExclusiveLockWithCoauthTransitionRequest.OutputType(); + + return result; + } + + public override GetSchemaLockRequest.OutputType HandleGetSchemaLock(GetSchemaLockRequest.InputType input, int protocolMajorVersion, int protocolMinorVersion) + { + GetSchemaLockRequest.OutputType result = new GetSchemaLockRequest.OutputType(); + + return result; + } + + + public override ReleaseExclusiveLockRequest.OutputType HandleReleaseExclusiveLock(ReleaseExclusiveLockRequest.InputType input) + { + ReleaseExclusiveLockRequest.OutputType result = new ReleaseExclusiveLockRequest.OutputType(); + + return result; + } + + public override ReleaseSchemaLockRequest.OutputType HandleReleaseSchemaLock(ReleaseSchemaLockRequest.InputType input, int protocolMajorVersion, int protocolMinorVersion) + { + ReleaseSchemaLockRequest.OutputType result = new ReleaseSchemaLockRequest.OutputType(); + + return result; + } + + public override RefreshSchemaLockRequest.OutputType HandleRefreshSchemaLock(RefreshSchemaLockRequest.InputType input, int protocolMajorVersion, int protocolMinorVersion) + { + RefreshSchemaLockRequest.OutputType result = new RefreshSchemaLockRequest.OutputType(); + result.Lock = LockType.SchemaLock; + + return result; + } + + public override ConvertSchemaLockToExclusiveLockRequest.OutputType HandleConvertSchemaLockToExclusiveLock(ConvertSchemaLockToExclusiveLockRequest.InputType input) + { + ConvertSchemaLockToExclusiveLockRequest.OutputType result = new ConvertSchemaLockToExclusiveLockRequest.OutputType(); + + return result; + } + + public override CheckSchemaLockAvailabilityRequest.OutputType HandleCheckSchemaLockAvailability(CheckSchemaLockAvailabilityRequest.InputType input) + { + CheckSchemaLockAvailabilityRequest.OutputType result = new CheckSchemaLockAvailabilityRequest.OutputType(); + + return result; + } + + public override JoinCoauthoringRequest.OutputType HandleJoinCoauthoring(JoinCoauthoringRequest.InputType input, int protocolMajorVersion, int protocolMinorVersion) + { + JoinCoauthoringRequest.OutputType result = new JoinCoauthoringRequest.OutputType(); + result.Lock = LockType.SchemaLock; + result.CoauthStatus = CoauthStatusType.Alone; + result.TransitionId = Guid.NewGuid(); + + return result; + } + + public override ExitCoauthoringRequest.OutputType HandleExitCoauthoring(ExitCoauthoringRequest.InputType input, int protocolMajorVersion, int protocolMinorVersion) + { + ExitCoauthoringRequest.OutputType result = new ExitCoauthoringRequest.OutputType(); + + return result; + } + + public override RefreshCoauthoringSessionRequest.OutputType HandleRefreshCoauthoring(RefreshCoauthoringSessionRequest.InputType input, int protocolMajorVersion, int protocolMinorVersion) + { + RefreshCoauthoringSessionRequest.OutputType result = new RefreshCoauthoringSessionRequest.OutputType(); + result.Lock = LockType.SchemaLock; + result.CoauthStatus = CoauthStatusType.Alone; + + return result; + } + + public override ConvertCoauthLockToExclusiveLockRequest.OutputType HandleConvertCoauthLockToExclusiveLock(ConvertCoauthLockToExclusiveLockRequest.InputType input) + { + ConvertCoauthLockToExclusiveLockRequest.OutputType result = new ConvertCoauthLockToExclusiveLockRequest.OutputType(); + + return result; + } + + public override CheckCoauthLockAvailabilityRequest.OutputType HandleCheckCoauthLockAvailability(CheckCoauthLockAvailabilityRequest.InputType input) + { + CheckCoauthLockAvailabilityRequest.OutputType result = new CheckCoauthLockAvailabilityRequest.OutputType(); + + return result; + } + + public override MarkCoauthTransitionCompleteRequest.OutputType HandleMarkCoauthTransitionComplete(MarkCoauthTransitionCompleteRequest.InputType input) + { + MarkCoauthTransitionCompleteRequest.OutputType result = new MarkCoauthTransitionCompleteRequest.OutputType(); + + return result; + } + + public override GetCoauthoringStatusRequest.OutputType HandleGetCoauthoringStatus(GetCoauthoringStatusRequest.InputType input) + { + GetCoauthoringStatusRequest.OutputType result = new GetCoauthoringStatusRequest.OutputType(); + result.CoauthStatus = CoauthStatusType.Alone; + + return result; + } + + public override Dictionary QueryEditorsTable() + { + return new Dictionary(); + } + + public override JoinEditingSessionRequest.OutputType HandleJoinEditingSession(JoinEditingSessionRequest.InputType input) + { + JoinEditingSessionRequest.OutputType result = new JoinEditingSessionRequest.OutputType(); + + return result; + } + + public override RefreshEditingSessionRequest.OutputType HandleRefreshEditingSession(RefreshEditingSessionRequest.InputType input) + { + RefreshEditingSessionRequest.OutputType result = new RefreshEditingSessionRequest.OutputType(); + + return result; + } + + public override LeaveEditingSessionRequest.OutputType HandleLeaveEditingSession(LeaveEditingSessionRequest.InputType input) + { + LeaveEditingSessionRequest.OutputType result = new LeaveEditingSessionRequest.OutputType(); + + return result; + } + + public override UpdateEditorMetadataRequest.OutputType HandleUpdateEditorMetadata(UpdateEditorMetadataRequest.InputType input) + { + UpdateEditorMetadataRequest.OutputType result = new UpdateEditorMetadataRequest.OutputType(); + + return result; + } + + public override RemoveEditorMetadataRequest.OutputType HandleRemoveEditorMetadata(RemoveEditorMetadataRequest.InputType input) + { + RemoveEditorMetadataRequest.OutputType result = new RemoveEditorMetadataRequest.OutputType(); + + return result; + } + + public override ulong GetEditorsTableWaterline() + { + return 0; + } + + public override AmIAloneRequest.OutputType HandleAmIAlone(AmIAloneRequest.InputType input) + { + AmIAloneRequest.OutputType result = new AmIAloneRequest.OutputType(); + result.AmIAlone = true; + + return result; + } + + public override DocMetaInfoRequest.OutputType HandleDocMetaInfo(DocMetaInfoRequest.InputType input) + { + DocMetaInfoRequest.OutputType result = new DocMetaInfoRequest.OutputType(); + + return result; + } + + public override VersionsRequest.OutputType HandleVersions(VersionsRequest.InputType input) + { + VersionsRequest.OutputType result = new VersionsRequest.OutputType(); + result.Enabled = false; + + return result; + } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/CobaltManager.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/CobaltManager.cs new file mode 100644 index 00000000..986bb5f0 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/CobaltManager.cs @@ -0,0 +1,56 @@ +using System; +using System.IO; +using Cobalt; +using WebsitePanel.WebDav.Core.Interfaces.Managers; +using WebsitePanel.WebDav.Core.Interfaces.Owa; + +namespace WebsitePanel.WebDav.Core.Owa +{ + public class CobaltManager : ICobaltManager + { + private readonly IWebDavManager _webDavManager; + private readonly IWopiFileManager _fileManager; + private readonly IAccessTokenManager _tokenManager; + + public CobaltManager(IWebDavManager webDavManager, IWopiFileManager fileManager, IAccessTokenManager tokenManager) + { + _webDavManager = webDavManager; + _fileManager = fileManager; + _tokenManager = tokenManager; + } + + public Atom ProcessRequest(int accessTokenId, Stream requestStream) + { + var token = _tokenManager.GetToken(accessTokenId); + + var atomRequest = new AtomFromStream(requestStream); + + var requestBatch = new RequestBatch(); + + var cobaltFile = _fileManager.Get(accessTokenId) ?? _fileManager.Create(accessTokenId); + + Object ctx; + ProtocolVersion protocolVersion; + + requestBatch.DeserializeInputFromProtocol(atomRequest, out ctx, out protocolVersion); + + cobaltFile.CobaltEndpoint.ExecuteRequestBatch(requestBatch); + + foreach (var request in requestBatch.Requests) + { + if (request.GetType() == typeof(PutChangesRequest) && request.PartitionId == FilePartitionId.Content && request.CompletedSuccessfully) + { + using (var saveStream = new MemoryStream()) + { + GenericFdaStream myCobaltStream = new GenericFda(cobaltFile.CobaltEndpoint, null).GetContentStream(); + myCobaltStream.CopyTo(saveStream); + + _webDavManager.UploadFile(token.FilePath, saveStream.ToArray()); + } + } + } + + return requestBatch.SerializeOutputToProtocol(protocolVersion); + } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/WopiServer.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/WopiServer.cs index c55f3e53..357615f3 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/WopiServer.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Owa/WopiServer.cs @@ -28,9 +28,17 @@ namespace WebsitePanel.WebDav.Core.Owa var cFileInfo = new CheckFileInfo { BaseFileName = resource.DisplayName, - OwnerId = @"4257508bfe174aa28b461536d8b6b648", + OwnerId = @"4257508bfe174aa28b461536d8b6b648",// WspContext.User.Login, Size = resource.ContentLength, - Version = @"%22%7B59CCD75F%2D0687%2D4F86%2DBBCF%2D059126640640%7D%2C1%22" + Version = DateTime.Now.ToString("s"), + SupportsCoauth = false, + SupportsCobalt = true, + SupportsFolders = true, + SupportsLocks = false, + SupportsScenarioLinks = false, + SupportsSecureStore = false, + SupportsUpdate = true, + UserCanWrite = true }; return cFileInfo; diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Storages/CacheTtlStorage.cs b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Storages/CacheTtlStorage.cs new file mode 100644 index 00000000..f7400098 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/Storages/CacheTtlStorage.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Caching; +using Microsoft.Web.Services3.Design; +using WebsitePanel.WebDav.Core.Config; +using WebsitePanel.WebDav.Core.Interfaces.Storages; + +namespace WebsitePanel.WebDav.Core.Storages +{ + public class CacheTtlStorage : ITtlStorage + { + private static readonly ObjectCache Cache; + + static CacheTtlStorage() + { + Cache = MemoryCache.Default; + } + + public TV Get(string id) + { + var value = (TV)Cache[id]; + + if (!EqualityComparer.Default.Equals(value, default(TV))) + { + SetTtl(id, value); + } + + return value; + } + + public bool Add(string id, TV value) + { + return Cache.Add(id, value, DateTime.Now.AddMinutes(WebDavAppConfigManager.Instance.OfficeOnline.CobaltFileTtl)); + } + + public bool Delete(string id) + { + if (Cache.Any(x => x.Key == id)) + { + Cache.Remove(id); + + return true; + } + + return false; + } + + public void SetTtl(string id, TV value) + { + Cache.Set(id, value, DateTime.Now.AddMinutes(WebDavAppConfigManager.Instance.OfficeOnline.CobaltFileTtl)); + } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/WebsitePanel.WebDav.Core.csproj b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/WebsitePanel.WebDav.Core.csproj index 5f810c52..81a0c36c 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDav.Core/WebsitePanel.WebDav.Core.csproj +++ b/WebsitePanel/Sources/WebsitePanel.WebDav.Core/WebsitePanel.WebDav.Core.csproj @@ -35,6 +35,9 @@ ..\packages\log4net.2.0.0\lib\net40-full\log4net.dll + + ..\..\Lib\References\Microsoft\Microsoft.CobaltCore.dll + True ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll @@ -49,6 +52,7 @@ + C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Web.dll @@ -128,6 +132,11 @@ + + + + + @@ -139,6 +148,8 @@ + + @@ -159,6 +170,7 @@ + diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/App_Data/SiteSettings.config b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/App_Data/SiteSettings.config index 91a5447b..4fc71ff6 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/App_Data/SiteSettings.config +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/App_Data/SiteSettings.config @@ -3,7 +3,7 @@ WebsitePanel - http://127.0.0.1:9555 + http://localhost:9002 UserCulture UserTheme diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/App_Start/RouteConfig.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/App_Start/RouteConfig.cs index c757a379..bf2fe2a4 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/App_Start/RouteConfig.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/App_Start/RouteConfig.cs @@ -26,22 +26,6 @@ namespace WebsitePanel.WebDavPortal #endregion - #region Owa - - routes.MapRoute( - name: OwaRouteNames.GetFile, - url: "owa/wopi*/files/{accessTokenId}/contents", - defaults: new { controller = "Owa", action = "GetFile" } - ); - - routes.MapRoute( - name: OwaRouteNames.CheckFileInfo, - url: "owa/wopi*/files/{accessTokenId}", - defaults: new { controller = "Owa", action = "CheckFileInfo" } - ); - - #endregion - routes.MapRoute( name: FileSystemRouteNames.DeleteFiles, url: "files-group-action/delete", diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/App_Start/WebApiConfig.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/App_Start/WebApiConfig.cs new file mode 100644 index 00000000..188e4fcd --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/App_Start/WebApiConfig.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Http; +using WebsitePanel.WebDavPortal.DependencyInjection; +using WebsitePanel.WebDavPortal.UI.Routes; + +namespace WebsitePanel.WebDavPortal.App_Start +{ + public class WebApiConfig + { + public static void Register(HttpConfiguration configuration) + { + #region Owa + + configuration.Routes.MapHttpRoute( + name: OwaRouteNames.GetFile, + routeTemplate: "owa/wopi*/files/{accessTokenId}/contents", + defaults: new {controller = "Owa", action = "GetFile"}); + + configuration.Routes.MapHttpRoute( + name: OwaRouteNames.CheckFileInfo, + routeTemplate: "owa/wopi*/files/{accessTokenId}", + defaults: new {controller = "Owa", action = "CheckFileInfo"}); + + #endregion + + + + configuration.Routes.MapHttpRoute("API Default", "api/{controller}/{id}", + new { id = RouteParameter.Optional }); + + configuration.DependencyResolver = new NinjectDependecyResolver(); + } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Configurations/ActionSelectors/OwaActionSelector.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Configurations/ActionSelectors/OwaActionSelector.cs new file mode 100644 index 00000000..3326a6cb --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Configurations/ActionSelectors/OwaActionSelector.cs @@ -0,0 +1,24 @@ +using System.Linq; +using System.Web.Http.Controllers; + +namespace WebsitePanel.WebDavPortal.Configurations.ActionSelectors +{ + public class OwaActionSelector : ApiControllerActionSelector + { + public override HttpActionDescriptor SelectAction(HttpControllerContext controllerContext) + { + if (controllerContext.Request.Headers.Contains("X-WOPI-Override")) + { + var matchingHeaders = controllerContext.Request.Headers.GetValues("X-WOPI-Override"); + var headerValue = (matchingHeaders == null) ? "" : (matchingHeaders.FirstOrDefault() ?? ""); + + if (!string.IsNullOrEmpty(headerValue)) + { + controllerContext.RouteData.Values["action"] = headerValue; + } + } + + return base.SelectAction(controllerContext); + } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Constraints/OrganizationRouteConstraint.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Configurations/Constraints/OrganizationRouteConstraint.cs similarity index 100% rename from WebsitePanel/Sources/WebsitePanel.WebDavPortal/Constraints/OrganizationRouteConstraint.cs rename to WebsitePanel/Sources/WebsitePanel.WebDavPortal/Configurations/Constraints/OrganizationRouteConstraint.cs diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Configurations/ControllerConfigurations/OwaControllerConfiguration.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Configurations/ControllerConfigurations/OwaControllerConfiguration.cs new file mode 100644 index 00000000..82820c75 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Configurations/ControllerConfigurations/OwaControllerConfiguration.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Http.Controllers; +using WebsitePanel.WebDavPortal.Configurations.ActionSelectors; + +namespace WebsitePanel.WebDavPortal.Configurations.ControllerConfigurations +{ + public class OwaControllerConfiguration : Attribute, IControllerConfiguration + { + public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor) + { + controllerSettings.Services.Replace(typeof(IHttpActionSelector), new OwaActionSelector()); + } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/Api/OwaController.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/Api/OwaController.cs new file mode 100644 index 00000000..79bf0713 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/Api/OwaController.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Web.Hosting; +using System.Web.Http; +using Cobalt; +using WebsitePanel.EnterpriseServer.Base.HostedSolution; +using WebsitePanel.WebDav.Core; +using WebsitePanel.WebDav.Core.Client; +using WebsitePanel.WebDav.Core.Entities.Owa; +using WebsitePanel.WebDav.Core.Interfaces.Managers; +using WebsitePanel.WebDav.Core.Interfaces.Owa; +using WebsitePanel.WebDav.Core.Interfaces.Security; +using WebsitePanel.WebDav.Core.Security.Cryptography; +using WebsitePanel.WebDav.Core.Wsp.Framework; +using WebsitePanel.WebDavPortal.Configurations.ControllerConfigurations; + +namespace WebsitePanel.WebDavPortal.Controllers.Api +{ + [Authorize] + [OwaControllerConfiguration] + public class OwaController : ApiController + { + private readonly IWopiServer _wopiServer; + private readonly IWebDavManager _webDavManager; + private readonly IAuthenticationService _authenticationService; + private readonly IAccessTokenManager _tokenManager; + private readonly ICryptography _cryptography; + //private static WopiSession _session; + private readonly ICobaltManager _cobaltManager; + + public OwaController(IWopiServer wopiServer, IWebDavManager webDavManager, IAuthenticationService authenticationService, IAccessTokenManager tokenManager, ICryptography cryptography, ICobaltManager cobaltManager) + { + _wopiServer = wopiServer; + _webDavManager = webDavManager; + _authenticationService = authenticationService; + _tokenManager = tokenManager; + _cryptography = cryptography; + _cobaltManager = cobaltManager; + } + + [HttpGet] + public CheckFileInfo CheckFileInfo(int accessTokenId) + { + var token = _tokenManager.GetToken(accessTokenId); + + var fileInfo = _wopiServer.GetCheckFileInfo(token.FilePath); + + return fileInfo; + } + + public HttpResponseMessage GetFile(int accessTokenId) + { + var token = _tokenManager.GetToken(accessTokenId); + + var bytes = _webDavManager.GetFileBytes(token.FilePath); + + var result = new HttpResponseMessage(HttpStatusCode.OK); + + var stream = new MemoryStream(bytes); + + result.Content = new StreamContent(stream); + result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); + + return result; + } + + [HttpPost] + public async Task Cobalt(int accessTokenId) + { + var memoryStream = new MemoryStream(); + + await Request.Content.CopyToAsync(memoryStream); + + var responseBatch = _cobaltManager.ProcessRequest(accessTokenId, memoryStream); + + var correlationId = Request.Headers.GetValues("X-WOPI-CorrelationID").FirstOrDefault() ?? ""; + + var response = new HttpResponseMessage(); + + response.Content = new PushStreamContent( + (stream, content, context) => + { + responseBatch.CopyTo(stream); + stream.Close(); + }); + + response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); + response.Content.Headers.ContentLength = responseBatch.Length; + + response.Headers.Add("X-WOPI-CorellationID", correlationId); + response.Headers.Add("request-id", correlationId); + + return response; + } + + [HttpPost] + public HttpResponseMessage Lock(int accessTokenId) + { + return new HttpResponseMessage(HttpStatusCode.OK); + } + + [HttpPost] + public HttpResponseMessage UnLock(int accessTokenId) + { + return new HttpResponseMessage(HttpStatusCode.OK); + } + + [HttpPost] + public async Task Put(int accessTokenId) + { + var token = _tokenManager.GetToken(accessTokenId); + + var bytes = await Request.Content.ReadAsByteArrayAsync(); + + _webDavManager.UploadFile(token.FilePath, bytes); + + return new HttpResponseMessage(HttpStatusCode.OK); + } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/FileSystemController.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/FileSystemController.cs index 65d352e6..28b6e19f 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/FileSystemController.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/FileSystemController.cs @@ -14,6 +14,7 @@ using WebsitePanel.WebDav.Core.Config; using WebsitePanel.WebDav.Core.Exceptions; using WebsitePanel.WebDav.Core.Interfaces.Managers; using WebsitePanel.WebDav.Core.Interfaces.Security; +using WebsitePanel.WebDav.Core.Security.Authorization.Enums; using WebsitePanel.WebDav.Core.Security.Cryptography; using WebsitePanel.WebDavPortal.CustomAttributes; using WebsitePanel.WebDavPortal.Extensions; @@ -84,14 +85,21 @@ namespace WebsitePanel.WebDavPortal.Controllers public ActionResult ShowOfficeDocument(string org, string pathPart = "") { + var permissions = _webDavAuthorizationService.GetPermissions(WspContext.User, pathPart); + var owaOpener = WebDavAppConfigManager.Instance.OfficeOnline.Single(x => x.Extension == Path.GetExtension(pathPart)); string fileUrl = WebDavAppConfigManager.Instance.WebdavRoot+ org + "/" + pathPart.TrimStart('/'); var accessToken = _tokenManager.CreateToken(WspContext.User, pathPart); - string wopiSrc = Server.UrlDecode(Url.RouteUrl(OwaRouteNames.CheckFileInfo, new { accessTokenId = accessToken.Id }, Request.Url.Scheme)); + var urlPart = Url.HttpRouteUrl(OwaRouteNames.CheckFileInfo, new {accessTokenId = accessToken.Id}); + var url = new Uri(Request.Url, urlPart).ToString(); - var uri = string.Format("{0}/{1}?WOPISrc={2}&access_token={3}", WebDavAppConfigManager.Instance.OfficeOnline.Url, owaOpener.OwaOpener, Server.UrlEncode(wopiSrc), Server.UrlEncode(accessToken.AccessToken.ToString("N"))); + string wopiSrc = Server.UrlDecode(url); + + string owaOpenerUri = permissions.HasFlag(WebDavPermissions.Write) ? owaOpener.OwaEditor : owaOpener.OwaView; + + var uri = string.Format("{0}/{1}WOPISrc={2}&access_token={3}", WebDavAppConfigManager.Instance.OfficeOnline.Url, owaOpenerUri, Server.UrlEncode(wopiSrc), Server.UrlEncode(accessToken.AccessToken.ToString("N"))); return View(new OfficeOnlineModel(uri, new Uri(fileUrl).Segments.Last())); } diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/OwaController.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/OwaController.cs deleted file mode 100644 index 2a56a3c0..00000000 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/OwaController.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Web; -using System.Web.Mvc; -using WebsitePanel.EnterpriseServer.Base.HostedSolution; -using WebsitePanel.WebDav.Core.Interfaces.Managers; -using WebsitePanel.WebDav.Core.Interfaces.Owa; -using WebsitePanel.WebDav.Core.Interfaces.Security; -using WebsitePanel.WebDav.Core.Security.Cryptography; -using WebsitePanel.WebDav.Core.Wsp.Framework; - -namespace WebsitePanel.WebDavPortal.Controllers -{ - [AllowAnonymous] - public class OwaController : Controller - { - private readonly IWopiServer _wopiServer; - private readonly IWebDavManager _webDavManager; - private readonly IAuthenticationService _authenticationService; - private readonly IAccessTokenManager _tokenManager; - private readonly ICryptography _cryptography; - private WebDavAccessToken _token; - - - public OwaController(IWopiServer wopiServer, IWebDavManager webDavManager, IAuthenticationService authenticationService, IAccessTokenManager tokenManager, ICryptography cryptography) - { - _wopiServer = wopiServer; - _webDavManager = webDavManager; - _authenticationService = authenticationService; - _tokenManager = tokenManager; - _cryptography = cryptography; - } - - public ActionResult CheckFileInfo(int accessTokenId) - { - if (!CheckAccess(accessTokenId)) - { - return new HttpStatusCodeResult(HttpStatusCode.NoContent); - } - - var fileInfo = _wopiServer.GetCheckFileInfo(_token.FilePath); - - return Json(fileInfo, JsonRequestBehavior.AllowGet); - } - - public ActionResult GetFile(int accessTokenId) - { - if (!CheckAccess(accessTokenId)) - { - return new HttpStatusCodeResult(HttpStatusCode.NoContent); - } - - return _wopiServer.GetFile((_token.FilePath)); - } - - protected override void OnActionExecuting(ActionExecutingContext filterContext) - { - base.OnActionExecuting(filterContext); - - if (!string.IsNullOrEmpty(Request["access_token"])) - { - var guid = Guid.Parse((Request["access_token"])); - - _tokenManager.ClearExpiredTokens(); - - _token = _tokenManager.GetToken(guid); - - var user = WSP.Services.ExchangeServer.GetAccount(_token.ItemId, _token.AccountId); - - _authenticationService.LogIn(user.UserPrincipalName, _cryptography.Decrypt(_token.AuthData)); - } - } - - private bool CheckAccess(int accessTokenId) - { - if (_token == null || accessTokenId != _token.Id) - { - return false; - } - - return true; - } - } -} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/DependencyInjection/NinjectDependecyResolver.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/DependencyInjection/NinjectDependecyResolver.cs index 480cb19e..2a03ef97 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/DependencyInjection/NinjectDependecyResolver.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/DependencyInjection/NinjectDependecyResolver.cs @@ -1,4 +1,5 @@ -using Ninject; +using System.Web.Http.Dependencies; +using Ninject; using System; using System.Collections.Generic; using System.Linq; @@ -7,13 +8,14 @@ using System.Web.Mvc; namespace WebsitePanel.WebDavPortal.DependencyInjection { - public class NinjectDependecyResolver : IDependencyResolver + public class NinjectDependecyResolver : System.Web.Mvc.IDependencyResolver, System.Web.Http.Dependencies.IDependencyResolver { IKernel kernal; public NinjectDependecyResolver() { kernal = new StandardKernel(new NinjectSettings { AllowNullInjection = true }); + AddBindings(); } @@ -27,9 +29,19 @@ namespace WebsitePanel.WebDavPortal.DependencyInjection return kernal.GetAll(serviceType); } + public IDependencyScope BeginScope() + { + return this; + } + private void AddBindings() { PortalDependencies.Configure(kernal); } + + public void Dispose() + { + + } } } \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/DependencyInjection/PortalDependencies.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/DependencyInjection/PortalDependencies.cs index 5589c751..704e5ee2 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/DependencyInjection/PortalDependencies.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/DependencyInjection/PortalDependencies.cs @@ -3,11 +3,13 @@ using System.Web.SessionState; using WebsitePanel.WebDav.Core.Interfaces.Managers; using WebsitePanel.WebDav.Core.Interfaces.Owa; using WebsitePanel.WebDav.Core.Interfaces.Security; +using WebsitePanel.WebDav.Core.Interfaces.Storages; using WebsitePanel.WebDav.Core.Managers; using WebsitePanel.WebDav.Core.Owa; using WebsitePanel.WebDav.Core.Security.Authentication; using WebsitePanel.WebDav.Core.Security.Authorization; using WebsitePanel.WebDav.Core.Security.Cryptography; +using WebsitePanel.WebDav.Core.Storages; using WebsitePanel.WebDavPortal.DependencyInjection.Providers; namespace WebsitePanel.WebDavPortal.DependencyInjection @@ -22,7 +24,10 @@ namespace WebsitePanel.WebDavPortal.DependencyInjection kernel.Bind().To(); kernel.Bind().To(); kernel.Bind().To(); + kernel.Bind().To(); kernel.Bind().To(); + kernel.Bind().To(); + kernel.Bind().To(); } } } \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Global.asax.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Global.asax.cs index 14649e45..8a658982 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Global.asax.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Global.asax.cs @@ -1,15 +1,18 @@ using System; using System.Threading; using System.Web; +using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using System.Web.Script.Serialization; using System.Web.Security; +using System.Web.SessionState; using WebsitePanel.WebDav.Core.Config; using WebsitePanel.WebDav.Core.Interfaces.Security; using WebsitePanel.WebDav.Core.Security.Authentication.Principals; using WebsitePanel.WebDav.Core.Security.Cryptography; +using WebsitePanel.WebDavPortal.App_Start; using WebsitePanel.WebDavPortal.Controllers; using WebsitePanel.WebDavPortal.DependencyInjection; using WebsitePanel.WebDavPortal.HttpHandlers; @@ -22,8 +25,10 @@ namespace WebsitePanel.WebDavPortal { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); + WebApiConfig.Register(GlobalConfiguration.Configuration); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); + GlobalConfiguration.Configuration.MessageHandlers.Add(new AccessTokenHandler()); DependencyResolver.SetResolver(new NinjectDependecyResolver()); @@ -57,31 +62,47 @@ namespace WebsitePanel.WebDavPortal Response.End(); } + protected void Application_BeginRequest(object sender, EventArgs e) + { + var s = HttpContext.Current.Request; + } + protected void Application_PostAuthenticateRequest(Object sender, EventArgs e) { - var contextWrapper = new HttpContextWrapper(Context); - HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName]; - - var authService = DependencyResolver.Current.GetService(); - var cryptography = DependencyResolver.Current.GetService(); - - if (authCookie != null) + if (!IsOwaRequest()) { - FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value); + var contextWrapper = new HttpContextWrapper(Context); + HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName]; - var serializer = new JavaScriptSerializer(); + var authService = DependencyResolver.Current.GetService(); + var cryptography = DependencyResolver.Current.GetService(); - var principalSerialized = serializer.Deserialize(authTicket.UserData); - - authService.LogIn(principalSerialized.Login, cryptography.Decrypt(principalSerialized.EncryptedPassword)); - - if (!contextWrapper.Request.IsAjaxRequest()) + if (authCookie != null) { - SetAuthenticationExpirationTicket(); + FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value); + + var serializer = new JavaScriptSerializer(); + + var principalSerialized = serializer.Deserialize(authTicket.UserData); + + authService.LogIn(principalSerialized.Login, + cryptography.Decrypt(principalSerialized.EncryptedPassword)); + + if (!contextWrapper.Request.IsAjaxRequest()) + { + SetAuthenticationExpirationTicket(); + } } } } + + + private bool IsOwaRequest() + { + return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith("~/owa"); + } + public static void SetAuthenticationExpirationTicket() { var expirationDateTimeInUtc = DateTime.UtcNow.AddMinutes(FormsAuthentication.Timeout.TotalMinutes).AddSeconds(1); diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/HttpHandlers/AccessTokenHandler.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/HttpHandlers/AccessTokenHandler.cs new file mode 100644 index 00000000..648f10a3 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/HttpHandlers/AccessTokenHandler.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using System.Web; +using System.Web.Mvc; +using WebsitePanel.WebDav.Core.Interfaces.Managers; +using WebsitePanel.WebDav.Core.Interfaces.Security; +using WebsitePanel.WebDav.Core.Security.Cryptography; +using WebsitePanel.WebDav.Core.Wsp.Framework; + +namespace WebsitePanel.WebDavPortal.HttpHandlers +{ + public class AccessTokenHandler : DelegatingHandler + { + private const string Bearer = "Bearer "; + + protected override async Task SendAsync( + HttpRequestMessage request, CancellationToken cancellationToken) + { + if (request.Headers.Contains("Authorization")) + { + var tokenString = request.Headers.GetValues("Authorization").First(); + if (!string.IsNullOrEmpty(tokenString) && tokenString.StartsWith(Bearer)) + { + try + { + var accessToken = tokenString.Substring(Bearer.Length - 1); + + var tokenManager = DependencyResolver.Current.GetService(); + + var guid = Guid.Parse(accessToken); + tokenManager.ClearExpiredTokens(); + + var token = tokenManager.GetToken(guid); + + if (token != null) + { + var authenticationService = DependencyResolver.Current.GetService(); + var cryptography = DependencyResolver.Current.GetService(); + + + var user = WSP.Services.ExchangeServer.GetAccount(token.ItemId, token.AccountId); + + authenticationService.LogIn(user.UserPrincipalName, cryptography.Decrypt(token.AuthData)); + } + } + catch (Exception) + { + } + } + } + + return await + base.SendAsync(request, cancellationToken); + } + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Web.config b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Web.config index e71f301b..4028afc3 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Web.config +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Web.config @@ -61,13 +61,13 @@ - - - - - - - + + + + + + + + @@ -101,7 +102,7 @@ - + diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/WebsitePanel.WebDavPortal.csproj b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/WebsitePanel.WebDavPortal.csproj index 2ea96586..59aa1925 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/WebsitePanel.WebDavPortal.csproj +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/WebsitePanel.WebDavPortal.csproj @@ -51,6 +51,9 @@ ..\packages\log4net.2.0.0\lib\net40-full\log4net.dll + + ..\..\Lib\References\Microsoft\Microsoft.CobaltCore.dll + ..\..\Lib\Microsoft.Web.Services3.dll @@ -71,6 +74,10 @@ + + False + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.2\lib\net45\System.Net.Http.Formatting.dll + @@ -81,6 +88,14 @@ False ..\packages\Microsoft.AspNet.WebPages.3.2.2\lib\net45\System.Web.Helpers.dll + + False + ..\packages\Microsoft.AspNet.WebApi.Core.5.2.2\lib\net45\System.Web.Http.dll + + + False + ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.2\lib\net45\System.Web.Http.WebHost.dll + False ..\packages\Microsoft.AspNet.Mvc.5.2.2\lib\net45\System.Web.Mvc.dll @@ -140,11 +155,15 @@ - + + + + - + + @@ -158,6 +177,7 @@ Global.asax + diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/packages.config b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/packages.config index f297089d..e930827a 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/packages.config +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/packages.config @@ -9,6 +9,10 @@ + + + +