Help

Archive

I had the privilege today of receiving some expert tutelage on Forge Addon development in Eclipse from the Forge Team’s George Gastaldi, and I was so impressed by how easy it was that I’m going to describe the process step by step so that you too can experience the pleasure of writing a Forge addon! This guide is written from the perspective of a complete Forge noob, so you can rest assured that it has made a very strong attempt to capture all the little important details. Also, while the addon itself won’t actually do that much, we’ll try to cover all the basics which will hopefully provide the solid foundation you need to be able to implement more complex features.

Setting up your environment

I strongly recommend that you work with the latest trunk of Forge, so fire up your terminal window and start by cloning the following repositories from GitHub:

https://github.com/forge/furnace

https://github.com/forge/furnace-cdi

https://github.com/forge/forge-core

https://github.com/jbosstools/jbosstools-forge

Once you have them all build each of them using Maven, making sure to skip the tests. You’ll need Maven 3.1 or higher for this step:

cd furnace
mvn clean install -Dmaven.test.skip=true

If all goes well you’ll get a BUILD SUCCESS message at the end:

Continue by building the other three projects (furnace-cdi, forge-code and jbosstools-forge) in order using the same Maven command.

Installing Forge

Once everything has built successfully, it’s time to install Forge. Open the forge-core/dist/target directory where you’ll find some zip files containing the Forge distribution:

You have a choice here, you can either use the (smaller) distribution zip which doesn’t come pre-packaged with all of the core addons, or you can use the larger offline distribution which has the addons packaged also. For now, I recommend you use the offline distribution, so extract it to its own folder somewhere. I generally like to install tools like this into a dedicated apps folder, and if you’re on a *nix based OS I recommend creating a symbolic link also:

Once that’s done, you need to create a FORGE_HOME environment variable pointing to this directory, and add the $FORGE_HOME/bin directory to your path (this will allow you to execute the forge shell from anywhere). This is an OS-specific step so it’s up to you to complete this by yourself. When that’s done, run the forge command from a command prompt.

(Note: If you didn’t install the offline distribution, you’ll be prompted to install the core addons. When you start forge, it will attempt to install the latest released versions, however in our case we want to install the artifacts that we just built ourselves, so to do that we must first run forge -i core,2.0.1-SNAPSHOT - alternatively just use the offline distribution and save yourself a lot of trouble.)

Forge will download a bunch of stuff and then prompt you to confirm the installation, so select Y and press Enter again.

Eventually the installation will complete and you’ll be sitting at the Forge command prompt:

From here you can press the tab key to see the available commands:

We’ve successfully set up the Forge shell! Let’s quit out for now by typing exit then pressing enter and we’ll go through the steps to install the JBoss Tools Forge plugin in Eclipse.

Installing the Forge Eclipse Plugin

At the time of writing I’m using the latest release version of Eclipse, Kepler SR1. If you are using an older version of Eclipse then I recommend that you update to the latest, otherwise results might be unpredictable. The first step in installing the Eclipse plugin is to start up Eclipse, then go to the Help -> Install New Software… menu option:

Then click the Add button, then in the popup window click Archive:

Browse to the jbosstools-forge/site/target directory, select the forge.site.XXX zip file and click OK:

Click OK again in the Add Repository window, then select the checkbox next to JBoss Tools Forge Nightly Build Update Site, then click the Next button:

You’ll be prompted to review the install details, so click the Next button again. You’ll be asked to review and accept the terms of the license agreement, so after accepting them click the Finish button and Eclipse will go ahead and install the plugin. If you get a security warning about unsigned content don’t panic; it’s just a warning and it's fine to click OK here. Once finished, Eclipse will inform you that it needs to be restarted so go ahead and click the Yes button. After Eclipse has restarted you can confirm that the Forge plugin has been successfully installed by pressing Ctrl + 5, which will present you with the following popup window:

If you can see the Forge command window after pressing Ctrl + 5, then congratulations on making it this far! Next we’ll go ahead and create our Forge Addon project.

Creating a Forge Addon Project

Forge does a great job of doing the heavy lifting when it comes to creating a new Addon. Let’s create the initial project structure for our addon by opening the Forge command window in Eclipse with Ctrl + 5, then scrolling down until we find the Project/Generation - Project: New option:

Clicking on the Project: New option gives us a dialog window that lets us enter some basic details for our addon project. Fill in the details for the project name, top level package, version, final name and project location. Select Forge Addon for the project type and leave the Build system default of Maven, then click Next.

On the next screen, click the checkbox next to Create API, Impl, SPI, Tests and Addon modules and select the following addons from the addon list:

org.jboss.forge.addon:projects

org.jboss.forge.addon:ui

org.jboss.forge.furnace.container:cdi

After you’ve selected these addons, click the Finish button to generate the new project.

After grinding away for a few seconds you should see the following new projects in the Eclipse project explorer:

Our new addon project is created! Next we’ll start adding some basic functionality.

Making the Addon do something

This is where we get into the exciting stuff. Let’s start off by creating a new class in the impl project, under the com.acme.helloworld.ui package called HelloWorldUICommand. Once you’ve created this class, edit its code and make it extend AbstractUICommand:

package com.acme.helloworld.ui;

import org.jboss.forge.addon.ui.command.AbstractUICommand;

public class HelloWorldUICommand extends AbstractUICommand {

}

Once that’s done, tell Eclipse to add the unimplemented methods:

package com.acme.helloworld.ui;

import org.jboss.forge.addon.ui.command.AbstractUICommand;
import org.jboss.forge.addon.ui.context.UIBuilder;
import org.jboss.forge.addon.ui.context.UIExecutionContext;
import org.jboss.forge.addon.ui.result.Result;

public class HelloWorldUICommand extends AbstractUICommand {

    @Override
    public void initializeUI(UIBuilder builder) throws Exception {
        // TODO Auto-generated method stub
        
    }

    @Override
    public Result execute(UIExecutionContext context) throws Exception {
        // TODO Auto-generated method stub

    }

}

Let’s write a bare minimum amount of code so that we can install our plugin and try it out. Implement the execute() method by adding the following code:

package com.acme.helloworld.ui;

import org.jboss.forge.addon.ui.command.AbstractUICommand;
import org.jboss.forge.addon.ui.context.UIBuilder;
import org.jboss.forge.addon.ui.context.UIExecutionContext;
import org.jboss.forge.addon.ui.result.Result;
import org.jboss.forge.addon.ui.result.Results;

public class HelloWorldUICommand extends AbstractUICommand {

    @Override
    public void initializeUI(UIBuilder builder) throws Exception {
        // TODO Auto-generated method stub
        
    }

    @Override
    public Result execute(UIExecutionContext context) throws Exception {
        return Results.success("Hello World");
    }

}

Once that’s done, save the class and build the project by running mvn clean install in the parent folder of your addon project:

Then in Eclipse, select the helloworld-addon project (or whatever you have called it), press Ctrl + 5 to display the Forge command window and select the Addon/Manage - Install an Addon command:

Leave the default values in the Install an Addon window unchanged and click the Finish button:

If everything has gone to plan, you should notice a little popup window in the bottom right corner of the screen confirming that the addon was successfully installed. We can test our new addon immediately by pressing Ctrl + 5 and scrolling down until we find our new command:

Clicking on the command should give us another popup window that displays our hello world message:

Creating User Interface Elements

The next step is to create some user interface elements that prompt the user for input. We’ll start simple by injecting a simple text input control and adding it to the dialog screen for our addon - here’s a complete code listing:

package com.acme.helloworld.ui;

import javax.inject.Inject;

import org.jboss.forge.addon.ui.command.AbstractUICommand;
import org.jboss.forge.addon.ui.context.UIBuilder;
import org.jboss.forge.addon.ui.context.UIExecutionContext;
import org.jboss.forge.addon.ui.input.UIInput;
import org.jboss.forge.addon.ui.result.Result;
import org.jboss.forge.addon.ui.result.Results;

public class HelloWorldUICommand extends AbstractUICommand {

    @Inject UIInput<String> input;
    
    @Override
    public void initializeUI(UIBuilder builder) throws Exception {
        builder.add(input);
    }

    @Override
    public Result execute(UIExecutionContext context) throws Exception {
        return Results.success("Hello World");
    }

}

Rebuild the project the same way as before, and reinstall the addon from the Forge command screen (Ctrl + 5 and select Addon/Manage : Install an Addon). Press Ctrl + 5 again and select HelloWorldUICommand, and you should now see the following dialog window:

We can customize our input control further by adding a @WithAttributes annotation - add the following import:

import org.jboss.forge.addon.ui.metadata.WithAttributes;

Then add the @WithAttributes annotation to the UIInput:

@Inject @WithAttributes(label = "Name", required = true, description = "Enter your name")  
    UIInput<String> input;

The control can be customized by setting the various member values:

label - The label for the input

required - Make the input a required field

description - The description when you mouse over the field.

Save your class, then build and reinstall the addon following the same steps as before. Execute the addon command using Ctrl + 5 and you should now see this:

We can now begin to see how simple it is to build a user interface for our addon. Let’s extend it further by adding a select control that allows you to select a Maven dependency. In this example I’ll use the PicketLink project as our guinea pig. We’ll need to @Inject a DependencyResolver to locate the dependencies that we’re interested in, and a UISelectOne control to allow the user to select one of the dependencies. Add this code to your class:

@Inject DependencyResolver dependencyResolver;    
    
@Inject @WithAttributes(label = "Version", required = true, description = "Select the version of PicketLink") 
private UISelectOne<Coordinate> version;    
    
@Override
public void initializeUI(UIBuilder builder) throws Exception {
    builder.add(input);
        
    DependencyQuery query = DependencyQueryBuilder
            .create("org.picketlink:picketlink-api")
            .setFilter(new NonSnapshotDependencyFilter());

    List<Coordinate> coordinates = dependencyResolver.resolveVersions(query);
    version.setValueChoices(coordinates);
    builder.add(version);        
}

The DependencyQuery is used to to declare the criteria that we’ll use to locate the dependencies we want, and the NonSnapshotDependencyFilter parameter tells the query that we’re only interested in non SNAPSHOT releases. The Coordinate class is used to represent a specific Maven dependency and contains property values for the dependency’s groupId, artifactId and so forth. We populate the select control with our list of Coordinates and finally add the control to the form via the builder.add() method. Once you’re done the complete code listing should look like this:

package com.acme.helloworld.ui;

import java.util.List;

import javax.inject.Inject;

import org.jboss.forge.addon.dependencies.Coordinate;
import org.jboss.forge.addon.dependencies.DependencyQuery;
import org.jboss.forge.addon.dependencies.DependencyResolver;
import org.jboss.forge.addon.dependencies.builder.DependencyQueryBuilder;
import org.jboss.forge.addon.dependencies.util.NonSnapshotDependencyFilter;
import org.jboss.forge.addon.ui.command.AbstractUICommand;
import org.jboss.forge.addon.ui.context.UIBuilder;
import org.jboss.forge.addon.ui.context.UIExecutionContext;
import org.jboss.forge.addon.ui.input.UIInput;
import org.jboss.forge.addon.ui.input.UISelectOne;
import org.jboss.forge.addon.ui.metadata.WithAttributes;
import org.jboss.forge.addon.ui.result.Result;
import org.jboss.forge.addon.ui.result.Results;

public class HelloWorldUICommand extends AbstractUICommand {
    @Inject @WithAttributes(label = "Name", required = true, description = "Enter your name")  
    UIInput<String> input;

    @Inject DependencyResolver dependencyResolver;    
    
    @Inject @WithAttributes(label = "Version", required = true, description = "Select the version of PicketLink") 
    private UISelectOne<Coordinate> version;    
    
    @Override
    public void initializeUI(UIBuilder builder) throws Exception {
        builder.add(input);
        
        DependencyQuery query = DependencyQueryBuilder
                .create("org.picketlink:picketlink-api")
                .setFilter(new NonSnapshotDependencyFilter());

        List<Coordinate> coordinates = dependencyResolver.resolveVersions(query);
        version.setValueChoices(coordinates);
        builder.add(version);        
    }

    @Override
    public Result execute(UIExecutionContext context) throws Exception {
        return Results.success("Hello World");
    }
}

Rebuild and reinstall the addon, then invoke it from the Forge command window - you should see something that looks like this:

We can use these newfound powers to make changes to an actual project. Let’s start by modifying our addon class so that it extends AbstractProjectCommand instead of AbstractUICommand:

public class HelloWorldUICommand extends AbstractProjectCommand {

This will cause Eclipse to complain about unimplemented methods, so use Eclipse to generate them:

    @Override
    protected boolean isProjectRequired() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    protected ProjectFactory getProjectFactory() {
        // TODO Auto-generated method stub
        return null;
    }

The isProjectRequired() method is used to determine whether this addon requires a project to execute. Since this is what we actually want, we’re going to return a value of true here:

    @Override
    protected boolean isProjectRequired() {
        return true;
    }

The getProjectFactory() method provides a reference to the currently selected project. To implement this method we first need to @Inject a ProjectFactory object into our addon class:

@Inject ProjectFactory projectFactory;

Then we simply return this value in the method body:

    @Override
    protected ProjectFactory getProjectFactory() {
        return projectFactory;
    }

So far so good! Now, since our goal is to add some dependencies to our project we need to somehow take the selected Coordinate value from our UISelectOne control and use it to add a dependency to the current project. Lucky for us Forge provides all of the tools to make this happen - first of all we’re going to inject a DependencyInstaller into our class:

@Inject DependencyInstaller dependencyInstaller;

Then, in the execute() method we’re going to use a DependencyBuilder to create a reference to the Dependency based on the Coordinate value contained in the UISelectOne control, after which we’ll tell the DependencyInstaller to install that Dependency into the selected project:

DependencyBuilder builder = DependencyBuilder.create();
builder.setCoordinate(version.getValue());

dependencyInstaller.install(getSelectedProject(context), builder);

The complete code listing should now look like this:

package com.acme.helloworld.ui;

import java.util.List;

import javax.inject.Inject;

import org.jboss.forge.addon.dependencies.Coordinate;
import org.jboss.forge.addon.dependencies.DependencyQuery;
import org.jboss.forge.addon.dependencies.DependencyResolver;
import org.jboss.forge.addon.dependencies.builder.DependencyBuilder;
import org.jboss.forge.addon.dependencies.builder.DependencyQueryBuilder;
import org.jboss.forge.addon.dependencies.util.NonSnapshotDependencyFilter;
import org.jboss.forge.addon.projects.ProjectFactory;
import org.jboss.forge.addon.projects.dependencies.DependencyInstaller;
import org.jboss.forge.addon.projects.ui.AbstractProjectCommand;
import org.jboss.forge.addon.ui.context.UIBuilder;
import org.jboss.forge.addon.ui.context.UIExecutionContext;
import org.jboss.forge.addon.ui.input.UIInput;
import org.jboss.forge.addon.ui.input.UISelectOne;
import org.jboss.forge.addon.ui.metadata.WithAttributes;
import org.jboss.forge.addon.ui.result.Result;
import org.jboss.forge.addon.ui.result.Results;

public class HelloWorldUICommand extends AbstractProjectCommand {
    
    @Inject ProjectFactory projectFactory;

    @Inject @WithAttributes(label = "Name", required = true, description = "Enter your name")  
    UIInput<String> input;
    
    @Inject DependencyInstaller dependencyInstaller;

    @Inject DependencyResolver dependencyResolver;    
    
    @Inject @WithAttributes(label = "Version", required = true, description = "Select the version of PicketLink") 
    private UISelectOne<Coordinate> version;    
    
    @Override
    public void initializeUI(UIBuilder builder) throws Exception {
        builder.add(input);
        
        DependencyQuery query = DependencyQueryBuilder
                .create("org.picketlink:picketlink-api")
                .setFilter(new NonSnapshotDependencyFilter());

        List<Coordinate> coordinates = dependencyResolver.resolveVersions(query);
        version.setValueChoices(coordinates);
        builder.add(version);        
    }

    @Override
    public Result execute(UIExecutionContext context) throws Exception {
        DependencyBuilder builder = DependencyBuilder.create();
        builder.setCoordinate(version.getValue());

        dependencyInstaller.install(getSelectedProject(context), builder);
        
        return Results.success("Hello World");
    }

    @Override
    protected boolean isProjectRequired() {
        return true;
    }

    @Override
    protected ProjectFactory getProjectFactory() {
        return projectFactory;
    }
}

Rebuild and reinstall your addon, then select a project in Eclipse and invoke our addon from the Forge command window. Keep in mind that we’ll be adding some new dependencies to the project you select so make sure it’s not something too important! I suggest you use the addon project itself as it is quite easy to remove the dependencies afterwards. Enter any value for the Name field (since we’ve marked it as required we’re forced to at least enter something) and select one of the dependencies from the Version control:

Click the Finish button and you should see the Hello World popup window appear briefly confirming that our addon ran successfully. To check it actually did something, open the project’s pom.xml in an editor and you should notice that Forge has added the selected dependency by declaring it in the <dependencyManagement> section:

And also adding the dependency itself to the <dependencies> section:

Congratulations, you’ve made it to the end! You’ve now created your first useful Forge addon that can add dependencies to an existing project. Hopefully this guide has helped by providing a high level overview of the Forge Addon architecture and a beginner’s primer on creating a new Forge Addon.

Summary

In this guide we went through the steps to build and install both the Forge Shell and Forge Eclipse Plugin, then learned how to create a new Forge Addon project. We also learned how to build and install our Addon, create custom dialog windows and have our Addon make changes to an existing project. If you’d like to learn more about Forge, then the following resources are available:

Forge web site: http://forge.jboss.org/

Documentation: http://forge.jboss.org/docs/index.html

Source code: https://github.com/forge

Thanks for reading!

13. Nov 2013, 02:48 CET, by Shane Bryzak

In this series we’ll be taking a magnifying glass to PicketLink, a security framework for Java EE. Each article in the series will examine a single aspect or feature of PicketLink in detail and also illuminate some of the design decisions made during the feature’s development.

Hopefully by the end of the series we’ll have helped you to gain a greater understanding of PicketLink, and how best to use it to address the security requirements of your own Java EE application.

Resources

At the time of writing the latest stable version of PicketLink is 2.5.2.Final. The latest version of both the PicketLink Reference Documentation and API Documentation can always be found at http://docs.jboss.org/picketlink/2/latest/.

The PicketLink binary distribution can be downloaded from the following page:

http://www.jboss.org/picketlink/downloads.html

If your build is Maven-based then please refer to the PicketLink Reference Documentation for information about how to include the PicketLink dependencies in your project.

You can find the latest version of the PicketLink source code at GitHub:

https://github.com/picketlink/picketlink

Identity Model

In this issue we’ll be looking at PicketLink’s Identity Model feature, a fundamental part of the PicketLink IDM (Identity Management) submodule that forms the foundation on which the majority of PicketLink’s other features are built.

During the design process for PicketLink IDM the development team spent a lot of time trying to work out the best way for modelling common security concepts such as roles and groups, etc. Faced with challenges relating to the lack of a common (or even de facto) standard for these core concepts it was decided that PicketLink required a certain level of abstraction to allow it to be customized per-deployment depending on the application’s security needs. Striking a balance between usability (for those that just wished to use PicketLink out of the box) and customizability (for applications that had more complex, or non-standard security requirements) posed a challenge that required an elegant solution.

The answer was the Identity Model - a set of interfaces and classes that provide a basic implementation so that PicketLink would just work(™), built on top of an abstraction layer that allows for a completely custom implementation.

The Identity Model defines the identity objects (such as Users and Roles) for your project, and the relationships between those objects. This is one of the major differences between PicketLink and other security frameworks - PicketLink supports a dynamic Identity Model, which means that there isn’t a hard-coded set of User, Role or Group classes that you are forced to use in your own application (PicketLink actually does provide these classes but you may choose not to use them). Why is it so important that PicketLink supports such a high level of customization? There are actually quite a number of reasons, so let’s take the time to explore a few of them in more detail.

Region Specific Attributes

Depending on where your project is being deployed, you may have specific requirements in relation to User state based on the region that your application may be servicing. For example, an application in the United States might require a User to have an SSN property, while an application in Russia might require a property to store a user’s patronym. In western culture a person may typically have a title, first name, middle name/s and last name, however this is certainly not the rule in many other cultures. It may be argued that these property values may be simply stored as arbitrary attribute values, like so:

user.setAttribute(new Attribute(“joinDate”, new Date()));

However storing these property values as ad-hoc attributes means we give up on important stuff like type safety and bean validation. If the property value is one that’s required for all of your users, then It’s much more desirable to be able to define these as first class properties of your bean, like so:

user.setJoinDate(new Date());

Custom Security Requirements

Security requirements may change drastically between applications, and a security architecture that may be sufficient for one application might be unsuitable for another. Let’s take roles for example - depending on who you ask, a role within an application might mean any number of things.

In some cases, a role equates to a simple, application-wide privilege that may be assigned directly to users. In Java EE5 for example the @RolesAllowed annotation may be used to restrict access to a class or method, allowing only the users that have been granted that specific role invocation rights:

@RolesAllowed(“admin”)
public void setBaseInterestRate(float value);

In other cases, a role might be something that may only be assigned to a group or mapped to a principal. Or it might only be valid within the context of a group (i.e. user jsmith has the logistics_support role for the retail_sales group). Or there may even be no such thing as roles at all, with all application privileges perhaps modeled as group memberships or using some alternative design. The point is there’s no single correct way to do security.

Because of the fact that everyone has different ideas of what constitutes a Security API, it is important that PicketLink provides the flexibility to allow custom scenarios to be supported.

Backend Identity Store Compatibility

In some environments your application may be required to conform to a pre existing security model. For example a corporate LDAP directory with heavily customised user attributes or a central SSO provider over which you have no direct control, or a legacy database containing user records. In these scenarios it's important that PicketLink is able to work within the bounds of an existing security architecture, and be able to accurately model its security objects.

Identity Model Building Blocks

Let’s get more technical now about what actually constitutes an Identity Model. A core set of interfaces from the org.picketlink.idm.model package define the foundation on which an Identity Model is built. Let’s take a look at these in the following class diagram:

We’ll be covering partitions in a future article so we can ignore the Partition interface for now. That leaves us with the following four core interfaces:

AttributedType - This is the root interface of the Identity Model. It basically provides methods for defining only two simple pieces of state - a globally unique identifier value assigned by PicketLink when an identity object is created, and a set of arbitrary attribute values. This means that every identity object (including users, roles, groups, and any other identity types you might create) at the very least has a unique identifier value, and is capable of storing a custom set of named attribute values of any Serializable type.

IdentityType - This is the parent interface for all identity types. In addition to the identifier and attribute properties inherited from the AttributedType interface, it also defines properties that reflect whether the identity object is enabled or not, the partition that it belongs to and the created date and optional expiration date for the identity.

Account - This is a special marker interface, which represents an identity type that is capable of authenticating. It is typically used as the superinterface for user identity types, or identity types that represent a third party process or agent that may interact with your application. Identity types such as roles or groups do not implement the Account interface as these types are not capable of authentication (i.e. they cannot log in), instead they implement the IdentityType interface directly.

Relationship - This is the super-interface for all relationship implementations. Like the Account interface it is also a marker interface, with no additional state (beyond that declared by its parent interface IdentityType) required by its contract.

There are a couple more important ingredients that we must know about besides the core interfaces described above when creating an identity model - the @AttributeProperty annotation and the @Unique annotation. We’ll cover these in more detail in the next section.

What does an actual Identity Model look like?

Good question! To answer this, we’ll take a look at the basic Identity Model provided out of the box by PicketLink. This model defines a number of commonly used identity objects and relationships, and in many cases may be sufficient for the security requirements of many typical applications. It can be easily extended or even copied to allow for convenient customization if desired.

Agent

The Agent class represents an external non-human identity that is capable of authenticating, hence it implements the Account interface. It defines getter and setter methods for a loginName property, which is used during authentication to identify the Account. Let’s take a look at the source code for this class (for the purpose of brevity some non-essential code has been trimmed):

public class Agent extends AbstractIdentityType implements Account {
    private String loginName;

    @AttributeProperty
    @Unique
    public String getLoginName() {
        return loginName;
    }

    public void setLoginName(String loginName) {
        this.loginName = loginName;
    }
}

There’s a few significant things to note about the above code. First of all, we can see that the Agent class extends AbstractIdentityType. This abstract class provides default implementations for all the methods of the parent IdentityType interface, allowing us to concentrate on the specific business logic for this identity class. If you wish to implement your own Identity Model then this can be a great time-saver.

Secondly, the @AttributeProperty annotation indicates that this is a property of the identity object that should be automatically mapped to the backend identity store. Each identity property that must be persisted should be annotated with @AttributeProperty. An example of a property that might not be annotated would be one with a calculated value, for instance a getFullName() method that simply concatenates the user’s first and last name together, yet doesn’t persist the calculated value itself in the backend identity store.

Thirdly, the @Unique annotation is used to indicate to PicketLink that the property must contain unique values. In this example it is used to ensure that the loginName property is unique (it should be obvious why it’s important that more than one account does not have the same login name).

Finally, we can observe that creating identity properties is as simple as defining a getter and setter method, following the JavaBean standard. Property values may be any serializable type.

User

The User class extends Agent to add some human-specific properties, such as first name, last name and so forth.

public class User extends Agent {
    @AttributeProperty
    private String firstName;

    @AttributeProperty
    private String lastName;

    @AttributeProperty
    private String email;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return this.email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

In the above code, you may notice that the @AttributeProperty annotation is on the actual field declaration itself, instead of the getter method. PicketLink allows the annotation on either the field or the getter method, similar in fashion to JPA and its annotations.

Group

The Group class is an identity type that may be used for modelling basic group-related identity privileges, such as group membership and group roles. It is intended to be used in a hierarchical structure, with each group capable of having multiple subgroups and so forth. The Group class defines three property values - the name property represents the group’s unique name within the same branch of its hierarchical structure, the parentGroup property is an optional property which may be used to specify the group’s parent group (or null if the group has no parent, i.e. it is a root group), and path which represents the fully qualified path of the group, i.e. the complete hierarchical group name structure starting from the root, delimited by a separator.

public class Group extends AbstractIdentityType {
    public static final String PATH_SEPARATOR = "/";

    private String name;
    private Group parentGroup;
    private String path;

    @AttributeProperty
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @AttributeProperty
    @Unique
    public String getPath() {
        this.path = buildPath(this);
        return this.path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    @AttributeProperty
    public Group getParentGroup() {
        return this.parentGroup;
    }

    @AttributeProperty
    public void setParentGroup(Group group) {
        this.parentGroup = group;
    }

    private String buildPath(Group group) {
        String name = PATH_SEPARATOR + group.getName();

        if (group.getParentGroup() != null) {
            name = buildPath(group.getParentGroup()) + name;
        }

        return name;
    }
}

You may notice in the above code that the parentGroup property has a type of Group. This is possible because PicketLink allows identity definitions to themselves directly reference other identities via their properties. We can also notice that the getPath() method is annotated with @Unique - this is to ensure that group names are unique within their same group branch (i.e. no other group with the same parent group may have the same name).

Role

The Role class is quite similar to the Group class except that it doesn’t support a hierarchical structure, therefore its code is significantly more simple:

public class Role extends AbstractIdentityType {
    private String name;

    @AttributeProperty
    @Unique
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Relationships

Relationships are the constructs that help us to define the privileges for our identity objects by defining how they relate to each other. Relationships are typed associations between two or more identity objects.

A relationship class must implement the Relationship interface, which itself extends AttributedType - this means that every concrete relationship instance is assigned a unique identifier value, and is also capable of storing an arbitrary set of attribute values.

The basic identity model defines only three relationship types - Grant, which associates an identity with a role; GroupMembership, which similarly associates an identity with a group; and lastly GroupRole, which is used to assign a group-specific role to an identity. Let’s take a look at them in closer detail:

Grant

The Grant relationship is a simple association between a role and an assignee, and is generally used to assign coarse-grained application-wide privileges to a set of users.

public class Grant extends AbstractAttributedType implements Relationship {
    
    private IdentityType assignee;
    private Role role;

    public IdentityType getAssignee() {
        return assignee;
    }

    public void setAssignee(IdentityType assignee) {
        this.assignee = assignee;
    }

    public Role getRole() {
        return role;
    }

    public void setRole(Role role) {
        this.role = role;
    }
}

You may notice that there is nothing special about the above code - the Grant class extends AbstractAttributedType to provide the basic framework methods, however the implementation of the relationship itself is simple a couple of fields with getter/setter methods. This simplicity is intentional - as long as your property values implement the IdentityType interface then PicketLink is perfectly capable of working with your relationship class without any further effort required. This makes it extremely easy to define new custom relationship types yourself.

If you do wish to define a non-identity attribute property for your relationship, then you may do so by annotating the property with @AttributeProperty, the same way as is done when creating an identity type. For example, let’s say we would like to store the grant date for the above relationship - we could simply add a property like this:

    private Date grantDate;

    @AttributeProperty
    public Date getGrantDate() {
        return grantDate;
    }

    public void setGrantDate(Date grantDate) {
        this.grantDate = grantDate;
    }

Then we would be able to use it like so:

Grant grant = new Grant();
grant.setGrantDate(new Date());

Alternatively, you could of course just set an arbitrary attribute value:

Grant grant = new Grant();
grant.setAttribute(new Attribute(“grantDate”, new Date()));

Which option you would choose is totally up to you.

GroupMembership

The GroupMembership relationship is very similar to Grant, except that it is used to associate an account with a Group.

public class GroupMembership extends AbstractAttributedType implements Relationship {
    private Account member;
    private Group group;

    public Account getMember() {
        return member;
    }

    public void setMember(Account member) {
        this.member = member;
    }

    public Group getGroup() {
        return group;
    }

    public void setGroup(Group group) {
        this.group = group;
    }
}

Once again, there is absolutely nothing special about this implementation - it simply defines two property values with getter/setter methods.

GroupRole

The GroupRole relationship is used to represent a group-specific role, similar to Grant however restricted to the scope of a single group. To simplify the implementation of this relationship it extends the Grant relationship (which already provides the assignee and role properties), and just adds a group property:

public class GroupRole extends Grant implements Relationship {
    private Group group;

    public Group getGroup() {
        return group;
    }

    public void setGroup(Group group) {
        this.group = group;
    }
}

Like the above examples, the implementation is extremely simple - a single property with getter/setter methods.

Summary

Hopefully this article has been helpful in explaining how to work with PicketLink’s identity model, both in using the provided basic model and in describing how to create your own custom model, complete with identity and relationship classes.

If you have any questions or comments please post them below, and if you have any special requests for future PicketLink Deep Dive topics please let us know also.

Thanks for reading!

I'm pleased to announce the release of PicketLink 2.5.0.Beta3. This release is actually the successor to 3.0.0.Beta2 - we've changed the version number to help avoid confusion going forward, if you want the nitty gritty details about this decision then you can find out more below.

In case you don't know what it is, PicketLink is a security framework for Java EE applications. If you are already familiar with Seam Security, then you might consider PicketLink to be its spiritual successor. For more info about its features check out this overview.

As always, here's the direct links to the various resources for this release:

PicketLink 2.5.0.Beta3 Distribution Zip

Reference Documentation

API Documentation

Source on GitHub

Report Issues

Mailing List

Mailing List Archives

Forums

For those of you that use Maven, we've made a slight change to the artifact ids - from now on a typical application will only need to declare the following dependencies:

<dependency>
  <groupId>org.picketlink</groupId>
  <artifactId>picketlink-api</artifactId>
  <version>2.5.0.Beta3</version>
</dependency>

<dependency>
  <groupId>org.picketlink</groupId>
  <artifactId>picketlink-impl</artifactId>
  <version>2.5.0.Beta3</version>
  <scope>runtime</scope>
</dependency>

<!-- Optional database schema when using Identity Management with JPA based Identity Store -->
<dependency>
  <groupId>org.picketlink</groupId>
  <artifactId>picketlink-idm-schema</artifactId>
  <version>2.5.0.Beta3</version>
  <scope>runtime</scope>
</dependency>

For this release, the reference documentation has received some special attention and so should be significantly more informative than previous betas. With that being said, the team is constantly improving the docs and will continue to add more content right up to the final release. There are also numerous quickstarts planned to help you get up and running quickly with the many security features provided by PicketLink - I'll be posting some more info about some of these shortly so keep an eye on this space.

There have also been a substantial number of bug fixes, new features and improvements made since the last beta release, the details of which you'll find in the release notes below.

As always, the PicketLink team is available to chat with at various times of the day on the #picketlink IRC channel at freenode.net; also feel free to drop them a line at the PicketLink Forums if you need help using PicketLink in your own application.

Version Change

If you've been following any PicketLink-related news lately, you'll know that the PicketLink team has been working hard towards releasing PicketLink 3 in the very near future. It has recently come to the team's attention though that there is some confusion surrounding the versioning of PicketLink, which is due to a couple of reasons; firstly, there is an existing committment to support PicketLink Federation 2.1 in EAP (the supported version of JBoss Application Server, recently renamed to WildFly). Secondly, there was never an actual 2.x release of PicketLink IDM (it was only ever officially released as version 1.x). Because of this, and since the PicketLink Federation that was planned to be released as 3.0 is actually backwards compatible with 2.1, the team has decided to rename the 3.0 release to 2.5. So what does this mean exactly? Well, besides the change in version number, not much at all really - PicketLink 2.5 will still include all of the next generation security features that the PicketLink team have been working on over the past months. We apologise for any inconvenience that this change might have caused, however the team felt it was best to resolve this confusion now, before the final release.

Release Notes

Bug

  • PLINK-116 - Change scope for JBoss Logging dependency in picketlink-common module to provided
  • PLINK-119 - DefaultIdentity is considering the User.id when comparing with the DefaultLoginCredentials.userId
  • PLINK-131 - Signed logout request does not contain the "Destination" attribute
  • PLINK-132 - PicketLink based SP's need to support different login and logout URLs
  • PLINK-136 - The IDM subsystem is always initialized even when a custom Authenticator is provided
  • PLINK-137 - Change the scope for CDI dependencies in picketlink-api and picketlink-impl to provided

Enhancement

  • PLINK-126 - Introduce individual annotations for JPA schema entities and properties
  • PLINK-135 - Add type parameters to CredentialHandler

Feature Request

  • PLINK-113 - Users should be able to use a IdentityManager for any of the configured realms.
  • PLINK-117 - The API documentation should aggregate the javadocs for the modules
  • PLINK-118 - Update documentation with the File and LDAP stores configuration
  • PLINK-120 - Login logic is not considering when the user is disabled/locked
  • PLINK-121 - Throw a specific exception when the user tries to authenticate twice using the same credentials
  • PLINK-124 - Add credential storage retrieval methods to IdentityManager
  • PLINK-127 - CredentialHandler implementations should check if the Agent is disabled
  • PLINK-128 - Refactor the Configuration API to provide a Fluent API using the build pattern
  • PLINK-142 - Provide more examples about how to mix identity stores

Task

  • PLINK-92 - Container Bindings Project
  • PLINK-122 - Provide test cases for the base module
  • PLINK-125 - Umbrella task for 2.5.0.Beta3 documentation issues
  • PLINK-129 - Import the container bindings modules from PicketLink v2
  • PLINK-133 - Use getAgent() instead of getUser() throughout authentication API
  • PLINK-138 - Change project version from 3.0.0 to 2.5.0
03. Mar 2013, 11:58 CET, by Shane Bryzak

I'm very pleased to announce the first alpha release of PicketLink 3.0. This release is the culmination of many months of effort by the JBoss Security team, with many contributions made from other project teams at JBoss and members of the community. I'd like to thank everyone involved with this release, from those who participated in the many design discussions on the security mailing list over the last few months, to those who contributed code and have started integrating PicketLink into their own projects.

For those who don't know, PicketLink is a CDI-based application security framework for Java EE, much in the same spirit of Seam Security. In fact, PicketLink 3.0 can be considered to be the spiritual successor to Seam Security, as it is more or less based on a similar core design albeit with a much more powerful and flexible identity model and feature set.

Before I go any further, here's the links to the goodies:

PicketLink 3.0.0.Alpha1 Distribution Zip

Reference Documentation * PLEASE NOTE that the reference documentation is still being written, so please be patient with us!

API Documentation

Source Code on GitHub

Report Issues

PicketLink forums

Mailing List

Mailing List Archives

For those of you that use Maven, here's the artifact id's to add to your pom.xml:

<dependency>
  <groupId>org.picketlink</groupId>
  <artifactId>picketlink-core-api</artifactId>
  <version>3.0.0.Alpha1</version>
</dependency>

<dependency>
  <groupId>org.picketlink</groupId>
  <artifactId>picketlink-core-impl</artifactId>
  <version>3.0.0.Alpha1</version>
  <scope>runtime</scope>
</dependency>

<dependency>
  <groupId>org.picketlink</groupId>
  <artifactId>picketlink-idm-api</artifactId>
  <version>3.0.0.Alpha1</version>
</dependency>

<dependency>
  <groupId>org.picketlink</groupId>
  <artifactId>picketlink-idm-impl</artifactId>
  <version>3.0.0.Alpha1</version>
  <scope>runtime</scope>
</dependency>

<!-- Optional database schema when using Identity Management with JPA based Identity Store -->
<dependency>
  <groupId>org.picketlink</groupId>
  <artifactId>picketlink-idm-schema</artifactId>
  <version>3.0.0.Alpha1</version>
  <scope>runtime</scope>
</dependency>

Currently the Maven artifacts are located in the JBoss Maven Repository, however they are planned to be synchronized to Maven Central shortly.

This initial Alpha release primarily concentrates on authentication, authorization and identity management features. Let's take a look at a couple of these features in a little more detail, starting with simple programmatic authentication.

Programmatic authentication

This simple form of authentication should be familiar to many Seam users. To control the authentication process yourself, simply provide a bean in your application that implements the following Authenticator interface:

package org.picketlink.authentication;

import org.picketlink.idm.model.User;

public interface Authenticator {
    public enum AuthenticationStatus {
        SUCCESS, 
        FAILURE, 
        DEFERRED
    }

    void authenticate();

    void postAuthenticate();

    AuthenticationStatus getStatus();

    User getUser();
}

To make things easier, we've provided an abstract base class that's already implemented everything for you except for the authenticate() method. There's only two things you need to do yourself in the authenticate() method; 1) set the authentication status to reflect whether authentication was successful, and 2) set the User object if authentication was successful. In this example, we're going to inject DefaultLoginCredentials (a simple holder bean provided by PicketLink which can be used for traditional username/password authentication) and allow authentication if the provided username is 'jsmith' and the password is '1234':

public class MyAuthenticator extends BaseAuthenticator {
    @Inject DefaultLoginCredentials credentials;

    public void authenticate() {
        if ("jsmith".equals(credentials.getUserId()) && "1234".equals(credentials.getPassword())) {
          setUser(new SimpleUser(credentials.getUserId()));
          setStatus(AuthenticationStatus.SUCCESS);
        } else {
          setStatus(AuthenticationStatus.FAILURE);
        }
    }
}

To actually perform the authentication, simply have your application set the username and password credentials on the DefaultLoginCredentials bean, then invoke the Identity.login() method - you can either do this directly from your view layer, or define a business method in your project that injects the Identity bean and then invokes its login() method. Your Authenticator bean will be found automatically by the authentication process and its authenticate() method invoked to determine whether authentication was successful. An example might look something like this:

public @RequestScoped class LoginAction {

  @Inject DefaultLoginCredentials credentials;
  @Inject Identity identity;
  
  public void login(String username, String password) {
    credentials.setUserId(username);
    credentials.setPassword(password);
    identity.login();
  }
}

Once authenticated, the Identity bean (which by default is session-scoped) can be injected into your application's beans so that you can access the currently authenticated User.

Identity Management

PicketLink's Identity Management (IDM) module provides a formal API for managing the Users, Roles and Groups of your application. Support is provided out of the box for LDAP, Database (via JPA) or File-based identity stores, and an extensible SPI allows for the implementation of additional identity storage backends. The IDM features provided by PicketLink are far too extensive to cover in a single blog post, so let's take a look at a simple example using the JPA identity store. To make things easier we provide a default database schema, included in the picketlink-idm-schema jar file. To make use of this schema, simply add the schema jar file to your project and then add a persistence-unit entry to your project's persistence.xml file, like so:

<persistence-unit name="picketlink-default"
                      transaction-type="JTA">
    <description>PicketLink Persistence Unit</description>
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>

    <class>org.picketlink.idm.jpa.schema.IdentityObject</class>
    <class>org.picketlink.idm.jpa.schema.PartitionObject</class>
    <class>org.picketlink.idm.jpa.schema.RelationshipObject</class>
    <class>org.picketlink.idm.jpa.schema.RelationshipIdentityObject</class>
    <class>org.picketlink.idm.jpa.schema.RelationshipObjectAttribute</class>
    <class>org.picketlink.idm.jpa.schema.IdentityObjectAttribute</class>
    <class>org.picketlink.idm.jpa.schema.CredentialObject</class>
    <class>org.picketlink.idm.jpa.schema.CredentialObjectAttribute</class>

    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
        <property name="hibernate.hbm2ddl.auto" value="create-drop" />
        <property name="hibernate.show_sql" value="true" />
        <property name="hibernate.format_sql" value="true" />
        <property name="hibernate.transaction.flush_before_completion"
                  value="true" />
    </properties>
</persistence-unit>

The one other thing you must provide is an EntityManager for PicketLink to use, we can do this by writing a CDI producer field - here's an example (make sure the unitName matches the one you've configured in persistence.xml):

public class Resources {
    @SuppressWarnings("unused")
    @Produces
    @PicketLink
    @PersistenceContext(unitName = "picketlink-default")
    private EntityManager picketLinkEntityManager;
}

Once we've done that, we're ready to go! No further configuration is necessary, PicketLink provides an IDM-based Authenticator implementation that is automatically configured if the appropriate entity classes are provided.

The first thing we might want to do is to create some default users for our application. We can do this by providing a @Startup bean - here's an example in which we create a user called 'admin', to which we grant the 'administrator' application role:

@Singleton
@Startup
public class CreateDefaultUser {
    @Inject IdentityManager identityManager;

    @PostConstruct
    public void create() {
        User admin = new SimpleUser("admin");
        admin.setEmail("admin@acme.com");

        this.identityManager.add(admin);
        this.identityManager.updateCredential(admin, new Password("secret"));

        Role roleAdmin = new SimpleRole("administrator");
        this.identityManager.add(roleAdmin);

        identityManager.grantRole(admin, roleAdmin);
    }
}

The IdentityManager bean is the gateway to all of PicketLink's IDM features. It allows you to create, update and delete Users, Groups, Roles and their relationships, plus manage and validate user credentials and attributes. It can be injected into any of your project's beans any time you wish to perform Identity Management related operations.

public class MyApplicationBean {

    @Inject IdentityManager identityManager;   

}

To create a new User, Group or Role, you simply create a new instance of the object (PicketLink provides default implementations called SimpleUser, SimpleGroup and SimpleRole) and pass it to IdentityManager.add():

  User bob = new SimpleUser("bob");
  identityManager.add(bob);
  
  Group managers = new SimpleGroup("managers");
  identityManager.add(managers);
  
  Role admin = new SimpleRole("admin");
  identityManager.add(admin);

To update something, you can first look it up using the appropriate method (such as getUser(), getGroup(), etc), make your changes and then pass the object to the update() method:

  User bob = identityManager.getUser("bob");
  bob.setEmail("bob@acme.com");
  identityManager.update(bob);

To remove something, you pass it to the remove() method:

  User bob = identityManager.getUser("bob");
  identityManager.remove(bob);

Relationships are used to model typed groupings of two or more identities. PicketLink provides a few built-in relationships, and provides support for custom user-defined relationships also. The built-in relationship types are provided to address the most common identity use cases, and the IdentityManager interface declares a number of convenience methods for working with these built-in relationships. Let's take a brief look at a few examples.

Firstly, the Grant relationship is used to represent an application role. This is a role that is typically granted to a user to give them some kind of application-wide privilege. The IdentityManager.grantRole() method is used to grant an application role:

  // Grant user "bob" the "admin" role
  User bob = identityManager.getUser("bob");
  Role admin = identityManager.getRole("admin");
  identityManager.grantRole(bob, admin);

To test whether a user has an application role we use the hasRole() method:

  User bob = identityManager.getUser("bob");
  Role admin = identityManager.getRole("admin");
  boolean bobIsAdmin = identityManager.hasRole(bob, admin);

To revoke an application role, we use revokeRole():

  User bob = identityManager.getUser("bob");
  Role admin = identityManager.getRole("admin");
  identityManager.revokeRole(bob, admin);

Next, the GroupMembership relationship is used to represent a user's membership within a group. The addToGroup() method is used to add a user to a particular group:

  // Add user "bob" to the "managers" group
  User bob = identityManager.getUser("bob");
  Group managers = identityManager.getGroup("managers");
  identityManager.addToGroup(bob, managers);

To test whether a user is a member of a group, we use the isMember() method:

  User bob = identityManager.getUser("bob");
  Group managers = identityManager.getGroup("managers");
  boolean isBobAManager = identityManager.isMember(bob, managers);

Lastly, to remove a member of a group we can use the removeFromGroup() method:

  User bob = identityManager.getUser("bob");
  Group managers = identityManager.getGroup("managers");
  identityManager.removeFromGroup(bob, managers);

The last built-in relationship type is the group role. This is used when we wish to grant a user a particular role for a group, but not make them a member of the group themselves. To grant a group role, we use the grantGroupRole() method:

  // Make user "bob" an admin of the "managers" group
  User bob = identityManager.getUser("bob");
  Role admin = identityManager.getRole("admin");
  Group managers = identityManager.getGroup("managers");
  identityManager.grantGroupRole(bob, admin, managers);

To test whether a user has a particular group role, we use the hasGroupRole() method:

  User bob = identityManager.getUser("bob");
  Role admin = identityManager.getRole("admin");
  Group managers = identityManager.getGroup("managers");
  boolean isBobManagersAdmin = identityManager.hasGroupRole(bob, admin, managers);

Finally, to remove a group role we use the revokeGroupRole() method:

  User bob = identityManager.getUser("bob");
  Role admin = identityManager.getRole("admin");
  Group managers = identityManager.getGroup("managers");
  identityManager.revokeGroupRole(bob, admin, managers);

What's Next

We're aiming to have a beta release ready by mid-March, for which we're planning to have more polished code, more unit tests, better documentation and hopefully an actual example and/or quickstart that you can play around with yourself. For those adventurous enough to take the alpha release for a spin, we would love to hear some feedback from you about what you found easy, what you found hard and what we can do to make things better. For future releases of PicketLink we'll be focusing on adding some really cool features, such as support for OAuth, OpenID, SAML, Rule-based permissions with Drools, a RESTFul API and much more. You can also look forward to some more blog posts that delve further into some of the more complex PicketLink features such as ACL style permissions, identity and relationship queries, plus multi-domain support. If you'd like to leave us some feedback or have questions, please comment below or on the PicketLink forums, or if you'd like to contribute to the development discussion please join the security-dev mailing list, or alternatively catch us on the #picketlink irc channel on FreeNode.

Thanks and enjoy!

03. Feb 2012, 01:01 CET, by Shane Bryzak

I recently had the pleasure of attending Red Hat's first JUDCon event in APAC, JUDCon India. The conference was hosted by Saltmarch Media in the Nimhans Convention Centre in Bangalore, and ran for 2 days. The sessions were organized into 3 separate tracks:

  • JBoss Application Server 7
  • OpenShift and Cloud
  • Rules, Workflow , SOA and EAI

It was a great opportunity to be able to interact with so many other JBoss developers and users, and with an attendance of around 800 people it was the biggest JUDCon ever. There was a high level of energy in the atmosphere, and most sessions were packed full of people, some with standing room only. It was also great to see so much interest in JBoss technologies - the sessions I attended (and presented) all received a substantial number of questions at their conclusion, sometimes even running into the next session. On the first evening of the conference we held an open Q&A session, allowing the audience to ask whichever questions they liked to a panel of JBoss project leads and experts.

All in all, it was an awesome event. If you are interested in attending a JUDCon, then don't worry, you'll get another chance! The next one will be held in Boston - If you are able to attend, I highly recommend you do as you won't get a better chance to network with the core developers at JBoss.

We'll also be making the videos of the sessions available online, so keep an eye out for them soon!

Showing 1 to 5 of 32 blog entries