Beispiel #1
0
        public void ProcessRequest(HttpContext context)
        {
            var request  = context.Request;
            var response = context.Response;

            VirtualFile vf       = null;
            var         filePath = request.FilePath;

            // Cross-Origin Resource Sharing (CORS)
            if (!HttpHeaderTools.IsOriginHeaderAllowed())
            {
                AuthenticationHelper.ThrowForbidden(filePath);
            }

            if (HostingEnvironment.VirtualPathProvider.FileExists(filePath))
            {
                vf = HostingEnvironment.VirtualPathProvider.GetFile(filePath);
            }

            if (vf == null)
            {
                throw new HttpException(404, "File does not exist");
            }

            response.ClearContent();

            // Set content type only if this is not a RepositoryFile, because the
            // Open method of RepositoryFile will set the content type itself.
            if (!(vf is RepositoryFile))
            {
                var extension = System.IO.Path.GetExtension(filePath);
                context.Response.ContentType = MimeTable.GetMimeType(extension);

                // add the necessary header for the css font-face rule
                if (MimeTable.IsFontType(extension))
                {
                    HttpHeaderTools.SetAccessControlHeaders();
                }
            }

            // The bytes we write into the output stream will NOT be buffered and will be sent
            // to the client immediately. This makes sure that the whole (potentially large)
            // file is not loaded into the memory on the server.
            response.BufferOutput = false;

            using (var stream = vf.Open())
            {
                response.AppendHeader("Content-Length", stream.Length.ToString());
                response.Clear();

                // Let ASP.NET handle sending bytes to the client (avoid Flush).
                stream.CopyTo(response.OutputStream);
            }
        }
Beispiel #2
0
        internal void AuthorizeRequest(HttpContext context)
        {
            PortalContext currentPortalContext = PortalContext.Current;

            if (currentPortalContext == null)
            {
                return;
            }
            var currentUser = context?.User.Identity as User;

            // deny access for visitors in case of webdav or office protocol requests, if they have no See access to the content
            if (currentUser != null && currentUser.Id == Identifiers.VisitorUserId && (currentPortalContext.IsOfficeProtocolRequest || currentPortalContext.IsWebdavRequest))
            {
                if (!currentPortalContext.IsRequestedResourceExistInRepository ||
                    currentPortalContext.ContextNodeHead == null ||
                    !SecurityHandler.HasPermission(currentPortalContext.ContextNodeHead, PermissionType.See))
                {
                    AuthenticationHelper.ForceBasicAuthentication(HttpContext.Current);
                }
            }

            if (context == null)
            {
                return;
            }

            if (currentPortalContext.IsRequestedResourceExistInRepository)
            {
                var authMode = currentPortalContext.AuthenticationMode;
                if (string.IsNullOrEmpty(authMode))
                {
                    authMode = WebApplication.DefaultAuthenticationMode;
                }

                bool appPerm;
                if (authMode == "Forms")
                {
                    appPerm = currentPortalContext.CurrentAction.CheckPermission();
                }
                else if (authMode == "Windows")
                {
                    currentPortalContext.CurrentAction.AssertPermissions();
                    appPerm = true;
                }
                else
                {
                    throw new NotSupportedException("None authentication is not supported");
                }

                var path            = currentPortalContext.RepositoryPath;
                var nodeHead        = NodeHead.Get(path);
                var permissionValue = SecurityHandler.GetPermission(nodeHead, PermissionType.Open);

                if (permissionValue == PermissionValue.Allowed && DocumentPreviewProvider.Current.IsPreviewOrThumbnailImage(nodeHead))
                {
                    // In case of preview images we need to make sure that they belong to a content version that
                    // is accessible by the user (e.g. must not serve images for minor versions if the user has
                    // access only to major versions of the content).
                    if (!DocumentPreviewProvider.Current.IsPreviewAccessible(nodeHead))
                    {
                        permissionValue = PermissionValue.Denied;
                    }
                }
                else if (permissionValue != PermissionValue.Allowed && appPerm && DocumentPreviewProvider.Current.HasPreviewPermission(nodeHead))
                {
                    // In case Open permission is missing: check for Preview permissions. If the current Document
                    // Preview Provider allows access to a preview, we should allow the user to access the content.
                    permissionValue = PermissionValue.Allowed;
                }

                if (permissionValue != PermissionValue.Allowed)
                {
                    if (nodeHead.Id == Identifiers.PortalRootId)
                    {
                        if (currentPortalContext.IsOdataRequest)
                        {
                            if (currentPortalContext.ODataRequest.IsMemberRequest)
                            {
                                permissionValue = PermissionValue.Allowed;
                            }
                        }
                    }
                }

                if (permissionValue != PermissionValue.Allowed || !appPerm)
                {
                    if (currentPortalContext.IsOdataRequest)
                    {
                        AuthenticationHelper.ThrowForbidden();
                    }
                    switch (authMode)
                    {
                    case "Forms":
                        if (User.Current.IsAuthenticated)
                        {
                            // user is authenticated, but has no permissions: return 403
                            context.Response.StatusCode = 403;
                            context.Response.Flush();
                            context.Response.Close();
                        }
                        else
                        {
                            // let webdav and office protocol handle authentication - in these cases redirecting to a login page makes no sense
                            if (PortalContext.Current.IsWebdavRequest || PortalContext.Current.IsOfficeProtocolRequest)
                            {
                                return;
                            }

                            // user is not authenticated and visitor has no permissions: redirect to login page
                            // Get the login page Url (eg. http://localhost:1315/home/login)
                            string loginPageUrl = currentPortalContext.GetLoginPageUrl();
                            // Append trailing slash
                            if (loginPageUrl != null && !loginPageUrl.EndsWith("/"))
                            {
                                loginPageUrl = loginPageUrl + "/";
                            }

                            // Cut down the querystring (eg. drop ?Param1=value1@Param2=value2)
                            string currentRequestUrlWithoutQueryString = currentPortalContext.RequestedUri.GetComponents(UriComponents.Scheme | UriComponents.Host | UriComponents.Port | UriComponents.Path, UriFormat.Unescaped);

                            // Append trailing slash
                            if (!currentRequestUrlWithoutQueryString.EndsWith("/"))
                            {
                                currentRequestUrlWithoutQueryString = currentRequestUrlWithoutQueryString + "/";
                            }

                            // Redirect to the login page, if neccessary.
                            if (currentRequestUrlWithoutQueryString != loginPageUrl)
                            {
                                context.Response.Redirect(loginPageUrl + "?OriginalUrl=" + System.Web.HttpUtility.UrlEncode(currentPortalContext.RequestedUri.ToString()), true);
                            }
                        }
                        break;

                    default:
                        AuthenticationHelper.DenyAccess(context);
                        break;
                    }
                }
            }
        }
Beispiel #3
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();
        }
Beispiel #4
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
                    if (PortalContext.Current == 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
                {
                    Logger.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);
            }
            ////<only_for_test>
            //if(propType == null)
            //{
            //    StringBuilder sb = new StringBuilder();
            //    sb.AppendFormat("Property {0} not found.", propertyName);
            //    sb.AppendLine();
            //    foreach (var pt in _node.PropertyTypes)
            //    {
            //        sb.AppendFormat("PropertyName='{0}' - DataType='{1}'", pt.Name, pt.DataType);
            //        sb.AppendLine();
            //    }
            //    return new MemoryStream(System.Text.Encoding.UTF8.GetBytes(sb.ToString()));
            //}
            ////</only_for_test>

            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.OriginalUri.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 &&
                    PortalContext.Current.RepositoryPath == _repositoryPath &&
                    (string.IsNullOrEmpty(PortalContext.Current.ActionName) || PortalContext.Current.ActionName == "Browse") &&
                    !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))
                    {
                        HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Origin", HttpContext.Current.Request.Url.GetComponents(UriComponents.SchemeAndServer, UriFormat.SafeUnescaped));
                    }
                }

                //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);
        }
Beispiel #5
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);
            }
        }
Beispiel #6
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;

                // Images need to be handled differently, because of these reasons:
                // - dimensions may be changed dynamically, based on request parameters
                // - preview images may be restricted (redacted) based on permissions
                if (_node is Image img)
                {
                    var parameters = HttpContext.Current?.Request.QueryString;

                    stream = img.GetImageStream(propertyName,
                                                parameters?.AllKeys.ToDictionary(k => k ?? string.Empty, k => (object)parameters[k]),
                                                out contentType,
                                                out fileName);
                }
                else
                {
                    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);
        }