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}