"Improving the Secured File UX" Part Two
In Improving the Secured File Download UX for Unauthenticated Users I elaborated on a workaround hack to display a friendly error message and redirect to the login page when trying to access a file that had been through DotNetNuke's file ticket system.
If you're in a situation where that solution makes sense for you - great. But what about future releases of the application? Should this functionality exist within the framework itself? Is it too trivial? Does it make sense for everyone that uses the framework so much so that it should be a part of it?
To find out, I put the issue in DotNetNuke's Gemini.
I suggest that when you encounter a workaround/hack/enhancement such as this:
- Don't just fix it for now
- These "fringe areas" are one of the places where the "community at large" can be of most help. Let the core team focus on the big stuff.
- So...put it in Gemini!
- And, ideally, try to give the core team a head start.
So, in an effort to follow the above path, I cracked open my handy DotNetNuke source code and made a few changes.
update: I'm providing the following information for reference only, I really don't think you should make this change to your DNN application. My intention is just to remind you that the core code is there, we can easily make changes, test them out, and suggest to the DotNetNuke team that they incorporate those changes into the application.
Background
When the user requests the file, the LinkClick.aspx "page" is requested. (e.g. "http://localhost/DotNetNuke_2/LinkClick.aspx?fileticket=XKDsP6pHvtQ=&tabid=36&mid=410"). This really isn't a page, but is a handler registered in the application's web.config.
The actual class that handles the file request is called "FileServerHandler" and is found in Library\Components\FileSystem\FileServerHandler.vb in version 4.9 and \Library\Services\FileSystem\FileServerHandler.vb in DotNetNuke 5.0 RC2.
While some slight refactoring has been done to this class between 4.9 and 5.0, the portion we're concerned with has not changed:
' serve the file If TabId = Null.NullInteger Then If Not FileSystemUtils.DownloadFile(_portalSettings.PortalId, Integer.Parse(UrlUtils.GetParameterValue(URL)), blnClientCache, blnForceDownload) Then context.Response.Write(Services.Localization.Localization.GetString("FilePermission.Error")) End If Else If Not FileSystemUtils.DownloadFile(_portalSettings, Integer.Parse(UrlUtils.GetParameterValue(URL)), blnClientCache, blnForceDownload) Then context.Response.Write(Services.Localization.Localization.GetString("FilePermission.Error")) End If End If
The above code is responsible for the original behavior of showing the localized file permission error message.
Goals for Improvement
If the user is not able to download the file:
- If they are not logged in, let's take them to the current portal's login URL with a return URL for the file ticket.
- If they are logged in, display the localized FilePermission.Error message, as usual.
Adding a Utility Method
First, let's add a method to help us get the current portal's login URL:
Private Function GetLoginUrl(ByVal portalSettings As PortalSettings, ByVal request As HttpRequest) As String If Not portalSettings Is Nothing AndAlso Not request Is Nothing Then Dim tabId As Integer = portalSettings.ActiveTab.TabID Dim controlKey As String = "Login" If Not Null.IsNull(portalSettings.LoginTabId) AndAlso String.IsNullOrEmpty(request.QueryString("override")) Then controlKey = String.Empty tabId = portalSettings.LoginTabId ElseIf Not Null.IsNull(portalSettings.HomeTabId) Then tabId = portalSettings.HomeTabId End If Return DotNetNuke.Common.Globals.NavigateURL(tabId, controlKey) Else Throw New ArgumentNullException(If(portalSettings Is Nothing, "portalSettings", "request")) End If End Function
This method is intended to yield the correct login page whether a custom one is specified or not. It could potentially use the current tab, or the home tab as well. Just playing it safe.
Adding the core enhancement
Now, we need to figure out whether or not the user is logged in, and perform one of two actions. Either redirect them to the login page, or let them know that they don't have sufficient privileges to view the file.
Private Sub RedirectToLoginOrDisplayErrorMessage(ByVal context As HttpContext, ByVal _portalSettings As PortalSettings) If (context.Request.IsAuthenticated = False) Then Dim loginPage As String = GetLoginUrl(_portalSettings, context.Request) Dim returnUrl As String = HttpUtility.UrlEncode(HttpContext.Current.Request.Url.PathAndQuery) context.Response.Redirect(loginPage + "?returnurl=" + returnUrl) Else context.Response.Write(Services.Localization.Localization.GetString("FilePermission.Error")) End If End Sub
Wiring it Up
Now, without changing the original code much, we can replace a couple lines of code, set a few break points, and try this thing out.
' serve the file If TabId = Null.NullInteger Then If Not FileSystemUtils.DownloadFile(_portalSettings.PortalId, Integer.Parse(UrlUtils.GetParameterValue(URL)), blnClientCache, blnForceDownload) Then RedirectToLoginOrDisplayErrorMessage(context, _portalSettings) End If Else If Not FileSystemUtils.DownloadFile(_portalSettings, Integer.Parse(UrlUtils.GetParameterValue(URL)), blnClientCache, blnForceDownload) Then RedirectToLoginOrDisplayErrorMessage(context, _portalSettings) End If End If
Summary
Mission accomplished. Now to see if it gets picked up anywhere along the way. Let me know what you think of this enhancement or the process in general. Also, I did enjoy picking apart this one trivial thing, so if you have any suggestions for future experiments, please let me know.