|
|
|
To do list part IV - Security
We have our todo list application in good shape now, except there is no security yet. Let's look at what Zero provides in the realm of securing our application and modify our application from the hardcoded user johndoe to authenticating the user and controlling access.
The following concepts are covered in this tutorial:
- Security configuration
- Authentication and authorization
Security configuration
So far, we have done nothing to secure our application. Once again, we go back to the configuration file for a Zero application, zero.config, to specify how we intend to secure the to do list application and use the following steps. For this tutorial, we will use the project we created in the previous tutorial and build upon the same project to add security.
- Stop the simpletodo3 application.
- Double click zero.config.
- Add the following content to the
zero.config file:
[@include ${zero.core}/config/security/rule.config]
uri=/.*
authType=Basic
groups=[ALL_AUTHENTICATED_USERS]
- Save the
zero.config file
You are already familiar with the format used in the zero.config file to configure a Zero application. Let's look at the security configuration that we just added:
- uri
- Defines what is to be protected and defines the protected resources.
- authtype
- Can either be Basic for basic authentication or Form for form based authentication.
- groups
- Defines the groups of users who are allowed to access the protected resources.
For more information on security configuration, see the Authentication and Authorization article.
Our application is secure and we have specified that any restful resource should only be accessed by someone whose identity has been verified. How do we verify someone's identity? We need a list of users who are authorized to access the application. Let's create such a list using the following steps:
- Select the config folder.
- Click File -> New -> File and name the new file zero.users as shown on the following screen.
- Add the following content to the
zero.users file:
johndoe:bed128365216c019988915ed3add75fb:todogroup
- Save the
zero.users file.
Working with the zero.users file
This file serves as a simple user registry that contains the username, encrypted password and the group(s) that the user belongs to. Currently, we have johndoe as the user, with passw0rd as the password, who belongs to the group todogroup. You might be wondering how we got from passw0rd to bed128365216c019988915ed3add75fb? That, and more, is explained in the file based user service article. There, you will also find instructions on how to create user accounts and generate your own user registry file.
Zero supports a file based user registry as well as an LDAP based directory for storing the information about user accounts. For more information on how to use these services, see the User Service article.
Authentication and authorization
The application is only accessible by johndoe now. In our earlier tutorial, we had hardcoded the user to always be johndoe. Let's change that now using the following steps:
- Create a file under /app/resources and name it
owners.groovy
We just created another RESTful resource named owners. We will use this to introduce the idea of each to do list resource being owned by an owner. Now, we are getting into nested resources. An owner resource is at app/resources/owners and a to do list resource is at app/resources/todos. In order to connect the two and treat them in a nested format, we will use the todos.bnd file.
- Create a file under /app/resources and name it
todos.bnd
- Add the following contents to the file
todos.bnd :
owners/todos
This file made the link between the two RESTful resources that we defined. For more information visit the resource handling article.
- Open the
/app/resources/todos.groovy file for editing.
- Replace the content of the file with the following:
/*
* ============================================================================
* Licensed Materials - Property of IBM
* Project Zero
*
* (C) Copyright IBM Corp. 2007 All Rights Reserved.
*
* US Government Users Restricted Rights - Use, duplication or disclosure
* restricted by GSA ADP Schedule Contract with IBM Corp.
* ============================================================================
*/
package todos;
def onList() {
logger.INFO {"onGET called"};
def ownerId = zero.util.XMLEncoder.escapeXML(request.params.ownersId[0].get());
def owner = request.subject.remoteUser.get();
if (!ownerId.equals(owner)) {
request.status = HttpURLConnection.HTTP_FORBIDDEN; //forbidden
} else {
logger.INFO {"user: ${owner}"};
sendJSONData(zero.data.groovy.Manager.create('simpletodo'), owner);
}
}
def onCreate() {
logger.INFO {"onPOST called"};
def ownerId = zero.util.XMLEncoder.escapeXML(request.params.ownersId[0].get());
def owner = request.subject.remoteUser.get();
if (!ownerId.equals(owner)) {
request.status = HttpURLConnection.HTTP_FORBIDDEN; //forbidden
} else {
def item = zero.util.XMLEncoder.escapeXML(request.params.item[0].get());
def priority = zero.util.XMLEncoder.escapeXML(request.params.priority[0].get());
logger.INFO {"Item to create: "+item};
if (item != null && item.size() > 0) {
def dm = zero.data.groovy.Manager.create('simpletodo');
def id = dm.insert("INSERT INTO todos (owner, item, priority, status) VALUES($owner, $item, $priority, 0)", ['id']);
request.status=HttpURLConnection.HTTP_CREATED
request.headers.out.Location=request.uri +'/'+id
sendJSONData(dm, owner);
}else {
request.status = HttpURLConnection.HTTP_NO_CONTENT;
}
}
}
def onDeleteCollection() {
logger.INFO {"onDELETE not used in the app"};
}
def onRetrieve() {
logger.INFO {"onGET called"};
def todosId = zero.util.XMLEncoder.escapeXML(request.params.todosId[0].get());
def ownerId = zero.util.XMLEncoder.escapeXML(request.params.ownersId[0].get());
def owner = request.subject.remoteUser.get();
if (!ownerId.equals(owner)) {
request.status = HttpURLConnection.HTTP_FORBIDDEN; //forbidden
} else if (todosId == null) {
request.status = HttpURLConnection.HTTP_BAD_REQUEST; //bad request
} else {
def dm = zero.data.groovy.Manager.create('simpletodo');
def data = dm.update("SELECT id, item, priority, status FROM todos WHERE owner = ? AND id = ?", ownerId, todosId);
request.view="JSON";
request.json.output = data;
render();
}
}
def onUpdate() {
logger.INFO {"onPUT called"};
def todosId = zero.util.XMLEncoder.escapeXML(request.params.todosId[0].get());
def ownerId = zero.util.XMLEncoder.escapeXML(request.params.ownersId[0].get());
def owner = request.subject.remoteUser.get();
if (!ownerId.equals(owner)) {
request.status = HttpURLConnection.HTTP_FORBIDDEN; //forbidden
} else if (todosId == null) {
request.status = HttpURLConnection.HTTP_BAD_REQUEST; //bad request
} else {
def status = zero.util.XMLEncoder.escapeXML(request.params.status[0].get());
def dm = zero.data.groovy.Manager.create('simpletodo');
dm.update("UPDATE todos SET status = $status WHERE owner = $owner AND id = $todosId")
}
}
def onDelete() {
logger.INFO {"onDELETE called"};
def todosId = zero.util.XMLEncoder.escapeXML(request.params.todosId[0].get());
def ownerId = zero.util.XMLEncoder.escapeXML(request.params.ownersId[0].get());
def owner = request.subject.remoteUser.get();
if (!ownerId.equals(owner)) {
request.status = HttpURLConnection.HTTP_FORBIDDEN; //forbidden
} else if (todosId == null) {
request.status = HttpURLConnection.HTTP_BAD_REQUEST; //bad request
} else {
def dm = zero.data.groovy.Manager.create('simpletodo');
dm.update("DELETE FROM todos WHERE owner = ? AND id = ?", [owner, todosId]);
}
}
def sendJSONData(dm, owner) {
data = dm.queryList("SELECT id, item, priority, status FROM todos WHERE owner = $owner ORDER BY priority");
logger.INFO {"sending json data: "+data};
request.headers.out."Content-Type"="application/json"
request.view="JSON";
request.json.output = data;
render();
}
Working with the todos.groovy file
Lets talk about what changes in todos.groovy. We first get the ownerId, which is the id of the person who owns the to do list that we are trying to access, and then we get the owner, which is the user information that was provided when the user was challenged, and given the oppotunity to provide login information. If they match, we go ahead and process the request, otherwise, we set the appropriate response code. Also, we have removed the hardcoded user johndoe and updated the code to reflect the logged in user as the owner when we update the to do list.
- Open the
/app/public/todo.gt file for editing.
- Add the following line at the beginning of the script section:
var owner = "<%=request.subject.remoteUser.get()%>"
- Replace all the instances of the following:
/resources/todos
with the following
/resources/owners/'+owner+'/todos
Working with the todo.gt file
This is where we clearly see our nested resources in action. You may remember that we created two resources: owners and todos, and nested the todos within the owners using the todos.bnd file. Lets discuss what changes we have made to the todo.gt file, which make use of the nested resource format. First, we obtain the logged in user and store in as owner. Now, any time that the to do list is accessed, we need to access it using the /resources/owners//todos format. That is what the updated todo.gt does. For more information, visit the resource handling article.
Running the application
To run the application, use the following steps:
- Right-click the project and click Run As -> Project Zero Application.
- Using your browser, go to the http://localhost:8080/todo.gt site.
- Provide your username and password (For this tutorial we used johndoe as the username and passw0rd as the password as shown on the following screen:
And... you are finished! You have created a rich and secure to do list Zero application that uses a database and a user registry. That was just too easy. Go ahead, check out Project Zero in depth and explore the possibilities !!!
<<< Previous
|