001    package org.crsh.plugins.crowd;
002    
003    import com.atlassian.crowd.exception.ApplicationPermissionException;
004    import com.atlassian.crowd.exception.InvalidAuthenticationException;
005    import com.atlassian.crowd.integration.rest.service.factory.RestCrowdClientFactory;
006    import com.atlassian.crowd.service.client.ClientProperties;
007    import com.atlassian.crowd.service.client.ClientPropertiesImpl;
008    import com.atlassian.crowd.service.client.ClientResourceLocator;
009    import com.atlassian.crowd.service.client.CrowdClient;
010    import org.crsh.auth.AuthenticationPlugin;
011    import org.crsh.plugin.CRaSHPlugin;
012    
013    import java.util.logging.Level;
014    import 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     */
026    public 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    }