001package org.crsh.plugins.crowd;
002
003import com.atlassian.crowd.exception.ApplicationPermissionException;
004import com.atlassian.crowd.exception.InvalidAuthenticationException;
005import com.atlassian.crowd.integration.rest.service.factory.RestCrowdClientFactory;
006import com.atlassian.crowd.service.client.ClientProperties;
007import com.atlassian.crowd.service.client.ClientPropertiesImpl;
008import com.atlassian.crowd.service.client.ClientResourceLocator;
009import com.atlassian.crowd.service.client.CrowdClient;
010import org.crsh.auth.AuthenticationPlugin;
011import org.crsh.plugin.CRaSHPlugin;
012
013import java.util.logging.Level;
014import java.util.logging.Logger;
015
016/**
017 * Allows to use an Atlassian Crowd serer to authenticate on CRaSH
018 * To use it you need to :
019 * <ul>
020 *   <li>Define the application on the crowd server side,</li>
021 *   <li>Use <pre>crash.auth=crowd</pre> in your crash.properties configuration file,</li>
022 *   <li>Create the <a href="https://confluence.atlassian.com/display/CROWD/The+crowd.properties+File"><pre>crowd.properties</pre> configuration file</a>
023 *   and add it in your application classpath or by defining its path with the system property crowd.properties (<pre>-Dcrowd.properties={FILE-PATH}/crowd.properties</pre>).</li>
024 * </ul>
025 */
026public class CrowdAuthenticationPlugin extends
027    CRaSHPlugin<AuthenticationPlugin> implements
028    AuthenticationPlugin<String> {
029
030  /**
031   * Logger
032   */
033  protected final Logger log = Logger.getLogger(getClass().getName());
034
035  /**
036   * Crowd client instance
037   */
038  private static volatile CrowdClient crowdClient;
039
040  /**
041   * Lock to create the crowd client
042   */
043  private static final Object lock = new Object();
044
045  /**
046   * Get a ready to use CrowdClient.
047   *
048   * @return a CrowdClient already initialized
049   */
050  private static CrowdClient getCrowdClient() {
051    if (crowdClient == null) {
052      synchronized (lock) {
053        if (crowdClient == null) {
054          ClientResourceLocator
055              crl = new ClientResourceLocator("crowd.properties");
056          if (crl.getProperties() == null) {
057            throw new NullPointerException("crowd.properties can not be found in classpath");
058          }
059          ClientProperties clientProperties = ClientPropertiesImpl.newInstanceFromResourceLocator(crl);
060          RestCrowdClientFactory restCrowdClientFactory = new RestCrowdClientFactory();
061          crowdClient = restCrowdClientFactory.newInstance(clientProperties);
062        }
063      }
064    }
065    return crowdClient;
066  }
067
068  public Class<String> getCredentialType() {
069    return String.class;
070  }
071
072  @Override
073  public String getName() {
074    return "crowd";
075  }
076
077  @Override
078  public boolean authenticate(String username, String password) throws Exception {
079    // Username and passwords are required
080    if (username == null || username.isEmpty() || password == null || password.isEmpty()) {
081      log.log(Level.WARNING, "Unable to logon without username and password.");
082      return false;
083    }
084    try {
085      // Authenticate the user
086      if (log.isLoggable(Level.FINE)) {
087        log.log(Level.FINE, "Authenticating '" + username + "' on crowd directory");
088      }
089      getCrowdClient().authenticateUser(username, password);
090      return true;
091    } catch (InvalidAuthenticationException e) {
092      log.log(Level.WARNING, "Authentication failed for user '" + username + "'");
093      return false;
094    } catch (ApplicationPermissionException e) {
095      log.log(Level.SEVERE, "Application not authorized to authenticate user '" + username + "'", e);
096      return false;
097    }
098  }
099
100  @Override
101  public AuthenticationPlugin getImplementation() {
102    return this;
103  }
104}