/*******************************************************************************
* Copyright 2009, 2010 Innovation Gate GmbH. All Rights Reserved.
*
* This file is part of the OpenWGA server platform.
*
* OpenWGA is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, a special exception is granted by the copyright holders
* of OpenWGA called "OpenWGA plugin exception". You should have received
* a copy of this exception along with OpenWGA in file COPYING.
* If not, see <http://www.openwga.com/gpl-plugin-exception>.
*
* OpenWGA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenWGA in file COPYING.
* If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package de.innovationgate.wgpublisher.auth;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import org.apache.log4j.Logger;
import de.innovationgate.utils.CertificateValidationUtils;
import de.innovationgate.utils.GroupMembershipResolver;
import de.innovationgate.utils.GroupResolvingException;
import de.innovationgate.utils.WGUtils;
import de.innovationgate.webgate.api.WGAPIException;
import de.innovationgate.webgate.api.WGArea;
import de.innovationgate.webgate.api.WGCSSJSModule;
import de.innovationgate.webgate.api.WGContent;
import de.innovationgate.webgate.api.WGContentEvent;
import de.innovationgate.webgate.api.WGContentEventListener;
import de.innovationgate.webgate.api.WGDatabase;
import de.innovationgate.webgate.api.WGException;
import de.innovationgate.webgate.api.WGFactory;
import de.innovationgate.webgate.api.WGFileContainer;
import de.innovationgate.webgate.api.WGScriptModule;
import de.innovationgate.webgate.api.WGStructEntry;
import de.innovationgate.webgate.api.WGStructEntryList;
import de.innovationgate.webgate.api.WGUnavailableException;
import de.innovationgate.webgate.api.auth.AuthenticationException;
import de.innovationgate.webgate.api.auth.AuthenticationSession;
import de.innovationgate.webgate.api.auth.AuthenticationSourceListener;
import de.innovationgate.webgate.api.auth.CertAuthCapableAuthModule;
import de.innovationgate.webgate.api.auth.ConfigurationException;
import de.innovationgate.wgpublisher.WGACore;
import de.innovationgate.wgpublisher.WGACoreEvent;
import de.innovationgate.wgpublisher.WGACoreEventListener;
import de.innovationgate.wgpublisher.expressions.ExpressionEngine;
import de.innovationgate.wgpublisher.expressions.ExpressionEngineFactory;
import de.innovationgate.wgpublisher.expressions.ExpressionResult;
import de.innovationgate.wgpublisher.webtml.utils.TMLContext;
public class CSAuthModule implements CoreAwareAuthModule, CertAuthCapableAuthModule, WGACoreEventListener, WGContentEventListener {
public static final Logger LOG = Logger.getLogger("wga.api.auth.cs");
public static final String DEFAULT_GROUPSROOT = "authgroups";
public static final String DEFAULT_USERSROOT = "authusers";
/**
* The database to take auth information from is yet unknown
*/
public static final int STATUS_AUTHDB_UNKNOWN = 0;
/**
* The database to take auth information from is known, prepared but not yet connected,
* so the auth module didn't yet collect login data
*/
public static final int STATUS_AUTHDB_PREPARED = 1;
/**
* The database to take auth information from is known and the login data has been fetched
*/
public static final int STATUS_READY = 2;
public class GroupMembership {
private String _name;
private Set _groups;
public GroupMembership(String name) {
_name = name;
_groups = new HashSet();
}
/**
* @return Returns the members.
*/
public Set getGroups() {
return _groups;
}
public void addGroup(String group) {
_groups.add(group);
}
/**
* @return Returns the name.
*/
public String getName() {
return _name;
}
}
public class CSGroupMembershipResolver extends GroupMembershipResolver {
private Map _groupMemberships;
public CSGroupMembershipResolver(Map groupMemberships) {
_groupMemberships = groupMemberships;
}
public Set resolveDirectMembership(String member) throws GroupResolvingException {
GroupMembership membership = (GroupMembership) _groupMemberships.get(member.toLowerCase());
if (membership != null) {
return membership.getGroups();
}
else {
return new HashSet();
}
}
}
public Set<String> _groupInformation = new HashSet<String>();
public class AuthCollector implements Runnable {
public static final String CSAUTH_PROPERTIES_FILE = "csauth.properties";
public void run() {
synchronized (CSAuthModule.this) {
try {
_currentCollectorThread = Thread.currentThread();
WGDatabase db = (WGDatabase) _core.getContentdbs().get(_dbkey);
if (db == null) {
_core.getLog().error("Cannot collect authentications from db '" + _dbkey + "' because the db is not connected");
return;
}
db.openSession();
db.getSessionContext().setTask("CS Authentication Module - Login collection task");
// Try to read configuration
readConfigurationProperties(db);
// Collect logins
if (_scriptCollect != null) {
doCustomCollect(db);
}
else {
doDefaultCollect(db);
}
// Notify listeners
synchronized (_listeners) {
Iterator listeners = _listeners.iterator();
while (listeners.hasNext()) {
((AuthenticationSourceListener) listeners.next()).authenticationDataChanged();
}
}
}
catch (WGUnavailableException e) {
_core.getLog().error("Cannot collect authentications from db '" + _dbkey + "' because the db is currently unavailable");
}
catch (Exception e) {
_core.getLog().error("Error collecting authentications from db' " + _dbkey + "'", e);
}
finally {
_currentCollectorThread = null;
WGFactory.getInstance().closeSessions();
}
}
}
private void readConfigurationProperties(WGDatabase db) throws WGAPIException {
try {
WGFileContainer system = db.getFileContainer("system");
if (system == null) {
return;
}
if (system.hasFile(CSAUTH_PROPERTIES_FILE)) {
Properties props = new Properties();
props.load(system.getFileData(CSAUTH_PROPERTIES_FILE));
configure(props);
_internalConfiguration = true;
}
}
catch (IOException e) {
_core.getLog().error("Unable to read csauth configuration from file " + CSAUTH_PROPERTIES_FILE, e);
}
}
private void doDefaultCollect(WGDatabase db) throws WGException {
Map newLoginInformation = new HashMap();
Set newGroupInformation = new HashSet();
// collect group struct entries / if any exist
Map groupMembership = new HashMap();
if (_groupsRootDoc != null) {
WGStructEntryList groupStructEntries = null;
WGContent rootContent = db.getContentByName(_groupsRootDoc);
if (rootContent != null) {
groupStructEntries = rootContent.getStructEntry().getChildEntries();
}
if (groupStructEntries == null) {
WGArea area = db.getArea(_groupsRootDoc);
if (area != null) {
groupStructEntries = area.getRootEntries();
}
}
if (groupStructEntries == null) {
throw new WGException("Root doc name '" + _groupsRootDoc + "' is neither the unique name of a content nor an area name. Please check if the default language " + db.getDefaultLanguage() + " of this database matches the language of group documents.");
}
// Recurse through group docs
Iterator structsIt = groupStructEntries.iterator();
while (structsIt.hasNext()) {
recurseGroupDocuments((WGStructEntry) structsIt.next(), groupMembership, newGroupInformation);
}
}
// Collect user struct entries {
WGStructEntryList structEntries = null;
WGContent rootContent = db.getContentByName(_userRootDoc);
if (rootContent != null) {
structEntries = rootContent.getStructEntry().getChildEntries();
}
if (structEntries == null) {
WGArea area = db.getArea(_userRootDoc);
if (area != null) {
structEntries = area.getRootEntries();
}
}
if (structEntries == null) {
throw new WGException("Root doc name '" + _userRootDoc + "' is neither the unique name of a content nor an area name. Please check if the default language " + db.getDefaultLanguage() + " of this database matches the language of user documents.");
}
// Recurse through user docs
Iterator structsIt = structEntries.iterator();
while (structsIt.hasNext()) {
recurseLoginDocuments((WGStructEntry) structsIt.next(), newLoginInformation);
}
// Merge users with group memberships
CSGroupMembershipResolver membershipResolver = new CSGroupMembershipResolver(groupMembership);
Iterator loginsIt = newLoginInformation.keySet().iterator();
String loginName;
Login login;
while (loginsIt.hasNext()) {
loginName = (String) loginsIt.next();
login = (Login) newLoginInformation.get(loginName);
try {
login.addGroups(membershipResolver.resolveMembership(loginName));
}
catch (GroupResolvingException e) {
_core.getLog().error("Error resolving group member ship for user name '" + loginName + "'", e);
}
}
_loginInformation = newLoginInformation;
_groupInformation = newGroupInformation;
}
private void recurseGroupDocuments(WGStructEntry entry, Map groupMembership, Set<String> groupInformation) throws WGAPIException {
// Look for content with necessary items. Fetch logins if available
WGContent content = entry.getReleasedContent(entry.getDatabase().getDefaultLanguage());
if (content != null && isGroupDefinition(content)) {
fetchDefaultGroupsFromDocument(groupMembership, content, groupInformation);
}
// Recurse thru children
Iterator children = entry.getChildEntries().iterator();
while (children.hasNext()) {
recurseGroupDocuments((WGStructEntry) children.next(), groupMembership, groupInformation);
}
}
private boolean isGroupDefinition(WGContent content) throws WGAPIException {
if (!content.hasItem(_groupnameItem) || !content.hasItem(_membersItem)) {
return false;
}
if (content.hasItem(_enabledItem) && !content.getItemText(_enabledItem).equals("true")) {
return false;
}
return true;
}
private void fetchDefaultGroupsFromDocument(Map groupMemberships, WGContent content, Set<String> groupInformation) throws WGAPIException {
String groupname = content.getItemText(_groupnameItem);
groupInformation.add(groupname);
List members = content.getItemValueList(_membersItem);
if (members == null) {
members = new ArrayList();
}
Iterator membersIt = members.iterator();
String member;
while (membersIt.hasNext()) {
member = (String) membersIt.next();
GroupMembership membership = (GroupMembership) groupMemberships.get(member.toLowerCase());
if (membership == null) {
membership = new GroupMembership(member);
groupMemberships.put(member.toLowerCase(), membership);
}
membership.addGroup(groupname);
}
}
private void recurseLoginDocuments(WGStructEntry entry, Map logins) throws WGAPIException {
// Look for content with necessary items. Fetch logins if available
WGContent content = entry.getReleasedContent(entry.getDatabase().getDefaultLanguage());
if (content != null && isLoginDefinition(content)) {
fetchDefaultLoginsFromDocument(logins, content);
}
// Recurse thru children
Iterator children = entry.getChildEntries().iterator();
while (children.hasNext()) {
recurseLoginDocuments((WGStructEntry) children.next(), logins);
}
}
private boolean isLoginDefinition(WGContent content) throws WGAPIException {
if (!content.hasItem(_usernameItem) || !content.hasItem(_passwordItem)) {
return false;
}
if (content.hasItem(_enabledItem) && content.getItemText(_enabledItem)!=null && !content.getItemText(_enabledItem).equals("true")) {
return false;
}
return true;
}
private void fetchDefaultLoginsFromDocument(Map logins, WGContent content) throws WGAPIException {
String password = content.getItemText(_passwordItem);
String username = content.getItemText(_usernameItem);
String email = content.getItemText(_emailItem);
List nameAliases = content.getItemValueList(_aliasesItem);
if (nameAliases == null) {
nameAliases = new ArrayList();
}
Login login = new Login(username, password, email, content.getDocumentKey(), new HashSet(nameAliases));
for (String labeledName : _labeledNames) {
if (content.hasItem(labeledName)) {
login.addLabeledName(labeledName, content.getItemValue(labeledName));
}
}
putLogin(logins, login);
}
private void putLogin(Map logins, Login login) {
// Test if this username is already in use
String distName = login.getDistinguishedName();
Login oldLogin = (Login) logins.get(distName);
if (oldLogin != null) {
// Both names are first user names. New one wins
if (distName.equals(oldLogin.getDistinguishedName())) {
_core.getLog().warn(
"Auth db '" + _dbkey + "': First user name '" + distName + "' is multiply defined by document '" + oldLogin.getDocumentkey() + "' and '"
+ login.getDocumentkey() + "'");
}
// New name is first user name, old is not. New wins
else {
_core.getLog().warn(
"Auth db '" + _dbkey + "': First user name '" + oldLogin.getDistinguishedName() + "' is also defined as alias by document '" + oldLogin.getDocumentkey()
+ "'. The alias will be overwritten.");
}
}
putLoginName(logins, login, distName);
// Put login under aliases
Iterator aliases = login.getAliases().iterator();
String alias;
while (aliases.hasNext()) {
alias = (String) aliases.next();
if (alias.trim().equals("")) {
continue;
}
// Test if this name is already in use
oldLogin = (Login) logins.get(alias);
if (oldLogin != null) {
// Old name is first user name, new is not. Old wins
// (exiting method)
if (oldLogin.getDistinguishedName().equals(alias)) {
_core.getLog().warn(
"Auth db '" + _dbkey + "': First user name '" + distName + "' is also defined as alias by document '" + login.getDocumentkey()
+ "'. The alias will be overwritten.");
continue;
}
// Both are aliases. New wins
else {
_core.getLog().warn(
"Auth db '" + _dbkey + "': Alias '" + alias + "' from document '" + login.getDocumentkey() + "' is also defined as alias by document '" + oldLogin.getDocumentkey()
+ "'. The alias of the second document will be overwritten.");
}
}
putLoginName(logins, login, alias);
}
}
private void putLoginName(Map logins, Login login, String name) {
logins.put(name.toLowerCase(), login);
}
private void doCustomCollect(WGDatabase db) throws WGException {
Map newLoginInformation = new HashMap();
Set newGroupInformation = new HashSet();
// Get collect script
WGCSSJSModule mod = db.getCSSJSModule(_scriptCollect, WGScriptModule.CODETYPE_TMLSCRIPT);
if (mod == null) {
throw new WGException("Database '" + _dbkey + "' does not contain a TMLScript module of name '" + _scriptCollect + "'");
}
if (!mod.getCodeType().equals(WGCSSJSModule.CODETYPE_TMLSCRIPT)) {
throw new WGException("Script module '" + _scriptCollect + "' in Database '" + _dbkey + "' is not of type TMLScript");
}
// Build a TMLScript runtime
ExpressionEngine engine = ExpressionEngineFactory.getTMLScriptEngine();
TMLContext context = new TMLContext(db.getDummyContent(db.getDefaultLanguage()), _core, null, null);
Map objects = new HashMap();
objects.put("logins", newLoginInformation);
objects.put("groups", newGroupInformation);
// Execute script
ExpressionResult result = engine.evaluateExpression(mod.getCode(), context, ExpressionEngine.TYPE_SCRIPT, objects);
if (result.isError()) {
throw new WGException("Error executing collect script", result.getException());
}
_loginInformation = newLoginInformation;
_groupInformation = newGroupInformation;
}
}
public static final String COPTION_DBKEY = "auth.cs.dbkey";
public static final String COPTION_ROOTDOC_USERS = "auth.cs.rootdoc.users";
public static final String COPTION_ROOTDOC_GROUPS = "auth.cs.rootdoc.groups";
public static final String COPTION_SCRIPT_COLLECT = "auth.cs.script.collect";
public static final String COPTION_ITEM_USERNAME = "auth.cs.item.username";
public static final String COPTION_ITEM_GROUPNAME = "auth.cs.item.groupname";
public static final String COPTION_ITEM_GROUPMEMBERS = "auth.cs.item.groupmembers";
public static final String COPTION_ITEM_PASSWORD = "auth.cs.item.password";
public static final String COPTION_ITEM_ALIASES = "auth.cs.item.aliases";
public static final String COPTION_ITEM_EMAIL = "auth.cs.item.email";
public static final String COPTION_ITEM_ENABLED = "auth.cs.item.enabled";
public static final String COPTION_LABELED_NAMES = "auth.cs.labelednames";
public static final String COPTION_COLLECT_CONDITION = "auth.cs.collect.condition";
public static final String DEFAULTITEM_EMAIL = "EMail";
public static final String DEFAULTITEM_USERALIASES = "UserAliases";
public static final String DEFAULTITEM_PASSWORD = "Password";
public static final String DEFAULTITEM_USERNAME = "UserName";
public static final String DEFAULTITEM_GROUPNAME = "GroupName";
public static final String DEFAULTITEM_MEMBERS = "Members";
public static final String DEFAULTITEM_ENABLED = "Enabled";
private WGACore _core;
private Map<String,Login> _loginInformation = new HashMap<String,Login>();
private String _dbkey;
private String _userRootDoc = DEFAULT_USERSROOT;
private volatile int _status = STATUS_AUTHDB_UNKNOWN;
private String _groupsRootDoc = DEFAULT_GROUPSROOT;
private WGDatabase _registeredDB = null;
private String _scriptCollect;
private String _usernameItem = DEFAULTITEM_USERNAME;
private String _passwordItem = DEFAULTITEM_PASSWORD;
private String _aliasesItem = DEFAULTITEM_USERALIASES;
private String _emailItem = DEFAULTITEM_EMAIL;
private String _enabledItem = DEFAULTITEM_ENABLED;
private String _membersItem = DEFAULTITEM_MEMBERS;
private String _groupnameItem = DEFAULTITEM_GROUPNAME;
private String _collectCondition = null;
private boolean _internalConfiguration = false;
private Set<String> _labeledNames = new HashSet<String>();
private List _listeners = new ArrayList();
private Thread _currentCollectorThread = null;
private X509Certificate _currentCA;
private long _currentCALastModified;
private String _certCA = null;
private String _certCRL = null;
private long _currentCRLLastModified;
private X509CRL _currentCRL;
private boolean _certAuth = false;
/**
* Enable certificate authentication
*/
public static final String COPTION_CERTAUTH = "auth.cs.certauth";
/**
* Filename of the CA to be used to verify clientcertificates
*/
public static final String COPTION_CA = "auth.cs.ca";
/**
* Filename of the CRL for the given CA
*/
public static final String COPTION_CRL = "auth.cs.crl";
public void init(Map params, WGDatabase db) throws ConfigurationException {
_dbkey = ((String) params.get(COPTION_DBKEY)).toLowerCase();
configure(params);
if (_dbkey == null) {
throw new ConfigurationException("The dbkey of the authentication db is not specified");
}
if (_scriptCollect == null && _userRootDoc == null) {
throw new ConfigurationException("Either a collection script or a root document for login documents must be specified");
}
// Try to fetch target db at this time. May get connected later
registerWithTargetDatabase((WGDatabase) _core.getContentdbs().get(_dbkey));
}
private void configure(Map params) {
if (params.containsKey(COPTION_ROOTDOC_USERS)) {
_userRootDoc = (String) params.get(COPTION_ROOTDOC_USERS);
}
if (params.containsKey(COPTION_ROOTDOC_GROUPS)) {
_groupsRootDoc = (String) params.get(COPTION_ROOTDOC_GROUPS);
}
_scriptCollect = (String) params.get(COPTION_SCRIPT_COLLECT);
_collectCondition = (String) params.get(COPTION_COLLECT_CONDITION);
if (params.containsKey(COPTION_ITEM_USERNAME)) {
_usernameItem = (String) params.get(COPTION_ITEM_USERNAME);
}
if (params.containsKey(COPTION_ITEM_PASSWORD)) {
_passwordItem = (String) params.get(COPTION_ITEM_PASSWORD);
}
if (params.containsKey(COPTION_ITEM_ALIASES)) {
_aliasesItem = (String) params.get(COPTION_ITEM_ALIASES);
}
if (params.containsKey(COPTION_ITEM_EMAIL)) {
_emailItem = (String) params.get(COPTION_ITEM_EMAIL);
}
if (params.containsKey(COPTION_ITEM_ENABLED)) {
_enabledItem = (String) params.get(COPTION_ITEM_ENABLED);
}
if (params.containsKey(COPTION_ITEM_GROUPNAME)) {
_groupnameItem = (String) params.get(COPTION_ITEM_GROUPNAME);
}
if (params.containsKey(COPTION_ITEM_GROUPMEMBERS)) {
_membersItem = (String) params.get(COPTION_ITEM_GROUPMEMBERS);
}
if (params.containsKey(COPTION_CERTAUTH)) {
_certAuth = Boolean.parseBoolean((String) params.get(COPTION_CERTAUTH));
}
if (params.containsKey(COPTION_CA)) {
_certCA = (String) params.get(COPTION_CA);
}
if (params.containsKey(COPTION_CRL)) {
_certCRL = (String) params.get(COPTION_CRL);
}
if (params.containsKey(COPTION_LABELED_NAMES)) {
_labeledNames.addAll(WGUtils.deserializeCollection((String) params.get(COPTION_LABELED_NAMES), ","));
}
}
private void registerWithTargetDatabase(WGDatabase dbTarget) {
if (dbTarget != null && dbTarget.getDbReference().equals(_dbkey)) {
dbTarget.addContentEventListener(this);
_registeredDB = dbTarget;
// If not yet connected, we will wait for our first login to collect
if (!dbTarget.isConnected()) {
_status = STATUS_AUTHDB_PREPARED;
return;
}
try {
runAuthCollector().join();
}
catch (InterruptedException e) {
}
}
}
private void unregisterFromTargetDatabase(WGDatabase dbTarget) {
if (dbTarget != null && dbTarget.getDbReference().equals(_dbkey)) {
dbTarget.removeContentEventListener(this);
_registeredDB = null;
}
}
public AuthenticationSession login(X509Certificate cert) throws AuthenticationException {
if (_status == STATUS_AUTHDB_UNKNOWN) {
throw new AuthenticationException("Authentication was unable to collect data for valid logins yet. Please ensure that source database '" + _dbkey + "' is enabled and can be connected.");
}
if (_status == STATUS_AUTHDB_PREPARED) {
try {
runAuthCollector().join();
}
catch (InterruptedException e) {
}
}
String user = cert.getSubjectDN().toString();
Login login = (Login) getLogin(user);
if (login != null) {
return login;
}
else {
LOG.warn("Failed login for '" + user + "': Unknown user (" + getAuthenticationSource() + ")");
return null;
}
}
public AuthenticationSession login(String user, Object credentials) throws AuthenticationException {
if (_status == STATUS_AUTHDB_UNKNOWN) {
throw new AuthenticationException("Authentication was unable to collect data for valid logins yet. Please ensure that source database '" + _dbkey + "' is enabled and can be connected.");
}
if (_status == STATUS_AUTHDB_PREPARED) {
try {
runAuthCollector().join();
}
catch (InterruptedException e) {
}
}
try {
String password = String.valueOf(credentials);
Login login = (Login) getLogin(user);
if (login != null) {
String hashedPassword = WGUtils.hashPassword(password);
if (login.getPassword() != null && login.getPassword().equals(hashedPassword)) {
return login;
}
else {
LOG.warn("Failed login for '" + user + "': Wrong password (" + getAuthenticationSource() + ")");
}
}
else {
LOG.warn("Failed login for '" + user + "': Unknown user (" + getAuthenticationSource() + ")");
}
return null;
}
catch (NoSuchAlgorithmException e) {
throw new AuthenticationException("Neccessary password hash algorithm not available: " + e.getMessage());
}
}
private CSUserGroupInfo buildUserInfo(Login login){
CSUserGroupInfo newUserEntry = new CSUserGroupInfo();
newUserEntry.setIsUser(true);
newUserEntry.setIsGroup(false);
newUserEntry.setFullQualifiedName(login.getDistinguishedName());
newUserEntry.setAliasNames(login.getAliases());
newUserEntry.getAttributes().put("mail", login.getMailAddress());
newUserEntry.getAttributes().put("groups", login.getGroups());
newUserEntry.getAttributes().put("documentKey", login.getDocumentkey());
if (login.getLabeledNames() != null) {
newUserEntry.getAttributes().putAll(login.getLabeledNames());
newUserEntry.getLabeledNames().putAll(login.getLabeledNames());
}
return newUserEntry;
}
private Object getLogin(String user) {
return _loginInformation.get(user.toLowerCase());
}
public String getEMailAddress(String user) {
Object loginObj = getLogin(user);
if (loginObj != null && loginObj instanceof Login) {
return ((Login) loginObj).getMailAddress();
}
else {
return null;
}
}
public void clearCache() {
}
public String getAuthenticationSource() {
return "Content-Store-Authentication against database " + _dbkey + (_internalConfiguration ? " (internaly configured)" : " (user documents under '" + _userRootDoc + "')");
}
public void setCore(WGACore core) {
_core = core;
core.addEventListener(this);
}
public void contentStoreConnected(WGACoreEvent event) {
registerWithTargetDatabase(event.getDatabase());
}
public void contentStoreDisconnected(WGACoreEvent event) {
unregisterFromTargetDatabase(event.getDatabase());
}
private synchronized Thread runAuthCollector() {
Thread collectorThread = new Thread(new AuthCollector());
collectorThread.start();
_status = STATUS_READY;
return collectorThread;
}
public boolean isTemporary() {
return false;
}
public void contentCreated(WGContentEvent contentEvent) {
}
public boolean contentSaved(WGContentEvent contentEvent) {
return true;
}
public void contentHasBeenSaved(WGContentEvent event) {
runAuthCollectorByEvent(event);
}
public void contentHasBeenDeleted(WGContentEvent event) {
runAuthCollector();
}
public void runAuthCollectorByEvent(WGContentEvent event) {
if (_collectCondition != null) {
try {
WGContent content = event.getContent();
if (content == null) {
content = (WGContent) event.getDatabase().getDocumentByDocumentKey(event.getDocumentKey());
}
if (content != null) {
TMLContext context = new TMLContext(event.getContent(), _core, null, null);
Map objects = new HashMap();
// Execute script
ExpressionEngine engine = ExpressionEngineFactory.getTMLScriptEngine();
ExpressionResult result = engine.evaluateExpression(_collectCondition, context, ExpressionEngine.TYPE_EXPRESSION, objects);
if (result.isError()) {
_core.getLog().error("Error auth collector condition script", result.getException());
return;
}
else if (result.isFalse()) {
return;
}
}
}
catch (WGAPIException e) {
_core.getLog().error("Error runAuthCollectorByEvent.", e);
return;
}
}
runAuthCollector();
}
public boolean isPoolable() {
return true;
}
/* (non-Javadoc)
* @see de.innovationgate.webgate.api.auth.AuthenticationModule#addAuthenticationSourceListener(de.innovationgate.webgate.api.auth.AuthenticationSourceListener)
*/
public void addAuthenticationSourceListener(AuthenticationSourceListener listener) {
synchronized (_listeners) {
_listeners.add(listener);
}
}
/* (non-Javadoc)
* @see de.innovationgate.webgate.api.auth.AuthenticationModule#removeAuthenticationSourceListener(de.innovationgate.webgate.api.auth.AuthenticationSourceListener)
*/
public void removeAuthenticationSourceListener(AuthenticationSourceListener listener) {
synchronized (_listeners) {
_listeners.remove(listener);
}
}
/* (non-Javadoc)
* @see de.innovationgate.webgate.api.auth.AuthenticationModule#getAllowedCredentialClasses()
*/
public Class[] getAllowedCredentialClasses() {
return new Class[] {String.class};
}
/*
* (non-Javadoc)
* @see de.innovationgate.webgate.api.auth.AuthenticationModule#isQueryable(java.lang.String)
*/
public boolean isQueryable(String queryType) {
if(queryType.equals(QUERY_USERS_AND_GROUPS)) {
return true;
}
else if (queryType.equals(QUERY_USER_DN)) {
return true;
}
return false;
}
/*
* (non-Javadoc)
* @see de.innovationgate.webgate.api.auth.AuthenticationModule#query(java.lang.Object, java.lang.String)
*/
public Object query(Object query, String queryType) {
if(queryType.equals(QUERY_USERS_AND_GROUPS)) {
return findUsersAndGroups((String) query);
}
else if (queryType.equals(QUERY_USER_DN)) {
return fetchUserByDN((String) query);
}
return null;
}
private Object fetchUserByDN(String query) {
Login login = _loginInformation.get(query.toLowerCase());
if (login != null && login.getDistinguishedName().equalsIgnoreCase(query)) {
return buildUserInfo(login);
}
else {
return null;
}
}
/**
* retrieves all users and groups with the given prefix in nameAttributes
* @param prefix to search for
* @return a list of de.innovationgate.webgate.api.auth.UserGroupInfo
*/
private List<CSUserGroupInfo> findUsersAndGroups(String prefix) {
ArrayList<CSUserGroupInfo> results = new ArrayList<CSUserGroupInfo>();
String current;
Iterator<String> it = _loginInformation.keySet().iterator();
while(it.hasNext()){
current = (String) it.next();
if(current.indexOf(prefix)!=-1){
Login login = _loginInformation.get(current);
results.add(buildUserInfo(login));
}
}
it = _groupInformation.iterator();
while (it.hasNext()) {
current = (String) it.next();
if(current.indexOf(prefix)!=-1){
results.add(buildGroupInfo(current));
}
}
return results;
}
private CSUserGroupInfo buildGroupInfo(String groupName) {
CSUserGroupInfo newUserEntry = new CSUserGroupInfo();
newUserEntry.setIsUser(false);
newUserEntry.setIsGroup(true);
newUserEntry.setFullQualifiedName(groupName);
return newUserEntry;
}
/**
* @return Returns the status.
*/
public int getStatus() {
return _status;
}
public void shutdownPostDisconnect(WGACoreEvent event) {
}
public void shutdownPreDisconnect(WGACoreEvent event) {
}
public void startupPostConnect(WGACoreEvent event) {
}
public void startupPreConnect(WGACoreEvent event) {
}
public void destroy() {
if (_core != null) {
_core.removeEventListener(this);
_core = null;
}
if (_registeredDB != null) {
_registeredDB.removeContentEventListener(this);
_registeredDB = null;
}
}
public boolean isGeneratesSessionToken() {
return false;
}
/**
* returns the unique name of the root document for users
* @return
*/
public String getUserRootDoc() {
return _userRootDoc;
}
/**
* returns the unique name of the root document for groups
* @return
*/
public String getGroupsRootDoc() {
return _groupsRootDoc;
}
/**
* returns the dbkey of the authentication source
* @return
*/
public String getDbkey() {
return _dbkey;
}
/**
* returns the script code for custom collection
* @return
*/
public String getScriptCollect() {
return _scriptCollect;
}
/**
* returns the username item
* @return
*/
public String getUsernameItem() {
return _usernameItem;
}
/**
* returns the password item name
* @return
*/
public String getPasswordItem() {
return _passwordItem;
}
/**
* returns the alias item name
* @return
*/
public String getAliasesItem() {
return _aliasesItem;
}
/**
* returns the email item name
* @return
*/
public String getEmailItem() {
return _emailItem;
}
/**
* returns the enabled item name
* @return
*/
public String getEnabledItem() {
return _enabledItem;
}
/**
* returns the members item name
* @return
*/
public String getMembersItem() {
return _membersItem;
}
/**
* returns the groupname item
* @return
*/
public String getGroupnameItem() {
return _groupnameItem;
}
/**
* returns the collect condition
* @return
*/
public String getCollectCondition() {
return _collectCondition;
}
public Thread getCurrentCollectorThread() {
return _currentCollectorThread;
}
public void contentHasBeenMoved(WGContentEvent event) {
runAuthCollectorByEvent(event);
}
private void loadCA(File caFile) throws AuthenticationException {
try {
CertificateFactory certificatefactory = CertificateFactory.getInstance("X.509");
FileInputStream fin = new FileInputStream(caFile);
_currentCA = (X509Certificate) certificatefactory.generateCertificate(fin);
_currentCALastModified = caFile.lastModified();
fin.close();
}
catch (Exception e) {
String message = "Could not load CA '" + caFile.getPath() + "'";
WGFactory.getLogger().error(message, e);
throw new AuthenticationException(message, e);
}
// verify crl is signed by ca
try {
getCRL().verify(_currentCA.getPublicKey());
}
catch (Exception e) {
String message = "CRL '" + caFile.getPath() + "' could not be verified against given CA.";
WGFactory.getLogger().error(message, e);
throw new AuthenticationException(message, e);
}
}
/**
* Returns the certificate authority for certificate authentication
*
* @throws AuthenticationException
*/
public X509Certificate getCA() throws AuthenticationException {
if (_certCA == null) {
throw new AuthenticationException("CA is not configured properly for auth source: " + getAuthenticationSource());
}
File caFile = _core.getWGAFile(_certCA);
if (caFile.exists()) {
// check if CA is already loaded
if (_currentCA != null) {
// check if CA has been changed
if (caFile.lastModified() != _currentCALastModified) {
// reload ca
loadCA(caFile);
}
}
else {
// load ca
loadCA(caFile);
}
return _currentCA;
}
else {
String message = "Could not find CA '" + _certCA + "'. No such file.";
WGFactory.getLogger().error(message);
throw new AuthenticationException(message);
}
}
private void loadCRL(File crlFile) throws AuthenticationException {
try {
CertificateFactory certificatefactory = CertificateFactory.getInstance("X.509");
FileInputStream fin = new FileInputStream(crlFile);
_currentCRL = (X509CRL) certificatefactory.generateCRL(fin);
_currentCRLLastModified = crlFile.lastModified();
fin.close();
}
catch (Exception e) {
String message = "Could not load CRL '" + crlFile.getPath() + "'";
WGFactory.getLogger().error(message, e);
throw new AuthenticationException(message, e);
}
// verify crl is signed by expected ca
try {
_currentCRL.verify(getCA().getPublicKey());
}
catch (Exception e) {
String message = "CRL '" + crlFile.getPath() + "' could not be verified against given CA.";
WGFactory.getLogger().error(message, e);
throw new AuthenticationException(message, e);
}
}
/**
* Returns the certificate revoke list for certificate authentication
*
* @throws AuthenticationException
*/
public X509CRL getCRL() throws AuthenticationException {
if (_certCRL == null) {
throw new AuthenticationException("CRL is not configured properly for auth source: " + getAuthenticationSource());
}
File crlFile = _core.getWGAFile(_certCRL);
if (crlFile.exists()) {
// check if CRL is already loaded
if (_certCRL != null) {
// check if CRL has been changed
if (crlFile.lastModified() != _currentCRLLastModified) {
// reload crl
loadCRL(crlFile);
}
}
else {
// load crl
loadCRL(crlFile);
}
return _currentCRL;
}
else {
String message = "Could not find CRL '" + _certCRL + "'. No such file.";
WGFactory.getLogger().error(message);
throw new AuthenticationException(message);
}
}
public boolean isCertAuthEnabled() {
return _certAuth;
}
public void contentStatusChanged(WGContentEvent event) {
}
}