View Javadoc

1   /*
2    * GecosMap.java
3    *
4    * Created on March 25, 2004, 4:11 PM
5    */
6   
7   package gov.bnl.gums.account;
8   
9   import gov.bnl.gums.GUMS;
10  import gov.bnl.gums.account.NISClient;
11  
12  import java.io.PrintStream;
13  import java.util.ArrayList;
14  import java.util.Collection;
15  import java.util.Date;
16  import java.util.Hashtable;
17  import java.util.Iterator;
18  import java.util.List;
19  import java.util.Map;
20  
21  import org.apache.commons.collections.MultiHashMap;
22  import org.apache.commons.collections.MultiMap;
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  
26  /** 
27   * Retrieves the map from the NIS server and provide a logic to match name and
28   * surname to an account.
29   *
30   * @author  Gabriele Carcassi, Jay Packard
31   */
32  public class GecosMap {
33      private Log log = LogFactory.getLog(NISClient.class);
34      private Log resourceAdminLog = LogFactory.getLog(GUMS.resourceAdminLog);
35      private Map accountToGecos = new Hashtable();
36      private Map accountToName = new Hashtable();
37      private Map accountToSurname = new Hashtable();
38      private MultiMap nameToAccount = new MultiHashMap();
39      private MultiMap surnameToAccount = new MultiHashMap();
40      private Date lastUpdate = null;
41      private long expiration = 60*60*1000;
42  
43      public void addEntry(String account, String gecos) {
44          String name = retrieveName(gecos);
45          String surname = retrieveSurname(gecos);
46          log.trace("Adding user '" + account + "': GECOS='" + gecos + "' name='" + name + "' surname='" + surname + "'");
47          accountToGecos.put(account, gecos);
48          if (name != null) {
49              accountToName.put(account, name);
50              nameToAccount.put(name.toLowerCase(), account);
51          }
52          accountToSurname.put(account, surname);
53          surnameToAccount.put(surname.toLowerCase(), account);
54          lastUpdate = new Date();
55      }
56      
57      public String findAccount(String name, String surname) {
58          Collection accountsWithName = (Collection) nameToAccount.get(name.toLowerCase());
59          Collection accountsWithSurname = (Collection) surnameToAccount.get(surname.toLowerCase());
60          log.trace("Account matching. Name: " + accountsWithName + "- Surname: " + accountsWithSurname);
61          if ((accountsWithName != null) && (accountsWithSurname != null)) {
62              // Accounts for both name and surname were found
63              List commonAccounts = new ArrayList(accountsWithName);
64              commonAccounts.retainAll(accountsWithSurname);
65              if (commonAccounts.size() == 1) {
66                  // Only one account matching both name and surname was found
67                  // Pretty likely is the correct one
68                  String account = (String) commonAccounts.get(0);
69                  log.trace("NIS account Name/Surname single match. Name: " + name + " - Surname: " + surname + " - account: " + account);
70                  return account;
71              } else if (commonAccounts.size() > 1) {
72                  // More than one account with the matching account has been found
73                  // It might be that only one account is really for the user, and the
74                  // other are group/system accounts
75                  // Check whether only one account contains the surname
76                  Iterator iter = commonAccounts.iterator();
77                  String matchingAccount = null;
78                  while (iter.hasNext()) {
79                      String account = (String) iter.next();
80                      if (account.indexOf(surname.toLowerCase()) != -1) {
81                          if (matchingAccount == null) {
82                              matchingAccount = account;
83                          } else {
84                              // Two accounts matched. Can't decide, return null.
85                              log.trace("NIS account Name/Surname multiple match, multiple account with surname." +
86                              " Name: " + name + " - Surname: " + surname + " - account: not defined");
87                              resourceAdminLog.warn("NIS mapping: couldn't find single match for surname='" + surname + "' name='" + name + "'. Undecided between " + commonAccounts);
88                              return null;
89                          }
90                      }
91                  }
92                  if (matchingAccount != null) {
93                      // Only one matching account was found. There is a chance
94                      // this is the right account
95                      log.trace("NIS account Name/Surname multiple match, single account with surname." +
96                      " Name: " + name + " - Surname: " + surname + " - account: " + matchingAccount);
97                      return matchingAccount;
98                  }
99                  // Can't decide which account is the correct one
100                 log.trace("NIS account Name/Surname multiple match, no account with surname." +
101                 " Name: " + name + " - Surname: " + surname + " - account: not defined");
102                 resourceAdminLog.warn("NIS mapping: couldn't find single match for surname='" + surname + "' name='" + name + "'. Undecided between " + commonAccounts);
103                 return null;
104             }
105             // Common Accounts has no items, disregarding the name
106         }
107         if (accountsWithSurname != null) {
108             if (accountsWithSurname.size() == 1) {
109                 String account = (String) accountsWithSurname.iterator().next();
110                 log.trace("NIS account Surname single match, no match on Name. Name: " + name + " - Surname: " + surname + " - account: " + account);
111                 return account;
112             } else {
113                 log.trace("NIS account Surname multiple match, no match on Name. Name: " + name + " - Surname: " + surname + " - account: undefined");
114                 resourceAdminLog.warn("NIS mapping: couldn't find single match for surname='" + surname + "' name='" + name + "'. Undecided between " + accountsWithSurname);
115                 return null;
116             }
117         }
118         log.trace("NIS account no match on Surname. Name: " + name + " - Surname: " + surname + " - account: undefined");
119         // Try reversing name and surname
120         accountsWithName = (Collection) nameToAccount.get(surname.toLowerCase());
121         accountsWithSurname = (Collection) surnameToAccount.get(name.toLowerCase());
122         if ((accountsWithName != null) && (accountsWithSurname != null)) {
123             // Accounts for both name and surname were found
124             List commonAccounts = new ArrayList(accountsWithName);
125             commonAccounts.retainAll(accountsWithSurname);
126             if (commonAccounts.size() == 1) {
127                 // Only one account matching both name and surname was found
128                 // Pretty likely is the correct one
129                 String account = (String) commonAccounts.get(0);
130                 log.trace("NIS account inverted Name/Surname single match. Name: " + surname + " - Surname: " + name + " - account: " + account);
131                 return account;
132             }
133         }
134         return null;
135     }
136     
137     /**
138      * How long the map will be valid since the last change.
139      * <p>
140      * By default is 60*60*1000 (one hour).
141      * @return Time of validity in ms.
142      */
143     public long getExpiration() {
144 
145         return this.expiration;
146     }
147     
148     public boolean isValid() {
149         if (lastUpdate == null) return false;
150         if ((System.currentTimeMillis() - lastUpdate.getTime()) > expiration) return false;
151         return true;
152     }
153     
154     public void printMaps(PrintStream out) {
155         out.println("account to gecos map");
156         out.println("---------------------");
157         out.println();
158         Iterator accounts = accountToGecos.keySet().iterator();
159         while (accounts.hasNext()) {
160             String account = (String) accounts.next();
161             String gecos = (String) accountToGecos.get(account);
162             String name = (String) accountToName.get(account);
163             String surname = (String) accountToSurname.get(account);
164             out.print(account);
165             for (int n = account.length(); n < 15; n++) {
166                 out.print(' ');
167             }
168             out.print(gecos);
169             for (int n = gecos.length(); n < 30; n++) {
170                 out.print(' ');
171             }
172             out.print(name);
173             for (int n = name.length(); n < 15; n++) {
174                 out.print(' ');
175             }
176             out.println(surname);
177         }
178     }
179     
180     /**
181      * How long the map will be valid since the last change.
182      * @param expiration Time of validity in ms.
183      */
184     public void setExpiration(long expiration) {
185 
186         this.expiration = expiration;
187     }
188     
189     private void printMap(PrintStream out, Map map, int offset) {
190         Iterator accounts = map.keySet().iterator();
191         while (accounts.hasNext()) {
192             String account = (String) accounts.next();
193             String gecos = (String) map.get(account);
194             out.print(account);
195             for (int n = account.length(); n < offset; n++) {
196                 out.print(' ');
197             }
198             out.println(gecos);
199             
200         }
201     }
202 
203     private String retrieveName(String gecos) {
204         gecos = gecos.trim();
205         int comma = gecos.indexOf(',');
206         if (comma != -1) {
207             gecos = gecos.substring(0, comma);
208         }
209         int index = gecos.lastIndexOf(' ');
210         if (index == -1) return "";
211         return gecos.substring(0, gecos.indexOf(' '));
212     }
213 
214     private String retrieveSurname(String gecos) {
215         gecos = gecos.trim();
216         int comma = gecos.indexOf(',');
217         if (comma != -1) {
218             gecos = gecos.substring(0, comma);
219         }
220         int index = gecos.lastIndexOf(' ');
221         if (index == -1) return gecos;
222         return gecos.substring(gecos.lastIndexOf(' ')+1);
223     }
224     
225 }