Commit a551ce90 authored by Guus der Kinderen's avatar Guus der Kinderen Committed by akrherz

OF-1455 and OF-1454 (#1012)

* OF-1455: Allow for bulk property migration.

* OF-1454: New Property-based Provider Mappers.
parent e96e77dd
/*
* Copyright 2017 Ignite Realtime Foundation
*
* 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.util.ClassUtils;
import org.jivesoftware.util.JiveGlobals;
import java.util.*;
/**
* A {@link AuthProviderMapper} that can be used to draw some users from another source than the other users.
*
* This implementation uses properties to define sets of usernames and a corresponding provider. When a user is not in
* any set, a fallback provider is used.
*
* Each set of usernames is defined by two properties. Use the following property to define the classname of an
* {@link AuthProvider} to be used for this set: <tt>propertyBasedAuthMapper.set.SET_NAME.provider.className</tt>
*
* Use the following property to identify a set of usernames: <tt>propertyBasedAuthMapper.set.SET_NAME.members</tt>. The
* value for this property must be the name of another property, which is a listing of usernames (such a property is
* likely re-used in a corresponding {@link org.jivesoftware.openfire.user.PropertyBasedUserProviderMapper}
* configuration).
*
* There is no upper bound on the amount of sets that can be configured.
*
* Users that are not in any set will use the fallback provider. This provider is defined by its class name in the
* property <tt>propertyBasedAuthMapper.fallbackProvider.className</tt>.
*
* The following example defines two sets. Set "A" serves users john, jane and jack, and uses a DefaultAuthProvider.
* Set "B" serves users dave and doris, and uses A JDBCAuthProvider. All other users are served by the fallback provider
* that is a NativeAuthProvider.
*
* <ul>
* <li><tt>members.set.A = List( "john", "jane", "jack" )</tt></li>
* <li><tt>members.set.B = List( "dave", "doris" )</tt></li>
* <li><tt>propertyBasedAuthMapper.set.A.provider.className = org.jivesoftware.openfire.auth.DefaultAuthProvider</tt></li>
* <li><tt>propertyBasedAuthMapper.set.A.members.propertyName = members.set.A</tt></li>
* <li><tt>propertyBasedAuthMapper.set.B.provider.className = org.jivesoftware.openfire.auth.JDBCAuthProvider</tt></li>
* <li><tt>propertyBasedAuthMapper.set.B.members.propertyName = members.set.B</tt></li>
* <li><tt>propertyBasedAuthMapper.fallbackProvider.className = org.jivesoftware.openfire.auth.NativeAuthProvider</tt></li>
* </ul>
*
* @author Guus der Kinderen, guus@goodbytes.nl
*/
public class PropertyBasedAuthProviderMapper implements AuthProviderMapper
{
protected final Map<String, AuthProvider> providersByPrefix = new HashMap<>();
protected AuthProvider fallbackProvider;
public PropertyBasedAuthProviderMapper()
{
// Migrate properties.
JiveGlobals.migratePropertyTree( "propertyBasedAuthMapper" );
// Instantiate the fallback provider
fallbackProvider = instantiateProvider( "propertyBasedAuthMapper.fallbackProvider.className" );
if ( fallbackProvider == null )
{
throw new IllegalStateException( "Expected a AuthProvider class name in property 'propertyBasedAuthMapper.fallbackProvider.className'" );
}
// Instantiate all sets
final List<String> setProperties = JiveGlobals.getPropertyNames( "propertyBasedAuthMapper.set" );
for ( final String setProperty : setProperties )
{
final AuthProvider provider = instantiateProvider( setProperty + ".provider.className" );
if ( provider == null )
{
throw new IllegalStateException( "Expected a AuthProvider class name in property '" + setProperty + ".provider.className'" );
}
providersByPrefix.put( setProperty, provider );
}
}
@Override
public AuthProvider getAuthProvider( String username )
{
for ( final Map.Entry<String, AuthProvider> entry : providersByPrefix.entrySet() )
{
final String usersProperty = JiveGlobals.getProperty( entry.getKey() + ".members.propertyName" );
if ( usersProperty != null )
{
final List<String> usersInSet = JiveGlobals.getListProperty( usersProperty, Collections.<String>emptyList() );
if ( usersInSet.contains( username ) )
{
return entry.getValue();
}
}
}
return fallbackProvider;
}
@Override
public Set<AuthProvider> getAuthProviders()
{
final Set<AuthProvider> result = new LinkedHashSet<>();
result.addAll( providersByPrefix.values() );
result.add( fallbackProvider );
return result;
}
protected static AuthProvider instantiateProvider( String propertyName )
{
final String className = JiveGlobals.getProperty( propertyName );
if ( className == null )
{
throw new IllegalStateException( "A class name must be specified via openfire.xml or the system properties." );
}
try
{
final Class c = ClassUtils.forName( className );
return (AuthProvider) c.newInstance();
}
catch ( Exception e )
{
throw new IllegalStateException( "Unable to create new instance of AuthProvider: " + className, e );
}
}
}
/*
* Copyright 2017 Ignite Realtime Foundation
*
* 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.user;
import org.jivesoftware.util.JiveGlobals;
import java.util.*;
/**
* A {@link UserProviderMapper} that can be used to draw some users from another source than the other users.
*
* This implementation uses properties to define sets of usernames and a corresponding provider. When a user is not in
* any set, a fallback provider is used.
*
* Each set of usernames is defined by two properties. Use the following property to define the classname of an
* {@link UserProvider} to be used for this set: <tt>propertyBasedUserMapper.set.SET_NAME.provider.className</tt>
*
* Use the following property to identify a set of usernames: <tt>propertyBasedUserMapper.set.SET_NAME.members</tt>. The
* value for this property must be the name of another property, which is a listing of usernames (such a property is
* likely re-used in a corresponding {@link org.jivesoftware.openfire.auth.PropertyBasedAuthProviderMapper}
* configuration).
*
* There is no upper bound on the amount of sets that can be configured.
*
* Users that are not in any set will use the fallback provider. This provider is defined by its class name in the
* property <tt>propertyBasedUserMapper.fallbackProvider.className</tt>.
*
* The following example defines two sets. Set "A" serves users john, jane and jack, and uses a DefaultUserProvider.
* Set "B" serves users dave and doris, and uses A JDBCUserProvider. All other users are served by the fallback provider
* that is a NativeUserProvider.
*
* <ul>
* <li><tt>members.set.A = List( "john", "jane", "jack" )</tt></li>
* <li><tt>members.set.B = List( "dave", "doris" )</tt></li>
* <li><tt>propertyBasedUserMapper.set.A.provider.className = org.jivesoftware.openfire.user.DefaultUserProvider</tt></li>
* <li><tt>propertyBasedUserMapper.set.A.members.propertyName = members.set.A</tt></li>
* <li><tt>propertyBasedUserMapper.set.B.provider.className = org.jivesoftware.openfire.user.JDBCUserProvider</tt></li>
* <li><tt>propertyBasedUserMapper.set.B.members.propertyName = members.set.B</tt></li>
* <li><tt>propertyBasedUserMapper.fallbackProvider.className = org.jivesoftware.openfire.auth.NativeUserProvider</tt></li>
* </ul>
*
* @author Guus der Kinderen, guus@goodbytes.nl
*/
public class PropertyBasedUserProviderMapper implements UserProviderMapper
{
protected final Map<String, UserProvider> providersByPrefix = new HashMap<>();
protected UserProvider fallbackProvider;
public PropertyBasedUserProviderMapper()
{
// Migrate properties.
JiveGlobals.migratePropertyTree( "propertyBasedUserMapper" );
// Instantiate the fallback provider
fallbackProvider = UserMultiProvider.instantiate( "propertyBasedUserMapper.fallbackProvider.className" );
if ( fallbackProvider == null )
{
throw new IllegalStateException( "Expected a UserProvider class name in property 'propertyBasedUserMapper.fallbackProvider.className'" );
}
// Instantiate all sets
final List<String> setProperties = JiveGlobals.getPropertyNames( "propertyBasedUserMapper.set" );
for ( final String setProperty : setProperties )
{
final UserProvider provider = UserMultiProvider.instantiate( setProperty + ".provider.className" );
if ( provider == null )
{
throw new IllegalStateException( "Expected a UserProvider class name in property '" + setProperty + ".provider.className'" );
}
providersByPrefix.put( setProperty, provider );
}
}
@Override
public UserProvider getUserProvider( String username )
{
for ( final Map.Entry<String, UserProvider> entry : providersByPrefix.entrySet() )
{
final String usersProperty = JiveGlobals.getProperty( entry.getKey() + ".members.propertyName" );
if ( usersProperty != null )
{
final List<String> usersInSet = JiveGlobals.getListProperty( usersProperty, Collections.<String>emptyList() );
if ( usersInSet.contains( username ) )
{
return entry.getValue();
}
}
}
return fallbackProvider;
}
@Override
public Set<UserProvider> getUserProviders()
{
final Set<UserProvider> result = new LinkedHashSet<>();
result.addAll( providersByPrefix.values() );
result.add( fallbackProvider );
return result;
}
}
...@@ -931,6 +931,31 @@ public class JiveGlobals { ...@@ -931,6 +931,31 @@ public class JiveGlobals {
openfireProperties.migrateProperty(name); openfireProperties.migrateProperty(name);
} }
/**
* Convenience routine to migrate a tree of XML propertis into the database
* storage method.
*
* @param name the name of the base property to migrate.
*/
public static void migratePropertyTree(String name) {
if (isSetupMode()) {
return;
}
if (openfireProperties == null) {
loadOpenfireProperties();
}
final String[] children = openfireProperties.getChildrenProperties( name );
if ( children != null )
{
for ( final String child : children )
{
migratePropertyTree( name + "." + child );
}
}
openfireProperties.migrateProperty(name);
}
/** /**
* Flags certain properties as being sensitive, based on * Flags certain properties as being sensitive, based on
* property naming conventions. Values for matching property * property naming conventions. Values for matching property
......
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