11 Replies Latest reply on Nov 19, 2009 6:17 PM by Joshua D

    How to persist user?

    Praty G Newbie
      Dear Team,

      Using seam examples I tried to persist user using UserHome. However it throws entity detached exception. Please guide as how to store the user with hashed password and generated salt.

      Even guide how to deal with creating and persisting Roles and RoleGroups.

      Thank you in advance.

      @Name("userHome")
      public class UserHome extends EntityHome<User> {
      .
      .

           @PrePersist
           @Transactional
           public void handlePrePersist() {
                createUser();
           }

           @Transactional
           private void createUser() {
                instance.setPassword(generateHashedPassword());
                identityManager.createUser(instance.getUserName(),instance.getPassword());
                identityManager.grantRole(instance.getUserName(), "user");
           }

           @Transactional
           private String generateHashedPassword() {
                byte[] salt;
                String passwordSalt = instance.getPasswordSalt();
                if (passwordSalt == null || "".equals(passwordSalt.trim())) {
                     salt = PasswordHash.instance().generateRandomSalt();
                     passwordSalt = BinTools.bin2hex(salt);
                     instance.setPasswordSalt(passwordSalt);
                } else {
                     salt = BinTools.hex2bin(passwordSalt);
                }
                return identityStore.generatePasswordHash(instance.getPassword(), salt);
           }
      }

      @Entity
      @Table(name = "USER", schema = "security", catalog = "philately")
      public class User implements Serializable {
           private static final long serialVersionUID = -520371581731672359L;

           private Long id;
           private String userName;
           private String password;
           private String passwordSalt;
           private boolean enabled;
           
           private Set<Role> roles;

           public User(){}
           
           public User(Long id, String userName, String password, boolean enabled) {
                this.id = id;
                this.userName = userName;
                this.password = password;
                this.enabled = enabled;
           }

           @Id
           @GeneratedValue(strategy = GenerationType.IDENTITY)
           @Column(name = "ID", unique = true, nullable = false)
           @NotNull
           public Long getId() {
                return id;
           }

           public void setId(Long id) {
                this.id = id;
           }

           @Column(name = "USER_NAME", unique = true, nullable = false, length = 15)
           @NotNull
           @UserPrincipal
           @Length(max = 15)
           public String getUserName() {
                return this.userName;
           }

           public void setUserName(String userName) {
                this.userName = userName;
           }

           @Column(name = "PASSWORD", nullable = false, length = 50)
           @NotNull
           @UserPassword(hash = "MD5")
           @Length(max = 50)
           public String getPassword() {
                return this.password;
           }

           public void setPassword(String password) {
                this.password = password;
                System.out.println("password" + password);
           }

           @Column(name = "PASSWORD_SALT", nullable = true, length = 50)
           @PasswordSalt
           public String getPasswordSalt() {
                return passwordSalt;
           }

           public void setPasswordSalt(String passwordSalt) {
                this.passwordSalt = passwordSalt;
           }
           
           @Column(name = "ENABLED", nullable = true)
           @UserEnabled
           public boolean isEnabled() {
                return enabled;
           }

           public void setEnabled(boolean enabled) {
                this.enabled = enabled;
           }

           @UserRoles
           @ManyToMany(targetEntity = Role.class)
           @JoinTable(name = "security.USER_ROLE",
                     joinColumns = @JoinColumn(name = "USER_ID"),
                     inverseJoinColumns = @JoinColumn(name = "ROLE_ID"))
           public Set<Role> getRoles() {
                return roles;
           }

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

      }
        • 1. Re: How to persist user?
          Martin Vician Newbie

          Hi


          In order to solve detached entity problem, you need to attach stack trace.


          I had a brief look at your code snippet, and I think that it wont work even in theory.


          For start @PrePersist annotation applies only to Entity and Entity Listeners, your UserHome is neither of them, unless you configured it in orm.xml.


          Not to mention that identityManager.createUser method takes plain password as a argument, so whole generateHashedPassword method is pointless really.



          Bye



          PS: I am a Seam newbie, so do not take my advice too seriously. :)

          • 2. Re: How to persist user?
            Praty G Newbie

            Thank you for clarification! I modified the code and overridden the persist method as:


                 @Override
                 public String persist(){
                      try {
                           createUser();
                      } catch (Exception e) {
                           e.printStackTrace();
                           return failure;
                      }
                      return success;
                 }


            The problem is createUser() from IdentityManager checks permission for isLoggedIn(). And therefore create user option fails.
            Errors: Login Failed !


            How should I create the default user account with passwordSalt? Please suggest a way for initial loading database tables.


            Regards,

            • 3. Re: How to persist user?
              Martin Vician Newbie

              Well, in order to use IdentityManager.createUser you have to be logged in and you have to posses Permission with target seam.user and action create. ...CATCH XXII :)...


              So if you need to populate your database with first user, you can use ordinary SQL INSERT statement in import-dev.sql (which is located in resources directory), provided you have generated your application with seam-gen and you are using dev profile. If not you can run INSERT statement against your database manually.


              However in order to do this you have to find out how to calculate passwordHash.
              I suggest you create small TestNG test to help you, something like this...


              public class PasswordHashTest extends SeamTest {
                  @Test
                  public void testPasswordHash() throws Exception {
                      new ComponentTest() {
                          @Override
                          protected void testComponents() throws Exception {
                              PasswordHash passwordHash = PasswordHash.instance();
              
                              final byte[] salt = passwordHash.generateRandomSalt();
                              final int iterations = 1000;
                              final String password = "password";
                              final String hash = passwordHash.createPasswordKey(password.toCharArray(), salt, iterations);
              
                              assert hash != null : "Hash not calculated!";
              
                              System.out.println("Password: " + password);
                              System.out.println("Salt: " + BinTools.bin2hex(salt));
                              System.out.println("Iterations: " + iterations);
                              System.out.println("Hash: " + hash);
                              
                          }
                      }.run();
                  }
              }
              



              This should print out password, salt, iterations and calculated hash, this works with default hashing algorithm and seam version 2.2.GA, it might not work with different version and you might need to modify it to work with different hashing algorithm...


              But If you use seam 2.2.GA and you change


               @Column(name = "PASSWORD", nullable = false, length = 50)
               @NotNull
               @UserPassword(hash = "MD5")
               @Length(max = 50)
               public String getPassword() {
                return this.password;
               }
              



              to


               @Column(name = "PASSWORD", nullable = false, length = 50)
               @NotNull
               @UserPassword()
               @Length(max = 50)
               public String getPassword() {
                return this.password;
               }
              



              it might just work...because now you have all fields you needed for your SQL statement.



              PS: Still I am seam newbie so there might be much easier way how to accomplish this.


              PS2: There is no guarantee what so ever, that any code I wrote is useful for any particular purpose. :)

              • 4. Re: How to persist user?
                Praty G Newbie

                It doesnt matter whether, about experienced-bee or newbie. But what matter is the way to think and break to simplicity.


                Martin Vician, your reply is useful for me. Thank you. I'll workout.

                • 5. Re: How to persist user?
                  Praty G Newbie
                  It worked! I'm able to login!

                  However, unable to create the user. The permission check in identityManager.createUser(...) failed.

                  12:24:41,171 ERROR [STDERR] org.jboss.seam.security.AuthorizationException: Authorization check failed for permission[seam.user,create]


                  I have created the default entries in tables: USER 'admin', ROLE 'admin', PERMISSION ['seam.user', 'create','admin'] and mapping in USER_ROLE.

                  Do I need to modify security.drl file? Or anything else m'I missing?
                  • 6. Re: How to persist user?
                    Martin Vician Newbie

                    You can use security.drl if you like, for example this is how you grant all permissions to admin user. (not role, but particular user)


                    rule AdminUserHasAllPermissions
                       no-loop
                       activation-group "permissions"   
                    when
                       $perm: PermissionCheck(granted == false)
                       Principal(name == "admin")
                    then
                       $perm.grant();
                    end
                    



                    But if you have set up JpaPermissionStore correctly in components.xml, you should not need to modify security.drl at all.


                    Permissions can be granted directly to User or to Role.
                    You can have 2 Permission tables, one for User and one for Role, or one Permission table for both with discriminator column.


                    In your case you need to post source code for your Permission table and your components.xml, and probably db scripts, otherwise it is hard to tell.


                    • 7. Re: How to persist user?
                      Praty G Newbie
                      Great experience, of speedy and precise replies/conversation. Thank you Martin!

                      To be frank I'm newbie excluding core java. I'm working with seam as trial-error method. Seam is the first technology I'm working after core java.

                      Ok, to the track. I have uploaded the source code of component.xml, Permission.java and create scripts of tables along with contents at:

                      http://www.sendspace.com/file/s8d0ua

                      Queries:

                      1. Purpose of discriminator column in PERMISSION table? Please elaborate your reply
                      2. Permissions granularity records/object/entity based instead of page/view based. Does Seam/Drools support it? I mean instead of restricting a view/operation can we impose restriction record based. Example: Login user will be able to view only the records he has created or last modified, etc.
                      3. Difference between permission with and without drools?

                      [If feasible please reply with the email-ID and preferred time.]

                      Contacts: seamuser@gmail.com  
                      • 8. Re: How to persist user?
                        Praty G Newbie
                        <blockquote>
                        _Praty G wrote on Sep 21, 2009 09:00:_<br/>

                        However, unable to create the user. The permission check in identityManager.createUser(...) failed.

                        12:24:41,171 ERROR [STDERR] org.jboss.seam.security.AuthorizationException: Authorization check failed for permission[seam.user,create]
                        </blockquote>

                        Actually I missed to add <security:jpa-permission-store user-permission-class="org.domain.philately.entity.Permission"/>
                        . Now the related exception is resolved.

                        However, it is throwing validation exception. Unable to predict which one doing it. If user.java and UserEdit.xhtml is needed please feel free.

                        Please clarify for the prior/preceding queries. 
                        • 9. Re: How to persist user?
                          Martin Vician Newbie

                          It must be pretty difficult to start with seam if you do not have any EJB/Hibernate skills...I suggest you read Seam in Action or Seam reference first...trial and error will not work in this case...


                          RE: source code



                          I see several problems:


                          1. you have to define permission class in your components.xml:


                          add something like this:


                          <security:jpa-permission-store user-permission-class="com.acme.model.AccountPermission"/>



                          details here:


                          15.6.10.3. JpaPermissionStore


                          2. I don't think that your admin role should be conditional true, change it to false


                          RE: questions



                          1. as I said you are storing permissions for two different entities, user and role in one table, in your case all permissions apply to admin role, if you changed discriminator to user, your permissions would apply directly to admin user, if you stored permissions in two different tables let's say UserPermissions and RolePermissions, then there would be no discriminator column...


                          2. yes it can be done, permission target will simply contain entity name and id separated by colon, for example provided you have an entity called Project, your permission target will contain Project:5.


                          details here:


                          15.6.10.3.5. Identifier Policy


                          3. Simply said:


                          When you set up your JpaPermissionResolver correctly you will have 2 resolvers in chain, if drools is first and jpa second, then if you grand permission in drools resolver, jpa resolver will be skipped...


                          If drools resolver does not grand permission, then jpa resolver will be queried and might grand permission...


                          If neither of them grands permission, then you will receive AuthorizationException.


                          So these resolvers are complementary...you might even write your own resolver if needed.


                          Have a look here:


                          15.6.8. The Permission Authorization Model

                          • 10. Re: How to persist user?
                            prati bha Newbie

                            Hi I am workng on the same issue of Password Hashing.Can you please share some code with me.


                            Thanks!!

                            • 11. Re: How to persist user?
                              Joshua D Novice

                              Refer to the seamspace example for a full blown example of user management using seam security