/*
This file is part of Subsonic.
Subsonic 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.
Subsonic 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 Subsonic. If not, see .
Copyright 2009 (C) Sindre Mehus
*/
package net.sourceforge.subsonic.ldap;
import net.sourceforge.subsonic.Logger;
import net.sourceforge.subsonic.domain.User;
import net.sourceforge.subsonic.service.SecurityService;
import net.sourceforge.subsonic.service.SettingsService;
import org.acegisecurity.BadCredentialsException;
import org.acegisecurity.ldap.DefaultInitialDirContextFactory;
import org.acegisecurity.ldap.search.FilterBasedLdapUserSearch;
import org.acegisecurity.providers.ldap.LdapAuthenticator;
import org.acegisecurity.providers.ldap.authenticator.BindAuthenticator;
import org.acegisecurity.userdetails.ldap.LdapUserDetails;
import org.apache.commons.lang.StringUtils;
import java.util.HashMap;
import java.util.Map;
/**
* LDAP authenticator which uses a delegate {@link BindAuthenticator}, and which
* supports dynamically changing LDAP provider URL and search filter.
*
* @author Sindre Mehus
*/
public class SubsonicLdapBindAuthenticator implements LdapAuthenticator {
private static final Logger LOG = Logger.getLogger(SubsonicLdapBindAuthenticator.class);
private SecurityService securityService;
private SettingsService settingsService;
private long authenticatorTimestamp;
private BindAuthenticator delegateAuthenticator;
public LdapUserDetails authenticate(String username, String password) {
// LDAP authentication must be enabled on the system.
if (!settingsService.isLdapEnabled()) {
throw new BadCredentialsException("LDAP authentication disabled.");
}
// User must be defined in Subsonic, unless auto-shadowing is enabled.
User user = securityService.getUserByName(username);
if (user == null && !settingsService.isLdapAutoShadowing()) {
throw new BadCredentialsException("User does not exist.");
}
// LDAP authentication must be enabled for the given user.
if (user != null && !user.isLdapAuthenticated()) {
throw new BadCredentialsException("LDAP authentication disabled for user.");
}
try {
createDelegate();
LdapUserDetails details = delegateAuthenticator.authenticate(username, password);
if (details != null) {
LOG.info("User '" + username + "' successfully authenticated in LDAP. DN: " + details.getDn());
if (user == null) {
User newUser = new User(username, "", null, true, 0L, 0L, 0L);
newUser.setStreamRole(true);
newUser.setSettingsRole(true);
securityService.createUser(newUser);
LOG.info("Created local user '" + username + "' for DN " + details.getDn());
}
}
return details;
} catch (RuntimeException x) {
LOG.info("Failed to authenticate user '" + username + "' in LDAP.", x);
throw x;
}
}
/**
* Creates the delegate {@link BindAuthenticator}.
*/
private synchronized void createDelegate() {
// Only create it if necessary.
if (delegateAuthenticator == null || authenticatorTimestamp < settingsService.getSettingsChanged()) {
DefaultInitialDirContextFactory contextFactory = new DefaultInitialDirContextFactory(settingsService.getLdapUrl());
String managerDn = settingsService.getLdapManagerDn();
String managerPassword = settingsService.getLdapManagerPassword();
if (StringUtils.isNotEmpty(managerDn) && StringUtils.isNotEmpty(managerPassword)) {
contextFactory.setManagerDn(managerDn);
contextFactory.setManagerPassword(managerPassword);
}
Map extraEnvVars = new HashMap();
extraEnvVars.put("java.naming.referral", "follow");
contextFactory.setExtraEnvVars(extraEnvVars);
FilterBasedLdapUserSearch userSearch = new FilterBasedLdapUserSearch("", settingsService.getLdapSearchFilter(), contextFactory);
userSearch.setSearchSubtree(true);
userSearch.setDerefLinkFlag(true);
delegateAuthenticator = new BindAuthenticator(contextFactory);
delegateAuthenticator.setUserSearch(userSearch);
authenticatorTimestamp = settingsService.getSettingsChanged();
}
}
public void setSecurityService(SecurityService securityService) {
this.securityService = securityService;
}
public void setSettingsService(SettingsService settingsService) {
this.settingsService = settingsService;
}
}