Home » geronimo-2.2-source-release » org.apache.geronimo.security.realm.providers » [javadoc | source]

    1   /**
    2    *  Licensed to the Apache Software Foundation (ASF) under one or more
    3    *  contributor license agreements.  See the NOTICE file distributed with
    4    *  this work for additional information regarding copyright ownership.
    5    *  The ASF licenses this file to You under the Apache License, Version 2.0
    6    *  (the "License"); you may not use this file except in compliance with
    7    *  the License.  You may obtain a copy of the License at
    8    *
    9    *     http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    *  Unless required by applicable law or agreed to in writing, software
   12    *  distributed under the License is distributed on an "AS IS" BASIS,
   13    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    *  See the License for the specific language governing permissions and
   15    *  limitations under the License.
   16    */
   17   
   18   package org.apache.geronimo.security.realm.providers;
   19   
   20   import java.io.IOException;
   21   import java.io.InputStream;
   22   import java.net.URI;
   23   import java.security.Principal;
   24   import java.security.cert.X509Certificate;
   25   import java.util.Arrays;
   26   import java.util.Collections;
   27   import java.util.Enumeration;
   28   import java.util.HashMap;
   29   import java.util.HashSet;
   30   import java.util.Iterator;
   31   import java.util.List;
   32   import java.util.Map;
   33   import java.util.Properties;
   34   import java.util.Set;
   35   import javax.security.auth.Subject;
   36   import javax.security.auth.callback.Callback;
   37   import javax.security.auth.callback.CallbackHandler;
   38   import javax.security.auth.callback.UnsupportedCallbackException;
   39   import javax.security.auth.login.LoginException;
   40   import javax.security.auth.login.FailedLoginException;
   41   import javax.security.auth.spi.LoginModule;
   42   import javax.security.auth.x500.X500Principal;
   43   
   44   import org.slf4j.Logger;
   45   import org.slf4j.LoggerFactory;
   46   import org.apache.geronimo.common.GeronimoSecurityException;
   47   import org.apache.geronimo.security.jaas.JaasLoginModuleUse;
   48   import org.apache.geronimo.security.jaas.WrappingLoginModule;
   49   import org.apache.geronimo.system.serverinfo.ServerInfo;
   50   
   51   
   52   /**
   53    * An example LoginModule that reads a list of credentials and group from a file on disk.
   54    * Authentication is provided by the SSL layer supplying the client certificate.
   55    * All we check is that it is present.  The
   56    * file should be formatted using standard Java properties syntax.  Expects
   57    * to be run by a GenericSecurityRealm (doesn't work on its own).
   58    *
   59    * The usersURI property file should have lines of the form token=certificatename
   60    * where certificate name is X509Certificate.getSubjectX500Principal().getName()
   61    *
   62    * The groupsURI property file should have lines of the form group=token1,token2,...
   63    * where the tokens were associated to the certificate names in the usersURI properties file.
   64    *
   65    * This login module checks security credentials so the lifecycle methods must return true to indicate success
   66    * or throw LoginException to indicate failure.
   67    *
   68    * @version $Rev: 653740 $ $Date: 2008-05-06 03:44:18 -0700 (Tue, 06 May 2008) $
   69    */
   70   public class CertificatePropertiesFileLoginModule implements LoginModule {
   71       private static final Logger log = LoggerFactory.getLogger(CertificatePropertiesFileLoginModule.class);
   72       public final static String USERS_URI = "usersURI";
   73       public final static String GROUPS_URI = "groupsURI";
   74       public final static List<String> supportedOptions = Collections.unmodifiableList(Arrays.asList(USERS_URI, GROUPS_URI));
   75               
   76       private final Map users = new HashMap();
   77       final Map groups = new HashMap();
   78   
   79       private Subject subject;
   80       private CallbackHandler handler;
   81       private X500Principal principal;
   82       private boolean loginSucceeded;
   83       private final Set<Principal> allPrincipals = new HashSet<Principal>();
   84   
   85       public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
   86           this.subject = subject;
   87           this.handler = callbackHandler;
   88           for(Object option: options.keySet()) {
   89               if(!supportedOptions.contains(option) && !JaasLoginModuleUse.supportedOptions.contains(option)
   90                       && !WrappingLoginModule.supportedOptions.contains(option)) {
   91                   log.warn("Ignoring option: "+option+". Not supported.");
   92               }
   93           }
   94           try {
   95               ServerInfo serverInfo = (ServerInfo) options.get(JaasLoginModuleUse.SERVERINFO_LM_OPTION);
   96               URI usersURI = new URI((String)options.get(USERS_URI));
   97               URI groupsURI = new URI((String)options.get(GROUPS_URI));
   98               loadProperties(serverInfo, usersURI, groupsURI);
   99           } catch (Exception e) {
  100               log.error("Failed to load properties", e);
  101               throw new IllegalArgumentException("Unable to configure properties file login module: "+e.getMessage(), e);
  102           }
  103       }
  104   
  105       public void loadProperties(ServerInfo serverInfo, URI usersURI, URI groupURI) throws GeronimoSecurityException {
  106           try {
  107               URI userFile = serverInfo.resolve(usersURI);
  108               URI groupFile = serverInfo.resolve(groupURI);
  109               InputStream stream = userFile.toURL().openStream();
  110               Properties tmpUsers = new Properties();
  111               tmpUsers.load(stream);
  112               stream.close();
  113   
  114               for (Iterator iterator = tmpUsers.entrySet().iterator(); iterator.hasNext();) {
  115                   Map.Entry entry = (Map.Entry) iterator.next();
  116                   users.put(entry.getValue(), entry.getKey());
  117               }
  118   
  119               Properties temp = new Properties();
  120               stream = groupFile.toURL().openStream();
  121               temp.load(stream);
  122               stream.close();
  123   
  124               Enumeration e = temp.keys();
  125               while (e.hasMoreElements()) {
  126                   String groupName = (String) e.nextElement();
  127                   String[] userList = ((String) temp.get(groupName)).split(",");
  128   
  129                   Set userset = (Set) groups.get(groupName);
  130                   if (userset == null) {
  131                       userset = new HashSet();
  132                       groups.put(groupName, userset);
  133                   }
  134   
  135                   for (int i = 0; i < userList.length; i++) {
  136                       String userName = userList[i];
  137                       userset.add(userName);
  138                   }
  139               }
  140   
  141           } catch (Exception e) {
  142               log.error("Properties File Login Module - data load failed", e);
  143               throw new GeronimoSecurityException(e);
  144           }
  145       }
  146   
  147   
  148       /**
  149        * This LoginModule is not to be ignored.  So, this method should never return false.
  150        * @return true if authentication succeeds, or throw a LoginException such as FailedLoginException
  151        *         if authentication fails
  152        */
  153       public boolean login() throws LoginException {
  154           loginSucceeded = false;
  155           Callback[] callbacks = new Callback[1];
  156   
  157           callbacks[0] = new CertificateCallback();
  158           try {
  159               handler.handle(callbacks);
  160           } catch (IOException ioe) {
  161               throw (LoginException) new LoginException().initCause(ioe);
  162           } catch (UnsupportedCallbackException uce) {
  163               throw (LoginException) new LoginException().initCause(uce);
  164           }
  165           assert callbacks.length == 1;
  166           X509Certificate certificate = ((CertificateCallback)callbacks[0]).getCertificate();
  167           if (certificate == null) {
  168               throw new FailedLoginException();
  169           }
  170           principal = certificate.getSubjectX500Principal();
  171   
  172           if(!users.containsKey(principal.getName())) {
  173               // Clear out the private state
  174               principal = null;
  175               throw new FailedLoginException();
  176           }
  177   
  178           loginSucceeded = true;
  179           return true;
  180       }
  181   
  182       /*
  183        * @exception LoginException if login succeeded but commit failed.
  184        *
  185        * @return true if login succeeded and commit succeeded, or false if login failed but commit succeeded.
  186        */
  187       public boolean commit() throws LoginException {
  188           if(loginSucceeded) {
  189               allPrincipals.add(principal);
  190               String userName = (String) users.get(principal.getName());
  191               allPrincipals.add(new GeronimoUserPrincipal(userName));
  192   
  193               Iterator e = groups.keySet().iterator();
  194               while (e.hasNext()) {
  195                   String groupName = (String) e.next();
  196                   Set users = (Set) groups.get(groupName);
  197                   Iterator iter = users.iterator();
  198                   while (iter.hasNext()) {
  199                       String user = (String) iter.next();
  200                       if (userName.equals(user)) {
  201                           allPrincipals.add(new GeronimoGroupPrincipal(groupName));
  202                           break;
  203                       }
  204                   }
  205               }
  206               subject.getPrincipals().addAll(allPrincipals);
  207           }
  208           // Clear out the private state
  209           principal = null;
  210           
  211           return loginSucceeded;
  212       }
  213   
  214       public boolean abort() throws LoginException {
  215           if(loginSucceeded) {
  216               // Clear out the private state
  217               principal = null;
  218               allPrincipals.clear();
  219           }
  220           return loginSucceeded;
  221       }
  222   
  223       public boolean logout() throws LoginException {
  224           // Clear out the private state
  225           loginSucceeded = false;
  226           principal = null;
  227           if(!subject.isReadOnly()) {
  228               // Remove principals added by this LoginModule
  229               subject.getPrincipals().removeAll(allPrincipals);
  230           }
  231           allPrincipals.clear();
  232           return true;
  233       }
  234   
  235   }

Home » geronimo-2.2-source-release » org.apache.geronimo.security.realm.providers » [javadoc | source]