001/** 002 * 003 * Copyright 2014-2019 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smack.sasl.packet; 018 019import java.util.Map; 020 021import javax.xml.namespace.QName; 022 023import org.jivesoftware.smack.packet.AbstractError; 024import org.jivesoftware.smack.packet.Nonza; 025import org.jivesoftware.smack.packet.XmlEnvironment; 026import org.jivesoftware.smack.sasl.SASLError; 027import org.jivesoftware.smack.util.Objects; 028import org.jivesoftware.smack.util.StringUtils; 029import org.jivesoftware.smack.util.XmlStringBuilder; 030 031public interface SaslNonza extends Nonza { 032 String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-sasl"; 033 034 @Override 035 default String getNamespace() { 036 return NAMESPACE; 037 } 038 039 /** 040 * Initiating SASL authentication by select a mechanism. 041 */ 042 class AuthMechanism implements SaslNonza { 043 public static final String ELEMENT = "auth"; 044 public static final QName QNAME = new QName(NAMESPACE, ELEMENT); 045 046 private final String mechanism; 047 private final String authenticationText; 048 049 public AuthMechanism(String mechanism, String authenticationText) { 050 this.mechanism = Objects.requireNonNull(mechanism, "SASL mechanism shouldn't be null."); 051 this.authenticationText = StringUtils.requireNotNullNorEmpty(authenticationText, 052 "SASL authenticationText must not be null nor empty (RFC6120 6.4.2)"); 053 } 054 055 @Override 056 public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { 057 XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment); 058 xml.attribute("mechanism", mechanism).rightAngleBracket(); 059 xml.escape(authenticationText); 060 xml.closeElement(this); 061 return xml; 062 } 063 064 public String getMechanism() { 065 return mechanism; 066 } 067 068 public String getAuthenticationText() { 069 return authenticationText; 070 } 071 072 @Override 073 public String getElementName() { 074 return ELEMENT; 075 } 076 } 077 078 /** 079 * A SASL challenge stream element. 080 */ 081 class Challenge implements SaslNonza { 082 public static final String ELEMENT = "challenge"; 083 public static final QName QNAME = new QName(NAMESPACE, ELEMENT); 084 085 private final String data; 086 087 public Challenge(String data) { 088 this.data = StringUtils.returnIfNotEmptyTrimmed(data); 089 } 090 091 public String getData() { 092 return data; 093 } 094 095 @Override 096 public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { 097 XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment); 098 xml.optTextChild(data, this); 099 return xml; 100 } 101 102 @Override 103 public String getElementName() { 104 return ELEMENT; 105 } 106 } 107 108 /** 109 * A SASL response stream element. 110 */ 111 class Response implements SaslNonza { 112 public static final String ELEMENT = "response"; 113 public static final QName QNAME = new QName(NAMESPACE, ELEMENT); 114 115 private final String authenticationText; 116 117 public Response() { 118 authenticationText = null; 119 } 120 121 public Response(String authenticationText) { 122 this.authenticationText = StringUtils.returnIfNotEmptyTrimmed(authenticationText); 123 } 124 125 @Override 126 public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { 127 XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment); 128 xml.optTextChild(authenticationText, this); 129 return xml; 130 } 131 132 public String getAuthenticationText() { 133 return authenticationText; 134 } 135 136 @Override 137 public String getElementName() { 138 return ELEMENT; 139 } 140 } 141 142 /** 143 * A SASL success stream element. 144 */ 145 class Success implements SaslNonza { 146 public static final String ELEMENT = "success"; 147 public static final QName QNAME = new QName(NAMESPACE, ELEMENT); 148 149 private final String data; 150 151 /** 152 * Construct a new SASL success stream element with optional additional data for the SASL layer. 153 * (RFC6120 6.3.10) 154 * 155 * @param data additional data for the SASL layer or <code>null</code> 156 */ 157 public Success(String data) { 158 this.data = StringUtils.returnIfNotEmptyTrimmed(data); 159 } 160 161 /** 162 * Returns additional data for the SASL layer or <code>null</code>. 163 * 164 * @return additional data or <code>null</code> 165 */ 166 public String getData() { 167 return data; 168 } 169 170 @Override 171 public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { 172 XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment); 173 xml.optTextChild(data, this); 174 return xml; 175 } 176 177 @Override 178 public String getElementName() { 179 return ELEMENT; 180 } 181 } 182 183 /** 184 * A SASL failure stream element, also called "SASL Error". 185 * @see <a href="http://xmpp.org/rfcs/rfc6120.html#sasl-errors">RFC 6120 6.5 SASL Errors</a> 186 */ 187 class SASLFailure extends AbstractError implements SaslNonza { 188 public static final String ELEMENT = "failure"; 189 public static final QName QNAME = new QName(NAMESPACE, ELEMENT); 190 191 private final SASLError saslError; 192 private final String saslErrorString; 193 194 public SASLFailure(String saslError) { 195 this(saslError, null); 196 } 197 198 public SASLFailure(String saslError, Map<String, String> descriptiveTexts) { 199 super(descriptiveTexts); 200 SASLError error = SASLError.fromString(saslError); 201 if (error == null) { 202 // RFC6120 6.5 states that unknown condition must be treat as generic authentication 203 // failure. 204 this.saslError = SASLError.not_authorized; 205 } 206 else { 207 this.saslError = error; 208 } 209 this.saslErrorString = saslError; 210 } 211 212 /** 213 * Get the SASL related error condition. 214 * 215 * @return the SASL related error condition. 216 */ 217 public SASLError getSASLError() { 218 return saslError; 219 } 220 221 /** 222 * Get the SASL error as String. 223 * @return the SASL error as String 224 */ 225 public String getSASLErrorString() { 226 return saslErrorString; 227 } 228 229 @Override 230 public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { 231 XmlStringBuilder xml = new XmlStringBuilder(); 232 xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket(); 233 xml.emptyElement(saslErrorString); 234 addDescriptiveTextsAndExtensions(xml); 235 xml.closeElement(ELEMENT); 236 return xml; 237 } 238 239 @Override 240 public String toString() { 241 return toXML().toString(); 242 } 243 244 @Override 245 public String getElementName() { 246 return ELEMENT; 247 } 248 } 249}