12 Replies Latest reply on Jun 15, 2011 9:45 PM by Clebert Suconic

    HornetQ Standalone and Security

    Luca Merolla Newbie

      Hello everybody,

       

      I'm trying to use HornetQ in the standalone version and I would like to configure the security authentication of the users.

      Adding the user and password to hornetq-users.xml seems to work fine. However it is not excatly what I need.

       

      In particular I would like HornetQ to take the users credentials form a database table rather than just hornetq-users.xml file.

      Is it possible?

       

      Thanks in advance,

      Luca

        • 2. Re: HornetQ Standalone and Security
          Luca Merolla Newbie

          In my case I'm trying to use Tomcat as AS, and I'm using DataSourceRealm as security manager implementation. I think Tomcat ships a JAASRealm implementation too but I don't think it will work with HornetQ.

          Is there something that could help me out with that or the only way is to write my custom implementation of the HornetQSecurityManager interafce?

          • 3. Re: HornetQ Standalone and Security
            Clebert Suconic Master

            If you want to use JAAS, you can pass org.hornetq.spi.core.security.JAASSecurityManager with configurationName="YOUR-config-name".

             

            Or you could also implement the bridge to DataSourceRealm yourself. (I'm not aware of anyone doing it at this point). It should be a simple task and we would accept contributions to make it part of the code.

             

            Let us know If you have any issues on doing it.

            • 4. Re: HornetQ Standalone and Security
              Luca Merolla Newbie

              OK thanks for the hints.

               

              However, since our HornetQ server is going to run as a standalone I was wondering if there is a way to re-use the DatabaseServerLoginModule that ships with the AS.

               

              Thanks in advance.

              • 5. Re: HornetQ Standalone and Security
                Clebert Suconic Master

                Possibly, but you will have to do the integration yourself.

                 

                We only support the JAAS integration on AS, and the HornetQSecurityManager.

                 

                 

                You can implement your own HOrnetQSecurityManager to use the DatabaseServerLonginModule. It shouldn't be very difficult to do it.

                 

                 

                I would (in your place) just implement a new HOrnetQSecurityManager, and reuse DAtabaseServerLoginModule in an aggregation.

                • 6. Re: HornetQ Standalone and Security
                  Luca Merolla Newbie

                  Probably there isn't already a way of defining a datasource for a database connection in the HornetQ configuration file, but I was wondering if you can have some suggestions for it.

                  Off the top of my head I'll probably need to use something like context.lookup("myDefinedDS") to search in the database tables.

                  • 7. Re: HornetQ Standalone and Security
                    Luca Merolla Newbie

                    I'm trying to do my implementation of HornetQSecurityManager. But for some reasons it doesn't seem to work. So I started to think that my approach is wrong. Basically, here is what I did:

                     

                    - I'm extending HornetQSecurityManagerImpl and injecting the DataSource that is defined in the hornetq-beans.xml file.

                    - I'm overrriding the validateUser with the following purpose:

                    • call the super.validateUser [so it checks if the User is in the users Map.
                    • if not, it look up for the user in the database.
                    • if it find the user in the database then I use addUser and addRole methods from the existing HornetQSecurityManagerImpl to add the users and roles to the Maps

                     

                    The reason why I'm doing this is that I don't want to make a database call every time a User interact with the Queue/Topics. So the call to the database will be every time the user is not in the Map and if is also not in the database it will not be authrized.

                     

                    Here is the code of the hornetq-beans.xml

                    <bean name="HornetQSecurityManager" class="org.hornetq.spi.core.security.HornetQDBSecurityManagerImpl">

                                <property name="dataSource">

                                          <inject bean="dataSource"/>

                                </property>

                          <property name="userTable">VUser</property>

                          <property name="userNameCol">vname</property>

                          <property name="userCredCol">vpassword</property>

                          <property name="userRoleTable">VRole</property>

                          <property name="roleNameCol">vrole</property>

                          <property name="digest">MD5</property>

                      </bean>

                     

                              <bean name="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

                                        <property name="driverClassName">com.mysql.jdbc.Driver</property>

                                        <property name="url">jdbc:mysql://localhost:3306/mydb</property>

                                        <property name="username">root</property>

                                        <property name="password">password1</property>

                              </bean>

                     

                    And here is the validateUser method of my HornetQDBSecurityManagerImpl class

                    public boolean validateUser(final String user, final String password) {

                                        if (super.validateUser(user, password)) {

                                                  // it's defined in the hornetq-users.xml

                                                  return true;

                                        } else {

                                                  // lookup in the database

                                                  Connection conn = null;

                                                  ResultSet rs = null;

                     

                     

                                                  try {

                                                            conn = dataSource.getConnection();

                                                            rs = getPreParedStatement(conn, preparedCredentials, user)

                                                                                .executeQuery();

                                                            if (rs.next() == false) {

                                                                      log.trace("No matching username found in Principals");

                                                                      return false;

                                                            }

                                                            String dbPassword = rs.getString(1);

                     

                                                            // check passwords are matching

                                                            if (password == null

                                                                                || compareHashedPasswords(password, dbPassword)) {

                                                                      return false;

                                                            }

                     

                     

                                                            // add the user

                                                            addUser(user, password);

                     

                     

                                                            rs = getPreParedStatement(conn, preparedRoles, user)

                                                                                .executeQuery();

                                                            while (rs.next()) {

                                                                      // add the role

                                                                      addRole(user, rs.getString(1));

                                                            }

                     

                                                            return super.validateUser(user, password);

                                                  } catch (SQLException e) {

                                                            log.error("An error occurs looking up in the database", e);

                                                  } finally {

                                                            close(conn);

                                                  }

                                        }

                                        return false;

                              }

                     

                     

                    When I try to use this I get the following error:

                    [Old I/O server worker (parentId: 1450554350, [id: 0x5675b3ee, localhost/127.0.0.1:5445])] 20:57:56,964 SEVERE [org.hornetq.core.protocol.core.impl.HornetQPacketHandler]  Failed to create session

                    HornetQException[errorCode=105 message=Unable to validate user: prova2]

                              at org.hornetq.core.security.impl.SecurityStoreImpl.authenticate(SecurityStoreImpl.java:141)

                              at org.hornetq.core.server.impl.HornetQServerImpl.createSession(HornetQServerImpl.java:916)

                              at org.hornetq.core.protocol.core.impl.HornetQPacketHandler.handleCreateSession(HornetQPacketHandler.java:168)

                              at org.hornetq.core.protocol.core.impl.HornetQPacketHandler.handlePacket(HornetQPacketHandler.java:84)

                              at org.hornetq.core.protocol.core.impl.ChannelImpl.handlePacket(ChannelImpl.java:474)

                              at org.hornetq.core.protocol.core.impl.RemotingConnectionImpl.doBufferReceived(RemotingConnectionImpl.java:496)

                              at org.hornetq.core.protocol.core.impl.RemotingConnectionImpl.bufferReceived(RemotingConnectionImpl.java:457)

                              at org.hornetq.core.remoting.server.impl.RemotingServiceImpl$DelegatingBufferHandler.bufferReceived(RemotingServiceImpl.java:459)

                              at org.hornetq.core.remoting.impl.netty.HornetQChannelHandler.messageReceived(HornetQChannelHandler.java:73)

                              at org.jboss.netty.channel.SimpleChannelHandler.handleUpstream(SimpleChannelHandler.java:100)

                              at org.jboss.netty.channel.StaticChannelPipeline.sendUpstream(StaticChannelPipeline.java:362)

                              at org.jboss.netty.channel.StaticChannelPipeline$StaticChannelHandlerContext.sendUpstream(StaticChannelPipeline.java:514)

                              at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:287)

                              at org.hornetq.core.remoting.impl.netty.HornetQFrameDecoder2.decode(HornetQFrameDecoder2.java:169)

                              at org.hornetq.core.remoting.impl.netty.HornetQFrameDecoder2.messageReceived(HornetQFrameDecoder2.java:134)

                              at org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:80)

                              at org.jboss.netty.channel.StaticChannelPipeline.sendUpstream(StaticChannelPipeline.java:362)

                              at org.jboss.netty.channel.StaticChannelPipeline.sendUpstream(StaticChannelPipeline.java:357)

                              at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:274)

                              at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:261)

                              at org.jboss.netty.channel.socket.oio.OioWorker.run(OioWorker.java:90)

                              at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108)

                              at org.jboss.netty.util.internal.IoWorkerRunnable.run(IoWorkerRunnable.java:46)

                              at org.jboss.netty.util.VirtualExecutorService$ChildExecutorRunnable.run(VirtualExecutorService.java:181)

                              at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)

                              at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)

                              at java.lang.Thread.run(Thread.java:662)

                     

                    And the problem seems to be in SecurityStoreImpl where the securityManager instance doesn't seems to use my HornetQDBSecurityManagerImpl

                    if (!securityManager.validateUser(user, password))

                     

                    I'm running out of ideas here. Do you have any suggestions for this problem?

                    Otherwise I'm thinking to rewrite it completely without extending HornetQSecurityManagerImpl and not using the Maps of Users and Roles but call the database, but I'm not sure about the lifecycle if this call will be done only at the first time or every time the client communicate with the hornetq server.

                     

                    Thanks in advance,

                    Luca

                    • 8. Re: HornetQ Standalone and Security
                      Luca Merolla Newbie

                      Sorry for the long post, I have solved it was a bug in my code.

                      Anyway, if somebody is interested in this solution I can post the full code. As DataSource I'm using Apache Commons DBCP so in that case, for database connection,a couple of more libraries are needed.

                      • 9. Re: HornetQ Standalone and Security
                        Clebert Suconic Master

                        You have to also change HornetqServer to hornetq-beans.xml to inject your Security Manager:

                         

                         

                         

                                <!-- The core server -->

                           <bean name="HornetQServer" class="org.hornetq.core.server.impl.HornetQServerImpl">

                              <constructor>

                                 <parameter>

                                    <inject bean="Configuration"/>

                                 </parameter>

                                 <parameter>

                                    <inject bean="MBeanServer"/>

                                 </parameter>

                                 <parameter>

                                    <inject bean="HornetQSecurityManager"/>

                                 </parameter>

                              </constructor>

                              <start ignored="true"/>

                              <stop ignored="true"/>

                           </bean

                         

                         

                         

                         

                        Or if you are on AS, maybe you could just use JAAS.

                        • 10. Re: HornetQ Standalone and Security
                          Clebert Suconic Master

                          <inject bean="YourSecurityManager"/>

                           

                           

                          got it?

                          • 11. Re: HornetQ Standalone and Security
                            Luca Merolla Newbie

                            Hello,

                             

                            if somebody is interested I have posted my solution here, so you can get the source code and the configuration instructions there:

                            http://lucamerolla.wordpress.com/2011/06/16/stand-alone-hornetq-security-database-module/

                             

                            Cheers,

                            Luca

                            • 12. Re: HornetQ Standalone and Security
                              Clebert Suconic Master

                              Can you provide some testcase, and a patch? This is the kind of thing that interests as a part of the product?

                               

                              If you can't create the test now.. maybe you could create a JIRA with the patch and someone here will take a look.