001/**
002 *
003 * Copyright 2014-2018 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 org.jivesoftware.smack.packet.AbstractError;
022import org.jivesoftware.smack.packet.Nonza;
023import org.jivesoftware.smack.sasl.SASLError;
024import org.jivesoftware.smack.util.Objects;
025import org.jivesoftware.smack.util.StringUtils;
026import org.jivesoftware.smack.util.XmlStringBuilder;
027
028public class SaslStreamElements {
029    public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-sasl";
030
031    /**
032     * Initiating SASL authentication by select a mechanism.
033     */
034    public static class AuthMechanism implements Nonza {
035        public static final String ELEMENT = "auth";
036
037        private final String mechanism;
038        private final String authenticationText;
039
040        public AuthMechanism(String mechanism, String authenticationText) {
041            this.mechanism = Objects.requireNonNull(mechanism, "SASL mechanism shouldn't be null.");
042            this.authenticationText = StringUtils.requireNotNullOrEmpty(authenticationText,
043                            "SASL authenticationText must not be null or empty (RFC6120 6.4.2)");
044        }
045
046        @Override
047        public XmlStringBuilder toXML(String enclosingNamespace) {
048            XmlStringBuilder xml = new XmlStringBuilder();
049            xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).attribute("mechanism", mechanism).rightAngleBracket();
050            xml.optAppend(authenticationText);
051            xml.closeElement(ELEMENT);
052            return xml;
053        }
054
055        public String getMechanism() {
056            return mechanism;
057        }
058
059        public String getAuthenticationText() {
060            return authenticationText;
061        }
062
063        @Override
064        public String getNamespace() {
065            return NAMESPACE;
066        }
067
068        @Override
069        public String getElementName() {
070            return ELEMENT;
071        }
072    }
073
074    /**
075     * A SASL challenge stream element.
076     */
077    public static class Challenge implements Nonza {
078        public static final String ELEMENT = "challenge";
079
080        private final String data;
081
082        public Challenge(String data) {
083            this.data = StringUtils.returnIfNotEmptyTrimmed(data);
084        }
085
086        @Override
087        public XmlStringBuilder toXML(String enclosingNamespace) {
088            XmlStringBuilder xml = new XmlStringBuilder().halfOpenElement(ELEMENT).xmlnsAttribute(
089                            NAMESPACE).rightAngleBracket();
090            xml.optAppend(data);
091            xml.closeElement(ELEMENT);
092            return xml;
093        }
094
095        @Override
096        public String getNamespace() {
097            return NAMESPACE;
098        }
099
100        @Override
101        public String getElementName() {
102            return ELEMENT;
103        }
104    }
105
106    /**
107     * A SASL response stream element.
108     */
109    public static class Response implements Nonza {
110        public static final String ELEMENT = "response";
111
112        private final String authenticationText;
113
114        public Response() {
115            authenticationText = null;
116        }
117
118        public Response(String authenticationText) {
119            this.authenticationText = StringUtils.returnIfNotEmptyTrimmed(authenticationText);
120        }
121
122        @Override
123        public XmlStringBuilder toXML(String enclosingNamespace) {
124            XmlStringBuilder xml = new XmlStringBuilder();
125            xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket();
126            xml.optAppend(authenticationText);
127            xml.closeElement(ELEMENT);
128            return xml;
129        }
130
131        public String getAuthenticationText() {
132            return authenticationText;
133        }
134
135        @Override
136        public String getNamespace() {
137            return NAMESPACE;
138        }
139
140        @Override
141        public String getElementName() {
142            return ELEMENT;
143        }
144    }
145
146    /**
147     * A SASL success stream element.
148     */
149    public static class Success implements Nonza {
150        public static final String ELEMENT = "success";
151
152        private final String data;
153
154        /**
155         * Construct a new SASL success stream element with optional additional data for the SASL layer.
156         * (RFC6120 6.3.10)
157         *
158         * @param data additional data for the SASL layer or <code>null</code>
159         */
160        public Success(String data) {
161            this.data = StringUtils.returnIfNotEmptyTrimmed(data);
162        }
163
164        /**
165         * Returns additional data for the SASL layer or <code>null</code>.
166         *
167         * @return additional data or <code>null</code>
168         */
169        public String getData() {
170            return data;
171        }
172
173        @Override
174        public XmlStringBuilder toXML(String enclosingNamespace) {
175            XmlStringBuilder xml = new XmlStringBuilder();
176            xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket();
177            xml.optAppend(data);
178            xml.closeElement(ELEMENT);
179            return xml;
180        }
181
182        @Override
183        public String getNamespace() {
184            return NAMESPACE;
185        }
186
187        @Override
188        public String getElementName() {
189            return ELEMENT;
190        }
191    }
192
193    /**
194     * A SASL failure stream element, also called "SASL Error".
195     * @see <a href="http://xmpp.org/rfcs/rfc6120.html#sasl-errors">RFC 6120 6.5 SASL Errors</a>
196     */
197    public static class SASLFailure extends AbstractError implements Nonza {
198        public static final String ELEMENT = "failure";
199
200        private final SASLError saslError;
201        private final String saslErrorString;
202
203        public SASLFailure(String saslError) {
204            this(saslError, null);
205        }
206
207        public SASLFailure(String saslError, Map<String, String> descriptiveTexts) {
208            super(descriptiveTexts);
209            SASLError error = SASLError.fromString(saslError);
210            if (error == null) {
211                // RFC6120 6.5 states that unknown condition must be treat as generic authentication
212                // failure.
213                this.saslError = SASLError.not_authorized;
214            }
215            else {
216                this.saslError = error;
217            }
218            this.saslErrorString = saslError;
219        }
220
221        /**
222         * Get the SASL related error condition.
223         *
224         * @return the SASL related error condition.
225         */
226        public SASLError getSASLError() {
227            return saslError;
228        }
229
230        /**
231         * Get the SASL error as String.
232         * @return the SASL error as String
233         */
234        public String getSASLErrorString() {
235            return saslErrorString;
236        }
237
238        @Override
239        public XmlStringBuilder toXML(String enclosingNamespace) {
240            XmlStringBuilder xml = new XmlStringBuilder();
241            xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket();
242            xml.emptyElement(saslErrorString);
243            addDescriptiveTextsAndExtensions(xml);
244            xml.closeElement(ELEMENT);
245            return xml;
246        }
247
248        @Override
249        public String toString() {
250            return toXML(null).toString();
251        }
252
253        @Override
254        public String getNamespace() {
255            return NAMESPACE;
256        }
257
258        @Override
259        public String getElementName() {
260            return ELEMENT;
261        }
262    }
263}