I was recently asked to create a solution for providing folder access and security on a Web site.
The idea was that a user could log in to the Web site and then be automatically directed to a default folder in which files were listed on another page as links. When the user clicks on the file name, the file would be delivered to the user. (This is akin to the process where a user's credential information directs them to a particular directory in FTP. The difference is this is a Web interface to a familiar process.) I'll walk you through my steps for creating this solution. (The code in this article was successfully tested and run on Windows 2000 Server with Internet Information Services (IIS) 5.0.)
Laying the groundwork
The Web site was located on a Windows 2000 machine. Since IIS and Windows security are tightly integrated in this environment, I decided to take advantage of this authentication mechanism. When the user logs in to the originating page, the LOGON_USER environment variable is used to get detailed information about the user. Since Windows takes care of authenticating and authorizing the user, the only thing to worry about is where to grab the file information for the currently logged in user.
Since there is no database interaction with the solution, my process uses XML files that provide the ability to persist user information. The XML data will be persisted in application variables--any changes to the application XML data will be persisted in the XML files. The only thing that is persisted is identifiable information about the currently logged in user. This is the SessionID of the current user session.
In order to provide flexibility with the security on folders, I wanted to allow users access as individual users or users of a group. This requires that folders provide access to users and groups, and this information must be stored in an XML file. So there should be three XML files: one for users, one for groups, and one for folders.
The user's XML file contains user information. For instance, each logged in user has a username. When the user visits the Web site, a session is created that is identified by a session identifier. Since there is only one user per session, the user node that identifies the user contains a sessionId attribute to hold the current session identifier. The sessionId attribute will be used to locate the user's ID for access information. You'll also need the default folder for the user. Listing A contains an example of the user's XML file.
The group's XML file contains information about groups and the users of each group. Each group node contains a group of users identified by their ID number. Listing B contains an example of the group's XML file.
The folder's XML file contains all the necessary information for granting access to individual users. Each folder node contains a list of users who have permission to view the contents of the folder. The folder node also specifies a friendly name for the folder--to protect the true identity of the folder--and an absolute path to the physical folder location on the server. Listing C contains the example for the folder's XML file.
When the user logs in to the Web site, he is "directed" to his default folder. This folder will display all the available files and folders within that folder that are accessible.
Putting all of the pieces together
First, the user must log in to the system. A generic login page will provide the means for prompting the user for their credentials. Since I don't want to mess with Windows NT authentication, I'm going to request that the user supplies their credentials through basic authentication. You can do this by setting the Response status to "401 Unauthorized" and adding the header "WWW-Authenticate: Basic". When the browser receives this status, it should automatically provide a login window for the user to enter his username and password. When the user enters and submits this information, IIS will automatically authenticate the user based on the supplied credentials.
It's important to understand that basic authentication will send the credential information as plain text back to the server. In order to avoid any security issues, it is recommended that any solution that implements this type of authentication mechanism do so through SSL. Also, Basic Authentication must be enabled for the login page through administration (usually through Internet Services Manager). (For more information about authentication using Windows 2000 Server, visit Microsoft's page on the subject.) When (and if) the user is authenticated, the LOGON_USER environment variable is set. This data can be used to query the user's XML for a particular user node during login.
If the user is already logged in, the sessionId attribute should be set. You can verify this by looking for the user node where the sessionId attribute value is the current session ID. If you cannot find this node, then you can log in the user by setting the sessionId attribute, persisting the XML data, and directing the user to their default folder. Listing D contains the code to accomplish this task.
This code assumes the following: that the users.xml file must be located in the root directory of the site for this code to work; and, the currently logged in user must have read privileges to this file. This presents a security hazard as long as the file is located under the root directory of the site. If you move this file to another location, such as a directory above the root directory, the user will not be able to view the file through a normal URL. Also, this code will work as long as the site is not on a Web farm. To handle this type of issue, the users.xml file must be in a shared directory for each server, and you must choose another mechanism other than SessionID, such as a generated GUID stored in a cookie. However, this is supposed to be a simple, one server solution. This code is saved to a file called Security.asp.
Once the user is logged in, he is redirected to GetFiles.asp. This page displays all the files and folders as links to which the user has access. The GetFiles.asp page checks to make sure there is a currently logged in user. If there isn't one, the browser is redirected back to the Security.asp page. If there is a currently logged in user, the page checks to see if a folder specification, the friendly name, was passed in as part of the request. If it wasn't, the user is provided with the folders and files of their default directory. Otherwise, the files and folders of the current directory to which the user has access are displayed. The user can then click on any file to retrieve the file or on any folder to navigate to that folder. Listing E contains the code to accomplish this task.
The IsUserOkay function checks to see if the user or the user's group(s) is valid for the friendly name passed in to the function. Finally, all the files and folders are provided as links in the outgoing HTML. A subfolder will not be displayed unless the user has privileges to this folder. Once again, this is all controlled in the folders.xml file.
When the user sees this page, they click on either a folder or a file. The onclick event handlers for the links take care of submitting the necessary data to the server. These event handlers are located in the getfiles.js file, as you can see in Listing F.
When the user clicks a folder, the page sets the friendlyName hidden input and submits the form. The destination for this submission is still the GetFiles.asp page. To wrap things up, another file called GetSecureFile.asp is responsible for delivering the file when it is clicked. GetSecureFile.asp once again checks the security of the user, and then delivers the file as an octet-stream if security is passed. View Listing G to see the necessary code.
When the user receives the file, he sees a pop-up asking whether to Save, Open, or Cancel the download. If the user types in the specified URL for getting information, he would have to be logged in. If the user is logged in, he would need to have privileges for the file or folder. If not, the user will always be redirected to the Security.asp page.
Download the source code for this solution. Try out the code on your own development platform using an administrator account. Once you get this to work on your platform, try tightening down security by moving the XML files outside of the site root. Also, use credentials for test users and provide the directories you're exposing with the appropriate permissions for these users.
Phillip Perkins is a contractor with Ajilon Consulting. His experience ranges from machine control and client/server to corporate intranet applications.