View Javadoc

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