javaauthorizationpicketlink

Custom IDM authentication and Authorizer


I have custom implementation of the Picketlink IDM JPA authentication model. I got it by coping an example from there https://github.com/jboss-developer/jboss-picketlink-quickstarts/tree/master/picketlink-idm-custom-identity-model to package org.picketlink.idm.jpa.model.custom.simple. Then I have implemented this example https://github.com/pedroigor/picketlink-quickstarts/tree/master/picketlink-deltaspike-authorization . I changed User class and UserTypeEntity in picketlink-idm-custom-identity-model example like this:

@IdentityStereotype(USER)
public class User extends AbstractIdentityType implements Account {
  public static final QueryParameter USER_NAME = QUERY_ATTRIBUTE.byName("userName");
  @StereotypeProperty(IDENTITY_USER_NAME)
  @AttributeProperty
  @Unique
  private String userName;
  @AttributeProperty
  private String firstName;
  @AttributeProperty
  private String lastName;
  @AttributeProperty
  private String email;
  @AttributeProperty
  private String middleName;
  @AttributeProperty
  private String telephone;
  @AttributeProperty
  private String address;
  @AttributeProperty
  private int postIndex;
  @AttributeProperty
  private Date registerDate;
  @AttributeProperty
  private Date lastVisitDate;
  @AttributeProperty
  private boolean isOrganizer;
  @AttributeProperty
  private boolean isAdmin;
  @Embedded
  private Organizer organizer;
  @Embedded
  private Customer customer;
  // getters and setters
}

class UserTypeEntity

  @IdentityManaged(User.class)
  @Entity
  public class UserTypeEntity extends AbstractIdentityTypeEntity {
  @AttributeValue
  private String userName;
  @OwnerReference
  @ManyToOne(fetch = FetchType.LAZY)
  private RealmTypeEntity realm;
  @AttributeProperty
  private String firstName;
  @AttributeProperty
  private String lastName;
  @AttributeProperty
  private String email;
  @AttributeProperty
  @Column(length = 255)
  private String middleName;
  @AttributeProperty
  @Size(max = 12)
  @Column(length = 12)
  private String telephone;
  @AttributeProperty
  @Column(length = 5000)
  @Size(max = 5000)
  private String address;
  @AttributeProperty
  private int postIndex;
  @AttributeProperty
  private Date registerDate;
  @AttributeProperty
  private Date lastVisitDate;
  @AttributeProperty
  private boolean isOrganizer;
  @AttributeProperty
  private boolean isAdmin;
  @Embedded
  private Organizer organizer;
  @Embedded
  private Customer customer;
  // getters and setters
}

Then I implemented Login controller:

@Named
@Stateless
public class LoginController {
  @Inject
  private Identity identity;
  @Inject
  private FacesContext facesContext;
  public void login() {
    AuthenticationResult result = identity.login();
    if (AuthenticationResult.FAILED.equals(result)) {
      facesContext.addMessage(
          null,
          new FacesMessage("Invalid user name or password"));
    }
  }
}

And Registration Controller:

@Named
@RequestScoped
public class RegistrationController {
  private IdentityManager identityManager;
  @Inject
  private PartitionManager partitionManager;
  @Inject
  private FacesContext facesContext;
  @Inject
  private User user;
  private String password;
  private String passwordVerify;
  private boolean isOrganizer;
  public RegistrationController() {
  }
  public User getUser() {
    return user;
  }
  public void setUser(User user) {
    this.user = user;
  }
  public String getPasswordVerify() {
    return passwordVerify;
  }
  public void setPasswordVerify(String passwordVerify) {
    this.passwordVerify = passwordVerify;
  }
  public boolean getIsOrganizer() {
    return isOrganizer;
  }
  public void setIsOrganizer(boolean isOrganizer) {
    this.isOrganizer = isOrganizer;
  }
  public String getPassword() {
    return password;
  }
  public void setPassword(String password) {
    this.password = password;
  }
  @Transactional
  public String register() throws Exception {
    if (password.isEmpty()) {
      String message = LocaleBean.loadErrorMessage(facesContext, LocaleBean.EX_RESOURCE_BUNDLE_NAME, "password.empty");
      facesContext.addMessage("signup:registrationPassword", new FacesMessage(message));
      return "returnToSignup";
    }
    if (!password.equals(passwordVerify)) {
      String message = LocaleBean.loadErrorMessage(facesContext, LocaleBean.EX_RESOURCE_BUNDLE_NAME, "password.NotEqual");
      facesContext.addMessage("signup:registrationPassword", new FacesMessage(message));
      return "returnToSignup";
    }
      identityManager = partitionManager.createIdentityManager(partitionManager.getPartition(Realm.class,
          Resources.REALM_ACME_NAME));
      if (isOrganizer) {
        user.setOrganizer(true);
        user.setOrganizer(new Organizer());
        try {
          identityManager.add(user);
        } catch (IdentityManagementException e) {
          String message = LocaleBean.loadErrorMessage(facesContext, LocaleBean.EX_RESOURCE_BUNDLE_NAME, "login.Registered");
          facesContext.addMessage(null, new FacesMessage(message));
          return "returnToSignup";
        }
        Password password = new Password(this.password);
        identityManager.updateCredential(user, password);
        RelationshipManager relationshipManager = partitionManager.createRelationshipManager();
        IdentityQuery<Group> query = identityManager.createIdentityQuery(Group.class);
        // query all childs of sales unit
        query.setParameter(Group.NAME, Resources.ORGANIZERS_GROUP_NAME);
        List<Group> groups = query.getResultList();
        Group organizersGroup = groups.get(0);
        relationshipManager.add(new GroupMembership(user, organizersGroup));
      } else {
      }
    return "signin";
  }
}

After that I implemented Authorizer:

@ApplicationScoped
public class SPAuthorizer {
  @Secures
  @Admins
  public boolean doAdminsCheck(Identity identity, IdentityManager identityManager, RelationshipManager relationshipManager) throws Exception {
    return hasGroup(identity, identityManager, relationshipManager, Resources.ADMINS_GROUP_NAME);
  }
  @Secures
  @Organizers
  public boolean doOrganizersCheck(Identity identity, IdentityManager identityManager, RelationshipManager relationshipManager) throws Exception {
    return hasGroup(identity, identityManager, relationshipManager, Resources.ORGANIZERS_GROUP_NAME);
  }
  @Secures
  @Customers
  public boolean doCustomersCheck(Identity identity, IdentityManager identityManager, RelationshipManager relationshipManager) throws Exception {
    return hasGroup(identity, identityManager, relationshipManager, Resources.CUSTOMERS_GROUP_NAME);
  }
  private boolean hasGroup(Identity identity, IdentityManager identityManager, RelationshipManager relationshipManager,
                           String groupName) {
    IdentityQuery<Group> queryGroup = identityManager.createIdentityQuery(Group.class);
    // query all childs of sales unit
    queryGroup.setParameter(Group.NAME, groupName);
    List<Group> groups = queryGroup.getResultList();
    if (groups.size() == 1) {
      Group group = groups.get(0);
      Account user = identity.getAccount();
      if (user == null) {
        return false;
      }
      RelationshipQuery<GroupMembership> query = relationshipManager.createRelationshipQuery(GroupMembership.class);
      query.setParameter(GroupMembership.GROUP, group);
      query.setParameter(GroupMembership.MEMBER, user);
      // user is assigned with two groups
      List<GroupMembership> resultList = query.getResultList();
      return resultList.size() > 0;
    }
    return false;
  }
}

So I implemented authorization checker to check belonging user to a some group in JSF:

@Named
@Stateless
public class AuthorizationChecker {
  @Inject
  private Identity identity;
  @Inject
  private PartitionManager partitionManager;
  public boolean hasGroup(String groupName) {
    IdentityManager identityManager = partitionManager.createIdentityManager(partitionManager.getPartition(Realm.class,
        Resources.REALM_ACME_NAME));
    IdentityQuery<Group> queryGroup = identityManager.createIdentityQuery(Group.class);
    // query all childs of sales unit
    queryGroup.setParameter(Group.NAME, groupName);
    List<Group> groups = queryGroup.getResultList();
    if (groups.size() == 1) {
      Group group = groups.get(0);
      Account user = identity.getAccount();
      RelationshipManager relationshipManager = partitionManager.createRelationshipManager();
      RelationshipQuery<GroupMembership> query = relationshipManager.createRelationshipQuery(GroupMembership.class);
      query.setParameter(GroupMembership.GROUP, group);
      query.setParameter(GroupMembership.MEMBER, user);
      // user is assigned with two groups
      List<GroupMembership> resultList = query.getResultList();
      return resultList.size() > 0;
    }
    return false;
  }
}

But an instance of Identity in hasGroup of the SPAuthorizer doesn't corresponds to an instance that I have in AuthorizationChecker. I checked it in debugger. And when I do identity.getAccount(); it returns null although user was authenticated. Any idea what to do?


Solution

  • The problem was in missed annotation @Stateless in Controller.

    There is workable example https://github.com/lynx-r/picketlink-deltaspike-authorization-and-idm-custom-identity-model.