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;
                }
            }
        }
Exemple #2
0
        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);
                }
            }
        }
Exemple #3
0
        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;
                }
            }
        }
Exemple #5
0
        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));
            }
        }
Exemple #7
0
        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);
        }