KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jivesoftware > messenger > handler > IQAuthHandler


1 /**
2  * $RCSfile: IQAuthHandler.java,v $
3  * $Revision: 1.29 $
4  * $Date: 2005/04/29 21:07:55 $
5  *
6  * Copyright (C) 2004 Jive Software. All rights reserved.
7  *
8  * This software is published under the terms of the GNU Public License (GPL),
9  * a copy of which is included in this distribution.
10  */

11
12 package org.jivesoftware.messenger.handler;
13
14 import org.dom4j.DocumentHelper;
15 import org.dom4j.Element;
16 import org.dom4j.QName;
17 import org.jivesoftware.messenger.*;
18 import org.jivesoftware.messenger.auth.AuthFactory;
19 import org.jivesoftware.messenger.auth.AuthToken;
20 import org.jivesoftware.messenger.auth.UnauthorizedException;
21 import org.jivesoftware.messenger.user.UserManager;
22 import org.jivesoftware.messenger.user.UserNotFoundException;
23 import org.jivesoftware.util.LocaleUtils;
24 import org.jivesoftware.util.Log;
25 import org.jivesoftware.util.JiveGlobals;
26 import org.jivesoftware.stringprep.Stringprep;
27 import org.jivesoftware.stringprep.StringprepException;
28 import org.xmpp.packet.IQ;
29 import org.xmpp.packet.JID;
30 import org.xmpp.packet.PacketError;
31 import org.xmpp.packet.StreamError;
32
33 import java.util.ArrayList JavaDoc;
34 import java.util.List JavaDoc;
35
36 /**
37  * Implements the TYPE_IQ jabber:iq:auth protocol (plain only). Clients
38  * use this protocol to authenticate with the server. A 'get' query
39  * runs an authentication probe with a given user name. Return the
40  * authentication form or an error indicating the user is not
41  * registered on the server.<p>
42  *
43  * A 'set' query authenticates with information given in the
44  * authentication form. An authenticated session may reset their
45  * authentication information using a 'set' query.
46  *
47  * <h2>Assumptions</h2>
48  * This handler assumes that the request is addressed to the server.
49  * An appropriate TYPE_IQ tag matcher should be placed in front of this
50  * one to route TYPE_IQ requests not addressed to the server to
51  * another channel (probably for direct delivery to the recipient).
52  *
53  * @author Iain Shigeoka
54  */

55 public class IQAuthHandler extends IQHandler implements IQAuthInfo {
56
57     private static boolean anonymousAllowed;
58
59     private Element probeResponse;
60     private IQHandlerInfo info;
61
62     private UserManager userManager;
63     private XMPPServer localServer;
64     private SessionManager sessionManager;
65
66     /**
67      * Clients are not authenticated when accessing this handler.
68      */

69     public IQAuthHandler() {
70         super("XMPP Authentication handler");
71         info = new IQHandlerInfo("query", "jabber:iq:auth");
72
73         probeResponse = DocumentHelper.createElement(QName.get("query", "jabber:iq:auth"));
74         probeResponse.addElement("username");
75         if (AuthFactory.isPlainSupported()) {
76             probeResponse.addElement("password");
77         }
78         if (AuthFactory.isDigestSupported()) {
79             probeResponse.addElement("digest");
80         }
81         probeResponse.addElement("resource");
82         anonymousAllowed = "true".equals(JiveGlobals.getProperty("xmpp.auth.anonymous"));
83     }
84
85     public IQ handleIQ(IQ packet) throws UnauthorizedException, PacketException {
86         try {
87             ClientSession session = sessionManager.getSession(packet.getFrom());
88             // If no session was found then answer an error (if possible)
89
if (session == null) {
90                 Log.error("Error during authentication. Session not found in " +
91                         sessionManager.getPreAuthenticatedKeys() +
92                         " for key " +
93                         packet.getFrom());
94                 // This error packet will probably won't make it through
95
IQ reply = IQ.createResultIQ(packet);
96                 reply.setChildElement(packet.getChildElement().createCopy());
97                 reply.setError(PacketError.Condition.internal_server_error);
98                 return reply;
99             }
100             IQ response = null;
101             try {
102                 Element iq = packet.getElement();
103                 Element query = iq.element("query");
104                 Element queryResponse = probeResponse.createCopy();
105                 if (IQ.Type.get == packet.getType()) {
106                     String JavaDoc username = query.elementTextTrim("username");
107                     if (username != null) {
108                         queryResponse.element("username").setText(username);
109                     }
110                     response = IQ.createResultIQ(packet);
111                     response.setChildElement(queryResponse);
112                     // This is a workaround. Since we don't want to have an incorrect TO attribute
113
// value we need to clean up the TO attribute and send directly the response.
114
// The TO attribute will contain an incorrect value since we are setting a fake
115
// JID until the user actually authenticates with the server.
116
if (session.getStatus() != Session.STATUS_AUTHENTICATED) {
117                         response.setTo((JID)null);
118                     }
119                 }
120                 // Otherwise set query
121
else {
122                     if (query.elements().isEmpty()) {
123                         // Anonymous authentication
124
response = anonymousLogin(session, packet);
125                     }
126                     else {
127                         String JavaDoc username = query.elementTextTrim("username");
128                         // Login authentication
129
String JavaDoc password = query.elementTextTrim("password");
130                         String JavaDoc digest = null;
131                         if (query.element("digest") != null) {
132                             digest = query.elementTextTrim("digest").toLowerCase();
133                         }
134
135                         // If we're already logged in, this is a password reset
136
if (session.getStatus() == Session.STATUS_AUTHENTICATED) {
137                             response = passwordReset(password, packet, username, session);
138                         }
139                         else {
140                             // it is an auth attempt
141
response =
142                                     login(username, query, packet, response, password, session,
143                                             digest);
144                         }
145                     }
146                 }
147             }
148             catch (UserNotFoundException e) {
149                 response = IQ.createResultIQ(packet);
150                 response.setChildElement(packet.getChildElement().createCopy());
151                 response.setError(PacketError.Condition.not_authorized);
152             }
153             catch (UnauthorizedException e) {
154                 response = IQ.createResultIQ(packet);
155                 response.setChildElement(packet.getChildElement().createCopy());
156                 response.setError(PacketError.Condition.not_authorized);
157             }
158             // Send the response directly since we want to be sure that we are sending it back
159
// to the correct session. Any other session of the same user but with different
160
// resource is incorrect.
161
session.process(response);
162         }
163         catch (Exception JavaDoc e) {
164             Log.error("Error handling authentication IQ packet", e);
165         }
166         return null;
167     }
168
169     private IQ login(String JavaDoc username, Element iq, IQ packet, IQ response, String JavaDoc password,
170             ClientSession session, String JavaDoc digest) throws UnauthorizedException,
171             UserNotFoundException
172     {
173         String JavaDoc resource = iq.elementTextTrim("resource");
174         if (resource != null) {
175             try {
176                 resource = Stringprep.resourceprep(resource);
177             }
178             catch (StringprepException e) {
179                 throw new IllegalArgumentException JavaDoc("Invalid resource: " + resource);
180             }
181         }
182         else {
183             // Answer a not_acceptable error since a resource was not supplied
184
response = IQ.createResultIQ(packet);
185             response.setChildElement(packet.getChildElement().createCopy());
186             response.setError(PacketError.Condition.not_acceptable);
187         }
188
189         // If a session already exists with the requested JID, then check to see
190
// if we should kick it off or refuse the new connection
191
if (sessionManager.isActiveRoute(username, resource)) {
192             ClientSession oldSession = null;
193             try {
194                 String JavaDoc domain = localServer.getServerInfo().getName();
195                 oldSession = sessionManager.getSession(username, domain, resource);
196                 oldSession.incrementConflictCount();
197                 int conflictLimit = sessionManager.getConflictKickLimit();
198                 if (conflictLimit != SessionManager.NEVER_KICK && oldSession.getConflictCount() > conflictLimit) {
199                     Connection conn = oldSession.getConnection();
200                     if (conn != null) {
201                         // Send a stream:error before closing the connection
202
StreamError error = new StreamError(StreamError.Condition.conflict);
203                         conn.getWriter().write(error.toXML());
204                         conn.close();
205                     }
206                 }
207                 else {
208                     response = IQ.createResultIQ(packet);
209                     response.setChildElement(packet.getChildElement().createCopy());
210                     response.setError(PacketError.Condition.forbidden);
211                 }
212             }
213             catch (Exception JavaDoc e) {
214                 Log.error("Error during login", e);
215             }
216         }
217         // If the connection was not refused due to conflict, log the user in
218
if (response == null) {
219             AuthToken token = null;
220             if (password != null && AuthFactory.isPlainSupported()) {
221                 token = AuthFactory.authenticate(username, password);
222             }
223             else if (digest != null && AuthFactory.isDigestSupported()) {
224                 token = AuthFactory.authenticate(username, session.getStreamID().toString(),
225                         digest);
226             }
227             if (token == null) {
228                 throw new UnauthorizedException();
229             }
230             else {
231                 session.setAuthToken(token, userManager, resource);
232                 packet.setFrom(session.getAddress());
233                 response = IQ.createResultIQ(packet);
234             }
235         }
236         return response;
237     }
238
239     private IQ passwordReset(String JavaDoc password, IQ packet, String JavaDoc username, Session session)
240             throws UnauthorizedException
241     {
242         IQ response;
243         if (password == null || password.length() == 0) {
244             throw new UnauthorizedException();
245         }
246         else {
247             try {
248                 userManager.getUser(username).setPassword(password);
249                 response = IQ.createResultIQ(packet);
250                 List JavaDoc params = new ArrayList JavaDoc();
251                 params.add(username);
252                 params.add(session.toString());
253                 Log.info(LocaleUtils.getLocalizedString("admin.password.update", params));
254             }
255             catch (UserNotFoundException e) {
256                 throw new UnauthorizedException();
257             }
258         }
259         return response;
260     }
261
262     private IQ anonymousLogin(ClientSession session, IQ packet) {
263         IQ response = IQ.createResultIQ(packet);;
264         if (anonymousAllowed) {
265             session.setAnonymousAuth();
266             Element auth = response.setChildElement("query", "jabber:iq:auth");
267             auth.addElement("resource").setText(session.getAddress().getResource());
268         }
269         else {
270             response.setChildElement(packet.getChildElement().createCopy());
271             response.setError(PacketError.Condition.forbidden);
272         }
273         return response;
274     }
275
276     public boolean isAllowAnonymous() {
277         return anonymousAllowed;
278     }
279
280     public void setAllowAnonymous(boolean isAnonymous) throws UnauthorizedException {
281         anonymousAllowed = isAnonymous;
282         JiveGlobals.setProperty("xmpp.auth.anonymous", anonymousAllowed ? "true" : "false");
283     }
284
285     public void initialize(XMPPServer server) {
286         super.initialize(server);
287         localServer = server;
288         userManager = server.getUserManager();
289         sessionManager = server.getSessionManager();
290     }
291
292     public IQHandlerInfo getInfo() {
293         return info;
294     }
295 }
Popular Tags