Spring JmsTemplate + Security - java

I've just refactored some code that published to a JMS topic to use Spring's JmsTemplate class and now I'm receiving an exception stating I'm not authenticated.
Previously I created the factory, made a connection, then session etc as follows:
MQTopicConnectionFactory factory = new MQTopicConnectionFactory();
factory.setQueueManager(qMgr);
factory.setTransportType(JMSC.MQJMS_TP_CLIENT_MQ_TCPIP);
factory.setHostName(hostname);
factory.setPort(listenerPort);
factory.setChannel(channel);
// setting username and password to be empty string ==> no authentication
connection = factory.createConnection("", "");
...
connection.start();
I don't see anywhere in the JmsTemplate to set the username and password to empty strings. My config looks like this:
<bean id="jmsFactory" class="com.ibm.mq.jms.MQTopicConnectionFactory">
<property name="queueManager">
<value>ACT01</value>
</property>
<property name="hostName">
<value>xx.xx.xx.xx</value>
</property>
<property name="port">
<value>15004</value>
</property>
<property name="transportType">
<value>1</value>
</property>
<property name="channel">
<value>CONDUCTOR.ACT01</value>
</property>
</bean>
<bean id="impactJmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory">
<bean class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory">
<ref local="jmsFactory" />
</property>
</bean>
</property>
</bean>
I have also tried wrapping the jmsFactory in a UserCredentialsConnectionFactoryAdapter object to no avail:
<bean id="jmsConnectionFactory" class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">
<property name="targetConnectionFactory" ref="jmsFactory"/>
<property name="username" value=""/>
<property name="password" value=""/>
</bean>
Stack trace:
Caused by: com.ibm.msg.client.jms.DetailedJMSSecurityException: JMSWMQ2013: The security authentication was not valid that was supplied for QueueManager 'LOROL' with connection mode 'Client' and host name 'xx.xx.xx.xx'. Please check if the supplied username and password are correct on the QueueManager you are connecting to
at com.ibm.msg.client.wmq.common.internal.Reason.reasonToException(Reason.java:531)
at com.ibm.msg.client.wmq.common.internal.Reason.createException(Reason.java:219)
at com.ibm.msg.client.wmq.internal.WMQConnection.<init>(WMQConnection.java:410)
at com.ibm.msg.client.wmq.factories.WMQConnectionFactory.createV7ProviderConnection(WMQConnectionFactory.java:7855)
at com.ibm.msg.client.wmq.factories.WMQConnectionFactory.createProviderConnection(WMQConnectionFactory.java:7331)
at com.ibm.msg.client.jms.admin.JmsConnectionFactoryImpl.createConnection(JmsConnectionFactoryImpl.java:276)
at com.ibm.mq.jms.MQConnectionFactory.createCommonConnection(MQConnectionFactory.java:6055)
at com.ibm.mq.jms.MQTopicConnectionFactory.createTopicConnection(MQTopicConnectionFactory.java:114)
at com.ibm.mq.jms.MQTopicConnectionFactory.createConnection(MQTopicConnectionFactory.java:197)
at org.springframework.jms.connection.SingleConnectionFactory.doCreateConnection(SingleConnectionFactory.java:343)
at org.springframework.jms.connection.SingleConnectionFactory.initConnection(SingleConnectionFactory.java:290)
at org.springframework.jms.connection.SingleConnectionFactory.createConnection(SingleConnectionFactory.java:227)
at org.springframework.jms.support.JmsAccessor.createConnection(JmsAccessor.java:184)
at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:461)
... 25 more
Caused by: com.ibm.mq.MQException: JMSCMQ0001: WebSphere MQ call failed with compcode '2' ('MQCC_FAILED') reason '2035' ('MQRC_NOT_AUTHORIZED').
at com.ibm.msg.client.wmq.common.internal.Reason.createException(Reason.java:206)
... 37 more

A few options here...
The properties you were setting before resulted in the channel running as an administrator. To get the same functionality you can set the channel's MCAUSER to whatever ID the QMgr is running as (commonly mqm for UNIX and MUSR_MQADMIN on Windows). Boom. Done. Bob's yer uncle.
Yes, this does mean that anyone connecting to that channel is an administrator. On the other hand, this is no worse than it was before as demonstrated by your previous code working the way it did.
You can still use Spring and pass in the ID and password as described in this forum post. Just keep in mind the password is not actually checked. Whatever ID you pass in is accepted at face value unless you use a channel exit to validate it.
For more on WMQ security over client connections, see the Hardening WebSphere MQ presentation. If you wanted to actually secure access to the QMgr you'd want to set MCAUSER to a low-privileged user ID, perform setmqaut commands to authorize that ID's group and then lock down all the other channels like SYSTEM.AUTO.* and SYSTEM.DEF.* so they could not run.

I am running Websphere in my local windows machine and connecting to MQ server in Unix machine . For me only the third option worked. Setting the userID from console didn't work.I tried both mqm and MUSR_MQADMIN.
//connection created using username and password
QueueConnection connection = factory.createQueueConnection("mqm","mqm");

Related

ActiveMQ 5.15.9 Security

I'm currently using ActiveMQ 5.15.9 deployed on our Test Server.
I have to implement some sort of security so that the Queues inside won't be access by anyone.
So far what I've done is add the following to the activemq.xml:
<plugins>
<simpleAuthenticationPlugin anonymousAccessAllowed ="false">
<users>
<authenticationUser
username="admin"
password="pass"
groups="admins,publishers,consumers" />
</users>
</simpleAuthenticationPlugin>
<authorizationPlugin>
<map>
<authorizationMap>
<authorizationEntries>
<authorizationEntry topic =">" write="producers" read="consumers" admin="admins" />
<authorizationEntry queue ="TEST.Q" write="producers" read="consumers" admin="admins" />
</authorizationEntries>
</authorizationMap>
</map>
</authorizationPlugin>
</plugins>
On my C# this is how a access the Queue:
private static void SendNewMessageQueue(string text)
{
string queueName = "TEST";
Console.WriteLine($"Adding message to queue topic: {queueName}");
string brokerUri = $"activemq:tcp://localhost:61616"; // Default port
NMSConnectionFactory factory = new NMSConnectionFactory(brokerUri);
using (IConnection connection = factory.CreateConnection("admin","pass"))
{
connection.Start();
using (ISession session = connection.CreateSession(AcknowledgementMode.AutoAcknowledge))
using (IDestination dest = session.GetQueue(queueName))
using (IMessageProducer producer = session.CreateProducer(dest))
{
producer.DeliveryMode = MsgDeliveryMode.NonPersistent;
producer.Send(session.CreateTextMessage(text));
Console.WriteLine($"Sent {text} messages");
}
}
}
But when I try and Run my Code I get the Following Error:
User admin is not authorized to write to: queue://TEST
I need this so I can expose this MQ to the Internet and have this secured by only allowing consumers/publishers who has the credentials.
You have configured security for a Queue called TEST.Q but you are trying to use a queue called TEST which is not the same so you are getting this error. If you want to expose all queues under the test prefix then it'd look more like:
<authorizationEntry queue ="TEST.>" write="producers" read="consumers" admin="admins" />
There are some docs for security configuration here, and also understanding the wildcard syntax will help.
Exposing a broker over the internet as you've mentioned is no small task so proceed with caution.

Custom attributes for flat file authentication in CAS

I am an absolute novice in this entire stack so I apologize in advance if this is a very dumb question.
I'm working on setting up a local (mock) CAS service so we're able to test our apps against an auth system which at least remotely resembles something we have on our staging/production environments.
I'm using https://github.com/ubc/vagrant-cas as a starting point. I've managed to set up this by modifying cas.properties and deployerConfigContext.xml to enable me to actually pass custom attributes when a user signs in. i.e.
<bean id="attributeRepository" class="org.jasig.services.persondir.support.StubPersonAttributeDao">
<property name="backingMap">
<map>
<entry key="uid" value="uid" />
<entry key="eduPersonAffiliation" value="eduPersonAffiliation" />
<entry key="groupMembership" value="groupMembership" />
<entry key="puid" value="12345678910" />
</map>
</property>
</bean>
This combined with the default org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" means that whenever I sign in with a username and password that is identical (i.e. username 'admin' password 'admin' ) then that user is signed in and the attribute puid is returned with the value of '12345678910' (this same PUID is returned for every username/password combo).
(I had to enable the attributes to be sent back in the 'Services Management' app)
What I actually need is to be able to have multiple users, all with different puid values. i.e.
username:password:1234
username2:password2:5678
etc.
I've noticed there is a org.jasig.cas.adaptors.generic.FileAuthenticationHandler but that only allows for username::password and no custom attributes. (so near yet so far).
I'm way out of my depth, I'm not a java programmer and have hit the limit of my google-fu. Any help pointing me in the right direction would be greatly appreciated.
File-based authn does not support custom attributes. You may be interested in this: https://github.com/Unicon/cas-addons/wiki/Configuring-JSON-ComplexStubPersonAttributeDao

Spring Java Mail - How to know send mail be read, bounce or forward by receiver?

I'm new on spring and java mail, may i know how the email send by java mail is read, bounce or forward. I used google search only found the way how to send a mail. Anyone know where can get the reference on it or provide me some example on it?
Thank you.
Below is my code send mail with spring java mail:
Spring-Mail.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="smtp.gmail.com" />
<property name="port" value="587" />
<property name="username" value="xxxxxxxx#gmail.com" />
<property name="password" value="xxxxxx" />
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.auth">true</prop>
<prop key="mail.smtp.starttls.enable">true</prop>
<prop key="mail.smtp.from">xxxxxxxx#hotmail.com</prop>
</props>
</property>
</bean>
<bean id="mailMail" class="com.penril.my.MailMail">
<property name="mailSender" ref="mailSender" />
</bean>
</beans>
MailMail.java
public class MailMail
{
private JavaMailSender mailSender;
public void setMailSender(JavaMailSender mailSender) {
this.mailSender = mailSender;
}
public void sendMail(String from, String to, String subject, String msg) {
MimeMessage message = mailSender.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(msg);
mailSender.send(message);
} catch (MessagingException e) {
throw new MailParseException(e);
}
}
}
MainClass.java
public class MainClass
{
public static void main( String[] args )
{
ApplicationContext context =
new ClassPathXmlApplicationContext("Spring-Mail.xml");
MailMail mm = (MailMail) context.getBean("mailMail");
mm.sendMail("xxx123xxx#gmail.com",
"xxx234xxx#hotmail.co1m",
"Testing123",
"Testing only \n\n Hello Spring Email Sender");
}
}
There is no standard way of doing this that's accepted and honored across the board. I see that you have some options, though:
Add a header "Return-Receipt-To" with your e-mail address in the value. If the recipient of the e-mail has a client which honors this header, then a return receipt will be sent to you when the e-mail is opened. This is not reliable, mind you, as the user can always decide not to send the receipt, even if he has a client that supports it.
Add an image into your e-mail that loads from your server and put a parameter on the image that includes the user's e-mail address. When the e-mail loads, the image will load from your server. Write a script that collects the e-mail parameter and then delivers a blank image. This is also not reliable, however, as many mail clients prompt users if they wish to download images and they can always choose not to. Also, some (mostly older) e-mail clients do not support images.
Perhaps the most reliable way is not to include the message in your e-mail at all. Include only a link to a website where the message can be read, and include their e-mail address or a unique code in the link. This way, you know exactly who read the message. Of course, this has the downside that people aren't actually getting the message in their inbox, and they also may choose not to go to the website to read it.
Ultimately, I think you're going to have to come up with a creative solution to solve this problem, unless you're happy getting spotty results.
Sign up for a free account for a Cloud Based SMTP service, for example Sendgrid, which will save you the trouble of manually implementing what was suggested in the previous answer.
https://sendgrid.com/
You can send 400 emails a day on the free tier. You can manually check the status of individual messages 'opened', 'bounced' etc using the management console or there are various APIs available to do this programatically (although some are only available on paid tiers).
For example their WebHooks API will call back your server when an event (opened, bounced, click etc.) occurs:
https://sendgrid.com/docs/API_Reference/Webhooks/event.html

Programmatically enabling remote jmx monitoring

I am attempting to enable my core java application for remote accessibility via JMX.
However, two restrictions are making it harder than it should be.
a) I am not at the liberty to change the script which starts the app on the linux box. Therefore, I cannot pass any of the "jmxremote" parameters to the jvm.
b) It is very possible that the remote port ( com.sun.management.jmxremote.port = xxxx ) I specify is not open and I cannot modify the script to try another open port. I must do it automatically.
I tried to get around these restrictions by writing a class with would set all the required jmxremote params as well as find a "free" port.
public class JmxRemoteConnectionHelper{
#Override
public void init( ) throws Exception{
InetAddress address = InetAddress.getLocalHost();
String ipAddress = address.getHostAddress();
String hostname = address.getHostName();
String port = String.valueOf( getFreePort( ) );
System.setProperty("java.rmi.server.hostname", ipAddress );
System.setProperty("com.sun.management.jmxremote", "true" );
System.setProperty("com.sun.management.jmxremote.authenticate", "false" );
System.setProperty("com.sun.management.jmxremote.ssl", "false" );
System.setProperty("com.sun.management.jmxremote.port", port );
}
private final int getFreePort( ){
**//seedPort is passed in the constructor**
int freePort = seedPort;
ServerSocket sSocket = null;
for( int i=ZERO; i<PORT_SCAN_COUNTER; i++ ){
try{
freePort = freePort + i;
sSocket = new ServerSocket( freePort );
//FOUND a free port.
break;
}catch( Exception e ){
//Log
}finally{
if( sSocket != null ){
try{
sSocket.close();
sSocket = null;
}catch(Exception e ){
//Log
}
}
}
}
return freePort;
}
}
As shown below, I, then initialize it via spring.
<bean id="JmxRemoteConnection" class="JmxRemoteConnectionHelper" init-method="init" />
<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean" depends-on="JmxRemoteConnection" />
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false" >
<property name="assembler" ref="assembler"/>
<property name="namingStrategy" ref="namingStrategy"/>
<property name="autodetect" value="true"/>
<property name="server" ref="mbeanServer"/>
</bean>
<bean id="jmxAttributeSource" class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>
<bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
<property name="attributeSource" ref="jmxAttributeSource"/>
</bean>
<bean id="namingStrategy" class="org.springframework.jmx.export.naming.MetadataNamingStrategy" lazy-init="true">
<property name="attributeSource" ref="jmxAttributeSource"/>
</bean>
To test, I start the app on my windows machine. It starts up correctly. However, when I bring up JConsole on the same box and try to connect via "remote process" (ip:port), I get a "connection refused" message at the bottom.
My suspicion is that the JMX agent is not seeing any of the remote system properties that I am setting.
I am using JDK 1.6.
Since you are already using Spring I think you should see if using a ConnectorServerFactoryBean can do what you are looking to do. I've never had to start a remote JMX server but it looks like that's what that object can do for you.
See the answers to this for enabling jmx within the process:
Is it possible to enable remote jmx monitoring programmatically?
To find a free port simply wrap LocateRegistry.createRegistry() in a loop which retries with a new port number until it succeeds.
Of course you'll have to communicate the final port number to whatever needs to connect. Alternatively running jstatd on the host should make it discoverable

Getting [SQLITE_BUSY] database file is locked with select statements

If I run multiple threads against my web app I get:
java.sql.SQLException: [SQLITE_BUSY] The database file is locked (database is locked)
at org.sqlite.DB.newSQLException(DB.java:383)
at org.sqlite.DB.newSQLException(DB.java:387)
at org.sqlite.DB.execute(DB.java:339)
at org.sqlite.PrepStmt.executeQuery(PrepStmt.java:75)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
I do know that only one thread can write to a sqlite database but I'm only reading from the database. So why do I get this error message ?
BTW: My connection pool looks like this:
<bean class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" id="dataSource">
<property name="driverClassName" value="${database.driverClassName}" />
<property name="url" value="${database.url}" />
<property name="username" value="${database.username}" />
<property name="password" value="${database.password}" />
<property name="initialSize" value="1" />
<property name="maxActive" value="2" />
<property name="maxIdle" value="1" />
<property name="poolPreparedStatements" value="true" />
</bean>
The setup is: Java 1.6, Tomcat 7.0.34, Spring 3.2, Hibernate 3.6.9 and sqlite3 3.7.2
Regards
Roger
After some googling I found that it is a bad practice to use multiple connections when connecting to SQLite. See
http://touchlabblog.tumblr.com/post/24474398246/android-sqlite-locking
Set your poolsize maxactive to 1 and try out.
For anyone who's having issues with it in WSL2:
Happened to me when I was using WSL2 & Datagrip, even tho the database wasn't busy.
It turns out that Datagrip has tried to connect to the database file that existed inside WSL2 via Windows' sqlite3.
Moving the file from WSL2 to a Windows file directory seems to solve this issue
There should be only ONE connection with your application.
you can use this to ensure.
public class SqliteHelper {
private static Connection c = null;
public static Connection getConn() throws Exception {
if(c == null){
Class.forName("org.sqlite.JDBC");
c = DriverManager.getConnection("jdbc:sqlite:D:/test.db");
}
return c;
}
}
Note also that this may happen if you accidentally forget to close your connection:
Connection connection;
try {
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(QUERY);
if (resultSet.next()) { /* do something */ }
catch (SQLException e) { /* handle exception */ }
finally {
if (connection != null) {
try {
connection.close(); // <-- This is important
} catch (SQLException e) {
/* handle exception */
}
}
}
While the first database connection may work well once the server is started, subsequent queries may not, depending on how the connection pool is configured.
Everytime you establish a connection make sure to close it after the work is done, It worked for me
like if you are using
Connection con = null;
PreparedStatement pst = con.prepareStatement("...query... ");
/*
do some stuff
*/
pst.executeQuery();
pst.close();
con.close();
I experienced the same problem, even though all connections, resulsets and statements were closed, I still had the error.
The problem for me was using the DB browser plugin in Intellij to visualize and manage tables. Disconnecting the database from this tool solved the problem.
So make sure that no external tool is connecting to the database and locking tables.
In my case, there are thread using sqlite connection in the background, which caused this error.
close sqlitebrowser
close electron app ( maybe need restart)
re-run your program.
For me the problem was that I was opening too much Sessions
So I made the session field in my DAO class static
Thanks from bowman han, I added a piece of code to his solution and it worked for me.
private static Connection c = null;
public static Connection connect() throws Exception {
if (c == null) {
c = (Connection) DriverManager.getConnection(url);
} else {
c.close();
c = (Connection) DriverManager.getConnection(url);
}
return c;
}
You have opened another application containing the database,
Try to close that application and run your program again. This worked for me
Try #Transactional(readonly=true) for those methods that only do reads. Maybe that works for you.

Categories

Resources