source: proiecte/HadoopJUnit/hadoop-0.20.1/src/core/org/apache/hadoop/security/UnixUserGroupInformation.java @ 120

Last change on this file since 120 was 120, checked in by (none), 14 years ago

Added the mail files for the Hadoop JUNit Project

  • Property svn:executable set to *
File size: 14.1 KB
Line 
1/**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements.  See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership.  The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License.  You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19package org.apache.hadoop.security;
20
21import java.io.DataInput;
22import java.io.DataOutput;
23import java.io.IOException;
24import java.util.Arrays;
25import java.util.HashMap;
26import java.util.StringTokenizer;
27import java.util.TreeSet;
28
29import javax.security.auth.login.LoginException;
30
31import org.apache.hadoop.conf.Configuration;
32import org.apache.hadoop.util.Shell;
33import org.apache.hadoop.io.Text;
34import org.apache.hadoop.io.WritableUtils;
35
36/** An implementation of UserGroupInformation in the Unix system */
37public class UnixUserGroupInformation extends UserGroupInformation {
38  public static final String DEFAULT_USERNAME = "DrWho";
39  public static final String DEFAULT_GROUP = "Tardis";
40
41  final static public String UGI_PROPERTY_NAME = "hadoop.job.ugi";
42  final static private HashMap<String, UnixUserGroupInformation> user2UGIMap =
43    new HashMap<String, UnixUserGroupInformation>();
44
45  /** Create an immutable {@link UnixUserGroupInformation} object. */
46  public static UnixUserGroupInformation createImmutable(String[] ugi) {
47    return new UnixUserGroupInformation(ugi) {
48      public void readFields(DataInput in) throws IOException {
49        throw new UnsupportedOperationException();
50      }
51    };
52  }
53
54  private String userName;
55  private String[] groupNames;
56
57  /** Default constructor
58   */
59  public UnixUserGroupInformation() {
60  }
61
62  /** Constructor with parameters user name and its group names.
63   * The first entry in the groups list is the default  group.
64   *
65   * @param userName a user's name
66   * @param groupNames groups list, first of which is the default group
67   * @exception IllegalArgumentException if any argument is null
68   */
69  public UnixUserGroupInformation(String userName, String[] groupNames) {
70    setUserGroupNames(userName, groupNames);
71  }
72
73  /** Constructor with parameter user/group names
74   *
75   * @param ugi an array containing user/group names, the first
76   *                     element of which is the user name, the second of
77   *                     which is the default group name.
78   * @exception IllegalArgumentException if the array size is less than 2
79   *                                     or any element is null.
80   */
81  public UnixUserGroupInformation(String[] ugi) {
82    if (ugi==null || ugi.length < 2) {
83      throw new IllegalArgumentException( "Parameter does contain at least "+
84          "one user name and one group name");
85    }
86    String[] groupNames = new String[ugi.length-1];
87    System.arraycopy(ugi, 1, groupNames, 0, groupNames.length);
88    setUserGroupNames(ugi[0], groupNames);
89  }
90 
91  /* Set this object's user name and group names
92   *
93   * @param userName a user's name
94   * @param groupNames groups list, the first of which is the default group
95   * @exception IllegalArgumentException if any argument is null
96   */
97  private void setUserGroupNames(String userName, String[] groupNames) {
98    if (userName==null || userName.length()==0 ||
99        groupNames== null || groupNames.length==0) {
100      throw new IllegalArgumentException(
101          "Parameters should not be null or an empty string/array");
102    }
103    for (int i=0; i<groupNames.length; i++) {
104      if(groupNames[i] == null || groupNames[i].length() == 0) {
105        throw new IllegalArgumentException("A null group name at index " + i);
106      }
107    }
108    this.userName = userName;
109    this.groupNames = groupNames;
110  }
111
112  /** Return an array of group names
113   */
114  public String[] getGroupNames() {
115    return groupNames;
116  }
117
118  /** Return the user's name
119   */
120  public String getUserName() {
121    return userName;
122  }
123
124  /* The following two methods implements Writable interface */
125  final private static String UGI_TECHNOLOGY = "STRING_UGI"; 
126  /** Deserialize this object
127   * First check if this is a UGI in the string format.
128   * If no, throw an IOException; otherwise
129   * set this object's fields by reading them from the given data input
130   * 
131   *  @param in input stream
132   *  @exception IOException is thrown if encounter any error when reading
133   */
134  public void readFields(DataInput in) throws IOException {
135    // read UGI type first
136    String ugiType = Text.readString(in);
137    if (!UGI_TECHNOLOGY.equals(ugiType)) {
138      throw new IOException("Expect UGI prefix: " + UGI_TECHNOLOGY +
139          ", but receive a prefix: " + ugiType);
140    }
141   
142    // read this object
143    userName = Text.readString(in);
144    int numOfGroups = WritableUtils.readVInt(in);
145    groupNames = new String[numOfGroups];
146    for (int i = 0; i < numOfGroups; i++) {
147      groupNames[i] = Text.readString(in);
148    }
149  }
150
151  /** Serialize this object
152   * First write a string marking that this is a UGI in the string format,
153   * then write this object's serialized form to the given data output
154   *
155   * @param out output stream
156   * @exception IOException if encounter any error during writing
157   */
158  public void write(DataOutput out) throws IOException {
159    // write a prefix indicating the type of UGI being written
160    Text.writeString(out, UGI_TECHNOLOGY);
161    // write this object
162    Text.writeString(out, userName);
163    WritableUtils.writeVInt(out, groupNames.length);
164    for (String groupName : groupNames) {
165      Text.writeString(out, groupName);
166    }
167  }
168
169  /* The following two methods deal with transferring UGI through conf.
170   * In this pass of implementation we store UGI as a string in conf.
171   * Later we may change it to be a more general approach that stores
172   * it as a byte array */
173  /** Store the given <code>ugi</code> as a comma separated string in
174   * <code>conf</code> as a property <code>attr</code>
175   *
176   * The String starts with the user name followed by the default group names,
177   * and other group names.
178   *
179   * @param conf configuration
180   * @param attr property name
181   * @param ugi a UnixUserGroupInformation
182   */
183  public static void saveToConf(Configuration conf, String attr, 
184      UnixUserGroupInformation ugi ) {
185    conf.set(attr, ugi.toString());
186  }
187 
188  /** Read a UGI from the given <code>conf</code>
189   *
190   * The object is expected to store with the property name <code>attr</code>
191   * as a comma separated string that starts
192   * with the user name followed by group names.
193   * If the property name is not defined, return null.
194   * It's assumed that there is only one UGI per user. If this user already
195   * has a UGI in the ugi map, return the ugi in the map.
196   * Otherwise, construct a UGI from the configuration, store it in the
197   * ugi map and return it.
198   *
199   * @param conf configuration
200   * @param attr property name
201   * @return a UnixUGI
202   * @throws LoginException if the stored string is ill-formatted.
203   */
204  public static UnixUserGroupInformation readFromConf(
205      Configuration conf, String attr) throws LoginException {
206    String[] ugi = conf.getStrings(attr);
207    if(ugi == null) {
208      return null;
209    }
210    UnixUserGroupInformation currentUGI = null;
211    if (ugi.length>0 ){
212      currentUGI = user2UGIMap.get(ugi[0]);
213    }
214    if (currentUGI == null) {
215      try {
216        currentUGI = new UnixUserGroupInformation(ugi);
217        user2UGIMap.put(currentUGI.getUserName(), currentUGI);
218      } catch (IllegalArgumentException e) {
219        throw new LoginException("Login failed: "+e.getMessage());
220      }
221    }
222   
223    return currentUGI;
224  }
225 
226  /**
227   * Get current user's name and the names of all its groups from Unix.
228   * It's assumed that there is only one UGI per user. If this user already
229   * has a UGI in the ugi map, return the ugi in the map.
230   * Otherwise get the current user's information from Unix, store it
231   * in the map, and return it.
232   *
233   * If the current user's UNIX username or groups are configured in such a way
234   * to throw an Exception, for example if the user uses LDAP, then this method
235   * will use a the {@link #DEFAULT_USERNAME} and {@link #DEFAULT_GROUP}
236   * constants.
237   */
238  public static UnixUserGroupInformation login() throws LoginException {
239    try {
240      String userName;
241
242      // if an exception occurs, then uses the
243      // default user
244      try {
245        userName =  getUnixUserName();
246      } catch (Exception e) {
247        userName = DEFAULT_USERNAME;
248      }
249
250      // check if this user already has a UGI object in the ugi map
251      UnixUserGroupInformation ugi = user2UGIMap.get(userName);
252      if (ugi != null) {
253        return ugi;
254      }
255
256      /* get groups list from UNIX.
257       * It's assumed that the first group is the default group.
258       */
259      String[]  groupNames;
260
261      // if an exception occurs, then uses the
262      // default group
263      try {
264        groupNames = getUnixGroups();
265      } catch (Exception e) {
266        groupNames = new String[1];
267        groupNames[0] = DEFAULT_GROUP;
268      }
269
270      // construct a Unix UGI
271      ugi = new UnixUserGroupInformation(userName, groupNames);
272      user2UGIMap.put(ugi.getUserName(), ugi);
273      return ugi;
274    } catch (Exception e) {
275      throw new LoginException("Login failed: "+e.getMessage());
276    }
277  }
278
279  /** Equivalent to login(conf, false). */
280  public static UnixUserGroupInformation login(Configuration conf)
281    throws LoginException {
282    return login(conf, false);
283  }
284 
285  /** Get a user's name & its group names from the given configuration;
286   * If it is not defined in the configuration, get the current user's
287   * information from Unix.
288   * If the user has a UGI in the ugi map, return the one in
289   * the UGI map.
290   *
291   *  @param conf either a job configuration or client's configuration
292   *  @param save saving it to conf?
293   *  @return UnixUserGroupInformation a user/group information
294   *  @exception LoginException if not able to get the user/group information
295   */
296  public static UnixUserGroupInformation login(Configuration conf, boolean save
297      ) throws LoginException {
298    UnixUserGroupInformation ugi = readFromConf(conf, UGI_PROPERTY_NAME);
299    if (ugi == null) {
300      ugi = login();
301      LOG.debug("Unix Login: " + ugi);
302      if (save) {
303        saveToConf(conf, UGI_PROPERTY_NAME, ugi);
304      }
305    }
306    return ugi;
307  }
308 
309  /* Return a string representation of a string array.
310   * Two strings are separated by a blank.
311   */
312  private static String toString(String[] strArray) {
313    if (strArray==null || strArray.length==0) {
314      return "";
315    }
316    StringBuilder buf = new StringBuilder(strArray[0]);
317    for (int i=1; i<strArray.length; i++) {
318      buf.append(' ');
319      buf.append(strArray[i]);
320    }
321    return buf.toString();
322  }
323 
324  /** Get current user's name from Unix by running the command whoami.
325   *
326   * @return current user's name
327   * @throws IOException if encounter any error while running the command
328   */
329  static String getUnixUserName() throws IOException {
330    String[] result = executeShellCommand(
331        new String[]{Shell.USER_NAME_COMMAND});
332    if (result.length!=1) {
333      throw new IOException("Expect one token as the result of " + 
334          Shell.USER_NAME_COMMAND + ": " + toString(result));
335    }
336    return result[0];
337  }
338
339  /** Get the current user's group list from Unix by running the command groups
340   *
341   * @return the groups list that the current user belongs to
342   * @throws IOException if encounter any error when running the command
343   */
344  private static String[] getUnixGroups() throws IOException {
345    return executeShellCommand(Shell.getGROUPS_COMMAND());
346  }
347 
348  /* Execute a command and return the result as an array of Strings */
349  private static String[] executeShellCommand(String[] command)
350  throws IOException {
351    String groups = Shell.execCommand(command);
352    StringTokenizer tokenizer = new StringTokenizer(groups);
353    int numOfTokens = tokenizer.countTokens();
354    String[] tokens = new String[numOfTokens];
355    for (int i=0; tokenizer.hasMoreTokens(); i++) {
356      tokens[i] = tokenizer.nextToken();
357    }
358
359    return tokens;
360  }
361
362  /** Decide if two UGIs are the same
363   *
364   * @param other other object
365   * @return true if they are the same; false otherwise.
366   */
367  public boolean equals(Object other) {
368    if (this == other) {
369      return true;
370    }
371   
372    if (!(other instanceof UnixUserGroupInformation)) {
373      return false;
374    }
375   
376    UnixUserGroupInformation otherUGI = (UnixUserGroupInformation)other;
377   
378    // check userName
379    if (userName == null) {
380      if (otherUGI.getUserName() != null) {
381        return false;
382      }
383    } else {
384      if (!userName.equals(otherUGI.getUserName())) {
385        return false;
386      }
387    }
388   
389    // checkGroupNames
390    if (groupNames == otherUGI.groupNames) {
391      return true;
392    }
393    if (groupNames.length != otherUGI.groupNames.length) {
394      return false;
395    }
396    // check default group name
397    if (groupNames.length>0 && !groupNames[0].equals(otherUGI.groupNames[0])) {
398      return false;
399    }
400    // check all group names, ignoring the order
401    return new TreeSet<String>(Arrays.asList(groupNames)).equals(
402           new TreeSet<String>(Arrays.asList(otherUGI.groupNames)));
403  }
404
405  /** Returns a hash code for this UGI.
406   * The hash code for a UGI is the hash code of its user name string.
407   *
408   * @return  a hash code value for this UGI.
409   */
410  public int hashCode() {
411    return getUserName().hashCode();
412  }
413 
414  /** Convert this object to a string
415   *
416   * @return a comma separated string containing the user name and group names
417   */
418  public String toString() {
419    StringBuilder buf = new StringBuilder();
420    buf.append(userName);
421    for (String groupName : groupNames) {
422      buf.append(',');
423      buf.append(groupName);
424    }
425    return buf.toString();
426  }
427
428  @Override
429  public String getName() {
430    return toString();
431  }
432}
Note: See TracBrowser for help on using the repository browser.