The following section provide several examples of ways to implement security using basic authentication and application authentication
The example in this section shows how you might implement user security using web-server basic authentication and two roles, user and administrator.
This example has two ColdFusion pages:
This page also includes the one-button form and logic for logging out a user, which appears at the top of each page.
You can test the security behavior by adding your own pages to the same directory as the Application.cfm page.
The Application.cfm page consists of the following:
<cfapplication name="Orders">
<cflogin> <cfif IsDefined( "cflogin" )> <cfif cflogin.name eq "admin"> <cfset roles = "user,admin"> <cfelse> <cfset roles = "user"> </cfif> <cfloginuser name = "#cflogin.name#" password = "#cflogin.password#" roles = "#roles#" /> <cfelse> <!--- this should never happen ---> <h4>Authentication data is missing.</h4> Try to reload the page or contact the site administrator. <cfabort> </cfif> </cflogin>
The Application.cfm page executes before the code in each ColdFusion page in an application. For more information on the Application.cfm page and when it is executed, see Chapter 13, "Designing and Optimizing a ColdFusion Application".
The following table describes the CFML code in Application.cfm and its function:
The securitytest.cfm page shows how any application page can use ColdFusion user authorization features. The web server ensures the existence of an authenticated user, and the Application.cfm page ensures that the user is assigned to roles the page content appears. The securitytest.cfm page uses the IsUserInRole
and GetAuthUser
functions to control the information that is displayed.
The securitytest.cfm page consists of the following code:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html> <head> <title>Basic authentication security test page</title> </head> <body> <cfoutput> <h2>Welcome #GetAuthUser()#!</h2> </cfoutput> ALL Logged-in Users see this message.<br> <br> <cfscript> if (IsUserInRole("admin")) WriteOutput("users in the admin role see this message.<br><br>"); if (IsUserInRole("user")) WriteOutput("Everyone in the user role sees this message.<br><br>"); </cfscript> </body> </html>
The following table describes the securitytest.cfm page CFML code and its function:
The example in this section shows how you might implement user security by authenticating users and then allowing users to see or use only the resources that they are authorized to access.
This example has three ColdFusion pages:
This page also includes the one-button form and logic for logging out a user, which appears at the top of each page.
You can test the security behavior by adding your own pages to the same directory as the Application.cfm page.
The example gets user information from the LoginInfo table of the CompanyInfo database that is installed with ColdFusion. You can replace this database with any database containing UserID, Password, and Roles fields. The sample database contains the following data:
UserID |
Password |
Roles |
---|---|---|
BobZ |
Ads10 |
Employee,Sales |
JaniceF |
Qwer12 |
Contractor,Documentation |
RandalQ |
ImMe |
Employee,Human Resources,Manager |
Because spaces are meaningful in roles strings, you should not follow the comma separators in the Roles fields with spaces.
The following sections contain listings and descriptions of each of the pages.
The Application.cfm page consists of the following:
<cfapplication name="Orders" sessionmanagement="Yes">
<cfif IsDefined("Form.logout")> <cflogout> </cfif> <cflogin> <cfif NOT IsDefined("cflogin")> <cfinclude template="loginform.cfm"> <cfabort> <cfelse> <cfif cflogin.name IS "" OR cflogin.password IS ""> <cfoutput> <H2>You must enter text in both the User Name and Password fields</H2> </cfoutput> <cfinclude template="loginform.cfm"> <cfabort> <cfelse> <cfquery name="loginQuery" dataSource="CompanyInfo"> SELECT UserID, Roles FROM LoginInfo WHERE UserID = '#cflogin.name#' AND Password = '#cflogin.password#' </cfquery> <cfif loginQuery.Roles NEQ ""> <cfloginuser name="#cflogin.name#" Password = "#cflogin.password#" roles="#loginQuery.Roles#"> <cfelse> <cfoutput> <H2>Your login information is not valid.<br> Please Try again</H2> </cfoutput> <cfinclude template="loginform.cfm"> <cfabort> </cfif> </cfif> </cfif> </cflogin> <cfif GetAuthUser() NEQ ""> <cfoutput> <form action=MyApp/index.cfm" method="Post"> <input type="submit" Name="Logout" value="Logout"> </form> </cfoutput> </cfif>
The Application.cfm page executes before the code in each ColdFusion page in an application. For more information on the Application.cfm page and when it is executed, see Chapter 13, "Designing and Optimizing a ColdFusion Application". The following table describes the CFML code in Application.cfm and its function:.
The loginform.cfm page consists of the following:
<cfset url="http://" & "#CGI.server_name#" & ":" &"#CGI.server_port#" &
"#CGI.script_name#"> <cfif CGI.query_string IS NOT ""> <cfset url=url & "?#CGI.query_string#"> </cfif> <H2>Please Log In</H2> <cfoutput> <form action="#url#" method="Post"> <table> <tr> <td>username:</td> <td><input type="text" name="j_username"></td> </tr> <tr> <td>password:</td> <td><input type="password" name="j_password"></td> </tr> </table> <br> <input type="submit" value="Log In"> </form> </cfoutput>
The following table describes the loginform.cfm page CFML code and its function:
The securitytest.cfm page shows how any application page can use ColdFusion user authorization features. Application.cfm ensures the existence of an authenticated user before the page content appears. The securitytest.cfm page uses the IsUserInRole
and GetAuthUser
functions to control the information that is displayed.
The securitytest.cfm page consists of the following code:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html> <head> <title>Security test page</title> </head> <body> <cfoutput> <h2>Welcome #GetAuthUser()#!</h2> </cfoutput> ALL Logged-in Users see this message.<br> <br> <cfscript> if (IsUserInRole("Human Resources")) WriteOutput("Human Resources members see this message.<br><br>"); if (IsUserInRole("Documentation")) WriteOutput("Documentation members see this message.<br><br>"); if (IsUserInRole("Sales")) WriteOutput("Sales members see this message.<br><br>"); if (IsUserInRole("Manager")) WriteOutput("Managers see this message.<br><br>"); if (IsUserInRole("Employee")) WriteOutput("Employees see this message.<br><br>"); if (IsUserInRole("Contractor")) WriteOutput("Contractors see this message.<br><br>"); </cfscript> </body> </html>
The following table describes the securitytest.cfm page CFML code and its function:
You do not have to create a login page to display a user login form; you can rely on the browser to display its standard login page. To do so, your cflogin
tag body returns an HTTP status 401 to the browser if the user is not logged in or if the login fails. The browser then displays its login page and returns the information to the cflogin
tag's cflogin
structure when the user clicks the login button.
For example, the following code tells the browser to display a login form if the user has not logged in, or if the user does not provide a name "user" and password "p1", or a name "admin" and password "p2":
<cflogin>
<cfif IsDefined( "cflogin" )> <cfif cflogin.name eq "admin" and cflogin.password eq "p1"> <cfset roles = "user,admin"> <cfelseif cflogin.name eq "user" and cflogin.password eq "p2"> <cfset roles = "user"> </cfif> </cfif> <cfif IsDefined( "roles" )> <cfloginuser name="#cflogin.name#" password="#cflogin.password#" roles="#roles#"> <cfelse> <!--- User has not logged in or authentication failed - send 401 ---> <cfsetting enablecfoutputonly="yes" showdebugoutput="no"> <cfheader statuscode="401"> <cfheader name="WWW-Authenticate" value="Basic realm=""MySecurity"""> <cfoutput>Not authorized</cfoutput> <cfabort> </cfif> </cflogin>
LDAP directories are often used to store security information. The following example cflogin
tag checks an LDAP directory to authenticate the user and retrieve the users roles.
The most important thing to note in this example is that it queries the directory twice, first as the directory manager, then with the user's identity:
username
attribute. This query gets the distinguished name that corresponds to the user-supplied user ID. Using the directory manager's identity ensures that there will be a valid response for any user ID in the directory.
username
attribute, and the user-supplied password as the password
attribute. This query succeeds, and thereby authenticates the user, only if the user's password allows that user to access the directory. In other words, the application uses the user's LDAP directory password as its own password. The "Reviewing the code" section that follows describes the code's function in detail. For more information on using LDAP directories with ColdFusion, see Chapter 23, "Managing LDAP Directories".
<cflogin>
<!--- setting basic attributes ---> <cfset root = "o=macromedia.com"> <cfset server="ldap.macromedia.com"> <cfset port="389"> <!--- These attributes are used in the first search. ---> <!--- This filter will look in the objectclass for the user's ID. ---> <cfset filter = "(&(objectclass=*)(uid=#Form.UserID#))"> <!--- Need directory manager's cn and password to get the user's password from the directory ---> <cfset LDAP_username = "cn=directory manager"> <cfset LDAP_password = "password"> <!--- Search for the user's dn information. This is used later to authenticate the user. NOTE: Do this as the Directory Manager to ensure access to the information ---> <cftry> <cfldap action="QUERY" name="userSearch" attributes="uid,dn" start="#root#" scope="SUBTREE" server="#server#" port="#port#" filter="#filter#" username="#LDAP_username#" password="#LDAP_password#" > <cfcatch type="Any"> <cfset UserSearchFailed = true> </cfcatch> </cftry> <!--- If user search failed or returns 0 rows abort ---> <cfif NOT userSearch.recordcount OR UserSearchFailed> <cfoutput> <script> alert("UID for #uid# not found"); </script> </cfoutput> <cfabort> </cfif> <!--- pass the user's DN and password to see if the user authenticates and get the user's roles> <cftry> <cfldap action="QUERY" name="auth" attributes="dn,roles" start="#root#" scope="SUBTREE" server="#server#" port="#port#" filter="#filter#" username="#userSearch.dn#" password="#Form.password#" > <cfcatch type="any"> <cfif FindNoCase("Invalid credentials", cfcatch.detail)> <cfoutput><script>alert("User ID or Password invalid for user: #Form.userID#")</script> </cfoutput> <cfabort> <cfelse> <cfoutput><script>alert("Unknown error for user: #Form.userID# #cfcatch.detail#")</script> </cfoutput> <cfabort> </cfif> </cfcatch> </cftry> <!--- If the LDAP query returned a record, the user is valid. ---> <cfif auth.recordcount> <cfloginuser name="#Form.userID#" password="#Form.password#" roles="#auth.roles#"> </cfif> </cflogin>
The following table describes the code and its function: