1

I'm using Picketlink (2.6.0.Final) with JPA (using Postgresql) for persistence. I have also implemented an initializer with the following basis:

@Singleton
@Startup
public class Initialiser {
    @Inject
    private PartitionManager partitionManager;

    // Create users
    @PostConstruct
    public void createUsers()
    {
        try {
            createUser("admin", new String[]{RoleConstants.BASIC});
        } catch (Exception ex ) {} // Ignore error

        try {
            createUser("paul", new String[]{RoleConstants.BASIC, RoleConstants.IS_ADMIN});
        } catch (Exception ex ) {} // Ignore error
    }

    private void createUser(String loginName, String[] roles)
    {
        User user = new User(loginName);

        IdentityManager identityManager = this.partitionManager.createIdentityManager();
        identityManager.add(user);

        identityManager.updateCredential(user, new Password(loginName + "Test"));

        for (String role: roles) {
            Role xrole = new Role(role);
            identityManager.add(xrole);
            RelationshipManager relationshipManager = this.partitionManager.createRelationshipManager();

            grantRole(relationshipManager, user, xrole);
        }
    }
}

After deploying my war into Wildfly, I indeed see my users being created in the configured postgresql database - in the accounttypeentity table. And I can query for and find these user accounts using the ordinary JPA approach (via the IdentityQuery lookups).

Problem:

I'm using PL to secure my Restful API (implemented using JAX-RS), and they way I've done this? Decorating each endpoint with a custom annotation that is meant to check if the caller of the endpoint has a login session, and whether the affiliated user account has the necessary roles (each endpoint passes the required roles as part of the annotation).

Now, the issue is, even when I have a user logged in - identity.isLoggedIn() == true, and even when this user is one of those i created at initialization with the required roles, a call to check if this user has any of the roles required fails! The essential code is:

hasRole(relationshipManager, user, getRole(identityManager, role)

And the entire custom annotation definition is:

@ApplicationScoped
public class CustomAuthorizer {

    Logger log = Logger.getLogger(CustomAuthorizer.class);

    @Inject
    Identity identity;

    @Inject
    IdentityManager identityManager;

    @Inject
    RelationshipManager relationshipManager;

    /**
     * This method is used to check if classes and methods annotated with {@link BasicRolesAllowed} can perform
     * the operation or not
     *
     * @param identity        The Identity bean, representing the currently authenticated user
     * @param identityManager The IdentityManager provides methods for checking a user's roles
     * @return true if the user can execute the method or class
     * @throws Exception
     */
    @Secures
    @BasicRolesAllowed
    public boolean hasBasicRolesCheck(InvocationContext invocationContext) throws Exception {
        log.error("** Checking roles...");

        BasicRolesAllowed basicRolesAllowed = getAnnotation(invocationContext,BasicRolesAllowed.class);

        String[] requiredRoles = basicRolesAllowed.value();// get these from annotation

        Identity identity = getIdentity();

        if((requiredRoles.length > 0) && (!getIdentity().isLoggedIn()))
        {
            log.error("** User Not Logged In: can't pass role checking...");
            return false;
        }

        boolean isAuthorized = true;

        User user = (User) getIdentity().getAccount();

        for (String role : requiredRoles) {
            isAuthorized = isAuthorized && hasRole(relationshipManager, user, getRole(identityManager, role));
            log.info(String.format("User:%s | Role: %s | Has?: %s", user, role, isAuthorized));
        }

        return isAuthorized;
    }

    private <T extends Annotation> T getAnnotation(InvocationContext invocationContext, Class<T> annotationType) {
        Class unproxiedClass = getUnproxiedClass(invocationContext.getTarget().getClass());
        T annotation = (T) unproxiedClass.getAnnotation(annotationType);
        Method invocationContextMethod = invocationContext.getMethod();

        if (annotation == null) {
            annotation = invocationContextMethod.getAnnotation(annotationType);
        }

        if (annotation == null) {
            throw new IllegalArgumentException("No annotation [" + annotationType + " found in type [" + unproxiedClass + "] or method [" + invocationContextMethod + ".");
        }

        return annotation;
    }

    private Identity getIdentity() {
        return this.identity;
    }

}

So, I shall cheat a bit and pose 3 questions (in one):

  1. How do I actually confirm that picketlink is persisting the roles I assign to the users I create?
  2. Where in the database (or elsewhere) are these assigned roles stored so I can confirm?
  3. If I'm checking user roles the wrong way (see my code), how should I rectify this?
JWL
  • 13,591
  • 7
  • 57
  • 63
  • Qs 1&2 aside, is there any reason that you can't accomplish what you want using the standard Java EE annotations? See [javax.annotation.security](http://docs.oracle.com/javaee/7/api/javax/annotation/security/package-summary.html). – Steve C Apr 14 '15 at 02:25
  • @SteveC, thanks for the alternatives, but the major reason I'm using PL Roles is because I have a requirement to manage the allocation and assignment of roles using the PL Basic Model - basically, it should be possible to map these roles to PL user and group entities, query and perform CRUD operations on these. So, I have to get this working with PL. But I'll also try to explore this alternative in case all else fails. – JWL Apr 14 '15 at 09:01
  • WildFly already uses PicketLink under the hood to implement what you are re-implementing. – Steve C Apr 14 '15 at 12:37
  • You can setup a security domain that uses a database structure that you define by following the steps at [Authentication Modules](https://docs.jboss.org/author/display/WFLY8/Authentication+Modules). As you have defined the tables you are free to build a JPA based editor to maintain them. – Steve C Apr 14 '15 at 13:00
  • @SteveC thanks for the hints. I've actually managed to get it working after I consulted with a friend who'd worked with PL a while. Answer is posted below... – JWL Apr 15 '15 at 19:56

1 Answers1

1

After consulting a friend. I implemented the following security annotation implementation, which works as expected:

package com.dsmagic.satex.auth;

import com.dsmagic.satex.annotations.BasicRolesAllowed;
import org.apache.deltaspike.security.api.authorization.Secures;
import org.jboss.logging.Logger;
import org.picketlink.Identity;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.RelationshipManager;
import org.picketlink.idm.model.basic.BasicModel;
import org.picketlink.idm.model.basic.Role;
import org.picketlink.idm.model.basic.User;

import javax.enterprise.context.ApplicationScoped;
import javax.interceptor.InvocationContext;

@ApplicationScoped
public class Authorizer {

    Logger log = Logger.getLogger(Authorizer.class);

    @Secures
    @BasicRolesAllowed
    public boolean isBasicRolesAllowed(InvocationContext invocationContext, Identity identity, IdentityManager identityManager, RelationshipManager relationshipManager) {

        boolean isUserAllowed = false;

        if (identity != null && identity.isLoggedIn() && identity.getAccount() != null)
        {
            User user = BasicModel.getUser(identityManager, ((User)identity.getAccount()).getLoginName());
            log.info(String.format("-------- Role-Checking for User: %s", user.getLoginName()));

            if (invocationContext.getMethod().isAnnotationPresent(BasicRolesAllowed.class))
            {
                BasicRolesAllowed basicRolesAllowed = invocationContext.getMethod().getAnnotation(BasicRolesAllowed.class);

                for (int i = 0; i < basicRolesAllowed.value().length; i++)
                {
                    Role role = BasicModel.getRole(identityManager, basicRolesAllowed.value()[i]);

                    if (user != null && role != null)
                    {
                        if (BasicModel.hasRole(relationshipManager, user, role))
                        {
                            isUserAllowed = true;
                            break;
                        }else
                            log.info(String.format("-------- Doesn't have ROLE: %s", role.getName()));
                    }

                }
            }
        }


        return isUserAllowed;
    }   

}

Hope this helps someone else too.

JWL
  • 13,591
  • 7
  • 57
  • 63