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); } }
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; } } } }
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 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); }
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); } }
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); }