The genesis of this article arose when I found myself in need of providing
a list of the access control lists (ACLs) for some of our Web pages. Since
the Internet site is hosted by a third party, this precluded the idea of
accessing this information through the Windows Explorer file properties.
After some thought, I embarked on a mission to display file ACLs on a Web
page in a browser. I quickly came to the conclusion that without Active
Directory installed, this was going to be a bit tricky.
I went looking for an object that has these values available. The logical
place to start was with the File System object. However, although the File
System object allows access to file attributes, it does not allow access to view
file ACLs. The more I looked, the more I realized that there was no direct
way to call up file ACLs using Active Server Pages without Active Directory
installed. I had to take an indirect approach.
Through some research, trial and error, I ended up getting my results
through a patchwork of technologies. I created an ASP script that does the
following:
uses the Windows Script Host WshShell object to run CACLS, a WinNT
utility, to pipe file ACLs to a text file;
opens the text file with the File System object;
parses through the text file line by line, removing unnecessary and
duplicate data;
creates an ADO recordset on the fly and adds each Access Control Entry
(ACE) as a record to a column in the recordset; and
displays the results of the recordset in alphabetical order on the ASP page
CACLS
CACLS is a Windows NT command line script that, among other things,
displays the ACLs of a given file. If you are not familiar with this
script, give it a
try. Go to your DOS prompt, and type in "CACLS." If this script is
installed, (it is installed by default with a Windows NT 4.0 workstation
installation) you will see a list of its capabilities and the requisite
syntax. You can do a number of things with this script, including edit
ACLs. If you want to simply display the ACLs of a given file, the syntax is
to type CACLS followed by the full pathname of the file:
c:\>cacls c:\inetpub\wwwroot\global.asa
The output you should see (assuming your global.asa file is located in the
directory specified above) is the name of the file and a list of each
ACE followed by a colon, and another list indicating the type of access to
the file. This is an easy way to display file ACLs, provided you have
access to the
command line. Since I didn't, I needed a way to get this data into a
Web page, and in a format where I could manipulate it. The wheels began to
spin.
The thought came to mind to pipe the results of the CACLS script into a
text file. Once in a text file, I knew I could access it from my ASP page.
Getting the results into a text file is easy. Simply use the pipe command
">" followed by the full pathname of the file you want to contain the
results:
By running this at the command line, I was able to get the results of my
script into a text file. But I still had the problem of getting the command
line
script to run inside of a Web page. The answer was with the Windows Script Host WshShell object.
Windows Script Host WshShell Object
The Windows Script Host WshShell object provides access to the Windows
shell and registry. This handy object is used for a variety of functions,
but
most importantly in our case, it can be used to run other programs. As you
will see, it has a "run" method that can accept a command string and execute
it. To script this, I first built the command string as follows:
The variable textFileToQuery contains the full pathname of the file whose
ACLs will be displayed. I use an ASP server variable called
PATH_TRANSLATED that provides the full pathname of the Web page my user has
navigated to. You could also use a form to get direct user input. Be
aware that this requires your users to know the full pathnames of the files
whose ACLs they want to query.
In the next line I encase the value in quotes. If any of the names have
blank spaces in them, the script will not know that the blank space is part
of the file name unless you enclose the whole name in quotes. The variable
strTextFile contains the text file name to receive the results of the CACLS
script.
The strACLCommand contains the completed command string. When I have a long
string, I like to build it in stages, as I have done here, to make it easier
to debug. You can include it on one line if you like. Notice the rather
cryptic string "cmd /c echo y|" just before I call CACLS. You are probably
wondering
what it is doing and why. As it turns out, although entering CACLS at the
command line runs the script, when you invoke the script through the run
method of WshShell, it requires the user to echo back "yes" to proceed.
That is what this part of the script is for. Finally, I add the pipe
command and the
strText File to complete the command string.
The next step is to pass the command string to the run method of the
WshShell object. This is done with the following piece of code:
Set objWSH = Server.CreateObject("WScript.Shell")
objRTC = objWSH.Run (strACLCommand , 0, True)
Set objWSH = Nothing
The run method of the WshShell object accepts three arguments. The first is
the command string; the second is the WindowStyle (optional); and the third
is WaitOnReturn (optional). In this case, I have set the WindowStyle to "0"
(hides the window and activates another window), and WaitOnReturn to
"TRUE" (returns any error code returned by the application). Finally, I
destroy the WshShell object because it has performed its duty and is no
longer needed.
Note that when you instantiate the WshShell object you strip off the Wsh part.
File System Object
If I were to stop at this point, I would have a file on my server that
contains a long string with the results of my command-line CACLS script.
The next part of my script invokes the File System object to read and parse through the
text file created. This is done with the following piece of code:
Const ForReading = 1
Set FSO = CreateObject("Scripting.FileSystemObject")
Set File = FSO.GetFile(strTextFile)
Set TS = File.OpenAsTextStream(ForReading)
After setting my constant ForReading, I instantiate the File System object.
The next line gets the text file that houses the results of our CACLS
script. The
third line opens the file as a text stream and assigns its value to the
variable TS. At this point, I am prepared to read the text file line by
line and add each
line as a new record to my recordset. However, before I do this, I need to
create the recordset on the fly.
Crafting an ADO Recordset
The term "crafted recordset" is sometimes used to describe a recordset that
is built from scratch from within an ASP page. There is no connection or
database from which the records are pulled. The object is created and
filled with records from the ASP script itself. The code for creating a
crafted ADO
recordset is as follows:
I was interested in creating a recordset with one column to contain the
name of the ACL. As you can see, to achieve this I first instantiate an ADO
Recordset using the adUseClient cursor. (Remember to include your
adovbs.inc file if you want this script to work). Next I use the append
method to
create a new column in the recordset. Note that the append method accepts
four arguments -- Name, Type, Defined Size (optional), and Attribute
(optional). In this case, I named the column ACL, where each record can
have up to 255 characters, and the record can contain null. The last line
opens
the recordset. Note that the first two arguments, Source and Active
Connection, are intentionally left blank since there is no connection to a
database.
The recordset is being crafted on the fly.
Next I read the text file line by line, clean it up, and populate my
recordset. The code for this is as follows:
strDomain = "MYDOMAIN\"
Do While Not TS.AtEndOfStream
s = TS.ReadLine
s = replace(s,strDomain,"")
length = instr(s,":")
length = length-1
s = left(s,length)
duplicate = instr(strFinal,s)
if duplicate = 0 then
strFinal = strFinal & s & "<br>"
rsNew.AddNew
rsNew.Fields("ACL").Value = s
end if
Loop
rsNew.UpdateBatch
As you can see, this is a standard Do While loop. The key here is to open
the text file as a text stream and use the ReadLine method to read each
line of
your text file. After the line is read, I clean it up. Your results will
display both the domain name as well as the read/write permissions of the
file. Since all I am interested in is the name of the access control entry
(ACE), I strip off both the domain name and everything following the colon.
Next I look for
duplicates. To do this I build a string (strFinal) consisting of each line
and use the instring method "instr()" to look for any occurrences of my
next line in
the last built-up string. If there are none, I add the line as a new record
to the ACL column of my recordset. Finally, I close the loop and call
UpdateBatch to update the recordset with the new values.
At this point I have a complete updated recordset. The next thing I need to
do is to order the entries and display them on the page. When I did this, I
noticed that the first record contains the pathname of the file. Since I
did not want to display this record, I simply skipped the record before
displaying it. The code for displaying the records is as follows:
rsNew.Sort = "ACL desc"
rsNew.MoveFirst
rsNew.MoveNext
strAdminUsers = "NT AUTHORITY\SYSTEM, new service 1, new service 2"
strAdminUsers= Trim(strAdminUsers)
do while not rsNew.eof
strACL = rsNew.Fields("ACL")
strACL = Trim(strACL)
posACL = instr(strAdminUsers, strACL)
if posACL = 0 then
Response.write strACL & "<BR>"
end if
rsNew.MoveNext
loop
rsNew.close
set rsNew = nothing
ts.Close
set ts = nothing
set fso= nothing
In this final piece of code I start by sorting the recordset, and then
skipping the first record as explained above. I next set up a string
consisting of all the ACLs that I do not want displayed, such as the admin
ACLs that appear on all of the files. I start another Do While loop cycling
through each record,
checking to see if any of the records match any of the values in my
strAdminUsers list. If they do not match, then the ACL is not for an admin
user so I write it out followed by a break tag. Finally, I close the loop
and clean it up.
Conclusion
ASP is indeed the glue that binds several technologies. In the above, I
was able to leverage the power of a "C-Script"
function like CACLS by using the Windows Script Host, the File System
object and an ADO recordset. It is easy to see how this ASP script can be
adapted to display or run other "C-Script" functions through a Web page.
About the Author
Larry Schwartz (
mailto:larry@larryschwartz.com) is currently a software engineer at
Moody's Investors Service, where he is the lead programmer for Moodys.com. Larry began his career in
programming in 1995 as a freelance HTML programmer and designer, working
with SOHO design shops and small, independent businesses. He has been
designing and programming Web sites ever since. Prior to coming to
Moody's, he was a consultant to several Fortune 500 companies and the
lead programmer for a major Internet startup. He holds a B.S. from SUNY
Albany and a J.D. from St. John's University School of Law.
Proposion N2N connects Microsoft .NET applications to Lotus Notes and Lotus Domino databases. This ADO.NET managed data provider allows you to perform blindingly fast queries and updates of Notes data from ASP.NET pages, .NET web services, Windows, or Mobile applications. An innovative SQL-like query language leverages the unique features of Notes and makes collaborative software accessible to relational database programmers.
Jonathan Zufi shows how to use the XMLHTTP object within JavaScript or VBScript to validate form-field information without having to submit a page and wait for the result. [Read This Article][Top]
Firing events on a Web server is an easy task. However most of the easy solutions require you to have your own dedicated IIS or SQL Server on the Internet to play with, a privilege not shared by many. In this article, Matthew Muller shows you how to get the same functionality in a shared hosting environment.
[Read This Article][Top]
Web galleries are an easy way to add interactivity and content to your Web site. However, how do you keep the Web galleries consistent with your site and how do you overcome the deficiencies of your Web gallery creation tool? John Sorensen explains a simple way to do both. [Read This Article][Top]
Using classes in ASP 3.0 we can create dynamic arrays of objects. Donnell DeLeon Smith's article also shows how we can implement a class of dynamic arrays of objects several layers deep, if required. [Read This Article][Top]
Even though SMS is now in high gear, developers remain slated with restrictive limits to carrier resources. Sending an SMS message via e-mail requires the acceptance of several hidden flaws. Joe Lauer shows how to avoid these complications by sending a wireless text-message through the use of ASP. [Read This Article][Top]
Add punch to your validation routines by adding regular expressions. Further prepare yourself for the coming ASP.NET regular expression validation control. This article shows you how to use regular expressions and provides sample patterns for different user inputs. [Read This Article][Top]
Servers-side validations on the client side...isn't that an oxymoron? Maybe, but Pandurang Nayak shows us how to accomplish a type of remote scripting using a mix of Javascript and ASP. [Read This Article][Top]
A function that calls itself repeatedly, satisfying some condition is
called a Recursive Function. Using recursion, we split a complex problem into its single simplest case.
The recursive function only knows how to solve that simplest case. You'll see the difference
between solving a problem iteratively and recursively later.
[Read This Article][Top]
Do you know what happens when you use multiple languages within your ASP page? Gopikrishna S throws light on how an ASP page behaves when multiple languages are used for server side scripting. [Read This Article][Top]
Mailing List
Want to receive email when the next article is published? Just Click Here to sign up.