/* * Copyright 2016 IgniteRealtime.org * * 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 org.jivesoftware.openfire.user.UserNotFoundException; import org.jivesoftware.util.ClassUtils; import org.jivesoftware.util.JiveGlobals; /** * A {@link AuthProvider} that delegates to a user-specific AuthProvider. * * This class related to, but is distinct from {@link HybridAuthProvider}. The Hybrid variant of the provider iterates * over providers, operating on the first applicable instance. This Mapped variant, however, maps each user to exactly * one provider. * * To use this provider, use the following system property definition: * * <ul> * <li><tt>provider.auth.className = org.jivesoftware.openfire.user.MappedAuthProvider</tt></li> * </ul> * * To be usable, a {@link AuthProviderMapper} must be configured using the <tt>mappedAuthProvider.mapper.className</tt> * system property. It is of importance to note that most AuthProviderMapper implementations will require additional * configuration. * * @author Guus der Kinderen, guus@goodbytes.nl */ public class MappedAuthProvider implements AuthProvider { /** * Name of the property of which the value is expected to be the classname of the AuthProviderMapper instance to be * used by instances of this class. */ public static final String PROPERTY_MAPPER_CLASSNAME = "mappedAuthProvider.mapper.className"; /** * Used to determine what provider is to be used to operate on a particular user. */ protected final AuthProviderMapper mapper; public MappedAuthProvider() { // Migrate properties. JiveGlobals.migrateProperty( PROPERTY_MAPPER_CLASSNAME ); // Instantiate mapper. final String mapperClass = JiveGlobals.getProperty( PROPERTY_MAPPER_CLASSNAME ); if ( mapperClass == null ) { throw new IllegalStateException( "A mapper must be specified via openfire.xml or the system properties." ); } try { final Class c = ClassUtils.forName( mapperClass ); mapper = (AuthProviderMapper) c.newInstance(); } catch ( Exception e ) { throw new IllegalStateException( "Unable to create new instance of AuthProviderMapper class: " + mapperClass, e ); } } @Override public void authenticate( String username, String password ) throws UnauthorizedException, ConnectionException, InternalUnauthenticatedException { final AuthProvider provider = mapper.getAuthProvider( username ); if ( provider == null ) { throw new UnauthorizedException(); } provider.authenticate( username, password ); } @Override public String getPassword( String username ) throws UserNotFoundException, UnsupportedOperationException { final AuthProvider provider = mapper.getAuthProvider( username ); if ( provider == null ) { throw new UserNotFoundException(); } return provider.getPassword( username ); } @Override public void setPassword( String username, String password ) throws UserNotFoundException, UnsupportedOperationException { final AuthProvider provider = mapper.getAuthProvider( username ); if ( provider == null ) { throw new UserNotFoundException(); } provider.setPassword( username, password ); } @Override public boolean supportsPasswordRetrieval() { // TODO Make calls concurrent for improved throughput. for ( final AuthProvider provider : mapper.getAuthProviders() ) { // If at least one provider supports password retrieval, so does this proxy. if ( provider.supportsPasswordRetrieval() ) { return true; } } return false; } @Override public boolean isScramSupported() { // TODO Make calls concurrent for improved throughput. for ( final AuthProvider provider : mapper.getAuthProviders() ) { // If at least one provider supports SCRAM, so does this proxy. if ( provider.isScramSupported() ) { return true; } } return false; } @Override public String getSalt(String username) throws UserNotFoundException { final AuthProvider provider = mapper.getAuthProvider( username ); if ( provider == null ) { throw new UserNotFoundException(); } return provider.getSalt( username ); } @Override public int getIterations(String username) throws UserNotFoundException { final AuthProvider provider = mapper.getAuthProvider( username ); if ( provider == null ) { throw new UserNotFoundException(); } return provider.getIterations( username ); } @Override public String getServerKey(String username) throws UserNotFoundException { final AuthProvider provider = mapper.getAuthProvider( username ); if ( provider == null ) { throw new UserNotFoundException(); } return provider.getServerKey( username ); } @Override public String getStoredKey(String username) throws UserNotFoundException { final AuthProvider provider = mapper.getAuthProvider( username ); if ( provider == null ) { throw new UserNotFoundException(); } return provider.getStoredKey( username ); } }