View Javadoc

1   /*
2    * NISAccountMapper.java
3    *
4    * Created on May 25, 2004, 2:25 PM
5    */
6   
7   package gov.bnl.gums;
8   
9   import java.util.*;
10  import org.apache.commons.logging.*;
11  
12  /*** Maps a user to a local account based on the CN of the certificate and the
13   * gecos field taken from a NIS/YP/LDAP/... database. The mapping can't be perfect, but contains
14   * a series of heuristics that solve up to 90% of the cases, depending on how
15   * the database itself is kept.
16   * <p>
17   * This is an abstact class, and will need the implementation of the method
18   * that retrieves the list of GECOS fields.
19   * <p>
20   * It's suggested not to use this policy by itself, but to have it part of a 
21   * CompositeAccountMapper in which a ManualAccountMapper comes first. This allows
22   * to override those user mapping that are not satisfying.
23   *
24   * @author Gabriele Carcassi
25   */
26  public abstract class GecosAccountMapper implements AccountMapper {
27      static private Log log = LogFactory.getLog(GecosAccountMapper.class);
28      
29      public String mapUser(String userDN) {
30          String[] nameSurname = parseNameAndSurname(userDN);
31          GecosMap map = gecosMap();
32          log.trace("GECOS findAccount. Name: " + nameSurname[0] + " - Surname: " + nameSurname[1] + " - GECOSMap: " + gecosMap());
33          return map.findAccount(nameSurname[0], nameSurname[1]);
34      }
35      
36      public static String[] parseNameAndSurname(String certificateSubject) {
37          int begin = certificateSubject.indexOf("CN=") + 3;
38          String CN = certificateSubject.substring(begin);
39          
40          StringTokenizer tokenizer = new StringTokenizer(CN);
41          List tokens = new ArrayList();
42          while (tokenizer.hasMoreTokens()) {
43              tokens.add(tokenizer.nextToken());
44          }
45          
46          String name = (String) tokens.get(0);
47  
48          int nSurname = 1;
49          while (!checkSurname((String) tokens.get(tokens.size()-nSurname))) {
50              nSurname++;
51          }
52          String surname = (String) tokens.get(tokens.size()-nSurname);
53          
54          log.trace("Certificate '" + certificateSubject + "' divided in name='" + name + "' and surname='" + surname + "'");
55          return new String[] {name, surname};
56      }
57      
58      static boolean checkSurname(String possibleSurname) {
59          if (Character.isDigit(possibleSurname.charAt(0))) {
60              return false;
61          }
62          if (possibleSurname.charAt(possibleSurname.length() - 1) == '.') {
63              return false;
64          }
65          
66          return true;
67      }
68      
69      private static Map gecosMaps = new Hashtable();
70      private GecosMap gecosMap() {
71          synchronized (gecosMaps) {
72              GecosMap map = (GecosMap) gecosMaps.get(mapName());
73              if (map != null) {
74                  if (map.isValid()) {
75                      log.trace("Reusing GECOS map for '" + mapName() +"'");
76                      return map;
77                  } else {
78                      log.trace("Invalidating expired GECOS map for '" + mapName() +"'");
79                      gecosMaps.remove(mapName());
80                  }
81              }
82              log.debug("Creating new GECOS map for '" + mapName() + "'");
83              map = createMap();
84              gecosMaps.put(mapName(), map);
85              return map;
86          }
87      }
88      
89      /*** Implements the creation of the map. The implementation is supposed to
90       * connect to the source, and fill an object of the GecosMap type.
91       */
92      protected abstract GecosMap createMap();
93      
94      /*** Returns an ID for the map used/created by this specific mapper. The
95       * name will be used to cache the generated map also across different
96       * instances of the mapper.
97       */
98      protected abstract String mapName();
99  }