/**
* $RCSfile$
* $Revision: 2654 $
* $Date: 2005-08-14 14:40:32 -0300 (Sun, 14 Aug 2005) $
*
* Copyright (C) 2004-2008 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.openfire.auth;
import java.util.Properties;
import javax.mail.NoSuchProviderException;
import javax.mail.Session;
import javax.mail.Store;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.user.UserAlreadyExistsException;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.StringUtils;
import org.jivesoftware.util.cache.Cache;
import org.jivesoftware.util.cache.CacheFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An AuthProvider that authenticates using a POP3 server. It will automatically create
* local user accounts as needed. To enable this provider, set system properties as follows:
*
* <ul>
* <li><tt>provider.auth.className = org.jivesoftware.openfire.auth.POP3AuthProvider</tt></li>
* <li><tt>provider.user.className = org.jivesoftware.openfire.user.POP3UserProvider</tt></li>
* </ul>
*
* The properties to configure the provider are as follows:
*
* <ul>
* <li>pop3.host -- <i>(required)</i> the name (or IP) of the POP3 server.
* <li>pop.port -- the port of the POP3 server. The default value is 110 for standard
* connections and 995 for SSL connections.
* <li>pop3.domain -- the mail domain (e.g. gmail.com).
* <li>pop3.authRequiresDomain -- set this to true if the POP3 server requires a
* full email address for authentication (foo@example.com) rather than just
* a username (foo). The default value is false.
* <li>pop3.ssl -- true if an SSL connection to the POP3 server should be used. The default
* is false.
* <li>pop3.debug -- true if debugging output for the POP3 connection should be enabled. The
* default is false.
* <li>pop3.authCache.enabled -- true if authentication checks should be cached locally.
* This will decrease load on the POP3 server if users continually authenticate.
* The default value is false.
* <li>pop3.authCache.size -- the maximum size of the authentication cache (in bytes). The
* default value is 512 K.
* <li>pop3.authCache.maxLifetime -- the maximum lifetime of items in the authentication
* cache (in milliseconds). The default value is one hour.
* </ul>
*
* @author Sean Meiners
*/
public class POP3AuthProvider implements AuthProvider {
private static final Logger Log = LoggerFactory.getLogger(POP3AuthProvider.class);
private Cache authCache = null;
private String host = null;
private String domain = null;
private int port = -1;
private boolean useSSL = false;
private boolean authRequiresDomain = false;
private boolean debugEnabled;
/**
* Initialiazes the POP3AuthProvider with values from the global config file.
*/
public POP3AuthProvider() {
// Convert XML based provider setup to Database based
JiveGlobals.migrateProperty("pop3.authCache.enabled");
JiveGlobals.migrateProperty("pop3.ssl");
JiveGlobals.migrateProperty("pop3.authRequiresDomain");
JiveGlobals.migrateProperty("pop3.host");
JiveGlobals.migrateProperty("pop3.debug");
JiveGlobals.migrateProperty("pop3.domain");
JiveGlobals.migrateProperty("pop3.port");
if (Boolean.valueOf(JiveGlobals.getProperty("pop3.authCache.enabled"))) {
String cacheName = "POP3 Authentication";
authCache = CacheFactory.createCache(cacheName);
}
useSSL = Boolean.valueOf(JiveGlobals.getProperty("pop3.ssl"));
authRequiresDomain = Boolean.valueOf(JiveGlobals.getProperty("pop3.authRequiresDomain"));
host = JiveGlobals.getProperty("pop3.host");
if (host == null || host.length() < 1) {
throw new IllegalArgumentException("pop3.host is null or empty");
}
debugEnabled = Boolean.valueOf(JiveGlobals.getProperty("pop3.debug"));
domain = JiveGlobals.getProperty("pop3.domain");
port = JiveGlobals.getIntProperty("pop3.port", useSSL ? 995 : 110);
if (Log.isDebugEnabled()) {
Log.debug("POP3AuthProvider: Created new POP3AuthProvider instance, fields:");
Log.debug("\t host: " + host);
Log.debug("\t port: " + port);
Log.debug("\t domain: " + domain);
Log.debug("\t useSSL: " + useSSL);
Log.debug("\t authRequiresDomain: " + authRequiresDomain);
Log.debug("\t authCacheEnabled: " + (authCache != null));
if (authCache != null) {
Log.debug("\t authCacheSize: " + authCache.getCacheSize());
Log.debug("\t authCacheMaxLifetime: " + authCache.getMaxLifetime());
}
}
}
public void authenticate(String username, String password) throws UnauthorizedException {
if (username == null || password == null) {
throw new UnauthorizedException();
}
if (username.contains("@")) {
// Check that the specified domain matches the server's domain
int index = username.indexOf("@");
String domain = username.substring(index + 1);
if (domain.equals(XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
username = username.substring(0, index);
}
} else {
// Unknown domain. Return authentication failed.
throw new UnauthorizedException();
}
Log.debug("POP3AuthProvider.authenticate("+username+", ******)");
// If cache is enabled, see if the auth is in cache.
if (authCache != null && authCache.containsKey(username)) {
String hash = (String)authCache.get(username);
if (StringUtils.hash(password).equals(hash)) {
return;
}
}
Properties mailProps = new Properties();
mailProps.setProperty("mail.debug", String.valueOf(debugEnabled));
Session session = Session.getInstance(mailProps, null);
Store store;
try {
store = session.getStore(useSSL ? "pop3s" : "pop3");
}
catch(NoSuchProviderException e) {
Log.error(e.getMessage(), e);
throw new UnauthorizedException(e);
}
try {
if (authRequiresDomain) {
store.connect(host, port, username + "@" + domain, password);
}
else {
store.connect(host, port, username, password);
}
}
catch(Exception e) {
Log.error(e.getMessage(), e);
throw new UnauthorizedException(e);
}
if (! store.isConnected()) {
throw new UnauthorizedException("Could not authenticate user");
}
try {
store.close();
}
catch (Exception e) {
// Ignore.
}
// If cache is enabled, add the item to cache.
if (authCache != null) {
authCache.put(username, StringUtils.hash(password));
}
// See if the user exists in the database. If not, automatically create them.
UserManager userManager = UserManager.getInstance();
try {
userManager.getUser(username);
}
catch (UserNotFoundException unfe) {
String email = username + "@" + (domain!=null?domain:host);
try {
Log.debug("POP3AuthProvider: Automatically creating new user account for " + username);
// Create user; use a random password for better safety in the future.
// Note that we have to go to the user provider directly -- because the
// provider is read-only, UserManager will usually deny access to createUser.
UserManager.getUserProvider().createUser(username, StringUtils.randomString(8),
null, email);
}
catch (UserAlreadyExistsException uaee) {
// Ignore.
}
}
}
public void authenticate(String username, String token, String digest)
throws UnauthorizedException
{
throw new UnauthorizedException("Digest authentication not supported.");
}
public boolean isPlainSupported() {
return true;
}
public boolean isDigestSupported() {
return false;
}
public String getPassword(String username)
throws UserNotFoundException, UnsupportedOperationException
{
throw new UnsupportedOperationException();
}
public void setPassword(String username, String password) throws UserNotFoundException {
throw new UnsupportedOperationException();
}
public boolean supportsPasswordRetrieval() {
return false;
}
}