Saturday, June 4, 2011

Create default User and Role for IdentityManager on startup

The problem that i have is that i don't get a default user and role for my application on first startup.
I've try to create a default user in the database, but i don't know what md5 algorithm is used by seam, so i only able to create a default user if i use plain text password, what in my opinion is an ugly solution. So i have several solutions. First i can find a out what md5 algorithm is used by seam and create a default user. Second solution is to overwrite the md5 password generation, or i create a default user on application startup if no default user is available.

I have chose solution three, because if i change anything in my user role environment, i can add special roles on start up, and i don't have to make manual updates on my database. Other advantages are that i don't have to run any sql if i wan't create a new instance if my application.

Now i will describe what is to do that you application is able to do the same.

First we need an identity manager  in seam you can read how to do this in the seam documentation.
Here is my default User Entity it is nearly the same that is described in the documentation.

@Entity(name="usr_user")
public class User{

   private Long userId;
   private String username;
   private String password;
   private Set<Role> roles;
   private String firstname;
   private String lastname;
   private boolean enabled;

   @Id
   @GeneratedValue
   public Long getUserId() {
      return userId;
   }

   public void setUserId(Long userId) {
      this.userId = userId;
   }

   @UserPrincipal
   public String getUsername() {
      return username;
   }

   public void setUsername(String username) {
      this.username = username;
   }

   @UserPassword(hash = "md5")
   public String getPassword() {
      return password;
   }

   public void setPassword(String password) {
      this.password = password;
   }

   @UserFirstName
   public String getFirstname() {
      return firstname;
   }

   public void setFirstname(String firstname) {
      this.firstname = firstname;
   }

   @UserLastName
   public String getLastname() {
      return lastname;
   }

   public void setLastname(String lastname) {
      this.lastname = lastname;
   }

   @UserEnabled
   public boolean isEnabled() {
      return enabled;
   }

   public void setEnabled(boolean enabled) {
      this.enabled = enabled;
   }
   
   @UserRoles
   @ManyToMany(targetEntity = Role.class)
   @JoinTable(name = "usr_user_roles",
            joinColumns = @JoinColumn(name = "UserId"),
            inverseJoinColumns = @JoinColumn(name = "RoleId"))
   public Set<Role> getRoles() {
      return roles;
   }

   public void setRoles(Set<Role> roles) {
      this.roles = roles;
   }
}





@Entity(name="usr_role")
public class Role {
   
   private Long roleId;
   private String rolename;
   private boolean conditional;
   private Set<Role> groups;

   @Id
   @GeneratedValue
   public Long getRoleId() {
      return roleId;
   }

   public void setRoleId(Long roleId) {
      this.roleId = roleId;
   }

   @RoleName
   public String getRolename() {
      return rolename;
   }

   public void setRolename(String rolename) {
      this.rolename = rolename;
   }

   @RoleConditional
   public boolean isConditional() {
      return conditional;
   }

   public void setConditional(boolean conditional) {
      this.conditional = conditional;
   }

   @RoleGroups
   @ManyToMany(targetEntity = Role.class)
   @JoinTable(name = "usr_role_groups",
      joinColumns = @JoinColumn(name = "roleId"),
      inverseJoinColumns = @JoinColumn(name = "groupId"))
   public Set<Role> getGroups() {
      return groups;
   }

   public void setGroups(Set<Role> groups) {
      this.groups = groups;
   }
}



Now we create the magic class that do all the work i am call it UserManagmentBootstrap
but there is an other problem with seam.... we need and active session context to use the IdentiyManager, so in this case we can't use the application session context. We have to use the "org.jboss.seam.postInitialization" event to initialize our bootstrap been. But now we have an other problem because the session is also not active right after this event. To work around this we can use the asynchronous call to start the real initialization. No we have a solution for all our problems. To get this work we have to create a other bean i've called UserManagmentBootstrapStartup here it is.


@Name("userManagementBootstrapStartup") 
public class UserManagmentBootstrapStartup { 

   @In(create=true) UserManagementBootstrap userManagementBootstrap; 

   /** 
    * Ugly workaround because we have to wait for a session context 
    */ 
   @Observer("org.jboss.seam.postInitialization") 
   public void startup() { 
      // Schedule a send for 50s time, by which time JSF will be initialized 
      userManagementBootstrap.configure(50 * 1001); 
   }
}


it call only the UserManagmentBootstrap been that do the real work. Here it is

start
@Name("userManagementBootstrap")
public class UserManagementBootstrap {

   @Logger
   private Log log;

   @In(create = true)
   IdentityManager identityManager;

   @Asynchronous
   @Transactional
   public void configure(@Duration long duration) {
      log.info("start module configuration");
      createDefaultUser();
      createDefaultRoles();
      createDefaultGroups();
      grantRoles();
   }

   public void createDefaultUser() {
      log.info("Disable security...");
      Identity.setSecurityEnabled(false);
      try {
         log.info("Check for default users...");
         if (!identityManager.userExists("admin")) {
            log.info("Creating default user accounts...");

            String password = "admin";
            log.info("USER \"admin\" WITH PASSWORD \"{0}\" CREATED \" \n"
                  + "LOG IN AND CHANGE PASSWORD IMMEDIATELY", password);

            identityManager.createUser("admin", password);
            log.info("Accounts created!");
         } else {
            log.info("Nothing to do");
         }

      } finally {
         Identity.setSecurityEnabled(true);
         log.info("Security restored...");
      }
   }

   public void createDefaultRoles() {
      log.info("Disable security...");
      Identity.setSecurityEnabled(false);
      try {
         log.info("Check for default roles...");
         if (!identityManager.roleExists("admin")) {
            log.info("Creating default roles...");
            identityManager.createRole("admin");
            identityManager.createRole("superuser");
            log.info("Role created!");
         }
      } finally {
         Identity.setSecurityEnabled(true);
         log.info("Security restored...");
      }
   }

   public void createDefaultGroups() {
      log.info("Disable security...");
      Identity.setSecurityEnabled(false);
      try {
         log.info("Check if default group exist");
         if (identityManager.getGrantedRoles("superuser").contains("admin")) {
            log.info("create group superuser...");
            identityManager.addRoleToGroup("admin", "superuser");
            log.info("group superuser created");
         }
      } finally {
         Identity.setSecurityEnabled(true);
         log.info("Security restored...");
      }
   }

   public void grantRoles() {
      log.info("Disable security...");
      Identity.setSecurityEnabled(false);
      try {
         log.info("Check for granted roles");
         if (identityManager.roleExists("admin")
               && identityManager.userExists("admin")
               && !isRoleGranted("admin", "admin")) {

            log.info("Grant admin role");

            // password is not really needed because we disabled security
            // but we have to login to be able to grand role
            identityManager.authenticate("admin", "admin");

            identityManager.grantRole("admin", "superuser");
            log.info("Superuser role granted to admin ;-)");
         }
      } finally {
         Identity.setSecurityEnabled(true);
         log.info("Security restored...");
      }
   }

   /**
    * Use to avoid user not exist exception
    * 
    * @param user
    * @param role
    * @return true if role is already granted otherwise false
    */
   private boolean isRoleGranted(String user, String role) {
      boolean isRoleGranted = false;

      if (identityManager.userExists(user)) {
         isRoleGranted = identityManager.getGrantedRoles("admin").contains(
               "admin");
      } else {
         log.info("user dose not exist {0}", user);
      }
      return isRoleGranted;
   }
}


That's is now user roles groups will be created in application startup with seam2

See also:
http://relation.to/2640.lace
http://docs.jboss.org/seam/2.2.2.Final/reference/en-US/html/
http://seamframework.org/
http://docs.jboss.org/seam/2.2.2.Final/reference/en-US/html/security.html#d0e9101

2 comments:

  1. Places your company into the spotlight and your products and services while watching individuals who matter. http://www.seitenschlaeferkissentest.com

    ReplyDelete