private void HandleResponseForClientCache(PortalContextInitInfo initInfo) { var context = HttpContext.Current; // binaryhandler if (_binaryHandlerClientCacheMaxAge.HasValue && initInfo.BinaryHandlerRequestedNodeHead != null) { HttpHeaderTools.SetCacheControlHeaders(_binaryHandlerClientCacheMaxAge.Value); // handle is-modified-since requests only for requests coming from proxy if (PortalContext.ProxyIPs.Contains(context.Request.UserHostAddress)) { HttpHeaderTools.EndResponseForClientCache(initInfo.BinaryHandlerRequestedNodeHead.ModificationDate); } return; } // images, and other content requested with their path (e.g. /Root/Global/images/myimage.png) string extension = System.IO.Path.GetExtension(context.Request.Url.AbsolutePath).ToLower(); if (_clientCacheConfig != null && _clientCacheConfig.ContainsKey(extension)) { // get requested nodehead if (initInfo.RequestedNodeHead == null) { return; } int seconds = _clientCacheConfig[extension]; HttpHeaderTools.SetCacheControlHeaders(seconds); // handle is-modified-since requests only for requests coming from proxy if (PortalContext.ProxyIPs.Contains(context.Request.UserHostAddress)) { HttpHeaderTools.EndResponseForClientCache(initInfo.RequestedNodeHead.ModificationDate); } return; } // applications if (initInfo.RequestedNodeHead != null) { Application app = null; // elevate to sysadmin, as we are startupuser here, and group 'everyone' should have permissions to application without elevation using (new SystemAccount()) { app = ApplicationStorage.Instance.GetApplication(string.IsNullOrEmpty(initInfo.ActionName) ? "browse" : initInfo.ActionName, initInfo.RequestedNodeHead, initInfo.DeviceName); } if (app != null) { var maxAge = app.NumericMaxAge; var cacheControl = app.CacheControlEnumValue; if (cacheControl.HasValue && maxAge.HasValue) { HttpHeaderTools.SetCacheControlHeaders(maxAge.Value, cacheControl.Value); if (PortalContext.ProxyIPs.Contains(context.Request.UserHostAddress)) { HttpHeaderTools.EndResponseForClientCache(initInfo.RequestedNodeHead.ModificationDate); } } return; } } }
private void OnAuthorize(object sender, EventArgs e) { // At this point the user has at least some permissions for the requested content. This means // we can respond with a 304 status if the content has not changed, without opening a security hole. // This value could be set earlier by the HandleResponseForClientCache method // (e.g. because of binaryhandler or application client cache values). if (PortalContext.Current.ModificationDateForClient.HasValue) { HttpHeaderTools.EndResponseForClientCache(PortalContext.Current.ModificationDateForClient.Value); } // check requested nodehead if (PortalContext.Current.ContextNodeHead == null) { return; } // Check if the requested content is executable (e.g. an aspx file) and has the correct file type. We must // call this here instead of OnEnter because the user must be authenticated for the check algorithm to work. CheckExecutableType(PortalContext.Current.ContextNodeHead, PortalContext.Current.ActionName); var modificationDate = PortalContext.Current.ContextNodeHead.ModificationDate; // If action name is given, do not do shortcircuit (eg. myimage.jpg?action=Edit // should be a server-rendered page) - except if this is an image resizer application. if (!string.IsNullOrEmpty(PortalContext.Current.ActionName)) { var remapAction = PortalContext.Current.CurrentAction as RemapHttpAction; if (remapAction?.HttpHandlerNode == null) { return; } if (!remapAction.HttpHandlerNode.GetNodeType().IsInstaceOfOrDerivedFrom("ImgResizeApplication")) { return; } // check if the image resizer app was modified since the last request if (remapAction.HttpHandlerNode.ModificationDate > modificationDate) { modificationDate = remapAction.HttpHandlerNode.ModificationDate; } } // set cache values for images, js/css files var cacheSetting = GetCacheHeaderSetting(PortalContext.Current.RequestedUri, PortalContext.Current.ContextNodeHead); if (cacheSetting.HasValue) { HttpHeaderTools.SetCacheControlHeaders(cacheSetting.Value); // in case of preview images do NOT return 304, because _undetectable_ permission changes // (on the image or on one of its parents) may change the preview image (e.g. display redaction or not). if (DocumentPreviewProvider.Current == null || !DocumentPreviewProvider.Current.IsPreviewOrThumbnailImage(PortalContext.Current.ContextNodeHead)) { // end response, if the content has not changed since the value posted by the client HttpHeaderTools.EndResponseForClientCache(modificationDate); } } }
public void OnAuthenticateRequest(object sender, EventArgs e) { var application = sender as HttpApplication; var context = GetContext(sender); //HttpContext.Current; var request = GetRequest(sender); bool anonymAuthenticated; var basicAuthenticated = DispatchBasicAuthentication(context, out anonymAuthenticated); if (IsTokenAuthenticationRequested(request)) { // Cross-Origin Resource Sharing (CORS) if (!HttpHeaderTools.IsOriginHeaderAllowed()) { AuthenticationHelper.ThrowForbidden("token auth"); } if (request?.HttpMethod == "OPTIONS") { // set allowed methods and headers HttpHeaderTools.SetPreflightResponse(); application?.CompleteRequest(); } if (basicAuthenticated && anonymAuthenticated) { SnLog.WriteException(new UnauthorizedAccessException("Invalid user.")); context.Response.StatusCode = HttpResponseStatusCode.Unauthorized; context.Response.Flush(); if (application?.Context != null) { application.CompleteRequest(); } } else { TokenAuthenticate(basicAuthenticated, context, application); } return; } // if it is a simple basic authentication case if (basicAuthenticated) { return; } string authenticationType = null; string repositoryPath = string.Empty; // Get the current PortalContext var currentPortalContext = PortalContext.Current; if (currentPortalContext != null) { authenticationType = currentPortalContext.AuthenticationMode; } // default authentication mode if (string.IsNullOrEmpty(authenticationType)) { authenticationType = WebApplication.DefaultAuthenticationMode; } // if no site auth mode, no web.config default, then exception... if (string.IsNullOrEmpty(authenticationType)) { throw new ApplicationException( "The engine could not determine the authentication mode for this request. This request does not belong to a site, and there was no default authentication mode set in the web.config."); } switch (authenticationType) { case "Windows": EmulateWindowsAuthentication(application); SetApplicationUser(application, authenticationType); break; case "Forms": application.Context.User = null; CallInternalOnEnter(sender, e); SetApplicationUser(application, authenticationType); break; case "None": // "None" authentication: set the Visitor Identity application.Context.User = new PortalPrincipal(User.Visitor); break; default: Site site = null; var problemNode = Node.LoadNode(repositoryPath); if (problemNode != null) { site = Site.GetSiteByNode(problemNode); if (site != null) { authenticationType = site.GetAuthenticationType(application.Context.Request.Url); } } var message = site == null ? string.Format( HttpContext.GetGlobalResourceObject("Portal", "DefaultAuthenticationNotSupported") as string, authenticationType) : string.Format( HttpContext.GetGlobalResourceObject("Portal", "AuthenticationNotSupportedOnSite") as string, site.Name, authenticationType); throw new NotSupportedException(message); } }
private void HandleResponseForClientCache(PortalContextInitInfo initInfo) { // binaryhandler if (initInfo.BinaryHandlerRequestedNodeHead != null) { var bhMaxAge = Settings.GetValue(PortalSettings.SETTINGSNAME, PortalSettings.SETTINGS_BINARYHANDLER_MAXAGE, initInfo.RepositoryPath, 0); if (bhMaxAge > 0) { HttpHeaderTools.SetCacheControlHeaders(bhMaxAge); // We're only handling these if the visitor has permissions to the node if (CheckVisitorPermissions(initInfo.RequestedNodeHead)) { // handle If-Modified-Since and Last-Modified headers HttpHeaderTools.EndResponseForClientCache(initInfo.BinaryHandlerRequestedNodeHead.ModificationDate); } else { //otherwise store the value for later use initInfo.ModificationDateForClient = initInfo.BinaryHandlerRequestedNodeHead.ModificationDate; } return; } } if (initInfo.IsWebdavRequest || initInfo.IsOfficeProtocolRequest) { //HttpHeaderTools.SetCacheControlHeaders(0, HttpCacheability.NoCache); //HttpContext.Current.Response.Headers.Add("Cache-Control", "no-store, no-cache, private"); HttpContext.Current.Response.Headers.Add("Pragma", "no-cache"); //HTTP 1.0 HttpContext.Current.Response.Headers.Add("Expires", "Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past return; } // get requested nodehead if (initInfo.RequestedNodeHead == null) { return; } // if action name is given, do not do shortcircuit (eg. myscript.js?action=Edit should be a server-rendered page) if (!string.IsNullOrEmpty(initInfo.ActionName)) { return; } // ********************************************************** // Image content check is moved to OnAuthorize event handler, because it needs the // fully loaded node. Here we handle only other content - e.g. js/css files. // ********************************************************** if (!initInfo.RequestedNodeHead.GetNodeType().IsInstaceOfOrDerivedFrom(typeof(Image).Name)) { var cacheSetting = GetCacheHeaderSetting(initInfo.RequestUri, initInfo.RequestedNodeHead); if (cacheSetting.HasValue) { HttpHeaderTools.SetCacheControlHeaders(cacheSetting.Value); // We're only handling these if the visitor has permissions to the node if (CheckVisitorPermissions(initInfo.RequestedNodeHead)) { // handle If-Modified-Since and Last-Modified headers HttpHeaderTools.EndResponseForClientCache(initInfo.RequestedNodeHead.ModificationDate); } else { //otherwise store the value for later use initInfo.ModificationDateForClient = initInfo.RequestedNodeHead.ModificationDate; } return; } } // applications Application app; // elevate to sysadmin, as we are startupuser here, and group 'everyone' should have permissions to application without elevation using (new SystemAccount()) { //load the application, or the node itself if it is an application if (initInfo.RequestedNodeHead.GetNodeType().IsInstaceOfOrDerivedFrom("Application")) { app = Node.LoadNode(initInfo.RequestedNodeHead) as Application; } else { app = ApplicationStorage.Instance.GetApplication(initInfo.ActionName, initInfo.RequestedNodeHead, initInfo.DeviceName); } } if (app == null) { return; } var maxAge = app.NumericMaxAge; var cacheControl = app.CacheControlEnumValue; if (cacheControl.HasValue && maxAge.HasValue) { HttpHeaderTools.SetCacheControlHeaders(maxAge.Value, cacheControl.Value); // We're only handling these if the visitor has permissions to the node if (CheckVisitorPermissions(initInfo.RequestedNodeHead)) { // handle If-Modified-Since and Last-Modified headers HttpHeaderTools.EndResponseForClientCache(initInfo.RequestedNodeHead.ModificationDate); } else { //otherwise store the value for later use initInfo.ModificationDateForClient = initInfo.RequestedNodeHead.ModificationDate; } } }
private void OnEnter(object sender, EventArgs e) { HttpContext httpContext = (sender as HttpApplication).Context; var request = httpContext.Request; SnTrace.Web.Write("PCM.OnEnter {0} {1}", request.RequestType, request.Url); // check if messages to process from msmq exceeds configured limit: delay current thread until it goes back to normal levels DelayCurrentRequestIfNecessary(); var initInfo = PortalContext.CreateInitInfo(httpContext); // Check for forbidden paths (custom request filtering), mainly for phisycal folders in the web folder. // The built-in Request filtering module is not capable of filtering folders only in the root, but let // us have folders with the same name somewhere else in the Content Repository. if (IsForbiddenFolder(initInfo)) { AuthenticationHelper.ThrowNotFound(); } // check if request came to a restricted site via another site if (Configuration.WebApplication.DenyCrossSiteAccessEnabled) { if (initInfo.RequestedNodeSite != null && initInfo.RequestedSite != null) { if (initInfo.RequestedNodeSite.DenyCrossSiteAccess && initInfo.RequestedSite.Id != initInfo.RequestedNodeSite.Id) { HttpContext.Current.Response.StatusCode = 404; HttpContext.Current.Response.Flush(); HttpContext.Current.Response.End(); return; } } } // add cache-control headers and handle ismodifiedsince requests HandleResponseForClientCache(initInfo); PortalContext portalContext = PortalContext.Create(httpContext, initInfo); // Cross-Origin Resource Sharing (CORS) if (!HttpHeaderTools.TrySetAllowedOriginHeader()) { AuthenticationHelper.ThrowForbidden("token auth"); } if (!portalContext.IsWebdavRequest && !portalContext.IsOfficeProtocolRequest && request.HttpMethod == "OPTIONS") { // set allowed methods and headers HttpHeaderTools.SetPreflightResponse(); (sender as HttpApplication)?.CompleteRequest(); return; } var action = HttpActionManager.CreateAction(portalContext); SnTrace.Web.Write("HTTP Action." + GetLoggedProperties(portalContext)); action.Execute(); }
public override VirtualFile GetFile(string virtualPath) { var currentPortalContext = PortalContext.Current; // office protocol: instruct microsoft office to open the document without further webdav requests when simply downloading the file // webdav requests would cause an authentication window to pop up when downloading a docx if (HttpContext.Current != null && HttpContext.Current.Response != null) { if (WebApplication.DownloadExtensions.Any(extension => virtualPath.EndsWith(extension, StringComparison.InvariantCultureIgnoreCase))) { // we need to do it this way to support a 'download' query parameter with or without a value var queryParams = HttpContext.Current.Request.QueryString.GetValues(null); var download = HttpContext.Current.Request.QueryString.AllKeys.Contains("download") || (queryParams != null && queryParams.Contains("download")); if (download) { var fName = string.Empty; if (currentPortalContext != null && currentPortalContext.IsRequestedResourceExistInRepository) { // look for a content var node = Node.LoadNode(virtualPath); if (node != null) { fName = DocumentBinaryProvider.Current.GetFileName(node); } } else { // look for a file in the file system fName = Path.GetFileName(virtualPath); } HttpHeaderTools.SetContentDispositionHeader(fName); } } } if (WebApplication.DiskFSSupportMode == DiskFSSupportMode.Prefer && base.FileExists(virtualPath)) { var result = base.GetFile(virtualPath); // let the client code log file downloads if (PortalContext.Current != null && PortalContext.Current.ContextNodePath != null && string.Compare(virtualPath, PortalContext.Current.ContextNodePath, StringComparison.Ordinal) == 0) { File.Downloaded(virtualPath); } return(result); } // Indicates that the VirtualFile is requested by a HttpRequest (a Page.LoadControl also can be a caller, or an aspx for its codebehind file...) var isRequestedByHttpRequest = (HttpContext.Current != null) && (string.Compare(virtualPath, HttpContext.Current.Request.Url.LocalPath, StringComparison.InvariantCultureIgnoreCase) == 0); if (isRequestedByHttpRequest && currentPortalContext.IsRequestedResourceExistInRepository) { return(new RepositoryFile(virtualPath, currentPortalContext.RepositoryPath)); } else if (IsFileExistsInRepository(virtualPath)) { return(new RepositoryFile(virtualPath, virtualPath)); } else if (IsFileExistsInAssembly(virtualPath)) { return(new EmbeddedFile(virtualPath)); } else { // Otherwise it may exist in the filesystem - call the base return(base.GetFile(virtualPath)); } }
public override Stream Open() { if (_node == null) { try { var allowCompiledContent = AllowCompiledContent(_repositoryPath); // http://localhost/TestDoc.docx?action=RestoreVersion&version=2.0A // When there are 'action' and 'version' parameters in the requested URL the portal is trying to load the desired version of the node of the requested action. // This leads to an exception when the action doesn't have that version. // _repositoryPath will point to the node of action and ContextNode will be the document // if paths are not equal then we will return the last version of the requested action. // We also have to ignore the version request parameter in case of binary handler, because that // ashx must not have multiple versions. if (PortalContext.Current == null || PortalContext.Current.BinaryHandlerRequestedNodeHead != null || string.IsNullOrEmpty(PortalContext.Current.VersionRequest) || _repositoryPath != PortalContext.Current.ContextNodePath) { if (allowCompiledContent) { // elevated mode: pages, ascx files, etc. using (new SystemAccount()) { _node = Node.LoadNode(_repositoryPath, VersionNumber.LastFinalized); } } else { _node = Node.LoadNode(_repositoryPath, VersionNumber.LastFinalized); } } else { VersionNumber version; if (VersionNumber.TryParse(PortalContext.Current.VersionRequest, out version)) { Node node; if (allowCompiledContent) { // elevated mode: pages, ascx files, etc. using (new SystemAccount()) { node = Node.LoadNode(_repositoryPath, version); } } else { node = Node.LoadNode(_repositoryPath, version); } if (node != null && node.SavingState == ContentSavingState.Finalized) { _node = node; } } } // we cannot serve the binary if the user has only See or Preview permissions for the content if (_node != null && (_node.IsHeadOnly || _node.IsPreviewOnly) && HttpContext.Current != null) { AuthenticationHelper.ThrowForbidden(_node.Name); } } catch (SenseNetSecurityException ex) // logged { SnLog.WriteException(ex); if (HttpContext.Current == null || (_repositoryPath != null && _repositoryPath.ToLower().EndsWith(".ascx"))) { throw; } AuthenticationHelper.DenyAccess(HttpContext.Current.ApplicationInstance); } } if (_node == null) { throw new ApplicationException(string.Format("{0} not found. RepositoryFile cannot be served.", _repositoryPath)); } string propertyName = string.Empty; if (PortalContext.Current != null) { propertyName = PortalContext.Current.QueryStringNodePropertyName; } if (string.IsNullOrEmpty(propertyName)) { propertyName = PortalContext.DefaultNodePropertyName; } var propType = _node.PropertyTypes[propertyName]; if (propType == null) { throw new ApplicationException("Property not found: " + propertyName); } var propertyDataType = propType.DataType; Stream stream; switch (propertyDataType) { case DataType.Binary: string contentType; BinaryFileName fileName; stream = DocumentBinaryProvider.Current.GetStream(_node, propertyName, out contentType, out fileName); if (stream == null) { throw new ApplicationException(string.Format("BinaryProperty.Value.GetStream() returned null. RepositoryPath={0}, OriginalUri={1}, AppDomainFriendlyName={2} ", this._repositoryPath, ((PortalContext.Current != null) ? PortalContext.Current.RequestedUri.ToString() : "PortalContext.Current is null"), AppDomain.CurrentDomain.FriendlyName)); } // Set MIME type only if this is the main content (skip asp controls and pages, // page templates and other repository files opened during the request). // We need this in case of special images, fonts, etc, and handle the variable // at the end of the request (PortalContextModule.EndRequest method). if (HttpContext.Current != null && PortalContext.Current != null && string.Equals(PortalContext.Current.RepositoryPath, _repositoryPath, StringComparison.InvariantCultureIgnoreCase) && (string.IsNullOrEmpty(PortalContext.Current.ActionName) || string.Equals(PortalContext.Current.ActionName, "Browse", StringComparison.InvariantCultureIgnoreCase)) && !string.IsNullOrEmpty(contentType) && contentType != "text/asp") { if (!HttpContext.Current.Items.Contains(RESPONSECONTENTTYPEKEY)) { HttpContext.Current.Items.Add(RESPONSECONTENTTYPEKEY, contentType); } // set the value anyway as it may be useful in case of our custom file handler (SenseNetStaticFileHandler) HttpContext.Current.Response.ContentType = contentType; // add the necessary header for the css font-face rule if (MimeTable.IsFontType(fileName.Extension)) { HttpHeaderTools.SetAccessControlHeaders(); } } // set compressed encoding if necessary if (HttpContext.Current != null && MimeTable.IsCompressedType(fileName.Extension)) { HttpContext.Current.Response.Headers.Add("Content-Encoding", "gzip"); } // let the client code log file downloads var file = _node as ContentRepository.File; if (file != null) { ContentRepository.File.Downloaded(file.Id); } break; case DataType.String: case DataType.Text: case DataType.Int: case DataType.DateTime: stream = new MemoryStream(Encoding.UTF8.GetBytes(_node[propertyName].ToString())); break; default: throw new NotSupportedException(string.Format("The {0} property cannot be served because that's datatype is {1}.", propertyName, propertyDataType)); } return(stream); }