1
2
3
4
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 }