Better error handling in htpasswd

CSP handling is now separate, and the MQ return codes are tidied up.
Also added defaultIdentityTest to JMS tests and fixed copyright dates for htpasswd code
This commit is contained in:
Arthur Barr
2021-01-07 11:10:20 +00:00
committed by Arthur J Barr
parent 76070234d4
commit a24258834e
16 changed files with 218 additions and 129 deletions

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2015, 2020 # © Copyright IBM Corporation 2015, 2021
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2017, 2020 # © Copyright IBM Corporation 2017, 2021
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2020 © Copyright IBM Corporation 2021
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ limitations under the License.
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "log.h" #include "log.h"
#include "htpass.h"
#include <linux/limits.h> #include <linux/limits.h>
#include <apr_general.h> #include <apr_general.h>
#include <apr_errno.h> #include <apr_errno.h>
@@ -51,9 +52,6 @@ bool htpass_valid_file(char *filename)
valid = false; valid = false;
break; break;
} }
else {
}
} }
fclose(fp); fclose(fp);
if (line) if (line)
@@ -106,12 +104,13 @@ char *find_hash(char *filename, char *user)
return hash; return hash;
} }
bool htpass_authenticate_user(char *filename, char *user, char *password) int htpass_authenticate_user(char *filename, char *user, char *password)
{ {
char *hash = find_hash(filename, user); char *hash = find_hash(filename, user);
bool result = false; int result = -1;
if (hash == NULL) if (hash == NULL)
{ {
result = HTPASS_INVALID_USER;
log_debugf("User does not exist. user=%s", user); log_debugf("User does not exist. user=%s", user);
} }
else else
@@ -122,11 +121,12 @@ bool htpass_authenticate_user(char *filename, char *user, char *password)
// status is usually either APR_SUCCESS or APR_EMISMATCH // status is usually either APR_SUCCESS or APR_EMISMATCH
if (status == APR_SUCCESS) if (status == APR_SUCCESS)
{ {
result = true; result = HTPASS_VALID;
log_debugf("Correct password supplied. user=%s", user); log_debugf("Correct password supplied. user=%s", user);
} }
else else
{ {
result = HTPASS_INVALID_PASSWORD;
log_debugf("Incorrect password supplied. user=%s", user); log_debugf("Incorrect password supplied. user=%s", user);
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2020 © Copyright IBM Corporation 2021
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -17,6 +17,10 @@ limitations under the License.
#ifndef _HTPASS_H #ifndef _HTPASS_H
#define _HTPASS_H #define _HTPASS_H
#define HTPASS_VALID 0
#define HTPASS_INVALID_USER 1
#define HTPASS_INVALID_PASSWORD 2
/** /**
* Validate an HTPasswd file for use with IBM MQ. * Validate an HTPasswd file for use with IBM MQ.
* *
@@ -30,8 +34,9 @@ _Bool htpass_valid_file(char *filename);
* @param filename the HTPasswd file * @param filename the HTPasswd file
* @param user the user name to authenticate * @param user the user name to authenticate
* @param password the password of the user * @param password the password of the user
* @return HTPASS_VALID, HTPASS_INVALID_USER or HTPASS_INVALID_PASSWORD
*/ */
_Bool htpass_authenticate_user(char *filename, char *user, char *password); int htpass_authenticate_user(char *filename, char *user, char *password);
/** /**
* Validate that a user exists in the password file. * Validate that a user exists in the password file.

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2020 © Copyright IBM Corporation 2021
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -65,9 +65,9 @@ void test_htpass_valid_file_too_long()
void test_htpass_authenticate_user_fred_valid() void test_htpass_authenticate_user_fred_valid()
{ {
test_start(); test_start();
int ok = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", "passw0rd"); int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", "passw0rd");
printf("%s: fred - %d\n", __func__, ok); printf("%s: fred - %d\n", __func__, rc);
if (!ok) if (rc != HTPASS_VALID)
test_fail(__func__); test_fail(__func__);
test_pass(); test_pass();
} }
@@ -75,9 +75,9 @@ void test_htpass_authenticate_user_fred_valid()
void test_htpass_authenticate_user_fred_invalid1() void test_htpass_authenticate_user_fred_invalid1()
{ {
test_start(); test_start();
int ok = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", "passw0rd "); int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", "passw0rd ");
printf("%s: fred - %d\n", __func__, ok); printf("%s: fred - %d\n", __func__, rc);
if (ok) if (rc != HTPASS_INVALID_PASSWORD)
test_fail(__func__); test_fail(__func__);
test_pass(); test_pass();
} }
@@ -85,9 +85,9 @@ void test_htpass_authenticate_user_fred_invalid1()
void test_htpass_authenticate_user_fred_invalid2() void test_htpass_authenticate_user_fred_invalid2()
{ {
test_start(); test_start();
int ok = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", ""); int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", "");
printf("%s: fred - %d\n", __func__, ok); printf("%s: fred - %d\n", __func__, rc);
if (ok) if (rc != HTPASS_INVALID_PASSWORD)
test_fail(__func__); test_fail(__func__);
test_pass(); test_pass();
} }
@@ -95,9 +95,9 @@ void test_htpass_authenticate_user_fred_invalid2()
void test_htpass_authenticate_user_fred_invalid3() void test_htpass_authenticate_user_fred_invalid3()
{ {
test_start(); test_start();
int ok = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", "clearlywrong"); int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", "clearlywrong");
printf("%s: fred - %d\n", __func__, ok); printf("%s: fred - %d\n", __func__, rc);
if (ok) if (rc != HTPASS_INVALID_PASSWORD)
test_fail(__func__); test_fail(__func__);
test_pass(); test_pass();
} }
@@ -105,9 +105,19 @@ void test_htpass_authenticate_user_fred_invalid3()
void test_htpass_authenticate_user_barney_valid() void test_htpass_authenticate_user_barney_valid()
{ {
test_start(); test_start();
int ok = htpass_authenticate_user("./src/htpass_test.htpasswd", "barney", "s3cret"); int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "barney", "s3cret");
printf("%s: barney - %d\n", __func__, ok); printf("%s: barney - %d\n", __func__, rc);
if (!ok) if (rc != HTPASS_VALID)
test_fail(__func__);
test_pass();
}
void test_htpass_authenticate_user_unknown()
{
test_start();
int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "george", "s3cret");
printf("%s: barney - %d\n", __func__, rc);
if (rc != HTPASS_INVALID_USER)
test_fail(__func__); test_fail(__func__);
test_pass(); test_pass();
} }
@@ -127,11 +137,11 @@ void *authenticate_many_times(void *p)
{ {
for (int i = 0; i < NUM_TESTS_PER_THREAD; i++) for (int i = 0; i < NUM_TESTS_PER_THREAD; i++)
{ {
int ok = htpass_authenticate_user("./src/htpass_test.htpasswd", "barney", "s3cret"); int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "barney", "s3cret");
if (!ok) if (rc != HTPASS_VALID)
test_fail(__func__); test_fail(__func__);
ok = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", "passw0rd"); rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", "passw0rd");
if (!ok) if (rc != HTPASS_VALID)
test_fail(__func__); test_fail(__func__);
} }
pthread_exit(NULL); pthread_exit(NULL);
@@ -205,6 +215,7 @@ int main()
test_htpass_authenticate_user_fred_invalid2(); test_htpass_authenticate_user_fred_invalid2();
test_htpass_authenticate_user_fred_invalid3(); test_htpass_authenticate_user_fred_invalid3();
test_htpass_authenticate_user_barney_valid(); test_htpass_authenticate_user_barney_valid();
test_htpass_authenticate_user_unknown();
log_close(); log_close();
// Call multi-threaded test last, because it re-initializes the log to use a file // Call multi-threaded test last, because it re-initializes the log to use a file

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2020 © Copyright IBM Corporation 2021
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2020 © Copyright IBM Corporation 2021
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2020 © Copyright IBM Corporation 2021
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -111,8 +111,109 @@ void MQENTRY MQStart(
return; return;
} }
/**
* Called during the connection of any application which supplies an MQCSP (Connection Security Parameters).
* This is the usual case.
* See https://www.ibm.com/support/knowledgecenter/SSFKSJ_latest/com.ibm.mq.ref.dev.doc/q095610_.html
*/
static void MQENTRY mqhtpass_authenticate_user_csp(
PMQCHAR pQMgrName,
PMQCSP pSecurityParms,
PMQZAC pApplicationContext,
PMQZIC pIdentityContext,
PMQPTR pCorrelationPtr,
PMQBYTE pComponentData,
PMQLONG pContinuation,
PMQLONG pCompCode,
PMQLONG pReason)
{
char *csp_user = NULL;
char *csp_pass = NULL;
// Firstly, create null-terminated strings from the user credentials in the MQ CSP object
csp_user = malloc(pSecurityParms->CSPUserIdLength + 1);
if (!csp_user)
{
log_errorf("%s is unable to allocate memory for a user", NAME);
*pCompCode = MQCC_FAILED;
*pReason = MQRC_SERVICE_ERROR;
return;
}
strncpy(csp_user, pSecurityParms->CSPUserIdPtr, pSecurityParms->CSPUserIdLength);
csp_user[pSecurityParms->CSPUserIdLength] = 0;
csp_pass = malloc((pSecurityParms->CSPPasswordLength + 1));
if (!csp_pass)
{
log_errorf("%s is unable to allocate memory for a password", NAME);
*pCompCode = MQCC_FAILED;
*pReason = MQRC_SERVICE_ERROR;
if (csp_user)
{
free(csp_user);
}
return;
}
strncpy(csp_pass, pSecurityParms->CSPPasswordPtr, pSecurityParms->CSPPasswordLength);
csp_pass[pSecurityParms->CSPPasswordLength] = 0;
log_debugf("%s with CSP user set. user=%s", __func__, csp_user);
int auth_result = htpass_authenticate_user(HTPASSWD_FILE, csp_user, csp_pass);
if (auth_result == HTPASS_VALID)
{
// An OK completion code means MQ will accept this user is authenticated
*pCompCode = MQCC_OK;
*pReason = MQRC_NONE;
// Tell the queue manager to stop trying other authorization services.
*pContinuation = MQZCI_STOP;
memcpy(pIdentityContext->UserIdentifier, csp_user, sizeof(pIdentityContext->UserIdentifier));
log_debugf("Authenticated user=%s", pIdentityContext->UserIdentifier);
}
// If the htpasswd file does not have an entry for this user
else if (auth_result == HTPASS_INVALID_USER)
{
*pCompCode = MQCC_WARNING;
*pReason = MQRC_NONE;
// Tell the queue manager to continue trying other authorization services, as they might have the user.
*pContinuation = MQZCI_CONTINUE;
log_debugf(
"User authentication failed due to invalid user. user=%s effuser=%s applname=%s csp_user=%s cc=%d reason=%d",
trim(pIdentityContext->UserIdentifier),
trim(pApplicationContext->EffectiveUserID),
trim(pApplicationContext->ApplName),
trim(csp_user),
*pCompCode,
*pReason);
}
// If the htpasswd file has an entry for this user, but the password supplied is incorrect
else if (auth_result == HTPASS_INVALID_PASSWORD)
{
*pCompCode = MQCC_WARNING;
*pReason = MQRC_NOT_AUTHORIZED;
// Tell the queue manager to stop trying other authorization services.
*pContinuation = MQZCI_STOP;
log_debugf(
"User authentication failed due to invalid password. user=%s effuser=%s applname=%s csp_user=%s cc=%d reason=%d",
trim(pIdentityContext->UserIdentifier),
trim(pApplicationContext->EffectiveUserID),
trim(pApplicationContext->ApplName),
trim(csp_user),
*pCompCode,
*pReason);
}
if (csp_user)
{
free(csp_user);
}
if (csp_pass)
{
free(csp_pass);
}
return;
}
/** /**
* Called during the connection of any application. * Called during the connection of any application.
* For more information on the parameters, see https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_latest/com.ibm.mq.ref.dev.doc/q110090_.html
*/ */
static void MQENTRY mqhtpass_authenticate_user( static void MQENTRY mqhtpass_authenticate_user(
PMQCHAR pQMgrName, PMQCHAR pQMgrName,
@@ -137,59 +238,7 @@ static void MQENTRY mqhtpass_authenticate_user(
if ((pSecurityParms->AuthenticationType) == MQCSP_AUTH_USER_ID_AND_PWD) if ((pSecurityParms->AuthenticationType) == MQCSP_AUTH_USER_ID_AND_PWD)
{ {
// Authenticating a user ID and password. mqhtpass_authenticate_user_csp(pQMgrName, pSecurityParms, pApplicationContext, pIdentityContext, pCorrelationPtr, pComponentData, pContinuation, pCompCode, pReason);
// Firstly, create null-terminated strings from the user credentials in the MQ CSP object
spuser = malloc(pSecurityParms->CSPUserIdLength + 1);
if (!spuser)
{
log_errorf("%s is unable to allocate memory for a user", NAME);
return;
}
strncpy(spuser, pSecurityParms->CSPUserIdPtr, pSecurityParms->CSPUserIdLength);
spuser[pSecurityParms->CSPUserIdLength] = 0;
sppass = malloc((pSecurityParms->CSPPasswordLength + 1));
if (!sppass)
{
log_errorf("%s is unable to allocate memory for a password", NAME);
if (spuser)
{
free(spuser);
}
return;
}
strncpy(sppass, pSecurityParms->CSPPasswordPtr, pSecurityParms->CSPPasswordLength);
sppass[pSecurityParms->CSPPasswordLength] = 0;
log_debugf("%s with CSP user set. user=%s", __func__, spuser);
bool authenticated = htpass_authenticate_user(HTPASSWD_FILE, spuser, sppass);
if (authenticated)
{
*pCompCode = MQCC_OK;
*pReason = MQRC_NONE;
*pContinuation = MQZCI_CONTINUE;
memcpy(pIdentityContext->UserIdentifier, spuser, sizeof(pIdentityContext->UserIdentifier));
log_debugf("Authenticated user=%s", pIdentityContext->UserIdentifier);
}
else
{
log_debugf(
"User authentication failed user=%s effuser=%s applname=%s cspuser=%s cc=%d reason=%d",
trim(pIdentityContext->UserIdentifier),
trim(pApplicationContext->EffectiveUserID),
trim(pApplicationContext->ApplName),
trim(spuser),
*pCompCode,
*pReason);
}
if (spuser)
{
free(spuser);
}
if (sppass)
{
free(sppass);
}
} }
else else
{ {
@@ -198,6 +247,8 @@ static void MQENTRY mqhtpass_authenticate_user(
if (!spuser) if (!spuser)
{ {
log_errorf("%s is unable to allocate memory to check a user", NAME); log_errorf("%s is unable to allocate memory to check a user", NAME);
*pCompCode = MQCC_FAILED;
*pReason = MQRC_SERVICE_ERROR;
return; return;
} }
strncpy(spuser, pApplicationContext->EffectiveUserID, strlen(pApplicationContext->EffectiveUserID)); strncpy(spuser, pApplicationContext->EffectiveUserID, strlen(pApplicationContext->EffectiveUserID));
@@ -219,7 +270,7 @@ static void MQENTRY mqhtpass_authenticate_user(
// An OK completion code means MQ will accept this user is authenticated // An OK completion code means MQ will accept this user is authenticated
*pCompCode = MQCC_OK; *pCompCode = MQCC_OK;
*pReason = MQRC_NONE; *pReason = MQRC_NONE;
*pContinuation = MQZCI_CONTINUE; *pContinuation = MQZCI_STOP;
memcpy(pIdentityContext->UserIdentifier, spuser, sizeof(pIdentityContext->UserIdentifier)); memcpy(pIdentityContext->UserIdentifier, spuser, sizeof(pIdentityContext->UserIdentifier));
} }
else else

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2018, 2020 © Copyright IBM Corporation 2018, 2021
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# -*- mode: sh -*- # -*- mode: sh -*-
# © Copyright IBM Corporation 2019 # © Copyright IBM Corporation 2019, 2021
# #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# -*- mode: sh -*- # -*- mode: sh -*-
# © Copyright IBM Corporation 2015, 2020 # © Copyright IBM Corporation 2015, 2021
# #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2020 © Copyright IBM Corporation 2020, 2021
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -43,6 +43,7 @@ func TestDevGoldenPath(t *testing.T) {
Env: []string{ Env: []string{
"LICENSE=accept", "LICENSE=accept",
"MQ_QMGR_NAME=" + qm, "MQ_QMGR_NAME=" + qm,
"DEBUG=true",
}, },
} }
id := runContainerWithPorts(t, cli, &containerConfig, []int{9443}) id := runContainerWithPorts(t, cli, &containerConfig, []int{9443})

View File

@@ -1,4 +1,4 @@
# © Copyright IBM Corporation 2018, 2019 # © Copyright IBM Corporation 2018, 2021
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -33,4 +33,5 @@ RUN find /usr/src/mymaven
FROM docker.io/ibmjava:8-jre FROM docker.io/ibmjava:8-jre
COPY --from=builder /usr/src/mymaven/target/*.jar /opt/app/ COPY --from=builder /usr/src/mymaven/target/*.jar /opt/app/
COPY --from=builder /usr/src/mymaven/target/lib/*.jar /opt/app/ COPY --from=builder /usr/src/mymaven/target/lib/*.jar /opt/app/
USER 1001
ENTRYPOINT ["java", "-classpath", "/opt/app/*", "org.junit.platform.console.ConsoleLauncher", "-p", "com.ibm.mqcontainer.test", "--details", "verbose"] ENTRYPOINT ["java", "-classpath", "/opt/app/*", "org.junit.platform.console.ConsoleLauncher", "-p", "com.ibm.mqcontainer.test", "--details", "verbose"]

View File

@@ -1,5 +1,5 @@
<!-- <!--
© Copyright IBM Corporation 2018, 2020 © Copyright IBM Corporation 2018, 2021
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -65,5 +65,9 @@ limitations under the License.
</executions> </executions>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
</project> </project>

View File

@@ -1,5 +1,5 @@
/* /*
© Copyright IBM Corporation 2018 © Copyright IBM Corporation 2018, 2021
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@ limitations under the License.
*/ */
package com.ibm.mqcontainer.test; package com.ibm.mqcontainer.test;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.*;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
@@ -32,9 +32,12 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import com.ibm.mq.MQException;
import com.ibm.mq.constants.MQConstants;
import com.ibm.mq.jms.MQConnectionFactory; import com.ibm.mq.jms.MQConnectionFactory;
import com.ibm.mq.jms.MQQueue; import com.ibm.mq.jms.MQQueue;
import com.ibm.msg.client.wmq.WMQConstants; import com.ibm.msg.client.wmq.WMQConstants;
import com.ibm.msg.client.jms.DetailedJMSSecurityRuntimeException;
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
@@ -57,27 +60,18 @@ class JMSTests {
static SSLSocketFactory createSSLSocketFactory() throws IOException, GeneralSecurityException { static SSLSocketFactory createSSLSocketFactory() throws IOException, GeneralSecurityException {
KeyStore ts=KeyStore.getInstance("jks"); KeyStore ts=KeyStore.getInstance("jks");
ts.load(new FileInputStream(TRUSTSTORE), PASSPHRASE.toCharArray()); ts.load(new FileInputStream(TRUSTSTORE), PASSPHRASE.toCharArray());
// KeyManagerFactory kmf=KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
TrustManagerFactory tmf=TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); TrustManagerFactory tmf=TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ts); tmf.init(ts);
// tmf.init();
SSLContext ctx = SSLContext.getInstance("TLSv1.2"); SSLContext ctx = SSLContext.getInstance("TLSv1.2");
// Security.setProperty("crypto.policy", "unlimited");
ctx.init(null, tmf.getTrustManagers(), null); ctx.init(null, tmf.getTrustManagers(), null);
return ctx.getSocketFactory(); return ctx.getSocketFactory();
} }
static JMSContext create(String channel, String addr, String user, String password) throws JMSException, IOException, GeneralSecurityException { static MQConnectionFactory createMQConnectionFactory(String channel, String addr) throws JMSException, IOException, GeneralSecurityException {
LOGGER.info(String.format("Connecting to %s/TCP/%s(1414) as %s", channel, addr, user));
MQConnectionFactory factory = new MQConnectionFactory(); MQConnectionFactory factory = new MQConnectionFactory();
factory.setTransportType(WMQConstants.WMQ_CM_CLIENT); factory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
factory.setChannel(channel); factory.setChannel(channel);
factory.setConnectionNameList(String.format("%s(1414)", addr)); factory.setConnectionNameList(String.format("%s(1414)", addr));
// If a password is set, make sure it gets sent to the queue manager for authentication
if (password != null) {
factory.setBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP, true);
}
// factory.setClientReconnectOptions(WMQConstants.WMQ_CLIENT_RECONNECT);
if (TRUSTSTORE == null) { if (TRUSTSTORE == null) {
LOGGER.info("Not using TLS"); LOGGER.info("Not using TLS");
} }
@@ -94,12 +88,33 @@ class JMSTests {
factory.setSSLCipherSuite("TLS_RSA_WITH_AES_128_CBC_SHA256"); factory.setSSLCipherSuite("TLS_RSA_WITH_AES_128_CBC_SHA256");
} }
} }
// Give up if unable to reconnect for 10 minutes return factory;
// factory.setClientReconnectTimeout(600); }
// LOGGER.info(String.format("user=%s pw=%s", user, password));
/**
* Create a JMSContext with the supplied user and password.
*/
static JMSContext create(String channel, String addr, String user, String password) throws JMSException, IOException, GeneralSecurityException {
LOGGER.info(String.format("Connecting to %s/TCP/%s(1414) as %s", channel, addr, user));
MQConnectionFactory factory = createMQConnectionFactory(channel, addr);
// If a password is set, make sure it gets sent to the queue manager for authentication
if (password != null) {
factory.setBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP, true);
}
LOGGER.info(String.format("CSP authentication: %s", factory.getBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP)));
return factory.createContext(user, password); return factory.createContext(user, password);
} }
/**
* Create a JMSContext with the default user identity (from the OS)
*/
static JMSContext create(String channel, String addr) throws JMSException, IOException, GeneralSecurityException {
LOGGER.info(String.format("Connecting to %s/TCP/%s(1414) as OS user '%s'", channel, addr, System.getProperty("user.name")));
MQConnectionFactory factory = createMQConnectionFactory(channel, addr);
LOGGER.info(String.format("CSP authentication: %s", factory.getBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP)));
return factory.createContext();
}
@BeforeAll @BeforeAll
private static void waitForQueueManager() { private static void waitForQueueManager() {
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
@@ -116,28 +131,34 @@ class JMSTests {
} }
} }
@BeforeEach
void connect() throws Exception {
context = create(CHANNEL, ADDR, USER, PASSWORD);
}
@Test @Test
void succeedingTest(TestInfo t) throws JMSException { void putGetTest(TestInfo t) throws Exception {
context = create(CHANNEL, ADDR, USER, PASSWORD);
Queue queue = new MQQueue("DEV.QUEUE.1"); Queue queue = new MQQueue("DEV.QUEUE.1");
context.createProducer().send(queue, t.getDisplayName()); context.createProducer().send(queue, t.getDisplayName());
Message m = context.createConsumer(queue).receive(); Message m = context.createConsumer(queue).receive();
assertNotNull(m.getBody(String.class)); assertNotNull(m.getBody(String.class));
} }
// @Test
// void failingTest() {
// fail("a failing test");
// }
@Test @Test
@Disabled("for demonstration purposes") void defaultIdentityTest(TestInfo t) throws Exception {
void skippedTest() { LOGGER.info(String.format("Password='%s'", PASSWORD));
// not executed try {
// Don't pass a user/password, which should cause the default identity to be used
context = create(CHANNEL, ADDR);
} catch (DetailedJMSSecurityRuntimeException ex) {
Throwable cause = ex.getCause();
assertNotNull(cause);
assertTrue(cause instanceof MQException);
assertEquals(MQConstants.MQRC_NOT_AUTHORIZED, ((MQException)cause).getReason());
return;
}
// The default developer config allows any user to appear as "app", and use a blank password. This is done with the MCAUSER on the channel.
// If this test is run on a queue manager without a password set, then it should be possible to connect without exception.
// If this test is run on a queue manager with a password set, then an exception should be thrown, because this test doesn't send a password.
if ((PASSWORD != null) && (PASSWORD != "")) {
fail("Exception not thrown");
}
} }
@AfterEach @AfterEach
@@ -146,9 +167,4 @@ class JMSTests {
context.close(); context.close();
} }
} }
@AfterAll
static void tearDownAll() {
}
} }