Password protect a web folder using .htaccess and htpasswd

This is a script i’ve been working on that allows you to create a folder on your web host and to password protect it by creating a .htaccess and .htpasswd file. As far as i know, for this to work your server must be running Apache.

If you’ve never heard of htaccess files before, you can read up on them here:
http://www.javascriptkit.com/howto/htaccess.shtml

The script can also just be used to clear, add or change a password on an existing folder. (At some point i would like to add the ability to delete a folder online, but couldn’t find an easy way to do this via shell scripts yet.)

This script was written to try to match how our host creates its own .htaccess/htpasswd files when using a “cpanel” tool Your host will most likely be different, so you will need to modify it as needed. I tried to put all the stuff you will need to change at the top of the script as properties, but there might be other places you need to modify the body of the script depending on what you are doing.

A warning about this script - i was a relative NOOB before writing this one, so use with caution! Things may not be done in the best way here - but please post any suggestions to make it better.

Oh - and there are parts of this script that are cannibalized from other scripts, mostly from macscripters. Along the way i lost track of where the code came from, so i apologize if you see some of your code in here and you aren’t credited!


--change the following to suit your server settings, etc.
--on our server, the password files and htaccess files are stored it 2 different locations/  the passwd  file is stored in a sub folder at /.htpasswds/clients/subfolder/  and the the .htaccess file is stored at the path /public_html/clients/subfolder/

property ftpInfo : " ftp://user:pass@ftp.server.com:21" -- in format ftp://user:pass@ftp.server.com:21
property passwdPath : "/.htpasswds/clients/" --base path for the passwd files online.  "folderName" gets appended to this in the script
property clientPath : "/public_html/clients/" --base path for the created folder "folderName"
property authUserPath : "/home/headgear/.htpasswds/clients/" --base path used in .htaccess file.  full path gets appended in script
property passwdFileName : "passwd"

--at various times during the script, "returns" are needed, and at other times unix "newlines" are needed.  
property charNewline : ASCII character 10
property charReturn : ASCII character 13



tell application "Finder"
	activate
	set localPath to container of (path to me) as string --gets path to this script, in path:to:me format
	set userChoice to {"Clear Password", "Change or Add Password", "Create Folder and add Password"}
	choose from list userChoice with prompt "Please choose the action you would like to take" OK button name "Ok" default items "Create Folder and add Password" without multiple selections allowed and empty selection allowed
	-- returns choice as string in a list
	set userChoice to result as string
	
	if userChoice is "Change or Add Password" then
		my changeThePassword(localPath)
	else if userChoice is "Create Folder and add Password" then
		my createTheFolder(localPath)
	else if userChoice is "Clear Password" then
		my clearThePassword(localPath)
	end if
end tell


on clearThePassword(localPath)
	tell application "Finder"
		activate
		set delDir to do shell script "curl -l " & ftpInfo & clientPath & "/" --gets list of existing folders at clientPath and puts it in delDir
		set delDir to delDir's paragraphs --changes the unix curl -l list into a list format suitable for applescript
		choose from list (delDir) with prompt "Please choose the folder you would like to clear the password for from the list below" OK button name "Choose" without multiple selections allowed
		set folderName to result as string
		if folderName is not "false" then
			display dialog "Are you sure you want to clear the password for the folder \"" & folderName & "\"?" buttons {"Cancel", "Yes"} default button 2 with icon caution
			if (button returned of result) is "Yes" then
				try
					do shell script "curl -Q 'DELE " & clientPath & folderName & "/.htaccess' " & ftpInfo
					try
						do shell script "curl -Q 'DELE " & passwdPath & folderName & "/passwd' " & ftpInfo
						do shell script "curl -Q 'RMD " & passwdPath & folderName & "' " & ftpInfo
					end try
					display dialog "Password successfully removed from \"" & folderName & "\""
				on error
					display dialog "Folder \"" & folderName & "\" isn't password protected." buttons {"Ok"} with icon stop
				end try
			end if
		end if
	end tell
end clearThePassword


on changeThePassword(localPath)
	--gets existing folders at the specified location and returns them to user in a list format to choose from, then username/password are collected and passed to createPasswdFile
	
	tell application "Finder"
		activate
		set delDir to do shell script "curl -l " & ftpInfo & clientPath & "/" --gets list of existing folders at clientPath and puts it in delDir
		set delDir to delDir's paragraphs --changes the unix curl -l list into a list format suitable for applescript
		choose from list (delDir) with prompt "Please choose the folder you would like to change the password for from the list below" OK button name "Choose" without multiple selections allowed and empty selection allowed
		set folderName to result as string
		display dialog "Are you sure you want to change the password for the folder \"" & folderName & "\"?" buttons {"Cancel", "Yes"} default button 2 with icon caution
		if (button returned of result) is "Yes" then
			my createPasswdFile(localPath, folderName)
		end if
	end tell
end changeThePassword


on createTheFolder(localPath)
	--gets a new client folder name, changes any letters in the name not found in "okCharacters" to a "_" to ensure that the name is safe for the web,
	--then creates the folder, if it doesn't already exist online.  If it does, options to add or change the password for it are given.
	
	tell application "Finder"
		repeat
			set testName to ""
			display dialog "Please enter the folder name." & return & "Don't use '/' unless you mean to create a subfolder (eg parent/subfolder)" default answer "" buttons {"Cancel", "Create"} default button 2
			set folderName to (text returned of result)
			
			--changes any characters not found in okCharacters to make it websafe:
			if result is not "" then
				set okCharacters to "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_/" --list of ok characters
				set allCharacters to characters of (folderName as text) --get the number of characters in folderName
				set characterList to allCharacters
				repeat with k from 1 to (count allCharacters) -- do the loop for the number of times found in in all characters, aka the number of characters in folderName
					if (item k of characterList is in okCharacters) then --if letter k folderName is in okCharacters, do nothing
					else --change the letter to a "_"
						set item k of characterList to "_"
					end if
				end repeat
				set okName to characterList as text
				set folderName to okName
				
				--test if folderName already exists online, and if it does ask user if they want to try a different name, or change the existing folders password:
				set testDir to do shell script "curl -l " & ftpInfo & clientPath & "/" --gets list of existing folders at clientPath via a unix shell command, and puts it in testDir
				set testDir to paragraphs of testDir
				repeat with k from 1 to count testDir
					if (testDir's item k) is equal to folderName then
						set testName to "yes"
					end if
				end repeat
				
				if testName is equal to "yes" then
					display dialog "The folder \"" & folderName & "\" already exists. Do you want to quit, re-enter a different folder name, or password protect this existing one? (will overide any existing passwords)" buttons {"Quit", "Re-Enter", "Enter password"} default button 3 with icon caution
					if (button returned of result) is "Quit" then
						exit repeat
					else if (button returned of result) is "Enter password" then
						my createPasswdFile(localPath, folderName)
						my createPrefFile(localPath, folderName)
						exit repeat
					end if
				else
					do shell script "curl -Q 'MKD " & clientPath & folderName & "' " & ftpInfo --creates the folder online 
					my createPrefFile(localPath, folderName)
					set link to "http://www.headgearanimation.com/clients/" & folderName & "/" as string
					set the clipboard to link
					display dialog "Client Folder created succesfully at: " & return & link & return & "Would you like to password protect this folder?" buttons {"Cancel", "Yes"} default button 2
					if (button returned of result) is "Yes" then
						my createPasswdFile(localPath, folderName)
						my createPrefFile(localPath, folderName)
					end if
					exit repeat
				end if
			else if result is "" then
				display dialog "Please do not leave blank" buttons "Ok" default button 1 with icon caution
			end if
			
		end repeat
	end tell
end createTheFolder


on createPrefFile(localPath, folderName)
	--creates the preference file "_webdata" containing folderName for later retrieval by other scripts
	
	set prefFile to (((localPath) as string) & "_webdata.txt") as file specification
	
	try
		open for access prefFile with write permission
		set eof of prefFile to 0
		set folderName to folderName as text
		write (folderName & return) to prefFile starting at eof
		close access prefFile
	on error
		try
			close access prefFile
		end try
	end try
end createPrefFile


on createPasswdFile(localPath, folderName)
	--gets a user name and password, and passes them to a shell script which uses os x's apache's htpasswd command for the creation of a password file.  Different servers use different naming conventions for this file - ours uses passwd so i kept it the same here.  change it as needed
	
	tell application "Finder"
		activate
		
		repeat
			repeat
				display dialog "Please enter a username" default answer "" buttons {"Cancel", "Continue."} default button 2
				set theUsername to (text returned of result)
				if theUsername is not "" then exit repeat
				display dialog "Please do not leave blank" buttons "Ok" default button 1 with icon caution
			end repeat
			
			repeat
				display dialog "Please enter a password" default answer "" buttons {"Cancel", "Ok"} default button 2
				set thePassword to (text returned of result)
				if thePassword is not "" then exit repeat
				display dialog "Please do not leave blank" buttons "Ok" default button 1 with icon caution
			end repeat
			
			display dialog "Folder \"" & folderName & "\" will be password protected with user name \"" & theUsername & "\" and password \"" & thePassword & "\"." buttons {"Re-enter", "Ok"} default button 2
			if (button returned of result) is "Ok" then exit repeat
		end repeat
		
		--most of the curl scripts below need to use "quoted form of" to handle special characters in the local path names.
		
		--creates the htpasswd encrypted file called passwd at the path localPath/passwd:
		set passwdFile to POSIX path of (localPath & passwdFileName) as string --shell scripts need the unix compatible path format /something/something/
		do shell script "htpasswd -bc " & quoted form of passwdFile & " " & theUsername & " " & thePassword
		
		--creates the htaccess file:
		--i think you only need to ensure each command is on a seperate line, so all the charNewLines here are probably extraneous,
		--but are here for consistency to  match what our server creates
		set htaccessFile to (((localPath) as string) & ".htaccess") as file specification
		try
			open for access htaccessFile with write permission
			set eof of htaccessFile to 0
			write (return & "AuthType Basic" & charNewline & charNewline) to htaccessFile starting at eof
			write ("AuthName \"Restricted Area\"" & charNewline & charNewline) to htaccessFile starting at eof
			write ("AuthUserFile \"" & authUserPath & folderName & "/passwd\"" & charNewline & charNewline & charNewline) to htaccessFile starting at eof
			write ("require valid-user" & charNewline) to htaccessFile starting at eof
			close access htaccessFile
		on error
			try
				close access htaccessFile
			end try
		end try
		
		--Upload the password file to the correct location online:
		set testPassDir to do shell script "curl -l " & ftpInfo & passwdPath & "/" --gets list of existing folders at passwdPath and puts it in testPassDir.  Used to ensure script doesn't try to create over an exisiting folder (which makes it fail)
		if testPassDir contains folderName then
			do shell script "curl -T " & quoted form of passwdFile & "  " & ftpInfo & passwdPath & folderName & "/" --uploads the passwd file
		else
			do shell script "curl -Q 'MKD " & passwdPath & folderName & "' " & ftpInfo --creates the password folder online			
			do shell script "curl -T " & quoted form of passwdFile & "  " & ftpInfo & passwdPath & folderName & "/" --uploads the passwd file
		end if
		
		--Upload the .htaccess file to the new folder online	
		set htaccessFile to POSIX path of htaccessFile
		do shell script "curl -T " & quoted form of htaccessFile & "  " & ftpInfo & clientPath & folderName & "/" --uploads the .htaccess file
		
		delete localPath & passwdFileName --cleans up
		delete localPath & ".htaccess"
		
		display dialog "Folder \"" & folderName & "\" was successfully password protected with user name \"" & theUsername & "\" and password \"" & thePassword & "\"." buttons "Ok" default button 1
		
	end tell
end createPasswdFile