Tuesday, 2 December 2008

Clickpass plugin for Grails, Part 1

Clickpass works by handling the OpenID authentication and sending it back to the site. This means that any Grails app using this plugin must also install the OpenID plugin. Also the site will need to be registered with Clickpass and some settings need to be set online. When the authenticated details are sent back, the user is logged in and sent to the welcome page, sent to a form for details to complete registration or asked to login to attach the OpenID to an existing user account.
  • grails create-plugin clickpass.
  • Add ClickpassController.groovy to grails-app/controller.
  • Add User.groovy to grails-app/domain. This is a dummy file required for the plugin to be packaged.
  • Add ClickpassTagLib.groovy to grails-app/taglib.
  • Add _clickpass.gsp to grails-app/views.
  • Add welcome.gsp to grails-app/views/clickpass

ClickpassController.groovy
class ClickpassController {
def openidService

def index = { response.sendError(404) }

def welcome = {
def user = null

if (openidService.isLoggedIn(session)) {
user = User.findByOpenid(openidService.getIdentifier(session))
if (user == null) {
def urlString = "http://www.clickpass.com/process_new_openid?
site_key=Xr2zelexGn&process_openid_registration_url=
http%3A%2F%2Flocalhost%3A8080%2Fsocialr%2Fclickpass%2F
register&new_openid_completion_url=http%3A%2F%2F
localhost%3A8080%2Fsocialr%2Fclickpass%2Fwelcome&
requested_fields=nickname%2Cemail&required_fields=&
nickname_label=Nickname&email_label=Email"
redirect(url:urlString)
}
}
}

def merge = {
def user = User.findByUsernameAndPassword(params.user_id, params.password)
def urlredirect =
java.net.URLDecoder.decode(params.clickpass_merge_callback_url) +
"&userid_authenticated=false"

if (user == null) {
redirect(url:urlredirect)
}

user.openid = openidService.getIdentifier(session)

if (user.save()) {
redirect(controller:'clickpass',action:'welcome')
}
else {
redirect(url:urlredirect)
}
}

def register = {
def user = new User()
user.username = params.nickname
user.email = params.email
user.openid = openidService.getIdentifier(session)
user.password = ''

if (user.save()) {
flash.message = "Thank you for signing up."
redirect(controller:'clickpass', action:'welcome')
}
else {
flash.message = "There is a problem signing up."
redirect(controller:'clickpass', action:'index')
}
}
}
  • index: Nothing for the index to do so set up a 404.
  • welcome: When a user is authenticated by OpenID, a search is made to see whether that OpenID is in the database, if not then redirect to the registration page. The urlString is obtained from Clickpass so this needs to be changed for each site
  • merge: A user, authenticated by OpenID, who wants to attach the ID to his account.
  • register: Create a new user account with the OpenID and save it.

User.groovy
Nothing in it except for the class declaration.

ClickpassTagLib.groovy
class ClickpassTagLib {
static namespace = "clickpass"

def bar = { attrs, body ->
try {
out << render(template:"/clickpass", contextPath:"${pluginContextPath}")
}
catch(Exception e) {
log.error(e)
}
}
}
  • The namespace allows one to use it, i.e <clickpass:foobar/>, in the code instead of the generic <g:foobar/>
  • For usage in an app, all that is required is <clickpass:bar/> to be inserted into the html.
  • The / in the template name is necessary to revert the path to the project base, otherwise it would go looking in the main app views folder.
  • The contextPath:"${pluginContextPath}" is not mentioned in the documention but after setting the path to the base, this sets it to the correct plugin views folder.

_clickpass.gsp
The html for this is generated by Clickpass, after registering your site, you can just copy and paste. This creates the Clickpass login bar.

welcome.gsp
<html>
<head>
<link rel="openid.server" href="http://www.clickpass.com/openid_server" />
<link rel="openid.delegate" href="http://clickpass.com/public/iseec" />
<title>Welcome</title>
</head>
<body>
<h1>Welcome</h1>
<openid:identifier />
<openid:ifLoggedIn>
<openid:logoutLink>Logout</openid:logoutLink>
</openid:ifLoggedIn>
</body>
</html>
  • This is a simple welcome page after the user successfully logs in and his account is verified.
  • One can replace it with their own or redirect from this page.
  • The OpenID is displayed as well as a logout link.

Aftermath
  • grails package-plugin to get it all into a zip file.
  • grails install-plugin /path/to/plugin/grails-example-0.1.zip to install it into an app.

Friday, 24 October 2008

IPhone MoviePlayer Sample Code 4

MyOverlayView

Interface

Instance methods:
  • -(void)awakeFromNib;
  • -(void)dealloc;
  • -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;

Implementation

Imports:
  • #import "MyViewController.h"
-(void)awakeFromNib
Rotate the overlay 90 degrees clockwise then translate it to the centre of the screen. This will match the orientation the movie player uses, full-screen landscape mode.

-(void)dealloc
Call super dealloc.

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
Similar to the MyImageView implementation. This creates a OverlayViewTouchNotification and fires it off.

IPhone MoviePlayer Sample Code 3

MyImageView

Interface

Imports:
  • #import "MyViewController.h"
Instance variables:
  • mViewController
Instance methods:
  • -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;

Implementation

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
Receives a set with the touches and the event. Gets a UITouch from the set and check it's phase. If it's UITouchPhaseBegan, play the movie.

Thursday, 23 October 2008

IPhone MoviePlayer Sample Code 2

MyViewController

Interface

Imports:
  • #import < MediaPlayer/MediaPlayer.h >
  • #import "MyOverlayView.h"
The line 'extern NSString * const OverlayViewTouchNotification' creates a global constant that will be used for touches to the overlay view. Any class importing MyViewController can access this variable.

Instance variables:
  • mMoviePlayer (MPMoviePlayerController)
  • mMovieURL (NSURL)
  • mOverlayView (MyOverlayView)
Instance methods:
  • -(NSURL *)movieURL;
  • -(void)initMoviePlayer;
  • -(IBAction)playMovie:(id)sender;
  • -(void)overlayViewTouches:(NSNotification *)notification;
  • -(IBAction)overlayViewButtonPress:(id)sender;

Implementation

Imports:
  • #import "MoviePlayerAppDelegate.h"
Initialise constant OverlayViewTouchNotification with the string 'overlayViewTouch'.

#pragma is a compiler directive. However the compiler ignores it if #pragma mark is used. #pragma mark helps with the organisation of your methods in the Xcode dropdown list.

'#pragma mark -' divvies up the methods by creating a spacer line while '#pragma mark Movie Player Routines' creates a bold label with the text 'Movie Player Routines'.

Movie Player Routines
- (void) moviePreloadDidFinish:(NSNotification*)notification
- (void) moviePlayBackDidFinish:(NSNotification*)notification
- (void) movieScalingModeDidChange:(NSNotification*)notification

These are events. When they are triggered is evident from the names. There is no code inside them.

-(void)initMoviePlayer
  • Register MPMoviePlayerContentPreloadDidFinishNotification (named after the definition in MPMoviePlayerController.h) with NSNotificationCenter.
  • The movie player object, mMoviePlayer, is created by allocating MPMoviePlayerController and initialising it with movieURL.
  • Register MPMoviePlayerPlaybackDidFinishNotification and MPMoviePlayerScalingModeDidChangeNotification, giving mMoviePlayer as the notification sender.
  • Obtain the MoviePlayerAppDelegate object and use its instance variables to set mMoviePlayer.
  • Register OverlayViewTouchNotification.
-(IBAction)playMovie:(id)sender
  • Play mMoviePlayer.
  • Get the application windows in an array.
  • Get the movie player window.
  • Add mOverlayView as the subview of the movie player window.
View Controller Routines
-(NSURL *)movieURL
  • Check mMovieURL whether it's null or not.
  • Passing will return mMovieURL.
  • Failing will get the main bundle, look for 'Movie.m4v', check it and convert the path to NSURL before assigning it to movieURL.
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation) interfaceOrientation
-(void)didReceiveMemoryWarning
-(void)overlayViewTouches:(NSNotification *)notification
-(IBAction)overlayViewButtonPress:(id)sender

Trivial or empty methods.

-(void)dealloc
Remove all registered notifications and release the movie player.

IPhone MoviePlayer Sample Code

Deconstructing the MoviePlayer app to see how the Media Player Framework ticks.

MoviePlayer has 4 classes:
  • MoviePlayerAppDelegate
  • MyImageView
  • MyOverlayView
  • MyViewController
A new IPhone application automatically creates 2 classes, with project name MPC, MPCAppDelegate and MPCViewController. Therefore, MyImageView and MyOverlayView are additions while MyViewController has been renamed.

MoviePlayerAppDelegate

Interface

There are 2 different ways of including a file/class:
  • #import "MyViewController.h"
  • @class MyViewController;
The interface lists 3 instance variables of type NSInteger aside from the default UIWindow and ViewController:
  • backgroundColor
  • controlMode
  • scalingMode
The @property declares the accessor methods for the 5 variables. It has attributes which modify the accessors.

Example: @property (nonatomic, retain) UIWindow *window;

nonatomic means the accessor returns the value directly, the default is to lock, get value, unlock which is meant for multithreaded environments.

assign, retain or copy must be explicitly set if garbage collection is not used. assign is the default and means the setter uses a simple assignment. retain keeps the object variable in-memory and cannot be deallocated without your permission. copy means that a copy of the object should be used for assignment and the previous one is sent a release message.

3 instance methods:
  • -(void)setUserSettingsDefaults;
  • -(void)applicationDidFinishLaunching:(UIApplication *)application;
  • -(void)dealloc;

Implementation

3 NSString keys:
  • kScalingModeKey = @"scalingMode"
  • kControlModeKey = @"controlMode"
  • kBackgroundColorKey = @"backgroundColor"
@synthesize generates the accessors according to the attributes listed. If implemented manually, the code must follow what has been specified in the @property. @synthesize will only generate accessors that don't exist so if a variable has a setter already, then only a getter will be generated.

-(void)setUserSettingsDefaults
Sets up a test calling NSUserDefaults to see if there's a match. Passing will set the scalingMode, controlMode and backgroundColor variables. Failing will create the values based on the settings bundle info and then set the variables:
  • Get the path to the main bundle of the project.
  • Append Settings.bundle to the path using stringByAppendingPathComponent, which takes care of adding separators.
  • Append Root.plist to the path, same as before.
  • Create a dictionary using dictionaryWithContentsOfFile, giving the path to Root.plist.
  • Create the prefSpecifierArray array from the dictionary using the key, PreferenceSpecifiers, which is a tag in Root.plist.
    • Root.plist is an XML file with dict, array and key tags. May be an easy way to read XML.
  • Declare 3 NSNumber variables and a dictionary, prefItem.
  • Loop prefSpecifierArray with the current value going into prefItem.
    • Set keyValueStr with the dictionary using the key, 'Key'.
    • Set defaultValue with the dictionary using the key, 'DefaultValue'.
    • Check if keyValueStr is equal to any NSStrings specified above.
    • Set the numbers with defaultValue if any is correct.
  • Create a dictionary containing the default values and their keys.
  • Register the dictionary with NSUserDefaults using registerDefaults.
  • Updates and writes to the persistent domains using synchronize.
-(void)applicationDidFinishLaunching:(UIApplication *)application
Add the ViewController's view as a subview of the UIWindow. Call self with setUserSettingsDefaults to get the default movie player settings. Lastly, call the ViewController with initMoviePlayer to create a movie player object.

-(void)dealloc
Release the UIWindow and the ViewController. Call the super's dealloc method.

Tuesday, 21 October 2008

Objective-C Classes

Objective-C (Obj-C) is C with Object Oriented Programming tacked on. The object syntax takes some time to get used to, as it is decidedly different from the normal C++/Java syntax.

There are two parts for an object in Obj-C, which are the interface and the implementation. The parts should be stored separately, the interface in a header file (*.h) and the implementation is a *.m file.

The interface stores the instance variables and the prototypes for the class and instance methods. The implementation defines those methods.

Sample code:

*** CounterController.h ***
#import < Cocoa/Cocoa.h >
@interface CounterController : NSObject {
IBOutlet id display_update;
int count;
}
- (IBAction)minus_button_pushed:(id)sender;
- (IBAction)p10_button_pushed:(id)sender;
- (IBAction)plus_button_pushed:(id)sender;
@end

*** CounterController.m ***
#import "CounterController.h"
@implementation CounterController
- (IBAction)minus_button_pushed:(id)sender {
count--;
[display_update setIntValue:count];
}
- (IBAction)p10_button_pushed:(id)sender {
count+=10;
[display_update setIntValue:count];
}
- (IBAction)plus_button_pushed:(id)sender {
count++;
[display_update setIntValue:count];
}
@end


Things of note:
  • Obj-C uses import instead of include.
  • The class CounterController is a subclass of NSObject.
  • Inside the brackets are the instance variables display_update and count.
  • The IBOutlet before display_update means the display_update is an output on the GUI display in the Interface Builder.
  • id is a general type, meaning any object.
  • (IBAction)minus_button_pushed:(id)sender translates to IBAction minus_button_pushed(id sender).
  • The - before the methods classify them as instance methods, + for class methods.
  • The brackets [] indicate messaging, [display_update setIntValue:count] translates to display_update.setIntValue(count).


http://www.gmonline.demon.co.uk/cscene/CS7/CS7-05.html
http://en.wikipedia.org/wiki/Objective-C
http://www.otierney.net/objective-c.html

Friday, 22 August 2008

Grails - JSecurity plugin

JSecurity is a Grails plugin that adds authentication and authorization to a web application. First we go about creating a project and installing the plugin:

grails create-app project_name
grails install-plugin jsecurity

Then we use the quick-start option, available once the plugin is installed, to generate JSecurity domain classes, controller and realm.

grails quick-start

We can follow the JSecurity Quick Start guide to create a functional security application or use the files present to customize and only take what is required.

Authentication


For the login authentication, we'll need the User class, the Auth controller and the DbRealm.

  • The User class will need a username and password field. The password field must be hashed, SHA1 is the default.

  • For the Auth controller, the important methods are signIn and signOut. For signIn, the important lines are the authToken definition, the jsecSecurityManager login and the try-catch block.

  • Authentication occurs in the DbRealm. The important lines in the authenticate method are the account definition and the credentialMatch. You may customize the authToken class by changing the static line, replacing the default with your own class.

  • credentialMatch defaults to SHA1 hashing, to change it, modify grails-app/conf/spring/resources.groovy and add the line credentialMatcher(org.jsecurity.authc.credential.hashMatcher). Replace hashMatcher with the hash class of your choice.


Roles


Roles depend on the hasRole method of the realm. This method accepts a principal, usually a username, and a rolename. The output is a simple true or false boolean, making the logic inside the method completely up to the programmer. The classes generated by the quick-start are not all necessary. We can easily use a variable in the user class to determine the role, modifying the hasRole method as required, thus no additional classes are needed.

For this project, we are using the default hasRole method and keeping the Role as well as the UserRoleRel class. This, along with the User class, allows us to have users and roles, assigning the users to a role in a many-to-one relationship. Refer to the JSecurity Quick Start guide for the way to bootstrap it.

Authorization


Having users and roles, we can now set up proper access control by creating a security filter file, grails-app/conf/SecurityFilters.groovy. Filter rules cannot overlap in actions, just modify the accessControl to include the desired roles. The rules return true or false, allowing or denying access.

Security Filter Example

import org.jsecurity.SecurityUtils
// Customise the interceptor behaviour by implementing onNotAuthenticated() and onUnauthorized() methods in your filters class.
// Default behaviour remains as before (redirect to login page for an unauthenticated user, redirect to unauthorized page for unauthorized access).
class SecurityFilters {
def filters = {
// Ensure that all controllers and actions require an authenticated user,
// except for a few controllers
auth(controller: "*", action: "*") {
before = {
// Exclude these controllers.
if ((controllerName == "search" && actionName == "unboundSearch") || (controllerName == "business" && actionName == "show") ||
(controllerName == "user" && actionName == "signup")) return true

// This just means that the user must be authenticated. He does
// not need any particular role or permission.
accessControl { true }
}
}

// Roles return true or false
// Add reviews to businesses. Only Administrators or Users can do this
addReview(controller: "review", action: "create") {
before = {
accessControl {
role("Administrator") || role("User")
}
}
}

// Add pets to users
addPets(controller: "pet", action: "(create|save)") {
before = {
accessControl {
def user = User.findByUserName(SecurityUtils.subject.principal)
def allowAccess = false

if (role("Administrator")) {
allowAccess = true
}
else if (params.id.toLong() == user.id) {
allowAccess = role("Business User") || role("User")
}
return allowAccess
}
}
}
}
}

Permissions


Permissions offer finer access control, like read, update, delete. Roles can be considered as named collections of permissions, like administrator, user, superuser. Nothing was done with permissions, it was entirely a role-based affair. Refer to the documentation for usage.

Links