Commit 5d9f2052 authored by Greg Thomas's avatar Greg Thomas

Fix CR/LF endings

parent a5ae728b
/* /*
* Copyright (C) 2004-2008 Jive Software. All rights reserved. * Copyright (C) 2004-2008 Jive Software. All rights reserved.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.openfire.auth; package org.jivesoftware.openfire.auth;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Map; import java.util.Map;
import org.jivesoftware.openfire.lockout.LockOutManager; import org.jivesoftware.openfire.lockout.LockOutManager;
import org.jivesoftware.openfire.user.UserNotFoundException; import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.Blowfish; import org.jivesoftware.util.Blowfish;
import org.jivesoftware.util.ClassUtils; import org.jivesoftware.util.ClassUtils;
import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils; import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.PropertyEventDispatcher; import org.jivesoftware.util.PropertyEventDispatcher;
import org.jivesoftware.util.PropertyEventListener; import org.jivesoftware.util.PropertyEventListener;
import org.jivesoftware.util.StringUtils; import org.jivesoftware.util.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* Pluggable authentication service. Users of Openfire that wish to change the AuthProvider * Pluggable authentication service. Users of Openfire that wish to change the AuthProvider
* implementation used to authenticate users can set the <code>AuthProvider.className</code> * implementation used to authenticate users can set the <code>AuthProvider.className</code>
* system property. For example, if you have configured Openfire to use LDAP for user information, * system property. For example, if you have configured Openfire to use LDAP for user information,
* you'd want to send a custom implementation of AuthFactory to make LDAP auth queries. * you'd want to send a custom implementation of AuthFactory to make LDAP auth queries.
* After changing the <code>AuthProvider.className</code> system property, you must restart your * After changing the <code>AuthProvider.className</code> system property, you must restart your
* application server. * application server.
* *
* @author Matt Tucker * @author Matt Tucker
*/ */
public class AuthFactory { public class AuthFactory {
private static final Logger Log = LoggerFactory.getLogger(AuthFactory.class); private static final Logger Log = LoggerFactory.getLogger(AuthFactory.class);
private static AuthProvider authProvider = null; private static AuthProvider authProvider = null;
private static MessageDigest digest; private static MessageDigest digest;
private static final Object DIGEST_LOCK = new Object(); private static final Object DIGEST_LOCK = new Object();
private static Blowfish cipher = null; private static Blowfish cipher = null;
static { static {
// Create a message digest instance. // Create a message digest instance.
try { try {
digest = MessageDigest.getInstance("SHA"); digest = MessageDigest.getInstance("SHA");
} }
catch (NoSuchAlgorithmException e) { catch (NoSuchAlgorithmException e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e); Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
} }
// Load an auth provider. // Load an auth provider.
initProvider(); initProvider();
// Detect when a new auth provider class is set // Detect when a new auth provider class is set
PropertyEventListener propListener = new PropertyEventListener() { PropertyEventListener propListener = new PropertyEventListener() {
@Override @Override
public void propertySet(String property, Map params) { public void propertySet(String property, Map params) {
if ("provider.auth.className".equals(property)) { if ("provider.auth.className".equals(property)) {
initProvider(); initProvider();
} }
} }
@Override @Override
public void propertyDeleted(String property, Map params) { public void propertyDeleted(String property, Map params) {
//Ignore //Ignore
} }
@Override @Override
public void xmlPropertySet(String property, Map params) { public void xmlPropertySet(String property, Map params) {
//Ignore //Ignore
} }
@Override @Override
public void xmlPropertyDeleted(String property, Map params) { public void xmlPropertyDeleted(String property, Map params) {
//Ignore //Ignore
} }
}; };
PropertyEventDispatcher.addListener(propListener); PropertyEventDispatcher.addListener(propListener);
} }
private static void initProvider() { private static void initProvider() {
// Convert XML based provider setup to Database based // Convert XML based provider setup to Database based
JiveGlobals.migrateProperty("provider.auth.className"); JiveGlobals.migrateProperty("provider.auth.className");
String className = JiveGlobals.getProperty("provider.auth.className", String className = JiveGlobals.getProperty("provider.auth.className",
"org.jivesoftware.openfire.auth.DefaultAuthProvider"); "org.jivesoftware.openfire.auth.DefaultAuthProvider");
// Check if we need to reset the auth provider class // Check if we need to reset the auth provider class
if (authProvider == null || !className.equals(authProvider.getClass().getName())) { if (authProvider == null || !className.equals(authProvider.getClass().getName())) {
try { try {
Class c = ClassUtils.forName(className); Class c = ClassUtils.forName(className);
authProvider = (AuthProvider)c.newInstance(); authProvider = (AuthProvider)c.newInstance();
} }
catch (Exception e) { catch (Exception e) {
Log.error("Error loading auth provider: " + className, e); Log.error("Error loading auth provider: " + className, e);
authProvider = new DefaultAuthProvider(); authProvider = new DefaultAuthProvider();
} }
} }
} }
/** /**
* Returns the currently-installed AuthProvider. <b>Warning:</b> in virtually all * Returns the currently-installed AuthProvider. <b>Warning:</b> in virtually all
* cases the auth provider should not be used directly. Instead, the appropriate * cases the auth provider should not be used directly. Instead, the appropriate
* methods in AuthFactory should be called. Direct access to the auth provider is * methods in AuthFactory should be called. Direct access to the auth provider is
* only provided for special-case logic. * only provided for special-case logic.
* *
* @return the current UserProvider. * @return the current UserProvider.
* @deprecated Prefer using the corresponding factory method, rather than * @deprecated Prefer using the corresponding factory method, rather than
* invoking methods on the provider directly * invoking methods on the provider directly
*/ */
public static AuthProvider getAuthProvider() { public static AuthProvider getAuthProvider() {
return authProvider; return authProvider;
} }
/** /**
* Returns whether the currently-installed AuthProvider is instance of a specific class. * Returns whether the currently-installed AuthProvider is instance of a specific class.
* @param c the class to compare with * @param c the class to compare with
* @return true - if the currently-installed AuthProvider is instance of c, false otherwise. * @return true - if the currently-installed AuthProvider is instance of c, false otherwise.
*/ */
public static boolean isProviderInstanceOf(Class<?> c) { public static boolean isProviderInstanceOf(Class<?> c) {
return c.isInstance(authProvider); return c.isInstance(authProvider);
} }
/** /**
* Indicates if the currently-installed AuthProvider is the HybridAuthProvider supporting a specific class. * Indicates if the currently-installed AuthProvider is the HybridAuthProvider supporting a specific class.
* *
* @param clazz the class to check * @param clazz the class to check
* @return {@code true} if the currently-installed AuthProvider is a HybridAuthProvider that supports an instance of clazz, otherwise {@code false}. * @return {@code true} if the currently-installed AuthProvider is a HybridAuthProvider that supports an instance of clazz, otherwise {@code false}.
*/ */
public static boolean isProviderHybridInstanceOf(Class<? extends AuthProvider> clazz) { public static boolean isProviderHybridInstanceOf(Class<? extends AuthProvider> clazz) {
return authProvider instanceof HybridAuthProvider && return authProvider instanceof HybridAuthProvider &&
((HybridAuthProvider) authProvider).isProvider(clazz); ((HybridAuthProvider) authProvider).isProvider(clazz);
} }
/** /**
* Returns true if the currently installed {@link AuthProvider} supports password * Returns true if the currently installed {@link AuthProvider} supports password
* retrieval. Certain implementation utilize password hashes and other authentication * retrieval. Certain implementation utilize password hashes and other authentication
* mechanisms that do not require the original password. * mechanisms that do not require the original password.
* *
* @return true if plain password retrieval is supported. * @return true if plain password retrieval is supported.
*/ */
public static boolean supportsPasswordRetrieval() { public static boolean supportsPasswordRetrieval() {
return authProvider.supportsPasswordRetrieval(); return authProvider.supportsPasswordRetrieval();
} }
/** /**
* Returns the user's password. This method will throw an UnsupportedOperationException * Returns the user's password. This method will throw an UnsupportedOperationException
* if this operation is not supported by the backend user store. * if this operation is not supported by the backend user store.
* *
* @param username the username of the user. * @param username the username of the user.
* @return the user's password. * @return the user's password.
* @throws UserNotFoundException if the given user could not be found. * @throws UserNotFoundException if the given user could not be found.
* @throws UnsupportedOperationException if the provider does not * @throws UnsupportedOperationException if the provider does not
* support the operation (this is an optional operation). * support the operation (this is an optional operation).
*/ */
public static String getPassword(String username) throws UserNotFoundException, public static String getPassword(String username) throws UserNotFoundException,
UnsupportedOperationException { UnsupportedOperationException {
return authProvider.getPassword(username.toLowerCase()); return authProvider.getPassword(username.toLowerCase());
} }
/** /**
* Sets the users's password. This method should throw an UnsupportedOperationException * Sets the users's password. This method should throw an UnsupportedOperationException
* if this operation is not supported by the backend user store. * if this operation is not supported by the backend user store.
* *
* @param username the username of the user. * @param username the username of the user.
* @param password the new plaintext password for the user. * @param password the new plaintext password for the user.
* @throws UserNotFoundException if the given user could not be loaded. * @throws UserNotFoundException if the given user could not be loaded.
* @throws UnsupportedOperationException if the provider does not * @throws UnsupportedOperationException if the provider does not
* support the operation (this is an optional operation). * support the operation (this is an optional operation).
*/ */
public static void setPassword(String username, String password) throws UserNotFoundException, public static void setPassword(String username, String password) throws UserNotFoundException,
UnsupportedOperationException, ConnectionException, InternalUnauthenticatedException { UnsupportedOperationException, ConnectionException, InternalUnauthenticatedException {
authProvider.setPassword(username, password); authProvider.setPassword(username, password);
} }
/** /**
* Authenticates a user with a username and plain text password and returns and * Authenticates a user with a username and plain text password and returns and
* AuthToken. If the username and password do not match the record of * AuthToken. If the username and password do not match the record of
* any user in the system, this method throws an UnauthorizedException. * any user in the system, this method throws an UnauthorizedException.
* *
* @param username the username. * @param username the username.
* @param password the password. * @param password the password.
* @return an AuthToken token if the username and password are correct. * @return an AuthToken token if the username and password are correct.
* @throws UnauthorizedException if the username and password do not match any existing user * @throws UnauthorizedException if the username and password do not match any existing user
* or the account is locked out. * or the account is locked out.
*/ */
public static AuthToken authenticate(String username, String password) public static AuthToken authenticate(String username, String password)
throws UnauthorizedException, ConnectionException, InternalUnauthenticatedException { throws UnauthorizedException, ConnectionException, InternalUnauthenticatedException {
if (LockOutManager.getInstance().isAccountDisabled(username)) { if (LockOutManager.getInstance().isAccountDisabled(username)) {
LockOutManager.getInstance().recordFailedLogin(username); LockOutManager.getInstance().recordFailedLogin(username);
throw new UnauthorizedException(); throw new UnauthorizedException();
} }
authProvider.authenticate(username, password); authProvider.authenticate(username, password);
return new AuthToken(username); return new AuthToken(username);
} }
/** /**
* Returns a digest given a token and password, according to JEP-0078. * Returns a digest given a token and password, according to JEP-0078.
* *
* @param token the token used in the digest. * @param token the token used in the digest.
* @param password the plain-text password to be digested. * @param password the plain-text password to be digested.
* @return the digested result as a hex string. * @return the digested result as a hex string.
*/ */
public static String createDigest(String token, String password) { public static String createDigest(String token, String password) {
synchronized (DIGEST_LOCK) { synchronized (DIGEST_LOCK) {
digest.update(token.getBytes()); digest.update(token.getBytes());
return StringUtils.encodeHex(digest.digest(password.getBytes())); return StringUtils.encodeHex(digest.digest(password.getBytes()));
} }
} }
/** /**
* Returns an encrypted version of the plain-text password. Encryption is performed * Returns an encrypted version of the plain-text password. Encryption is performed
* using the Blowfish algorithm. The encryption key is stored as the Jive property * using the Blowfish algorithm. The encryption key is stored as the Jive property
* "passwordKey". If the key is not present, it will be automatically generated. * "passwordKey". If the key is not present, it will be automatically generated.
* *
* @param password the plain-text password. * @param password the plain-text password.
* @return the encrypted password. * @return the encrypted password.
* @throws UnsupportedOperationException if encryption/decryption is not possible; * @throws UnsupportedOperationException if encryption/decryption is not possible;
* for example, during setup mode. * for example, during setup mode.
*/ */
public static String encryptPassword(String password) { public static String encryptPassword(String password) {
if (password == null) { if (password == null) {
return null; return null;
} }
Blowfish cipher = getCipher(); Blowfish cipher = getCipher();
if (cipher == null) { if (cipher == null) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
return cipher.encryptString(password); return cipher.encryptString(password);
} }
/** /**
* Returns a decrypted version of the encrypted password. Encryption is performed * Returns a decrypted version of the encrypted password. Encryption is performed
* using the Blowfish algorithm. The encryption key is stored as the Jive property * using the Blowfish algorithm. The encryption key is stored as the Jive property
* "passwordKey". If the key is not present, it will be automatically generated. * "passwordKey". If the key is not present, it will be automatically generated.
* *
* @param encryptedPassword the encrypted password. * @param encryptedPassword the encrypted password.
* @return the encrypted password. * @return the encrypted password.
* @throws UnsupportedOperationException if encryption/decryption is not possible; * @throws UnsupportedOperationException if encryption/decryption is not possible;
* for example, during setup mode. * for example, during setup mode.
*/ */
public static String decryptPassword(String encryptedPassword) { public static String decryptPassword(String encryptedPassword) {
if (encryptedPassword == null) { if (encryptedPassword == null) {
return null; return null;
} }
Blowfish cipher = getCipher(); Blowfish cipher = getCipher();
if (cipher == null) { if (cipher == null) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
return cipher.decryptString(encryptedPassword); return cipher.decryptString(encryptedPassword);
} }
/** /**
* Returns a Blowfish cipher that can be used for encrypting and decrypting passwords. * Returns a Blowfish cipher that can be used for encrypting and decrypting passwords.
* The encryption key is stored as the Jive property "passwordKey". If it's not present, * The encryption key is stored as the Jive property "passwordKey". If it's not present,
* it will be automatically generated. * it will be automatically generated.
* *
* @return the Blowfish cipher, or <tt>null</tt> if Openfire is not able to create a Cipher; * @return the Blowfish cipher, or <tt>null</tt> if Openfire is not able to create a Cipher;
* for example, during setup mode. * for example, during setup mode.
*/ */
private static synchronized Blowfish getCipher() { private static synchronized Blowfish getCipher() {
if (cipher != null) { if (cipher != null) {
return cipher; return cipher;
} }
// Get the password key, stored as a database property. Obviously, // Get the password key, stored as a database property. Obviously,
// protecting your database is critical for making the // protecting your database is critical for making the
// encryption fully secure. // encryption fully secure.
String keyString; String keyString;
try { try {
keyString = JiveGlobals.getProperty("passwordKey"); keyString = JiveGlobals.getProperty("passwordKey");
if (keyString == null) { if (keyString == null) {
keyString = StringUtils.randomString(15); keyString = StringUtils.randomString(15);
JiveGlobals.setProperty("passwordKey", keyString); JiveGlobals.setProperty("passwordKey", keyString);
// Check to make sure that setting the property worked. It won't work, // Check to make sure that setting the property worked. It won't work,
// for example, when in setup mode. // for example, when in setup mode.
if (!keyString.equals(JiveGlobals.getProperty("passwordKey"))) { if (!keyString.equals(JiveGlobals.getProperty("passwordKey"))) {
return null; return null;
} }
} }
cipher = new Blowfish(keyString); cipher = new Blowfish(keyString);
} }
catch (Exception e) { catch (Exception e) {
Log.error(e.getMessage(), e); Log.error(e.getMessage(), e);
} }
return cipher; return cipher;
} }
public static boolean supportsScram() { public static boolean supportsScram() {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return authProvider.isScramSupported(); return authProvider.isScramSupported();
} }
public static String getSalt(String username) throws UnsupportedOperationException, UserNotFoundException { public static String getSalt(String username) throws UnsupportedOperationException, UserNotFoundException {
return authProvider.getSalt(username); return authProvider.getSalt(username);
} }
public static int getIterations(String username) throws UnsupportedOperationException, UserNotFoundException { public static int getIterations(String username) throws UnsupportedOperationException, UserNotFoundException {
return authProvider.getIterations(username); return authProvider.getIterations(username);
} }
public static String getServerKey(String username) throws UnsupportedOperationException, UserNotFoundException { public static String getServerKey(String username) throws UnsupportedOperationException, UserNotFoundException {
return authProvider.getServerKey(username); return authProvider.getServerKey(username);
} }
public static String getStoredKey(String username) throws UnsupportedOperationException, UserNotFoundException { public static String getStoredKey(String username) throws UnsupportedOperationException, UserNotFoundException {
return authProvider.getStoredKey(username); return authProvider.getStoredKey(username);
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment