It is possible to associate a particular NetConnection instance with a username and password combination that allows you to secure both ColdFusion page services and ColdFusion Components. You can secure services by user, role, or application.
The required client-side ActionScript code is straightforward. Simply call the setCredentials( ) method on the NetConnection instance from which you will get your secured services:
NetServices.setDefaultGatewayURL("http://localhost/flashservices/gateway"); var my_conn = NetServices.createGatewayConnection( ); my_conn.setCredentials("someUsername", "somePassword"); var someService = my_conn.getService("com.oreilly.util.someService", this); someService.execute( );
To properly implement an authentication scheme, the services being called have to be properly protected (a process we will review shortly). The username and password are sent to the server in the same request that actually invokes the remote function. If authentication is successful, the service will process normally; upon failure to authenticate, execution halts and an error is returned to the client through the onStatus( ) function of your responder object. It is perfectly acceptable to call setCredentials( ) on a NetConnection instance more than once if different credentials are being used for different functions.
The username and password arguments passed into setCredentials( ) can be the username and password of a particular user (for example, the user who is currently using your application), or it can be more general, such as a username and password combination pertaining to your application rather than who is using it. For example, if only one or two people have access to certain services on your ColdFusion Server, you should authenticate at the level of the particular user.
If all or most users of a particular application are allowed to access essentially the same set of services, you may want to authenticate at the level of the application itself. You could even come up with a single username and password combination that all your users and applications could use, which would simply protect against any party not associated with any of your applications from invoking services. The differences between the levels of authentication granularity are not at all reflected in the code itself. In other words, there is actually no difference between the way in which you authenticate a particular user and how you would authenticate an entire application or even a set of applications. The difference is purely in the significance that you decide to attach to the usernames and passwords that are chosen and how you architect your application.
As mentioned previously, you may also choose to protect services by user role. A role is just a group of users who have the same security restrictions. The Flash client does not explicitly specify a user's role when setting credentials on the NetConnection instance, since roles are typically not exposed to the client. For example, when you log into an account on either a Unix or a Windows computer, your role (or the sets of permissions that are granted to you) is unconditionally associated with your authentication information, meaning you don't say your name is "frank", your password is "frank123", and you would like your role to be "root" or "administrator". The computer you are logging into determines your role based on who you say you are. In this case, the computer you are logging into or authenticating against is the ColdFusion Server.
ColdFusion MX supports three tags and two functions associated with authentication:
This tag is typically to implement your authentication code. It is usually placed in your Application.cfm page and can be customized to authenticate in any manner you choose, such as checking a username and password against a database, text file, or LDAP server. The tag is executed only if the request is coming from a user who ColdFusion determines is not already logged in.
This tag tells the ColdFusion Server that the user has successfully authenticated. The <cfloginuser> tag has three attributes: name, password, and roles. Executing this tag associates those three attributes with the current request and all future requests from the user's client for the remainder of the user's session.
Tells the ColdFusion Server that the current user is no longer logged in. Any future requests from the client automatically invoke the <cflogin> tag and require authentication again.
Determines whether the user making the present request is assigned to the specified role. If so, this function returns true; otherwise, false is returned.
Returns the name of the user making the present request, if he is logged in.
In addition to the preceding tags, the <cffunction> tag used in this chapter also takes a roles attribute that allows you to manage security and define user roles for individual functions.
Example 5-12 uses basic web authentication to prompt or challenge users who have not yet successfully logged in. Basic web authentication is a simple protocol supported by most web servers and browsers. When a web server that has been configured to use basic authentication receives a request that does not have an Authorization header containing a base 64-encoded username and password, or if the username and password are incorrect, it returns a 401 code to the client, which indicates that the client is responsible for gathering a username and password and returning it in all subsequent requests.
Most browsers ask users for their authentication information by opening up a small dialog box in which the user can enter a username and password. The username and password are then base 64-encoded (so they are not visible as plain text to anyone spying on HTTP traffic) and returned to the server as the value of the Authorization header. If the username and password are correct, the resource that was originally requested is returned. If the username and password are not correct, an authorization failure policy is executed, which usually means that an error page is returned to the browser. The code in Example 5-12, when placed in an Application.cfm file (preferably at the top so that it is executed first), uses a combination of basic authentication and ColdFusion security to authenticate users.
<cfapplication name="myApplication"> <cflogin> <cfif IsDefined("cflogin") AND cflogin.name EQ "someUsername" AND cflogin.password EQ "somePassword"> <cfset roles = "administrator" /> <cfloginuser name = "#cflogin.name#" password = "#cflogin.password#" roles = "#roles#" /> <cfelse> <cfheader statuscode = "401"> <cfheader name = "WWW-Authenticate" value="Basic realm=""SomeRealm"""> <cfoutput><html><body><b>Not authorized</b></body></html></cfoutput> <cfabort /> </cfif> </cflogin>
The cflogin variable scope is available within the <cflogin> tag, if the Authorization header was defined in the request. If the user has not yet been prompted to enter a username and password, the cflogin variable scope is not available, which is why you must ensure it is defined before trying to access its properties.
If the cflogin variable scope is not available or the username and password contained in the Authorization header are not "someUsername" and "somePassword" respectively, Example 5-12 uses the <cfheader> tag to return a 401 status code. In addition, the <cfheader> tag passes the "WWW-Authenticate" header, which indicates to the client that it is responsible for prompting the user for authentication information and returning it in the next request. It is then necessary to use the <cfabort> tag to ensure that the rest of the page is not processed and no more content is returned.
If the cflogin variable scope exists and it contains username and password properties that equal "someUsername" and "somePassword", the user is logged into the ColdFusion Server using the <cfloginuser> tag. At this point, you can associate roles with a specific user using the roles attribute. To associate more than one role with a user, specify a comma-delimited list of roles. When using the IsUserInRole( ) function, ColdFusion checks the roles against the value specified by the roles attribute of the <cfloginuser> tag.
Although the preceding code has the username and password hardcoded as "someUsername" and "somePassword", you should implement your own form of authentication, such as comparing the information against a database table or an LDAP server.
If you make a request from your browser for any page inside the same directory as the Application.cfm file containing the preceding authentication code, you are prompted to enter a username and password, as shown in Figure 5-6.
When accessing a secure resource through Flash Remoting, the process of authentication works slightly differently. As previously mentioned, the username and password are sent along in the same request that either calls the ColdFusion page or invokes the CFC function. If authentication is successful, processing of the service continues; if it fails, the onStatus( ) method of your responder object is called.
You can control access to your CFC functions by specifying the roles attribute in the <cffunction> tag. Notice that you are associating either a role or a comma-delimited list of roles with the protected function rather than an actual username and password. It is obviously much more practical to associate a group of users with a function than it is to associate a single username and password combination per function. If you need that type of fine-grained access control, however, you can simply assign one user per role, which essentially accomplishes the same thing.
Here is an example of a CFC that allows access only for users who have the role of either "manager" or "admin":
<cfcomponent> <cffunction name="getEmployees" access="remote" roles="manager,admin" returnType="query"> <cfquery name="rsEmployees" datasource="someDatasource"> SELECT lastName FROM Employees </cfquery> <cfreturn #rsEmployees# /> </cffunction> </cfcomponent>
A user who is not logged in or who does not have the role of either "manager" or "admin" is not permitted to invoke the getEmployees( ) method. When the ColdFusion Server encounters such a request, it looks for authentication code to run, such as the login code we reviewed earlier in the Application.cfm file. The username and password that your authentication code uses should be set using NetConnection.setCredentials( ):
NetServices.setDefaultGatewayURL("http://localhost/flashservices/gateway"); var my_conn = NetServices.createGatewayConnection( ); my_conn.setCredentials("someUsername", "somePassword"); var myService = my_conn.getService("com.oreilly.frdg.admin.adminServices", this); myService.getEmployees( );
If the user successfully authenticates according to your authentication implementation inside of the <cflogin> tag and the <cfloginuser> tag is used to log the user into the ColdFusion Server, the ColdFusion Server compares the role of the user who made the request with the value of the roles attribute in the <cffunction> tag. If a match is found, the function is allowed to execute normally; otherwise, the responder object's onStatus( ) method is invoked with an error object indicating an authorization failure.
Implementing security at such a low level (the component level as opposed to putting all the logic on the client) has the following advantages:
Since one of the goals of Flash Remoting services is code re-use, it is nice to be able to reuse your security logic across clients in addition to reusing the code it is securing. When you associate roles with services in the services themselves, you can assume they are secure regardless of what types of clients are accessing them.
The closer your security is to your data, the more sure you can be that your data is safe. In other words, there is less room or opportunity for the wrong user to either get a hold of or update data he is not authorized to access. If a function is a security risk when executed by the wrong party, take the extra time to make sure the code is secure. A function that assumes its invoker has been properly authenticated is potentially vulnerable; therefore, use verification at the function level to ensure security.