Commit 4818ae96 authored by Dave Cridland's avatar Dave Cridland Committed by GitHub

Merge pull request #812 from guusdk/OF-1336_UserPropertyProvider

OF-1336: Introduce UserPropertyProvider
parents 9df410ae 47798ec7
...@@ -29,6 +29,8 @@ import org.dom4j.Element; ...@@ -29,6 +29,8 @@ import org.dom4j.Element;
import org.jivesoftware.openfire.XMPPServer; import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.event.UserEventDispatcher; import org.jivesoftware.openfire.event.UserEventDispatcher;
import org.jivesoftware.openfire.event.UserEventListener; import org.jivesoftware.openfire.event.UserEventListener;
import org.jivesoftware.openfire.user.property.DefaultUserPropertyProvider;
import org.jivesoftware.openfire.user.property.UserPropertyProvider;
import org.jivesoftware.util.ClassUtils; import org.jivesoftware.util.ClassUtils;
import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.PropertyEventDispatcher; import org.jivesoftware.util.PropertyEventDispatcher;
...@@ -50,7 +52,7 @@ import org.xmpp.packet.JID; ...@@ -50,7 +52,7 @@ import org.xmpp.packet.JID;
*/ */
public class UserManager implements IQResultListener { public class UserManager implements IQResultListener {
private static final Logger Log = LoggerFactory.getLogger(UserManager.class); private static final Logger Log = LoggerFactory.getLogger(UserManager.class);
// Wrap this guy up so we can mock out the UserManager class. // Wrap this guy up so we can mock out the UserManager class.
private static class UserManagerContainer { private static class UserManagerContainer {
...@@ -69,6 +71,21 @@ public class UserManager implements IQResultListener { ...@@ -69,6 +71,21 @@ public class UserManager implements IQResultListener {
return UserManagerContainer.instance.provider; return UserManagerContainer.instance.provider;
} }
/**
* Returns the currently-installed UserPropertyProvider.
*
* <b>Warning:</b> in virtually all cases the user property provider should not be used directly. Instead, use the
* Map returned by {@link User#getProperties() ) to create, read, update or delete user properties. Failure to do so
* is likely to result in inconsistent data behavior and race conditions. Direct access to the user property
* provider is only provided for special-case logic.
*
* @return the current UserPropertyProvider.
* @see User#getProperties
*/
public static UserPropertyProvider getUserPropertyProvider() {
return UserManagerContainer.instance.propertyProvider;
}
/** /**
* Returns a singleton UserManager instance. * Returns a singleton UserManager instance.
* *
...@@ -83,14 +100,16 @@ public class UserManager implements IQResultListener { ...@@ -83,14 +100,16 @@ public class UserManager implements IQResultListener {
/** Cache if a local or remote user exists. */ /** Cache if a local or remote user exists. */
private Cache<String, Boolean> remoteUsersCache; private Cache<String, Boolean> remoteUsersCache;
private UserProvider provider; private UserProvider provider;
private UserPropertyProvider propertyProvider;
private UserManager() { private UserManager() {
// Initialize caches. // Initialize caches.
userCache = CacheFactory.createCache("User"); userCache = CacheFactory.createCache("User");
remoteUsersCache = CacheFactory.createCache("Remote Users Existence"); remoteUsersCache = CacheFactory.createCache("Remote Users Existence");
// Load a user provider. // Load a user & property provider.
initProvider(); initProvider();
initPropertyProvider();
// 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() {
...@@ -99,6 +118,9 @@ public class UserManager implements IQResultListener { ...@@ -99,6 +118,9 @@ public class UserManager implements IQResultListener {
if ("provider.user.className".equals(property)) { if ("provider.user.className".equals(property)) {
initProvider(); initProvider();
} }
if ("provider.userproperty.className".equals(property)) {
initPropertyProvider();
}
} }
@Override @Override
...@@ -106,6 +128,9 @@ public class UserManager implements IQResultListener { ...@@ -106,6 +128,9 @@ public class UserManager implements IQResultListener {
if ("provider.user.className".equals(property)) { if ("provider.user.className".equals(property)) {
initProvider(); initProvider();
} }
if ("provider.userproperty.className".equals(property)) {
initPropertyProvider();
}
} }
@Override @Override
...@@ -492,4 +517,23 @@ public class UserManager implements IQResultListener { ...@@ -492,4 +517,23 @@ public class UserManager implements IQResultListener {
} }
} }
} }
private void initPropertyProvider() {
// Convert XML based provider setup to Database based
JiveGlobals.migrateProperty("provider.userproperty.className");
String className = JiveGlobals.getProperty("provider.userproperty.className",
"org.jivesoftware.openfire.user.property.DefaultUserPropertyProvider");
// Check if we need to reset the provider class
if (propertyProvider == null || !className.equals(propertyProvider.getClass().getName())) {
try {
Class c = ClassUtils.forName(className);
propertyProvider = (UserPropertyProvider) c.newInstance();
}
catch (Exception e) {
Log.error("Error loading user property provider: " + className, e);
propertyProvider = new DefaultUserPropertyProvider();
}
}
}
} }
\ No newline at end of file
/*
* Copyright 2017 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.user.property;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.openfire.user.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Default implementation of the UserPropertyProvider interface, which reads and writes data from the
* <tt>ofUserProp</tt> database table.
*
* This implementation will not explicitly verify if a user exists, when operating on its properties. The methods of
* this implementation will <em>not</em> throw {@link org.jivesoftware.openfire.user.UserNotFoundException}.
*
* <b>Warning:</b> in virtually all cases a user property provider should not be used directly. Instead, use the
* Map returned by {@link User#getProperties() ) to create, read, update or delete user properties. Failure to do so
* is likely to result in inconsistent data behavior and race conditions. Direct access to the user property
* provider is only provided for special-case logic.
*
* @author Guus der Kinderen, guus@goodbytes.nl
* @see User#getProperties
*/
public class DefaultUserPropertyProvider implements UserPropertyProvider
{
private static final Logger Log = LoggerFactory.getLogger( DefaultUserPropertyProvider.class );
private static final String LOAD_PROPERTIES = "SELECT name, propValue FROM ofUserProp WHERE username=?";
private static final String LOAD_PROPERTY = "SELECT propValue FROM ofUserProp WHERE username=? AND name=?";
private static final String DELETE_PROPERTY = "DELETE FROM ofUserProp WHERE username=? AND name=?";
private static final String UPDATE_PROPERTY = "UPDATE ofUserProp SET propValue=? WHERE name=? AND username=?";
private static final String INSERT_PROPERTY = "INSERT INTO ofUserProp (username, name, propValue) VALUES (?, ?, ?)";
@Override
public Map<String, String> loadProperties( String username )
{
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
final Map<String, String> properties = new ConcurrentHashMap<>();
try
{
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement( LOAD_PROPERTIES );
pstmt.setString( 1, username );
rs = pstmt.executeQuery();
while ( rs.next() )
{
properties.put( rs.getString( 1 ), rs.getString( 2 ) );
}
}
catch ( SQLException sqle )
{
Log.error( sqle.getMessage(), sqle );
}
finally
{
DbConnectionManager.closeConnection( rs, pstmt, con );
}
return properties;
}
@Override
public String loadProperty( String username, String propertyName )
{
String propertyValue = null;
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement( LOAD_PROPERTY );
pstmt.setString( 1, username );
pstmt.setString( 2, propertyName );
rs = pstmt.executeQuery();
while ( rs.next() )
{
propertyValue = rs.getString( 1 );
}
}
catch ( SQLException sqle )
{
Log.error( sqle.getMessage(), sqle );
}
finally
{
DbConnectionManager.closeConnection( rs, pstmt, con );
}
return propertyValue;
}
@Override
public void insertProperty( String username, String propName, String propValue )
{
Connection con = null;
PreparedStatement pstmt = null;
try
{
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement( INSERT_PROPERTY );
pstmt.setString( 1, username );
pstmt.setString( 2, propName );
pstmt.setString( 3, propValue );
pstmt.executeUpdate();
}
catch ( SQLException e )
{
Log.error( e.getMessage(), e );
}
finally
{
DbConnectionManager.closeConnection( pstmt, con );
}
}
@Override
public void updateProperty( String username, String propName, String propValue )
{
Connection con = null;
PreparedStatement pstmt = null;
try
{
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement( UPDATE_PROPERTY );
pstmt.setString( 1, propValue );
pstmt.setString( 2, propName );
pstmt.setString( 3, username );
pstmt.executeUpdate();
}
catch ( SQLException e )
{
Log.error( e.getMessage(), e );
}
finally
{
DbConnectionManager.closeConnection( pstmt, con );
}
}
@Override
public void deleteProperty( String username, String propName )
{
Connection con = null;
PreparedStatement pstmt = null;
try
{
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement( DELETE_PROPERTY );
pstmt.setString( 1, username );
pstmt.setString( 2, propName );
pstmt.executeUpdate();
}
catch ( SQLException e )
{
Log.error( e.getMessage(), e );
}
finally
{
DbConnectionManager.closeConnection( pstmt, con );
}
}
@Override
public boolean isReadOnly()
{
return false;
}
}
/*
* Copyright 2017 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.user.property;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
/**
* The JDBC user property provider allows you to use an external database to define the user properties. It is best used
* with the JDBCUserProvider, JDBCAuthProvider &and JDBCGroupProvider to provide integration between your external
* system and Openfire. All data is treated as read-only so any set operations will result in an exception.
*
* This implementation will not explicitly verify if a user exists, when operating on its properties. The methods of
* this implementation will <em>not</em> throw {@link org.jivesoftware.openfire.user.UserNotFoundException}.
*
* To enable this provider, set the following in the system properties:
*
* <ul>
* <li><tt>provider.userproperty.className = org.jivesoftware.openfire.user.property.JDBCUserPropertyProvider</tt></li>
* </ul>
*
* Then you need to set your driver, connection string and SQL statements:
*
* <ul>
* <li><tt>jdbcUserPropertyProvider.driver = com.mysql.jdbc.Driver</tt></li>
* <li><tt>jdbcUserPropertyProvider.connectionString = jdbc:mysql://localhost/dbname?user=username&amp;password=secret</tt></li>
* <li><tt>jdbcUserPropertyProvider.loadPropertySQL = SELECT propName, propValue FROM myUser WHERE user = ? AND propName = ?</tt></li>
* <li><tt>jdbcUserPropertyProvider.loadPropertiesSQL = SELECT propValue FROM myUser WHERE user = ?</tt></li>
* </ul>
*
* In order to use the configured JDBC connection provider do not use a JDBCconnection string, set the following
* property:
*
* <tt>jdbcUserPropertyProvider.useConnectionProvider = true</tt></li>
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public class JDBCUserPropertyProvider implements UserPropertyProvider
{
private static final Logger Log = LoggerFactory.getLogger( JDBCUserPropertyProvider.class );
private String loadPropertySQL;
private String loadPropertiesSQL;
private String connectionString;
private boolean useConnectionProvider;
/**
* Constructs a new JDBC user property provider.
*/
public JDBCUserPropertyProvider()
{
// Convert XML based provider setup to Database based
JiveGlobals.migrateProperty( "jdbcUserPropertyProvider.driver" );
JiveGlobals.migrateProperty( "jdbcUserPropertyProvider.connectionString" );
JiveGlobals.migrateProperty( "jdbcUserPropertyProvider.loadPropertySQL" );
JiveGlobals.migrateProperty( "jdbcUserPropertyProvider.loadPropertiesSQL" );
useConnectionProvider = JiveGlobals.getBooleanProperty( "jdbcUserProvider.useConnectionProvider" );
// Load the JDBC driver and connection string.
if ( !useConnectionProvider )
{
String jdbcDriver = JiveGlobals.getProperty( "jdbcUserPropertyProvider.driver" );
try
{
Class.forName( jdbcDriver ).newInstance();
}
catch ( Exception e )
{
Log.error( "Unable to load JDBC driver: " + jdbcDriver, e );
return;
}
connectionString = JiveGlobals.getProperty( "jdbcProvider.connectionString" );
}
// Load database statements for user data.
loadPropertySQL = JiveGlobals.getProperty( "jdbcUserPropertyProvider.loadPropertySQL" );
loadPropertiesSQL = JiveGlobals.getProperty( "jdbcUserPropertyProvider.loadPropertiesSQL" );
}
private Connection getConnection() throws SQLException
{
if ( useConnectionProvider )
{
return DbConnectionManager.getConnection();
}
else
{
return DriverManager.getConnection( connectionString );
}
}
@Override
public Map<String, String> loadProperties( String username ) throws UnsupportedOperationException
{
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
con = getConnection();
pstmt = con.prepareStatement( loadPropertiesSQL );
pstmt.setString( 1, username );
rs = pstmt.executeQuery();
final Map<String, String> result = new HashMap<>();
while ( rs.next() )
{
final String propName = rs.getString( 1 );
final String propValue = rs.getString( 2 );
result.put( propName, propValue );
}
return result;
}
catch ( Exception e )
{
throw new UnsupportedOperationException( e );
}
finally
{
DbConnectionManager.closeConnection( rs, pstmt, con );
}
}
@Override
public String loadProperty( String username, String propName )
{
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
con = getConnection();
pstmt = con.prepareStatement( loadPropertySQL );
pstmt.setString( 1, username );
pstmt.setString( 2, propName );
rs = pstmt.executeQuery();
final Map<String, String> result = new HashMap<>();
if ( rs.next() )
{
return rs.getString( 1 );
}
return null;
}
catch ( Exception e )
{
throw new UnsupportedOperationException( e );
}
finally
{
DbConnectionManager.closeConnection( rs, pstmt, con );
}
}
@Override
public void insertProperty( String username, String propName, String propValue ) throws UnsupportedOperationException
{
throw new UnsupportedOperationException();
}
@Override
public void updateProperty( String username, String propName, String propValue ) throws UnsupportedOperationException
{
throw new UnsupportedOperationException();
}
@Override
public void deleteProperty( String username, String propName ) throws UnsupportedOperationException
{
throw new UnsupportedOperationException();
}
@Override
public boolean isReadOnly()
{
return true;
}
}
/*
* Copyright 2017 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.user.property;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.ClassUtils;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
/**
* A {@link UserPropertyProvider} that delegates to a user-specific UserPropertyProvider.
*
* This implementation will explicitly verify if a user exists, when operating on its properties, but only if the
* corresponding mapped provider does so. If that is the case, then the methods of this implementation will throw
* {@link org.jivesoftware.openfire.user.UserNotFoundException}.
*
* This class related to, but is distinct from {@link HybridUserPropertyProvider}. 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.userproperty.className = org.jivesoftware.openfire.user.MappedUserPropertyProvider</tt></li>
* </ul>
*
* To be usable, a {@link UserPropertyProviderMapper} must be configured using the <tt>mappedUserPropertyProvider.mapper.className</tt>
* system property. It is of importance to note that most UserPropertyProviderMapper implementations will require additional
* configuration.
*
* @author Guus der Kinderen, guus@goodbytes.nl
*/
public class MappedUserPropertyProvider implements UserPropertyProvider
{
/**
* Name of the property of which the value is expected to be the classname of the UserPropertyProviderMapper
* instance to be used by instances of this class.
*/
public static final String PROPERTY_MAPPER_CLASSNAME = "mappedUserPropertyProvider.mapper.className";
private static final Logger Log = LoggerFactory.getLogger( MappedUserPropertyProvider.class );
/**
* Used to determine what provider is to be used to operate on a particular user.
*/
protected final UserPropertyProviderMapper mapper;
public MappedUserPropertyProvider()
{
// 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 = (UserPropertyProviderMapper) c.newInstance();
}
catch ( Exception e )
{
throw new IllegalStateException( "Unable to create new instance of UserPropertyProviderMapper class: " + mapperClass, e );
}
}
/**
* Instantiates a UserPropertyProvider based on a property value (that is expected to be a class name). When the
* property is not set, this method returns null. When the property is set, but an exception occurs while
* instantiating the class, this method logs the error and returns null.
*
* UserProvider classes are required to have a public, no-argument constructor.
*
* @param propertyName A property name (cannot ben ull).
* @return A user provider (can be null).
*/
public static UserPropertyProvider instantiate( String propertyName )
{
final String className = JiveGlobals.getProperty( propertyName );
if ( className == null )
{
Log.debug( "Property '{}' is undefined. Skipping.", propertyName );
return null;
}
Log.debug( "About to to instantiate an UserPropertyProvider '{}' based on the value of property '{}'.", className, propertyName );
try
{
final Class c = ClassUtils.forName( className );
final UserPropertyProvider provider = (UserPropertyProvider) c.newInstance();
Log.debug( "Instantiated UserPropertyProvider '{}'", className );
return provider;
}
catch ( Exception e )
{
Log.error( "Unable to load UserPropertyProvider '{}'. Users in this provider will be disabled.", className, e );
return null;
}
}
@Override
public Map<String, String> loadProperties( String username ) throws UserNotFoundException
{
return mapper.getUserPropertyProvider( username ).loadProperties( username );
}
@Override
public String loadProperty( String username, String propName ) throws UserNotFoundException
{
return mapper.getUserPropertyProvider( username ).loadProperty( username, propName );
}
@Override
public void insertProperty( String username, String propName, String propValue ) throws UserNotFoundException
{
mapper.getUserPropertyProvider( username ).insertProperty( username, propName, propValue );
}
@Override
public void updateProperty( String username, String propName, String propValue ) throws UserNotFoundException
{
mapper.getUserPropertyProvider( username ).updateProperty( username, propName, propValue );
}
@Override
public void deleteProperty( String username, String propName ) throws UserNotFoundException
{
mapper.getUserPropertyProvider( username ).deleteProperty( username, propName );
}
/**
* Returns whether <em>all</em> backing providers are read-only. When read-only, properties can not be created,
* deleted, or modified. If at least one provider is not read-only, this method returns false.
*
* @return true when all backing providers are read-only, otherwise false.
*/
@Override
public boolean isReadOnly()
{
// TODO Make calls concurrent for improved throughput.
for ( final UserPropertyProvider provider : mapper.getUserPropertyProviders() )
{
// If at least one provider is not readonly, neither is this proxy.
if ( !provider.isReadOnly() )
{
return false;
}
}
return true;
}
}
/*
* Copyright 2017 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.user.property;
import org.jivesoftware.openfire.user.User;
import org.jivesoftware.openfire.user.UserNotFoundException;
import java.util.Map;
/**
* A provider for user properties. User properties are defined Map of String key and values that does not support null.
* value.
*
* Some, but not all, implementations are expected to store user properties in a relation to an existing user object.
* This interface definition does not require implementations to verify that a user object indeed exists, when
* processing data. As a result, methods defined here may, but are not required to throw {@link UserNotFoundException}
* when processing property data for non-existing users. Implementations should clearly document their behavior in
* this respect.
*
* <b>Warning:</b> in virtually all cases a user property provider should not be used directly. Instead, use the
* Map returned by {@link User#getProperties() ) to create, read, update or delete user properties. Failure to do so
* is likely to result in inconsistent data behavior and race conditions. Direct access to the user property
* provider is only provided for special-case logic.
*
* @author Guus der Kinderen, guus@goodbytes.nl
* @see User#getProperties
*/
public interface UserPropertyProvider
{
/**
* Returns true if this UserPropertyProvider is read-only. When read-only, properties can not be created, deleted or
* modified. Invocation of the corresponding methods should result in an {@link UnsupportedOperationException}.
*
* @return true if the user provider is read-only.
*/
boolean isReadOnly();
/**
* Retrieves all properties for a particular user.
*
* @param username The identifier of the user (cannot be null or empty).
* @return A collection, possibly empty, but never null.
*/
Map<String, String> loadProperties( String username ) throws UserNotFoundException;
/**
* Retrieves a property value for a user.
*
* This method will return null when the desired property was not defined for the user (null values are not
* supported).
*
* @param username The identifier of the user (cannot be null or empty).
* @param propName The property name (cannot be null or empty).
* @return The property value (possibly null).
*/
String loadProperty( String username, String propName ) throws UserNotFoundException;
/**
* Adds a property for an user.
*
* The behavior of inserting a duplicate property name is not defined by this interface.
*
* @param username The identifier of the user (cannot be null or empty).
* @param propName The property name (cannot be null or empty).
* @param propValue The property value (cannot be null).
*/
void insertProperty( String username, String propName, String propValue ) throws UserNotFoundException, UnsupportedOperationException;
/**
* Changes a property value for an user.
*
* The behavior of updating a non-existing property is not defined by this interface.
*
* @param username The identifier of the user (cannot be null or empty).
* @param propName The property name (cannot be null or empty).
* @param propValue The property value (cannot be null).
*/
void updateProperty( String username, String propName, String propValue ) throws UserNotFoundException, UnsupportedOperationException;
/**
* Removes one particular property for a particular user.
*
* The behavior of deleting a non-existing property is not defined by this interface.
*
* @param username The identifier of the user (cannot be null or empty).
* @param propName The property name (cannot be null or empty).
*/
void deleteProperty( String username, String propName ) throws UserNotFoundException, UnsupportedOperationException;
}
/*
* Copyright 2017 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.user.property;
import java.util.Set;
/**
* Implementations are used to determine what UserPropertyProvider is to be used for a particular username.
*
* Note that the provided username need not reflect a pre-existing user (the instance might be used to determine in
* which provider a new user is to be created).
*
* Implementation must have a no-argument constructor.
*
* @author Guus der Kinderen, guus@goodbytes.nl
* @see MappedUserPropertyProvider
*/
public interface UserPropertyProviderMapper
{
/**
* Finds a suitable UserPropertyProvider for the user.
*
* Note that the provided username need not reflect a pre-existing user (the instance might be used to determine in
* which provider a new user is to be created).
*
* Implementations are expected to be able to find a UserPropertyProvider for any username. If an implementation
* fails to do so, such a failure is assumed to be the result of a problem in implementation or configuration.
*
* @param username A user identifier (cannot be null or empty).
* @return A UserPropertyProvider for the user (never null).
*/
UserPropertyProvider getUserPropertyProvider( String username );
/**
* Returns all providers that are used by this instance.
*
* The returned collection should have a consistent, predictable iteration order.
*
* @return all providers (never null).
*/
Set<UserPropertyProvider> getUserPropertyProviders();
}
...@@ -44,6 +44,12 @@ ...@@ -44,6 +44,12 @@
Just married Plugin Changelog Just married Plugin Changelog
</h1> </h1>
<p><b>1.2.1</b> -- May 31, 2017</p>
<ul>
<li>Updated to match new API in Openfire 4.2.0</li>
<li>Slight optimization for copying the user properties.</li>
</ul>
<p><b>1.2.0</b> -- October 12, 2015</p> <p><b>1.2.0</b> -- October 12, 2015</p>
<ul> <ul>
<li>[<a href='http://www.igniterealtime.org/issues/browse/OF-953'>OF-953</a>] - Updated JSP libraries.</li> <li>[<a href='http://www.igniterealtime.org/issues/browse/OF-953'>OF-953</a>] - Updated JSP libraries.</li>
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
<name>Just married</name> <name>Just married</name>
<description>Allows admins to rename or copy users</description> <description>Allows admins to rename or copy users</description>
<author>Holger Bergunde</author> <author>Holger Bergunde</author>
<version>1.2.0</version> <version>1.2.1</version>
<date>10/12/2015</date> <date>05/31/2017</date>
<minServerVersion>4.0.0</minServerVersion> <minServerVersion>4.0.0</minServerVersion>
<adminconsole> <adminconsole>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
</parent> </parent>
<groupId>org.igniterealtime.openfire.plugins</groupId> <groupId>org.igniterealtime.openfire.plugins</groupId>
<artifactId>justmarried</artifactId> <artifactId>justmarried</artifactId>
<version>1.2.0</version> <version>1.2.1</version>
<name>JustMarried Plugin</name> <name>JustMarried Plugin</name>
<description>Allows admins to rename or copy users</description> <description>Allows admins to rename or copy users</description>
......
...@@ -98,9 +98,7 @@ public class JustMarriedPlugin implements Plugin { ...@@ -98,9 +98,7 @@ public class JustMarriedPlugin implements Plugin {
} }
private static void copyProperties(User currentUser, User newUser) { private static void copyProperties(User currentUser, User newUser) {
for (String key : currentUser.getProperties().keySet()) { newUser.getProperties().putAll( currentUser.getProperties() );
newUser.getProperties().put(key, User.getPropertyValue(currentUser.getUsername(), key));
}
} }
private static void copyRoster(User currentUser, User newUser, String currentUserName) { private static void copyRoster(User currentUser, User newUser, String currentUserName) {
......
...@@ -44,6 +44,12 @@ ...@@ -44,6 +44,12 @@
REST API Plugin Changelog REST API Plugin Changelog
</h1> </h1>
<p><b>1.2.6</b> -- May 31, 2017</p>
<ul>
<li>Updated to match new API in Openfire 4.2.0</li>
<li>Slight optimization for copying the user properties.</li>
</ul>
<p><b>1.2.5</b> -- October 14th, 2016</p> <p><b>1.2.5</b> -- October 14th, 2016</p>
<ul> <ul>
<li>Updated to match new API in Openfire 4.1.0</li> <li>Updated to match new API in Openfire 4.1.0</li>
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
<name>REST API</name> <name>REST API</name>
<description>Allows administration over a RESTful API.</description> <description>Allows administration over a RESTful API.</description>
<author>Roman Soldatow</author> <author>Roman Soldatow</author>
<version>1.2.5</version> <version>1.2.6</version>
<date>10/14/2016</date> <date>05/31/2017</date>
<minServerVersion>4.1.0</minServerVersion> <minServerVersion>4.1.0</minServerVersion>
<adminconsole> <adminconsole>
<tab id="tab-server"> <tab id="tab-server">
......
...@@ -8,7 +8,9 @@ ...@@ -8,7 +8,9 @@
</parent> </parent>
<groupId>org.igniterealtime.openfire.plugins</groupId> <groupId>org.igniterealtime.openfire.plugins</groupId>
<artifactId>restAPI</artifactId> <artifactId>restAPI</artifactId>
<version>1.2.6</version>
<name>Rest API Plugin</name> <name>Rest API Plugin</name>
<description>Allows administration over a RESTful API.</description>
<build> <build>
<sourceDirectory>src/java</sourceDirectory> <sourceDirectory>src/java</sourceDirectory>
......
...@@ -148,9 +148,7 @@ public class JustMarriedController { ...@@ -148,9 +148,7 @@ public class JustMarriedController {
* the new user * the new user
*/ */
private static void copyProperties(User currentUser, User newUser) { private static void copyProperties(User currentUser, User newUser) {
for (String key : currentUser.getProperties().keySet()) { newUser.getProperties().putAll( currentUser.getProperties() );
newUser.getProperties().put(key, User.getPropertyValue(currentUser.getUsername(), key));
}
} }
/** /**
......
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