Commit 310e3cb3 authored by Tom Evans's avatar Tom Evans Committed by tevans

OF-657: Provider-based integration for Atlassian Crowd (courtesy Issa Gorissen)

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@13617 b35dd754-fafc-0310-a699-88a17e54d16e
parent 44d2153b
...@@ -113,6 +113,7 @@ exit 0 ...@@ -113,6 +113,7 @@ exit 0
%{homedir}/bin/embedded-db-viewer.sh %{homedir}/bin/embedded-db-viewer.sh
%dir %{homedir}/conf %dir %{homedir}/conf
%config(noreplace) %{homedir}/conf/openfire.xml %config(noreplace) %{homedir}/conf/openfire.xml
%config(noreplace) %{homedir}/conf/crowd.properties
%dir %{homedir}/lib %dir %{homedir}/lib
%{homedir}/lib/*.jar %{homedir}/lib/*.jar
%{homedir}/lib/log4j.xml %{homedir}/lib/log4j.xml
......
#
# This file defines the configuration properties required
# when using the Atlassian Crowd integration for Openfire.
#
# https://confluence.atlassian.com/display/CROWD/The+crowd.properties+file
#
# To activate the Crowd integration for Openfire, you must define
# the following Openfire system properties:
#
# provider.admin.className org.jivesoftware.openfire.crowd.CrowdAdminProvider
# provider.auth.className org.jivesoftware.openfire.crowd.CrowdAuthProvider
# provider.group.className org.jivesoftware.openfire.crowd.CrowdGroupProvider
# provider.user.className org.jivesoftware.openfire.crowd.CrowdUserProvider
# provider.vcard.className org.jivesoftware.openfire.crowd.CrowdVCardProvider
#
# In addition, you may customize the Crowd provider using the following Openfire
# system properties:
#
# admin.authorizedGroups <comma-separated list of Crowd groups having Openfire admin rights>
# crowd.groups.cache.ttl.seconds 3600
# crowd.users.cache.ttl.seconds 3600
#
# The REST URL for your Crowd server.
crowd.server.url=https://YOUR-CROWD-SERVER:8095/crowd/
# These properties are required to authenticate with the Crowd server.
# They must match the values specified in the Crowd configuration.
application.name=openfire
application.password=<password>
# Other optional configuration properties.
#http.proxy.host=
#http.proxy.port=
#http.proxy.username=
#http.proxy.password=
# These properties can be used to tune the Crowd integration.
#http.max.connections=20
#http.timeout=5000
#http.socket.timeout=20000
/*
* Copyright (C) 2012 Issa Gorissen <issa-gorissen@usa.net>. 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.crowd;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.admin.AdminProvider;
import org.jivesoftware.openfire.group.Group;
import org.jivesoftware.openfire.group.GroupManager;
import org.jivesoftware.openfire.group.GroupNotFoundException;
import org.jivesoftware.openfire.group.GroupProvider;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;
/**
* Admin provider which will map a crowd group with openfire authorized admin users
*/
public class CrowdAdminProvider implements AdminProvider {
private static final Logger LOG = LoggerFactory.getLogger(CrowdAdminProvider.class);
private static final String JIVE_AUTHORIZED_GROUPS = "admin.authorizedGroups";
public List<JID> getAdmins() {
List<JID> results = new ArrayList<JID>();
GroupProvider provider = GroupManager.getInstance().getProvider();
String groups = JiveGlobals.getProperty(JIVE_AUTHORIZED_GROUPS);
groups = (groups == null || groups.trim().length() == 0) ? "" : groups;
JiveGlobals.setProperty(JIVE_AUTHORIZED_GROUPS, groups); // make sure the property is created
StringTokenizer tokenizer = new StringTokenizer(groups, ",");
while (tokenizer.hasMoreTokens()) {
String groupName = tokenizer.nextToken().trim().toLowerCase();
if (groupName != null && groupName.length() > 0) {
try {
LOG.info("Adding admin users from group: " + groupName);
Group group = provider.getGroup(groupName);
if (group != null) {
results.addAll(group.getMembers());
}
} catch (GroupNotFoundException gnfe) {
LOG.error("Error when trying to load the members of group:" + String.valueOf(groupName), gnfe);
}
}
}
if (results.isEmpty()) {
// Add default admin account when none was specified
results.add(new JID("admin", XMPPServer.getInstance().getServerInfo().getXMPPDomain(), null, true));
}
if (LOG.isDebugEnabled()) {
LOG.debug("admin users: " + results.toString());
}
return results;
}
public void setAdmins(List<JID> admins) {
return;
}
public boolean isReadOnly() {
return true;
}
}
/*
* Copyright (C) 2012 Issa Gorissen <issa-gorissen@usa.net>. 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.crowd;
import java.rmi.RemoteException;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.AuthProvider;
import org.jivesoftware.openfire.auth.ConnectionException;
import org.jivesoftware.openfire.auth.InternalUnauthenticatedException;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;
/**
* Auth provider for Atlassian Crowd
*/
public class CrowdAuthProvider implements AuthProvider {
private static final Logger LOG = LoggerFactory.getLogger(CrowdAuthProvider.class);
private CrowdManager manager = null;
public CrowdAuthProvider() {
try {
manager = CrowdManager.getInstance();
} catch (Exception e) {
LOG.error("Failure to load the Crowd manager", e);
}
}
public boolean isPlainSupported() {
return true;
}
public boolean isDigestSupported() {
return false;
}
/**
* Returns if the username and password are valid; otherwise this
* method throws an UnauthorizedException.<p>
*
* If {@link #isPlainSupported()} returns false, this method should
* throw an UnsupportedOperationException.
*
* @param username the username or full JID.
* @param password the password
* @throws UnauthorizedException if the username and password do
* not match any existing user.
* @throws ConnectionException it there is a problem connecting to user and group sytem
* @throws InternalUnauthenticatedException if there is a problem authentication Openfire itself into the user and group system
*/
public void authenticate(String username, String password) throws UnauthorizedException, ConnectionException, InternalUnauthenticatedException {
if (manager == null) {
throw new ConnectionException("Unable to connect to Crowd");
}
if (username == null || password == null || "".equals(password.trim())) {
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();
}
}
// Un-escape username.
username = JID.unescapeNode(username);
try {
manager.authenticate(username, password);
LOG.info("authenticated user:" + username);
} catch (RemoteException re) {
throw new UnauthorizedException();
}
}
public void authenticate(String username, String token, String digest) throws UnauthorizedException, ConnectionException, InternalUnauthenticatedException {
throw new UnsupportedOperationException("XMPP digest authentication not supported by this version of authentication provider");
}
public String getPassword(String username) throws UserNotFoundException, UnsupportedOperationException {
throw new UnsupportedOperationException("Retrieve password not supported by this version of authentication provider");
}
public void setPassword(String username, String password) throws UserNotFoundException, UnsupportedOperationException {
throw new UnsupportedOperationException("Setting password not implemented by this version of authentication provider");
}
public boolean supportsPasswordRetrieval() {
return false;
}
}
/*
* Copyright (C) 2012 Issa Gorissen <issa-gorissen@usa.net>. 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.crowd;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.group.AbstractGroupProvider;
import org.jivesoftware.openfire.group.Group;
import org.jivesoftware.openfire.group.GroupNotFoundException;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.cache.Cache;
import org.jivesoftware.util.cache.CacheFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;
/**
* Atlassian Crowd implementation of the GroupProvider. We do not permit
* modifications of groups from this provider - only read-only access.
*/
public class CrowdGroupProvider extends AbstractGroupProvider {
private static final Logger LOG = LoggerFactory.getLogger(CrowdGroupProvider.class);
private static final int CACHE_TTL = 3600; // ttl in seconds - one hour
private static final String JIVE_CROWD_GROUPS_CACHE_TTL_SECS = "crowd.groups.cache.ttl.seconds";
private static final String GROUP_CACHE_NAME = "crowdGroup";
private static final String GROUP_MEMBERSHIP_CACHE_NAME = "crowdGroupMembership";
private static final String USER_MEMBERSHIP_CACHE_NAME = "crowdUserMembership";
private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private static final ScheduledExecutorService crowdGroupSync = Executors.newSingleThreadScheduledExecutor();
private static final CrowdManager manager = CrowdManager.getInstance();
private static List<String> groups = new ArrayList<String>();
private final XMPPServer server = XMPPServer.getInstance();
static {
String propertyValue = JiveGlobals.getProperty(JIVE_CROWD_GROUPS_CACHE_TTL_SECS);
int ttl = (propertyValue == null || propertyValue.trim().length() == 0) ? CACHE_TTL : Integer.parseInt(propertyValue);
crowdGroupSync.scheduleAtFixedRate(new GroupSynch(), 0, ttl, TimeUnit.SECONDS);
JiveGlobals.setProperty(JIVE_CROWD_GROUPS_CACHE_TTL_SECS, String.valueOf(ttl));
}
public CrowdGroupProvider() {
String propertyValue = JiveGlobals.getProperty(JIVE_CROWD_GROUPS_CACHE_TTL_SECS);
int ttl = (propertyValue == null || propertyValue.trim().length() == 0) ? CACHE_TTL : Integer.parseInt(propertyValue);
Cache<String, Collection<JID>> groupMembershipCache = CacheFactory.createLocalCache(GROUP_MEMBERSHIP_CACHE_NAME);
groupMembershipCache.setMaxCacheSize(-1);
groupMembershipCache.setMaxLifetime(ttl * 1000); // msecs instead of sec - see Cache API
Cache<JID, Collection<String>> userMembershipCache = CacheFactory.createLocalCache(USER_MEMBERSHIP_CACHE_NAME);
userMembershipCache.setMaxCacheSize(-1);
userMembershipCache.setMaxLifetime(ttl * 1000); // msecs instead of sec - see Cache API
Cache<String, org.jivesoftware.openfire.crowd.jaxb.Group> groupCache = CacheFactory.createLocalCache(GROUP_CACHE_NAME);
userMembershipCache.setMaxCacheSize(-1);
userMembershipCache.setMaxLifetime(ttl * 1000); // msecs instead of sec - see Cache API
}
public Group getGroup(String name) throws GroupNotFoundException {
try {
Cache<String, org.jivesoftware.openfire.crowd.jaxb.Group> groupCache = CacheFactory.createLocalCache(GROUP_CACHE_NAME);
org.jivesoftware.openfire.crowd.jaxb.Group group = groupCache.get(name);
if (group == null) {
group = manager.getGroup(name);
groupCache.put(name, group);
}
Collection<JID> members = getGroupMembers(name);
Collection<JID> admins = Collections.emptyList();
return new Group(name, group.description, members, admins);
} catch (RemoteException re) {
LOG.error("Failure to load group:" + String.valueOf(name), re);
throw new GroupNotFoundException(re);
}
}
private Collection<JID> getGroupMembers(String groupName) {
Cache<String, Collection<JID>> groupMembershipCache = CacheFactory.createLocalCache(GROUP_MEMBERSHIP_CACHE_NAME);
Collection<JID> members = groupMembershipCache.get(groupName);
if (members != null) {
return members;
}
try {
List<String> users = manager.getGroupMembers(groupName);
Collection<JID> results = new ArrayList<JID>();
for (String username : users) {
results.add(server.createJID(JID.escapeNode(username), null));
}
groupMembershipCache.put(groupName, results);
return results;
} catch (RemoteException re) {
LOG.error("Failure to get the members of crowd group:" + String.valueOf(groupName), re);
}
groupMembershipCache.put(groupName, new ArrayList<JID>());
return Collections.emptyList();
}
public Collection<String> getGroupNames(JID user) {
Cache<JID, Collection<String>> userMembershipCache = CacheFactory.createCache(USER_MEMBERSHIP_CACHE_NAME);
Collection<String> groups = userMembershipCache.get(user);
if (groups != null) {
return groups;
}
try {
groups = manager.getUserGroups(user.getNode());
userMembershipCache.put(user, groups);
return groups;
} catch (RemoteException re) {
LOG.error("Failure to load the groups of user:" + String.valueOf(user), re);
}
userMembershipCache.put(user, new ArrayList<String>());
return Collections.emptyList();
}
public int getGroupCount() {
lock.readLock().lock();
try {
return groups.size();
} finally {
lock.readLock().unlock();
}
}
public Collection<String> getGroupNames() {
lock.readLock().lock();
try {
return groups;
} finally {
lock.readLock().unlock();
}
}
public Collection<String> getGroupNames(int startIndex, int numResults) {
lock.readLock().lock();
try {
Collection<String> results = new ArrayList<String>(numResults);
for (int i = 0, j = startIndex; i < numResults && j < groups.size(); ++i, ++j) {
results.add(groups.get(j));
}
return results;
} finally {
lock.readLock().unlock();
}
}
public Collection<String> search(String query) {
lock.readLock().lock();
try {
ArrayList<String> results = new ArrayList<String>();
if (query != null && query.trim().length() > 0) {
if (query.endsWith("*")) {
query = query.substring(0, query.length() - 1);
}
if (query.startsWith("*")) {
query = query.substring(1);
}
query = query.toLowerCase();
for (String groupName : groups) {
if (groupName.toLowerCase().contains(query)) {
results.add(groupName);
}
}
}
return results;
} finally {
lock.readLock().unlock();
}
}
public Collection<String> search(String query, int startIndex, int numResults) {
lock.readLock().lock();
try {
ArrayList<String> foundGroups = (ArrayList<String>) search(query);
Collection<String> results = new ArrayList<String>();
for (int i = 0, j = startIndex; i < numResults && j < foundGroups.size(); ++i, ++j) {
results.add(foundGroups.get(j));
}
return results;
} finally {
lock.readLock().unlock();
}
}
/**
* Modifying group not implemented - read-only for now
*/
public boolean isReadOnly() {
return true;
}
public boolean isSearchSupported() {
return true;
}
/**
* @see org.jivesoftware.openfire.group.AbstractGroupProvider#search(java.lang.String, java.lang.String)
*/
// TODO search on group attributes in Crowd
@Override
public Collection<String> search(String key, String value) {
LOG.info("Search groups on attibutes not implemented yet");
return Collections.emptyList();
}
static class GroupSynch implements Runnable {
public void run() {
LOG.info("running synch with crowd...");
CrowdManager manager = null;
try {
manager = CrowdManager.getInstance();
} catch (Exception e) {
LOG.error("Failure to load the Crowd manager", e);
return;
}
List<String> allGroups = null;
try {
allGroups = manager.getAllGroupNames();
} catch (RemoteException re) {
LOG.error("Failure to fetch all crowd groups name", re);
return;
}
if (allGroups != null && allGroups.size() > 0) {
CrowdGroupProvider.lock.writeLock().lock();
try {
CrowdGroupProvider.groups = allGroups;
} finally {
CrowdGroupProvider.lock.writeLock().unlock();
}
}
LOG.info("crowd synch done, returned " + allGroups.size() + " groups");
}
}
}
This diff is collapsed.
/*
* Copyright (C) 2012 Issa Gorissen <issa-gorissen@usa.net>. 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.crowd;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import org.apache.commons.lang.StringUtils;
import org.jivesoftware.util.JiveGlobals;
/**
* Reading the file crowd.properties which should be found in the conf folder
* of openfire
*
*/
public class CrowdProperties {
private static final String APPLICATION_NAME = "application.name";
private static final String APPLICATION_PASSWORD = "application.password";
private static final String CROWD_SERVER_URL = "crowd.server.url";
private static final String HTTP_PROXY_HOST = "http.proxy.host";
private static final String HTTP_PROXY_PORT = "http.proxy.port";
private static final String HTTP_PROXY_USERNAME = "http.proxy.username";
private static final String HTTP_PROXY_PASSWORD = "http.proxy.password";
private static final String HTTP_MAX_CONNECTIONS = "http.max.connections";
private static final String HTTP_TIMEOUT = "http.timeout";
private static final String HTTP_SOCKET_TIMEOUT = "http.socket.timeout";
private Properties props;
public CrowdProperties() throws IOException {
props = new Properties();
File file = new File(JiveGlobals.getHomeDirectory() + File.separator + "conf" + File.separator + "crowd.properties");
if (!file.exists()) {
throw new IOException("The file crowd.properties is missing from Openfire conf folder");
} else {
try {
props.load(new FileInputStream(file));
} catch (IOException ioe) {
throw new IOException("Unable to load crowd.properties file", ioe);
}
}
// checking for required info in file
if (StringUtils.isBlank(props.getProperty(APPLICATION_NAME))
|| StringUtils.isBlank(props.getProperty(APPLICATION_PASSWORD))
|| StringUtils.isBlank(props.getProperty(CROWD_SERVER_URL))) {
throw new IOException("crowd.properties is missing required information (app name, app passwd, crowd url)");
}
}
public String getApplicationName() {
return props.getProperty(APPLICATION_NAME);
}
public String getApplicationPassword() {
return props.getProperty(APPLICATION_PASSWORD);
}
public String getCrowdServerUrl() {
String url = props.getProperty(CROWD_SERVER_URL);
if (!url.endsWith("/")) {
url += "/";
}
return url;
}
public String getHttpProxyHost() {
return props.getProperty(HTTP_PROXY_HOST);
}
public int getHttpProxyPort() {
return getIntegerValue(HTTP_PROXY_PORT, 0);
}
public String getHttpProxyUsername() {
return noNull(props.getProperty(HTTP_PROXY_USERNAME));
}
public String getHttpProxyPassword() {
return noNull(props.getProperty(HTTP_PROXY_PASSWORD));
}
public int getHttpMaxConnections() {
return getIntegerValue(HTTP_MAX_CONNECTIONS, 20);
}
public int getHttpConnectionTimeout() {
return getIntegerValue(HTTP_TIMEOUT, 5000);
}
public int getHttpSocketTimeout() {
return getIntegerValue(HTTP_SOCKET_TIMEOUT, 20000);
}
private int getIntegerValue(String propKey, int defaultValue) {
int i = 0;
try {
i = Integer.parseInt(props.getProperty(propKey));
} catch (NumberFormatException nfe) {
i = defaultValue;
}
return i;
}
private String noNull(String str) {
return (str != null) ? str : "";
}
}
/*
* Copyright (C) 2012 Issa Gorissen <issa-gorissen@usa.net>. 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.crowd;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.jivesoftware.openfire.user.User;
import org.jivesoftware.openfire.user.UserAlreadyExistsException;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.openfire.user.UserProvider;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Atlassian Crowd implementation of the UserProvider. We do not permit
* modifications of users from this provider - only read-only access.
*/
public class CrowdUserProvider implements UserProvider {
private static final Logger LOG = LoggerFactory.getLogger(CrowdUserProvider.class);
private static final int CACHE_TTL = 3600; // default ttl in seconds - 1 hour
private static final String JIVE_CROWD_USERS_CACHE_TTL_SECS = "crowd.users.cache.ttl.seconds";
private static final String SEARCH_FIELD_USERNAME = "Username";
private static final String SEARCH_FIELD_NAME = "Name";
private static final String SEARCH_FIELD_EMAIL = "Email";
private static final Set<String> SEARCH_FIELDS = new TreeSet<String>(Arrays.asList(
new String[]{SEARCH_FIELD_USERNAME, SEARCH_FIELD_NAME, SEARCH_FIELD_EMAIL}));
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final ScheduledExecutorService crowdUserSync = Executors.newSingleThreadScheduledExecutor();
private Map<String, org.jivesoftware.openfire.crowd.jaxb.User> usersCache = new TreeMap<String, org.jivesoftware.openfire.crowd.jaxb.User>();
private List<org.jivesoftware.openfire.crowd.jaxb.User> users = new ArrayList<org.jivesoftware.openfire.crowd.jaxb.User>();
public CrowdUserProvider() {
String propertyValue = JiveGlobals.getProperty(JIVE_CROWD_USERS_CACHE_TTL_SECS);
int ttl = (propertyValue == null || propertyValue.trim().length() == 0) ? CACHE_TTL : Integer.parseInt(propertyValue);
crowdUserSync.scheduleAtFixedRate(new UserSynch(this), 0, ttl, TimeUnit.SECONDS);
JiveGlobals.setProperty(JIVE_CROWD_USERS_CACHE_TTL_SECS, String.valueOf(ttl));
// workaround to load the sync of groups with crowd
new CrowdGroupProvider();
}
public User loadUser(String username) throws UserNotFoundException {
lock.readLock().lock();
try {
return getCrowdUser(username).getOpenfireUser();
} finally {
lock.readLock().unlock();
}
}
public org.jivesoftware.openfire.crowd.jaxb.User getCrowdUser(String username) throws UserNotFoundException {
lock.readLock().lock();
try {
if (usersCache.containsKey(username)) {
return usersCache.get(username);
} else {
throw new UserNotFoundException("User : '" + String.valueOf(username) + "'");
}
} finally {
lock.readLock().unlock();
}
}
public int getUserCount() {
lock.readLock().lock();
try {
return usersCache.size();
} finally {
lock.readLock().unlock();
}
}
public Collection<User> getUsers() {
lock.readLock().lock();
try {
Collection<User> results = new ArrayList<User>();
for (org.jivesoftware.openfire.crowd.jaxb.User user : usersCache.values()) {
results.add(user.getOpenfireUser());
}
return results;
} finally {
lock.readLock().unlock();
}
}
public Collection<String> getUsernames() {
lock.readLock().lock();
try {
return usersCache.keySet();
} finally {
lock.readLock().unlock();
}
}
public Collection<User> getUsers(int startIndex, int numResults) {
lock.readLock().lock();
try {
Collection<User> results = new ArrayList<User>(numResults);
for (int i = 0, j = startIndex; i < numResults && j < users.size(); ++i, ++j) {
results.add(users.get(j).getOpenfireUser());
}
return results;
} finally {
lock.readLock().unlock();
}
}
public Set<String> getSearchFields() throws UnsupportedOperationException {
return SEARCH_FIELDS;
}
public Collection<User> findUsers(Set<String> fields, String query) throws UnsupportedOperationException {
lock.readLock().lock();
try {
ArrayList<User> results = new ArrayList<User>();
if (query != null && query.trim().length() > 0) {
if (query.endsWith("*")) {
query = query.substring(0, query.length() - 1);
}
if (query.startsWith("*")) {
query = query.substring(1);
}
query = query.toLowerCase();
if (SEARCH_FIELDS.containsAll(fields)) {
if (fields.contains(SEARCH_FIELD_USERNAME)) {
for (org.jivesoftware.openfire.crowd.jaxb.User user : users) {
if (user.name.toLowerCase().contains(query)) {
results.add(user.getOpenfireUser());
}
}
} else if (fields.contains(SEARCH_FIELD_NAME)) {
for (org.jivesoftware.openfire.crowd.jaxb.User user : users) {
if (user.displayName.toLowerCase().contains(query)) {
results.add(user.getOpenfireUser());
}
}
} else {
for (org.jivesoftware.openfire.crowd.jaxb.User user : users) {
if (user.email.toLowerCase().contains(query)) {
results.add(user.getOpenfireUser());
}
}
}
}
}
return results;
} finally {
lock.readLock().unlock();
}
}
public Collection<User> findUsers(Set<String> fields, String query, int startIndex, int numResults) throws UnsupportedOperationException {
lock.readLock().lock();
try {
ArrayList<User> foundUsers = (ArrayList<User>) findUsers(fields, query);
Collection<User> results = new ArrayList<User>(foundUsers.size());
for (int i = 0, j = startIndex; i < numResults && j < foundUsers.size(); ++i, ++j) {
results.add(foundUsers.get(j));
}
return results;
} finally {
lock.readLock().unlock();
}
}
public boolean isReadOnly() {
return true;
}
public boolean isNameRequired() {
return false;
}
public boolean isEmailRequired() {
return false;
}
/*
* Not implemented methods
*/
public User createUser(String username, String password, String name, String email) throws UserAlreadyExistsException {
throw new UnsupportedOperationException("Create new user not implemented by this version of user provider");
}
public void deleteUser(String username) {
throw new UnsupportedOperationException("Delete a user not implemented by this version of user provider");
}
public void setName(String username, String name) throws UserNotFoundException {
throw new UnsupportedOperationException("Setting user name not implemented by this version of user provider");
}
public void setEmail(String username, String email) throws UserNotFoundException {
throw new UnsupportedOperationException("Setting user email not implemented by this version of user provider");
}
public void setCreationDate(String username, Date creationDate) throws UserNotFoundException {
throw new UnsupportedOperationException("Setting user creation date unsupported by this version of user provider");
}
public void setModificationDate(String username, Date modificationDate) throws UserNotFoundException {
throw new UnsupportedOperationException("Setting user modification date unsupported by this version of user provider");
}
static class UserSynch implements Runnable {
CrowdUserProvider userProvider;
public UserSynch(CrowdUserProvider userProvider) {
this.userProvider = userProvider;
}
public void run() {
LOG.info("running synch with crowd...");
CrowdManager manager = null;
try {
manager = CrowdManager.getInstance();
} catch (Exception e) {
LOG.error("Failure to load the Crowd manager", e);
return;
}
List<org.jivesoftware.openfire.crowd.jaxb.User> allUsers = null;
try {
allUsers = manager.getAllUsers();
} catch (RemoteException re) {
LOG.error("Failure to fetch all crowd users", re);
return;
}
if (allUsers != null && allUsers.size() > 0) {
Map<String, org.jivesoftware.openfire.crowd.jaxb.User> usersMap = new TreeMap<String, org.jivesoftware.openfire.crowd.jaxb.User>();
for (org.jivesoftware.openfire.crowd.jaxb.User user : allUsers) {
usersMap.put(user.name, user);
}
userProvider.lock.writeLock().lock();
try {
userProvider.users = allUsers;
userProvider.usersCache = usersMap;
} finally {
userProvider.lock.writeLock().unlock();
}
}
LOG.info("crowd synch done, returned " + allUsers.size() + " users");
}
}
}
/*
* Copyright (C) 2012 Issa Gorissen <issa-gorissen@usa.net>. 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.crowd;
import java.io.StringReader;
import java.util.concurrent.ConcurrentHashMap;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.jivesoftware.openfire.crowd.jaxb.User;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.openfire.vcard.DefaultVCardProvider;
import org.jivesoftware.util.AlreadyExistsException;
import org.jivesoftware.util.NotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* VCard provider for Crowd.
*
* <p>The name, email will be provided by crowd. All other information
* can be managed by the XMPP client
*/
public class CrowdVCardProvider extends DefaultVCardProvider {
private static final Logger LOG = LoggerFactory.getLogger(CrowdVCardProvider.class);
private static final String VCARD_TEMPLATE
= "<vCard xmlns=\"vcard-temp\"><FN>@displayname@</FN><N><FAMILY>@lastname@</FAMILY><GIVEN>@firstname@</GIVEN></N><NICKNAME>@nickname@</NICKNAME><EMAIL><USERID>@email@</USERID></EMAIL></vCard>";
private static final ConcurrentHashMap<String, Object> MUTEX = new ConcurrentHashMap<String, Object>();
/**
* @see org.jivesoftware.openfire.vcard.DefaultVCardProvider#loadVCard(java.lang.String)
*/
@Override
public Element loadVCard(String username) {
if (LOG.isDebugEnabled()) {
LOG.debug("loadvcard:" + username);
}
if (MUTEX.containsKey(username)) {
// preventing looping
return null;
}
try {
MUTEX.put(username, username);
Element vcard = super.loadVCard(username);
if (vcard == null) {
CrowdUserProvider userProvider = (CrowdUserProvider) UserManager.getUserProvider();
try {
User user = userProvider.getCrowdUser(username);
String str = VCARD_TEMPLATE.replace("@displayname@", user.displayName)
.replace("@lastname@", user.lastName)
.replace("@firstname@", user.firstName)
.replace("@email@", user.email)
.replace("@nickname@", username);
SAXReader xmlReader = new SAXReader();
xmlReader.setEncoding("UTF-8");
vcard = xmlReader.read(new StringReader(str)).getRootElement();
} catch (UserNotFoundException unfe) {
LOG.error("Unable to find user:" + String.valueOf(username) + " for loading its vcard", unfe);
return null;
} catch (DocumentException de) {
LOG.error("vcard parsing error", de);
return null;
}
if (LOG.isDebugEnabled()) {
LOG.debug(vcard != null ? vcard.asXML() : "vcard is null");
}
// store this new vcard
if (vcard != null) {
try {
createVCard(username, vcard);
} catch (AlreadyExistsException aee) {
LOG.error("Unable to create and store a new vcard for user:" + username + "; one already exists", aee);
}
}
}
return vcard;
} catch (RuntimeException re) {
LOG.error("Failure occured when loading a vcard for user:" + username, re);
throw re;
} finally {
MUTEX.remove(username);
}
}
/**
* @see org.jivesoftware.openfire.vcard.DefaultVCardProvider#createVCard(java.lang.String, org.dom4j.Element)
*/
@Override
public Element createVCard(String username, Element vCardElement) throws AlreadyExistsException {
if (LOG.isDebugEnabled()) {
LOG.debug("createvcard:" + vCardElement.asXML());
}
return super.createVCard(username, vCardElement);
}
/**
* @see org.jivesoftware.openfire.vcard.DefaultVCardProvider#updateVCard(java.lang.String, org.dom4j.Element)
*/
@Override
public Element updateVCard(String username, Element vCard) throws NotFoundException {
// make sure some properties have not been overridden
Element nickNameNode = vCard.element("NICKNAME");
Element displayNameNode = vCard.element("FN");
Element nameNode = vCard.element("N");
Element lastNameNode = nameNode.element("FAMILY");
Element firstnameNode = nameNode.element("GIVEN");
Element emailNode = vCard.element("EMAIL").element("USERID");
CrowdUserProvider userProvider = (CrowdUserProvider) UserManager.getUserProvider();
try {
User user = userProvider.getCrowdUser(username);
nickNameNode.setText(username);
displayNameNode.setText(user.displayName);
lastNameNode.setText(user.lastName);
firstnameNode.setText(user.firstName);
emailNode.setText(user.email);
} catch (UserNotFoundException unfe) {
LOG.error("Unable to find user:" + String.valueOf(username) + " for updating its vcard", unfe);
}
if (LOG.isDebugEnabled()) {
LOG.debug("updatevcard:" + vCard.asXML());
}
return super.updateVCard(username, vCard);
}
}
/*
* Copyright (C) 2012 Issa Gorissen <issa-gorissen@usa.net>. 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.crowd.jaxb;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name="password")
public class AuthenticatePost {
public String value;
}
/*
* Copyright (C) 2012 Issa Gorissen <issa-gorissen@usa.net>. 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.crowd.jaxb;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name="group")
public class Group {
@XmlAttribute
public String name;
public String description;
}
/*
* Copyright (C) 2012 Issa Gorissen <issa-gorissen@usa.net>. 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.crowd.jaxb;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name="groups")
public class Groups {
public List<Group> group;
}
/*
* Copyright (C) 2012 Issa Gorissen <issa-gorissen@usa.net>. 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.crowd.jaxb;
import java.util.Date;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
public class User {
@XmlAttribute
public String name;
@XmlElement(name="display-name")
public String displayName;
@XmlElement(name="first-name")
public String firstName;
@XmlElement(name="last-name")
public String lastName;
public String email;
private org.jivesoftware.openfire.user.User openfireUser;
public synchronized org.jivesoftware.openfire.user.User getOpenfireUser() {
if (openfireUser == null) {
openfireUser = new org.jivesoftware.openfire.user.User(name, displayName, email, new Date(), new Date());
}
return openfireUser;
}
}
/*
* Copyright (C) 2012 Issa Gorissen <issa-gorissen@usa.net>. 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.crowd.jaxb;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name="users")
public class Users {
public List<User> user;
}
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