Browse Source

add setting for sms gateway https certificate

Jason Rivard 7 years ago
parent
commit
ab8271254e

+ 10 - 6
server/src/main/java/password/pwm/PwmApplication.java

@@ -25,6 +25,7 @@ package password.pwm;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.provider.ChaiProvider;
+import password.pwm.bean.SessionLabel;
 import password.pwm.bean.SmsItemBean;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.Configuration;
@@ -620,22 +621,25 @@ public class PwmApplication {
     }
 
     public void sendSmsUsingQueue(
-            final SmsItemBean smsItem,
+            final String to,
+            final String message,
+            final SessionLabel sessionLabel,
             final MacroMachine macroMachine
     ) {
         final SmsQueueManager smsQueue = getSmsQueue();
         if (smsQueue == null) {
-            LOGGER.error("SMS queue is unavailable, unable to send SMS: " + smsItem.toString());
+            LOGGER.error(sessionLabel, "SMS queue is unavailable, unable to send SMS to: " + to);
             return;
         }
 
-        final SmsItemBean rewrittenSmsItem = new SmsItemBean(
-                macroMachine.expandMacros(smsItem.getTo()),
-                macroMachine.expandMacros(smsItem.getMessage())
+        final SmsItemBean smsItemBean = new SmsItemBean(
+                macroMachine.expandMacros(to),
+                macroMachine.expandMacros(message),
+                sessionLabel
         );
 
         try {
-            smsQueue.addSmsToQueue(rewrittenSmsItem);
+            smsQueue.addSmsToQueue(smsItemBean);
         } catch (PwmUnrecoverableException e) {
             LOGGER.warn("unable to add sms to queue: " + e.getMessage());
         }

+ 1 - 1
server/src/main/java/password/pwm/bean/SmsItemBean.java

@@ -34,7 +34,7 @@ import java.io.Serializable;
 public class SmsItemBean implements Serializable {
     private final String to;
     private final String message;
-
+    private final SessionLabel sessionLabel;
 
     public String toString() {
         return "SMS Item: " + JsonUtil.serialize(this);

+ 6 - 4
server/src/main/java/password/pwm/config/PwmSetting.java

@@ -335,10 +335,10 @@ public enum PwmSetting {
 
 
     // sms settings
-    SMS_MAX_QUEUE_AGE(
-            "sms.queueMaxAge", PwmSettingSyntax.DURATION, PwmSettingCategory.SMS_GATEWAY),
     SMS_GATEWAY_URL(
             "sms.gatewayURL", PwmSettingSyntax.STRING, PwmSettingCategory.SMS_GATEWAY),
+    SMS_GATEWAY_CERTIFICATES(
+            "sms.gatewayCertificates", PwmSettingSyntax.X509CERT, PwmSettingCategory.SMS_GATEWAY),
     SMS_GATEWAY_USER(
             "sms.gatewayUser", PwmSettingSyntax.STRING, PwmSettingCategory.SMS_GATEWAY),
     SMS_GATEWAY_PASSWORD(
@@ -357,8 +357,6 @@ public enum PwmSetting {
             "sms.httpRequestHeaders", PwmSettingSyntax.STRING_ARRAY, PwmSettingCategory.SMS_GATEWAY),
     SMS_MAX_TEXT_LENGTH(
             "sms.maxTextLength", PwmSettingSyntax.NUMERIC, PwmSettingCategory.SMS_GATEWAY),
-    SMS_RESPONSE_OK_REGEX(
-            "sms.responseOkRegex", PwmSettingSyntax.STRING_ARRAY, PwmSettingCategory.SMS_GATEWAY),
     SMS_SENDER_ID(
             "sms.senderID", PwmSettingSyntax.STRING, PwmSettingCategory.SMS_GATEWAY),
     SMS_PHONE_NUMBER_FORMAT(
@@ -371,12 +369,16 @@ public enum PwmSetting {
             "sms.requestId.length", PwmSettingSyntax.NUMERIC, PwmSettingCategory.SMS_GATEWAY),
     SMS_USE_URL_SHORTENER(
             "sms.useUrlShortener", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.SMS_GATEWAY),
+    SMS_RESPONSE_OK_REGEX(
+            "sms.responseOkRegex", PwmSettingSyntax.STRING_ARRAY, PwmSettingCategory.SMS_GATEWAY),
     SMS_SUCCESS_RESULT_CODE(
             "sms.successResultCodes", PwmSettingSyntax.STRING_ARRAY, PwmSettingCategory.SMS_GATEWAY),
     URL_SHORTENER_CLASS(
             "urlshortener.classname", PwmSettingSyntax.STRING, PwmSettingCategory.SMS_GATEWAY),
     URL_SHORTENER_PARAMETERS(
             "urlshortener.parameters", PwmSettingSyntax.STRING_ARRAY, PwmSettingCategory.SMS_GATEWAY),
+    SMS_MAX_QUEUE_AGE(
+            "sms.queueMaxAge", PwmSettingSyntax.DURATION, PwmSettingCategory.SMS_GATEWAY),
 
     SMS_CHALLENGE_TOKEN_TEXT(
             "sms.challenge.token.message", PwmSettingSyntax.LOCALIZED_STRING, PwmSettingCategory.SMS_MESSAGES),

+ 59 - 0
server/src/main/java/password/pwm/config/function/SMSGatewayCertImportFunction.java

@@ -0,0 +1,59 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2017 The PWM Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package password.pwm.config.function;
+
+import password.pwm.PwmConstants;
+import password.pwm.config.PwmSetting;
+import password.pwm.config.stored.StoredConfigurationImpl;
+import password.pwm.error.ErrorInformation;
+import password.pwm.error.PwmError;
+import password.pwm.error.PwmOperationalException;
+
+import java.net.URI;
+
+public class SMSGatewayCertImportFunction extends AbstractUriCertImportFunction {
+
+
+    @Override
+    String getUri(final StoredConfigurationImpl storedConfiguration, final PwmSetting pwmSetting, final String profile, final String extraData) throws PwmOperationalException {
+
+
+        final String uriString;
+        final String menuDebugLocation;
+
+        uriString = (String)storedConfiguration.readSetting(PwmSetting.SMS_GATEWAY_URL).toNativeObject();
+        menuDebugLocation = PwmSetting.SMS_GATEWAY_URL.toMenuLocationDebug( null, PwmConstants.DEFAULT_LOCALE );
+
+        if (uriString.isEmpty()) {
+            final ErrorInformation errorInformation = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,"Setting " + menuDebugLocation + " must first be configured");
+            throw new PwmOperationalException(errorInformation);
+        }
+        try {
+            URI.create(uriString);
+        } catch (IllegalArgumentException e) {
+            final ErrorInformation errorInformation = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,"Setting " + menuDebugLocation + " has an invalid URL syntax");
+            throw new PwmOperationalException(errorInformation);
+        }
+        return uriString;
+    }
+}

+ 19 - 13
server/src/main/java/password/pwm/http/servlet/ActivateUserServlet.java

@@ -32,7 +32,6 @@ import password.pwm.PwmConstants;
 import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.bean.LoginInfoBean;
-import password.pwm.bean.SmsItemBean;
 import password.pwm.bean.TokenDestinationItem;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.Configuration;
@@ -581,8 +580,12 @@ public class ActivateUserServlet extends AbstractPwmServlet {
             return false;
         }
 
-        final SmsItemBean smsItem = new SmsItemBean(toSmsNumber, message);
-        pwmApplication.sendSmsUsingQueue(smsItem, pwmSession.getSessionManager().getMacroMachine(pwmApplication));
+        pwmApplication.sendSmsUsingQueue(
+                toSmsNumber,
+                message,
+                pwmRequest.getSessionLabel(),
+                pwmSession.getSessionManager().getMacroMachine(pwmApplication)
+        );
         return true;
     }
 
@@ -719,7 +722,7 @@ public class ActivateUserServlet extends AbstractPwmServlet {
             final String toSmsNumber,
             final String tokenKey
     )
-            throws PwmUnrecoverableException, ChaiUnavailableException
+            throws PwmUnrecoverableException
     {
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
         final Configuration config = pwmApplication.getConfig();
@@ -730,15 +733,18 @@ public class ActivateUserServlet extends AbstractPwmServlet {
         final MacroMachine macroMachine = MacroMachine.forUser(pwmRequest, userIdentity);
 
         final List<TokenDestinationItem.Type> sentTypes = TokenService.TokenSender.sendToken(
-                pwmApplication,
-                null,
-                macroMachine,
-                emailItemBean,
-                pref,
-                toAddress,
-                toSmsNumber,
-                smsMessage,
-                tokenKey
+                TokenService.TokenSendInfo.builder()
+                .pwmApplication( pwmApplication )
+                .userInfo( null )
+                .macroMachine( macroMachine )
+                .configuredEmailSetting( emailItemBean )
+                .tokenSendMethod( pref )
+                .emailAddress( toAddress )
+                .smsNumber( toSmsNumber )
+                .smsMessage( smsMessage )
+                .tokenKey( tokenKey )
+                .sessionLabel( pwmRequest.getSessionLabel() )
+                .build()
         );
 
         return TokenService.TokenSender.figureDisplayString(

+ 3 - 5
server/src/main/java/password/pwm/http/servlet/ForgottenUsernameServlet.java

@@ -27,13 +27,11 @@ import password.pwm.PwmConstants;
 import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.bean.SessionLabel;
-import password.pwm.bean.SmsItemBean;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.Configuration;
-import password.pwm.config.value.data.FormConfiguration;
-import password.pwm.util.form.FormUtility;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.option.MessageSendMethod;
+import password.pwm.config.value.data.FormConfiguration;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmOperationalException;
@@ -49,6 +47,7 @@ import password.pwm.ldap.search.SearchConfiguration;
 import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.util.CaptchaUtility;
+import password.pwm.util.form.FormUtility;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroMachine;
@@ -307,8 +306,7 @@ public class ForgottenUsernameServlet extends AbstractPwmServlet {
 
         final MacroMachine macroMachine = new MacroMachine(pwmApplication, sessionLabel, userInfo, null);
 
-        final SmsItemBean smsItem = new SmsItemBean(toNumber, smsMessage);
-        pwmApplication.sendSmsUsingQueue(smsItem, macroMachine);
+        pwmApplication.sendSmsUsingQueue(toNumber, smsMessage, sessionLabel, macroMachine);
         return null;
     }
 

+ 22 - 3
server/src/main/java/password/pwm/http/servlet/UpdateProfileServlet.java

@@ -716,7 +716,17 @@ public class UpdateProfileServlet extends ControlledPwmServlet {
                         pwmSession.getSessionStateBean().getLocale());
 
                 try {
-                    TokenService.TokenSender.sendSmsToken(pwmApplication, null, macroMachine, toNum, message, tokenKey);
+                    TokenService.TokenSender.sendSmsToken(
+                            TokenService.TokenSendInfo.builder()
+                            .pwmApplication( pwmApplication )
+                            .userInfo( null )
+                            .macroMachine( macroMachine )
+                            .smsNumber( toNum )
+                            .smsMessage( message )
+                            .tokenKey( tokenKey )
+                            .sessionLabel( pwmSession.getLabel() )
+                            .build()
+                    );
                 } catch (Exception e) {
                     throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN));
                 }
@@ -762,8 +772,17 @@ public class UpdateProfileServlet extends ControlledPwmServlet {
                         configuredEmailSetting.getBodyHtml().replace("%TOKEN%", tokenKey));
 
                 try {
-                    TokenService.TokenSender.sendEmailToken(pwmApplication, null, macroMachine, emailItemBean,
-                            toAddress, tokenKey);
+                    TokenService.TokenSender.sendEmailToken(
+                            TokenService.TokenSendInfo.builder()
+                            .pwmApplication( pwmApplication )
+                            .userInfo( null )
+                            .macroMachine( macroMachine )
+                            .configuredEmailSetting( emailItemBean )
+                            .emailAddress( toAddress )
+                            .tokenKey( tokenKey )
+                            .sessionLabel( pwmRequest.getSessionLabel() )
+                                    .build()
+                    );
                 } catch (Exception e) {
                     throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN));
                 }

+ 7 - 2
server/src/main/java/password/pwm/http/servlet/configeditor/ConfigEditorServlet.java

@@ -639,9 +639,14 @@ public class ConfigEditorServlet extends ControlledPwmServlet {
             returnRecords.add(new HealthRecord(HealthStatus.INFO, HealthTopic.SMS, "SMS not configured"));
         } else {
             final Map<String, String> testParams = pwmRequest.readBodyAsJsonStringMap();
-            final SmsItemBean testSmsItem = new SmsItemBean(testParams.get("to"), testParams.get("message"));
+            final SmsItemBean testSmsItem = new SmsItemBean(testParams.get("to"), testParams.get("message"), pwmRequest.getSessionLabel());
             try {
-                final String responseBody = SmsQueueManager.sendDirectMessage(config, testSmsItem);
+                final String responseBody = SmsQueueManager.sendDirectMessage(
+                        pwmRequest.getPwmApplication(),
+                        config,
+                        pwmRequest.getSessionLabel(),
+                        testSmsItem
+                );
                 returnRecords.add(new HealthRecord(HealthStatus.INFO, HealthTopic.SMS, "message sent"));
                 returnRecords.add(new HealthRecord(HealthStatus.INFO, HealthTopic.SMS, "response body: \n" + StringUtil.escapeHtml(responseBody)));
             } catch (PwmException e) {

+ 12 - 9
server/src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordUtil.java

@@ -463,15 +463,18 @@ class ForgottenPasswordUtil {
         final String smsMessage = config.readSettingAsLocalizedString(PwmSetting.SMS_CHALLENGE_TOKEN_TEXT, pwmRequest.getLocale());
 
         final List<TokenDestinationItem.Type> sentTypes = TokenService.TokenSender.sendToken(
-                pwmRequest.getPwmApplication(),
-                userInfo,
-                macroMachine,
-                emailItemBean,
-                tokenSendMethod,
-                outputDestrestTokenDataClient.getEmail(),
-                outputDestrestTokenDataClient.getSms(),
-                smsMessage,
-                tokenKey
+                TokenService.TokenSendInfo.builder()
+                .pwmApplication( pwmRequest.getPwmApplication() )
+                .userInfo( userInfo )
+                .macroMachine( macroMachine )
+                .configuredEmailSetting( emailItemBean )
+                .tokenSendMethod( tokenSendMethod )
+                .emailAddress( outputDestrestTokenDataClient.getEmail() )
+                .smsNumber( outputDestrestTokenDataClient.getSms() )
+                .smsMessage( smsMessage )
+                .tokenKey( tokenKey )
+                .sessionLabel( pwmRequest.getSessionLabel() )
+                .build()
         );
 
         StatisticsManager.incrementStat(pwmRequest, Statistic.RECOVERY_TOKENS_SENT);

+ 12 - 9
server/src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskServlet.java

@@ -738,15 +738,18 @@ public class HelpdeskServlet extends ControlledPwmServlet {
 
         try {
             TokenService.TokenSender.sendToken(
-                    pwmRequest.getPwmApplication(),
-                    userInfo,
-                    macroMachine,
-                    emailItemBean,
-                    tokenSendMethod,
-                    destEmailAddress,
-                    userInfo.getUserSmsNumber(),
-                    smsMessage,
-                    tokenKey
+                    TokenService.TokenSendInfo.builder()
+                    .pwmApplication( pwmRequest.getPwmApplication() )
+                    .userInfo( userInfo )
+                    .macroMachine( macroMachine )
+                    .configuredEmailSetting( emailItemBean )
+                    .tokenSendMethod( tokenSendMethod )
+                    .emailAddress( destEmailAddress )
+                    .smsNumber( userInfo.getUserSmsNumber() )
+                    .smsMessage( smsMessage )
+                    .tokenKey( tokenKey )
+                    .sessionLabel( pwmRequest.getSessionLabel() )
+                    .build()
             );
         } catch (PwmException e) {
             LOGGER.error(pwmRequest, e.getErrorInformation());

+ 22 - 4
server/src/main/java/password/pwm/http/servlet/newuser/NewUserUtils.java

@@ -514,8 +514,17 @@ class NewUserUtils {
                         pwmSession.getSessionStateBean().getLocale());
 
                 try {
-                    TokenService.TokenSender.sendSmsToken(pwmApplication, null, macroMachine,
-                            outputDestTokenData.getSms(), message, tokenKey);
+                    TokenService.TokenSender.sendSmsToken(
+                            TokenService.TokenSendInfo.builder()
+                            .pwmApplication( pwmApplication )
+                            .userInfo( null )
+                            .macroMachine( macroMachine )
+                            .smsNumber( outputDestTokenData.getSms() )
+                            .smsMessage( message )
+                            .tokenKey( tokenKey )
+                            .sessionLabel( pwmRequest.getSessionLabel() )
+                            .build()
+                    );
                 } catch (Exception e) {
                     throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN));
                 }
@@ -573,8 +582,17 @@ class NewUserUtils {
                         configuredEmailSetting.getBodyHtml().replace("%TOKEN%", tokenKey));
 
                 try {
-                    TokenService.TokenSender.sendEmailToken(pwmApplication, null, macroMachine, emailItemBean,
-                            outputDestTokenData.getEmail(), tokenKey);
+                    TokenService.TokenSender.sendEmailToken(
+                            TokenService.TokenSendInfo.builder()
+                                    .pwmApplication( pwmApplication )
+                                    .userInfo( null )
+                                    .macroMachine( macroMachine )
+                                    .configuredEmailSetting( emailItemBean )
+                                    .emailAddress( outputDestTokenData.getEmail() )
+                                    .tokenKey( tokenKey )
+                                    .sessionLabel( pwmRequest.getSessionLabel() )
+                                    .build()
+                    );
                 } catch (Exception e) {
                     throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN));
                 }

+ 55 - 52
server/src/main/java/password/pwm/svc/token/TokenService.java

@@ -23,12 +23,13 @@
 package password.pwm.svc.token;
 
 import com.novell.ldapchai.exception.ChaiUnavailableException;
+import lombok.Builder;
+import lombok.Value;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
 import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.SessionLabel;
-import password.pwm.bean.SmsItemBean;
 import password.pwm.bean.TokenDestinationItem;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.Configuration;
@@ -61,6 +62,7 @@ import password.pwm.util.db.DatabaseDataStore;
 import password.pwm.util.db.DatabaseTable;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.localdb.LocalDBDataStore;
@@ -613,17 +615,24 @@ public class TokenService implements PwmService {
         return tokenPayload;
     }
 
+    @Value
+    @Builder
+    public static class TokenSendInfo {
+        private PwmApplication pwmApplication;
+        private UserInfo userInfo;
+        private MacroMachine macroMachine;
+        private EmailItemBean configuredEmailSetting;
+        private MessageSendMethod tokenSendMethod;
+        private String emailAddress;
+        private String smsNumber;
+        private String smsMessage;
+        private String tokenKey;
+        private SessionLabel sessionLabel;
+    }
+
     public static class TokenSender {
         public static List<TokenDestinationItem.Type> sendToken(
-                final PwmApplication pwmApplication,
-                final UserInfo userInfo,
-                final MacroMachine macroMachine,
-                final EmailItemBean configuredEmailSetting,
-                final MessageSendMethod tokenSendMethod,
-                final String emailAddress,
-                final String smsNumber,
-                final String smsMessage,
-                final String tokenKey
+                final TokenSendInfo tokenSendInfo
         )
                 throws PwmUnrecoverableException
         {
@@ -631,82 +640,76 @@ public class TokenService implements PwmService {
 
             final List<TokenDestinationItem.Type> sentTypes = new ArrayList<>();
 
-            try {
-                switch (tokenSendMethod) {
-                    case NONE:
-                        // should never read here
-                        LOGGER.error("attempt to send token to destination type 'NONE'");
-                        throw new PwmUnrecoverableException(PwmError.ERROR_UNKNOWN);
-                    case SMSONLY:
-                        // Only try SMS
-                        success = sendSmsToken(pwmApplication, userInfo, macroMachine, smsNumber, smsMessage, tokenKey);
-                        sentTypes.add(TokenDestinationItem.Type.sms);
-                        break;
-                    case EMAILONLY:
-                    default:
-                        // Only try email
-                        success = sendEmailToken(pwmApplication, userInfo, macroMachine, configuredEmailSetting, emailAddress, tokenKey);
-                        sentTypes.add(TokenDestinationItem.Type.email);
-                        break;
-                }
-            } catch (ChaiUnavailableException e) {
-                throw new PwmUnrecoverableException(PwmError.forChaiError(e.getErrorCode()));
+            switch (tokenSendInfo.getTokenSendMethod()) {
+                case NONE:
+                    // should never read here
+                    LOGGER.error("attempt to send token to destination type 'NONE'");
+                    throw new PwmUnrecoverableException(PwmError.ERROR_UNKNOWN);
+                case SMSONLY:
+                    // Only try SMS
+                    success = sendSmsToken(tokenSendInfo);
+                    sentTypes.add(TokenDestinationItem.Type.sms);
+                    break;
+                case EMAILONLY:
+                default:
+                    // Only try email
+                    success = sendEmailToken(tokenSendInfo);
+                    sentTypes.add(TokenDestinationItem.Type.email);
+                    break;
             }
 
             if (!success) {
                 throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_TOKEN_MISSING_CONTACT));
             }
+
+            final PwmApplication pwmApplication = tokenSendInfo.getPwmApplication();
             pwmApplication.getStatisticsManager().incrementValue(Statistic.TOKENS_SENT);
 
             return sentTypes;
         }
 
         public static boolean sendEmailToken(
-                final PwmApplication pwmApplication,
-                final UserInfo userInfo,
-                final MacroMachine macroMachine,
-                final EmailItemBean configuredEmailSetting,
-                final String toAddress,
-                final String tokenKey
+                final TokenSendInfo tokenSendInfo
         )
-                throws PwmUnrecoverableException, ChaiUnavailableException
+                throws PwmUnrecoverableException
         {
-            if (toAddress == null || toAddress.length() < 1) {
+            final String toAddress = tokenSendInfo.getEmailAddress();
+            if ( StringUtil.isEmpty( toAddress ) ) {
                 return false;
             }
 
+            final PwmApplication pwmApplication = tokenSendInfo.getPwmApplication();
             pwmApplication.getIntruderManager().mark(RecordType.TOKEN_DEST, toAddress, null);
 
+            final EmailItemBean configuredEmailSetting = tokenSendInfo.getConfiguredEmailSetting();
             pwmApplication.getEmailQueue().submitEmailImmediate(new EmailItemBean(
                     toAddress,
                     configuredEmailSetting.getFrom(),
                     configuredEmailSetting.getSubject(),
-                    configuredEmailSetting.getBodyPlain().replace("%TOKEN%", tokenKey),
-                    configuredEmailSetting.getBodyHtml().replace("%TOKEN%", tokenKey)
-            ), userInfo, macroMachine);
+                    configuredEmailSetting.getBodyPlain().replace("%TOKEN%", tokenSendInfo.getTokenKey()),
+                    configuredEmailSetting.getBodyHtml().replace("%TOKEN%", tokenSendInfo.getTokenKey())
+            ), tokenSendInfo.getUserInfo(), tokenSendInfo.getMacroMachine());
             LOGGER.debug("token email added to send queue for " + toAddress);
             return true;
         }
 
         public static boolean sendSmsToken(
-                final PwmApplication pwmApplication,
-                final UserInfo userInfo,
-                final MacroMachine macroMachine,
-                final String smsNumber,
-                final String smsMessage,
-                final String tokenKey
+                final TokenSendInfo tokenSendInfo
         )
-                throws PwmUnrecoverableException, ChaiUnavailableException
+                throws PwmUnrecoverableException
         {
-            if (smsNumber == null || smsNumber.length() < 1) {
+            final String smsNumber = tokenSendInfo.getSmsNumber();
+            if ( StringUtil.isEmpty( smsNumber) ) {
                 return false;
             }
 
-            final String modifiedMessage = smsMessage.replaceAll("%TOKEN%", tokenKey);
 
-            pwmApplication.getIntruderManager().mark(RecordType.TOKEN_DEST, smsNumber, null);
+            final String modifiedMessage = tokenSendInfo.getSmsMessage().replaceAll("%TOKEN%", tokenSendInfo.getTokenKey());
+
+            final PwmApplication pwmApplication = tokenSendInfo.getPwmApplication();
+            pwmApplication.getIntruderManager().mark(RecordType.TOKEN_DEST, smsNumber, tokenSendInfo.getSessionLabel());
 
-            pwmApplication.sendSmsUsingQueue(new SmsItemBean(smsNumber, modifiedMessage), macroMachine);
+            pwmApplication.sendSmsUsingQueue(smsNumber, modifiedMessage, tokenSendInfo.getSessionLabel(), tokenSendInfo.getMacroMachine());
             LOGGER.debug("token SMS added to send queue for " + smsNumber);
             return true;
         }

+ 4 - 5
server/src/main/java/password/pwm/util/operations/PasswordUtility.java

@@ -43,15 +43,12 @@ import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.LoginInfoBean;
 import password.pwm.bean.PasswordStatus;
 import password.pwm.bean.SessionLabel;
-import password.pwm.bean.SmsItemBean;
 import password.pwm.bean.UserIdentity;
-import password.pwm.config.option.StrengthMeterType;
-import password.pwm.config.value.data.ActionConfiguration;
 import password.pwm.config.Configuration;
 import password.pwm.config.PwmSetting;
-import password.pwm.config.value.data.UserPermission;
 import password.pwm.config.option.HelpdeskClearResponseMode;
 import password.pwm.config.option.MessageSendMethod;
+import password.pwm.config.option.StrengthMeterType;
 import password.pwm.config.profile.ForgottenPasswordProfile;
 import password.pwm.config.profile.HelpdeskProfile;
 import password.pwm.config.profile.LdapProfile;
@@ -59,6 +56,8 @@ import password.pwm.config.profile.ProfileType;
 import password.pwm.config.profile.ProfileUtility;
 import password.pwm.config.profile.PwmPasswordPolicy;
 import password.pwm.config.profile.PwmPasswordRule;
+import password.pwm.config.value.data.ActionConfiguration;
+import password.pwm.config.value.data.UserPermission;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmDataValidationException;
 import password.pwm.error.PwmError;
@@ -160,7 +159,7 @@ public class PasswordUtility {
 
         message = message.replace("%TOKEN%", newPassword.getStringValue());
 
-        pwmApplication.sendSmsUsingQueue(new SmsItemBean(toNumber, message), macroMachine);
+        pwmApplication.sendSmsUsingQueue(toNumber, message, null, macroMachine);
         LOGGER.debug(String.format("password SMS added to send queue for %s", toNumber));
         return null;
     }

+ 102 - 83
server/src/main/java/password/pwm/util/queue/SmsQueueManager.java

@@ -22,15 +22,9 @@
 
 package password.pwm.util.queue;
 
-import org.apache.http.HttpResponse;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpRequestBase;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.util.EntityUtils;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
+import password.pwm.bean.SessionLabel;
 import password.pwm.bean.SmsItemBean;
 import password.pwm.config.Configuration;
 import password.pwm.config.PwmSetting;
@@ -43,22 +37,25 @@ import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.health.HealthMessage;
 import password.pwm.health.HealthRecord;
 import password.pwm.http.HttpHeader;
+import password.pwm.http.HttpMethod;
 import password.pwm.http.client.PwmHttpClient;
+import password.pwm.http.client.PwmHttpClientConfiguration;
+import password.pwm.http.client.PwmHttpClientRequest;
+import password.pwm.http.client.PwmHttpClientResponse;
 import password.pwm.svc.PwmService;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsManager;
 import password.pwm.util.BasicAuthInfo;
 import password.pwm.util.PasswordData;
-import password.pwm.util.java.TimeDuration;
-import password.pwm.util.localdb.WorkQueueProcessor;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
+import password.pwm.util.java.TimeDuration;
 import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.localdb.LocalDBStoredQueue;
+import password.pwm.util.localdb.WorkQueueProcessor;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.PwmRandom;
 
-import java.io.IOException;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -132,7 +129,7 @@ public class SmsQueueManager implements PwmService {
 
         workQueueProcessor = new WorkQueueProcessor<>(pwmApplication, localDBStoredQueue, settings, new SmsItemProcessor(), this.getClass());
 
-        smsSendEngine = new SmsSendEngine(pwmApplication.getConfig());
+        smsSendEngine = new SmsSendEngine(pwmApplication, pwmApplication.getConfig());
 
         status = STATUS.OPEN;
     }
@@ -142,7 +139,7 @@ public class SmsQueueManager implements PwmService {
         public WorkQueueProcessor.ProcessResult process(final SmsItemBean workItem) {
             try {
                 for (final String msgPart : splitMessage(workItem.getMessage())) {
-                    smsSendEngine.sendSms(workItem.getTo(), msgPart);
+                    smsSendEngine.sendSms(workItem.getTo(), msgPart, workItem.getSessionLabel());
                 }
                 StatisticsManager.incrementStat(pwmApplication, Statistic.SMS_SEND_SUCCESSES);
                 lastError = null;
@@ -186,12 +183,16 @@ public class SmsQueueManager implements PwmService {
         }
     }
 
-    SmsItemBean shortenMessageIfNeeded(final SmsItemBean smsItem) throws PwmUnrecoverableException {
+    SmsItemBean shortenMessageIfNeeded(
+            final SmsItemBean smsItem
+    )
+            throws PwmUnrecoverableException
+    {
         final Boolean shorten = pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.SMS_USE_URL_SHORTENER);
         if (shorten) {
             final String message = smsItem.getMessage();
             final String shortenedMessage = pwmApplication.getUrlShortener().shortenUrlInText(message);
-            return new SmsItemBean(smsItem.getTo(), shortenedMessage);
+            return new SmsItemBean(smsItem.getTo(), shortenedMessage, smsItem.getSessionLabel());
         }
         return smsItem;
     }
@@ -412,38 +413,59 @@ public class SmsQueueManager implements PwmService {
         return returnValue;
     }
 
-
-
     private static class SmsSendEngine {
         private static final PwmLogger LOGGER = PwmLogger.forClass(SmsSendEngine.class);
+        private final PwmApplication pwmApplication;
         private final Configuration config;
         private String lastResponseBody;
 
-        private SmsSendEngine(final Configuration configuration)
+        private SmsSendEngine( final PwmApplication pwmApplication, final Configuration configuration)
         {
+            this.pwmApplication = pwmApplication;
             this.config = configuration;
         }
 
-
-        /**
-         *
-         * @param to
-         * @param message
-         * @throws PwmUnrecoverableException - If operation failed and a retry is unlikely to succeed
-         * @throws PwmOperationalException - If operation failed and should be retried.
-         */
-        protected void sendSms(final String to, final String message)
+        protected void sendSms(final String to, final String message, final SessionLabel sessionLabel)
                 throws PwmUnrecoverableException, PwmOperationalException
         {
             lastResponseBody = null;
 
-            final String gatewayUser = config.readSettingAsString(PwmSetting.SMS_GATEWAY_USER);
-            final PasswordData gatewayPass = config.readSettingAsPassword(PwmSetting.SMS_GATEWAY_PASSWORD);
+            final String requestData = makeRequestData( to, message );
 
-            final String contentType = config.readSettingAsString(PwmSetting.SMS_REQUEST_CONTENT_TYPE);
-            final SmsDataEncoding encoding = SmsDataEncoding.valueOf(config.readSettingAsString(PwmSetting.SMS_REQUEST_CONTENT_ENCODING));
+            LOGGER.trace("preparing to send SMS data: " + requestData);
 
-            final List<String> extraHeaders = config.readSettingAsStringArray(PwmSetting.SMS_GATEWAY_REQUEST_HEADERS);
+            final PwmHttpClientRequest pwmHttpClientRequest = makeRequest( requestData );
+
+            final PwmHttpClientConfiguration pwmHttpClientConfiguration = PwmHttpClientConfiguration.builder()
+                    .certificates( config.readSettingAsCertificate( PwmSetting.SMS_GATEWAY_CERTIFICATES ) )
+                    .build();
+
+            final PwmHttpClient pwmHttpClient = new PwmHttpClient( pwmApplication, sessionLabel, pwmHttpClientConfiguration );
+
+            try {
+                final PwmHttpClientResponse pwmHttpClientResponse = pwmHttpClient.makeRequest( pwmHttpClientRequest );
+                final int resultCode = pwmHttpClientResponse.getStatusCode();
+
+                final String responseBody = pwmHttpClientResponse.getBody();
+                lastResponseBody = responseBody;
+
+                determineIfResultSuccessful(config, resultCode, responseBody);
+                LOGGER.debug("SMS send successful, HTTP status: " + resultCode);
+            } catch (PwmUnrecoverableException e) {
+                final ErrorInformation errorInformation = new ErrorInformation(
+                        PwmError.ERROR_SMS_SEND_ERROR,
+                        "error while sending SMS, discarding message: " + e.getMessage());
+                throw new PwmUnrecoverableException(errorInformation);
+            }
+        }
+
+        private String makeRequestData(
+                final String to,
+                final String message
+        ) {
+            final String gatewayUser = config.readSettingAsString(PwmSetting.SMS_GATEWAY_USER);
+            final PasswordData gatewayPass = config.readSettingAsPassword(PwmSetting.SMS_GATEWAY_PASSWORD);
+            final SmsDataEncoding encoding = config.readSettingAsEnum(PwmSetting.SMS_REQUEST_CONTENT_ENCODING, SmsDataEncoding.class);
 
             String requestData = config.readSettingAsString(PwmSetting.SMS_REQUEST_DATA);
 
@@ -470,69 +492,64 @@ public class SmsQueueManager implements PwmService {
                 requestData = requestData.replaceAll(TOKEN_REQUESTID, smsDataEncode(requestId, encoding));
             }
 
+            return requestData;
+        }
+
+        private PwmHttpClientRequest makeRequest(
+                final String requestData
+        )
+                throws PwmUnrecoverableException
+        {
+            final String gatewayUser = config.readSettingAsString(PwmSetting.SMS_GATEWAY_USER);
+            final PasswordData gatewayPass = config.readSettingAsPassword(PwmSetting.SMS_GATEWAY_PASSWORD);
+            final String contentType = config.readSettingAsString(PwmSetting.SMS_REQUEST_CONTENT_TYPE);
+            final List<String> extraHeaders = config.readSettingAsStringArray(PwmSetting.SMS_GATEWAY_REQUEST_HEADERS);
             final String gatewayUrl = config.readSettingAsString(PwmSetting.SMS_GATEWAY_URL);
             final String gatewayMethod = config.readSettingAsString(PwmSetting.SMS_GATEWAY_METHOD);
             final String gatewayAuthMethod = config.readSettingAsString(PwmSetting.SMS_GATEWAY_AUTHMETHOD);
 
-            LOGGER.trace("preparing to send SMS data: " + requestData);
-            try {
-                final HttpRequestBase httpRequest;
-                if ("POST".equalsIgnoreCase(gatewayMethod)) {
-                    // POST request
-                    httpRequest = new HttpPost(gatewayUrl);
-                    if (contentType != null && contentType.length()>0) {
-                        httpRequest.setHeader("Content-Type", contentType);
-                    }
-                    ((HttpPost) httpRequest).setEntity(new StringEntity(requestData));
-                } else {
-                    // GET request
-                    final String fullUrl = gatewayUrl.endsWith("?") ? gatewayUrl + requestData : gatewayUrl + "?" + requestData;
-                    httpRequest = new HttpGet(fullUrl);
+            final HttpMethod httpMethod = "POST".equalsIgnoreCase(gatewayMethod)
+                    ? HttpMethod.POST
+                    : HttpMethod.GET;
+
+            final Map<String,String> headers = new LinkedHashMap<>(  );
+            {
+                if ( "HTTP".equalsIgnoreCase( gatewayAuthMethod ) && gatewayUser != null && gatewayPass != null ) {
+                    final BasicAuthInfo basicAuthInfo = new BasicAuthInfo( gatewayUser, gatewayPass );
+                    headers.put( HttpHeader.Authorization.getHttpName(), basicAuthInfo.toAuthHeader() );
                 }
 
-                if (extraHeaders != null) {
-                    final Pattern pattern = Pattern.compile("^([A-Za-z0-9_\\.-]+):[ \t]*([^ \t].*)");
-                    for (final String header : extraHeaders) {
-                        final Matcher matcher = pattern.matcher(header);
-                        if (matcher.matches()) {
-                            final String hname = matcher.group(1);
-                            final String hvalue = matcher.group(2);
-                            LOGGER.debug("Adding HTTP header \"" + hname + "\" with value \"" + hvalue + "\"");
-                            httpRequest.addHeader(hname, hvalue);
+
+                if ( !StringUtil.isEmpty( contentType ) && httpMethod == HttpMethod.POST ) {
+                    headers.put( HttpHeader.Content_Type.getHttpName(), contentType );
+                }
+
+                if ( extraHeaders != null ) {
+                    final Pattern pattern = Pattern.compile( "^([A-Za-z0-9_\\.-]+):[ \t]*([^ \t].*)" );
+                    for ( final String header : extraHeaders )
+                    {
+                        final Matcher matcher = pattern.matcher( header );
+                        if ( matcher.matches() ) {
+                            final String headerName = matcher.group( 1 );
+                            final String headerValue = matcher.group( 2 );
+                            headers.put( headerName, headerValue );
                         } else {
-                            LOGGER.warn("Cannot parse HTTP header: " + header);
+                            LOGGER.warn( "Cannot parse HTTP header: " + header );
                         }
                     }
                 }
 
-                if ("HTTP".equalsIgnoreCase(gatewayAuthMethod) && gatewayUser != null && gatewayPass != null) {
-                    LOGGER.debug("Using Basic Authentication");
-                    final BasicAuthInfo ba = new BasicAuthInfo(gatewayUser, gatewayPass);
-                    httpRequest.addHeader(HttpHeader.Authorization.getHttpName(), ba.toAuthHeader());
-                }
+            }
 
-                final HttpClient httpClient = PwmHttpClient.getHttpClient(config);
-                final HttpResponse httpResponse = httpClient.execute(httpRequest);
-                final String responseBody = EntityUtils.toString(httpResponse.getEntity());
-                final int resultCode = httpResponse.getStatusLine().getStatusCode();
-                lastResponseBody = httpResponse.getStatusLine() + "\n" + responseBody;
-                LOGGER.trace("sms send result body: " + httpResponse.getStatusLine().toString() + "\n" + responseBody);
+            final String fullUrl = httpMethod == HttpMethod.POST
+                    ? gatewayUrl
+                    : gatewayUrl.endsWith("?") ? gatewayUrl + requestData : gatewayUrl + "?" + requestData;
 
-                determineIfResultSuccessful(config, resultCode, responseBody);
-                LOGGER.debug("SMS send successful, HTTP status: " + httpResponse.getStatusLine().getStatusCode());
-            } catch (IOException e) {
-                final ErrorInformation errorInformation = new ErrorInformation(
-                        PwmError.ERROR_SMS_SEND_ERROR,
-                        "IO error while sending SMS: " + e.getMessage());
-                throw new PwmOperationalException(errorInformation);
-            } catch (PwmOperationalException e) {
-                throw e;
-            } catch (Exception e) {
-                final ErrorInformation errorInformation = new ErrorInformation(
-                        PwmError.ERROR_SMS_SEND_ERROR,
-                        "unexpected error while sending SMS, discarding message: " + e.getMessage());
-                throw new PwmUnrecoverableException(errorInformation);
-            }
+            final String body = httpMethod == HttpMethod.POST
+                    ? requestData
+                    : null;
+
+            return new PwmHttpClientRequest( httpMethod, fullUrl, body, headers );
         }
 
         public String getLastResponseBody()
@@ -542,14 +559,16 @@ public class SmsQueueManager implements PwmService {
     }
 
     public static String sendDirectMessage(
+            final PwmApplication pwmApplication,
             final Configuration configuration,
+            final SessionLabel sessionLabel,
             final SmsItemBean smsItemBean
 
     )
             throws PwmUnrecoverableException, PwmOperationalException
     {
-        final SmsSendEngine smsSendEngine = new SmsSendEngine(configuration);
-        smsSendEngine.sendSms(smsItemBean.getTo(), smsItemBean.getMessage());
+        final SmsSendEngine smsSendEngine = new SmsSendEngine(pwmApplication, configuration);
+        smsSendEngine.sendSms(smsItemBean.getTo(), smsItemBean.getMessage(), sessionLabel);
         return smsSendEngine.getLastResponseBody();
     }
 

+ 9 - 3
server/src/main/resources/password/pwm/config/PwmSetting.xml

@@ -867,6 +867,12 @@
             <value />
         </default>
     </setting>
+    <setting hidden="false" key="sms.gatewayCertificates" level="1">
+        <default/>
+        <properties>
+            <property key="Cert_ImportHandler">password.pwm.config.function.SMSGatewayCertImportFunction</property>
+        </properties>
+    </setting>
     <setting hidden="false" key="sms.gatewayPassword" level="1">
     </setting>
     <setting hidden="false" key="sms.gatewayMethod" level="1" required="true">
@@ -1004,7 +1010,7 @@
             <value><![CDATA[Thank you for activating your account.]]></value>
         </default>
     </setting>
-    <setting hidden="false" key="sms.useUrlShortener" level="2">
+    <setting hidden="true" key="sms.useUrlShortener" level="2">
         <default>
             <value>false</value>
         </default>
@@ -3635,12 +3641,12 @@
             <value />
         </default>
     </setting>
-    <setting hidden="false" key="urlshortener.classname" level="2">
+    <setting hidden="true" key="urlshortener.classname" level="2">
         <default>
             <value />
         </default>
     </setting>
-    <setting hidden="false" key="urlshortener.parameters" level="2">
+    <setting hidden="true" key="urlshortener.parameters" level="2">
         <regex>^[a-zA-Z0-9.]+=.+$</regex>
         <default>
             <value />

+ 2 - 0
server/src/main/resources/password/pwm/i18n/PwmSetting.properties

@@ -638,6 +638,7 @@ Setting_Description_sms.challenge.token.message=Specify the message text of the
 Setting_Description_sms.defaultCountryCode=Specify the default country code for the SMS phone number. For a list of country codes, see <a href\="http\://countrycode.org/" target\="_blank">http\://countrycode.org/</a>. Set to 0 to disable this option.
 Setting_Description_sms.forgottenUsername.message=Specify the text of the SMS @PwmAppName@ sends upon a successful forgotten user name sequence, if you configured it.
 Setting_Description_sms.gatewayAuthMethod=Select the method @PwmAppName@ uses for authentication to the SMS gateway.
+Setting_Description_sms.gatewayCertificates=Certificate for remote SMS service
 Setting_Description_sms.gatewayMethod=Select the HTTPS protocol method @PwmAppName@ uses for sending the SMS messages.
 Setting_Description_sms.gatewayPassword=Specify the user password for the SMS gateway.
 Setting_Description_sms.gatewayURL=Specify the URL for the SMS gateway.
@@ -1119,6 +1120,7 @@ Setting_Label_sms.challenge.token.message=Forgotten Password SMS Text
 Setting_Label_sms.defaultCountryCode=Default SMS Country Code
 Setting_Label_sms.forgottenUsername.message=Forgotten User Name SMS Text
 Setting_Label_sms.gatewayAuthMethod=SMS Gateway Authentication Method
+Setting_Label_sms.gatewayCertificates=SMS Gateway Certificates
 Setting_Label_sms.gatewayMethod=HTTP(S) Method
 Setting_Label_sms.gatewayPassword=SMS Gateway Password
 Setting_Label_sms.gatewayURL=SMS Gateway