001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.hadoop.security.authorize;
020
021import java.util.Collection;
022import java.util.HashMap;
023import java.util.Map;
024import java.util.Map.Entry;
025import java.util.regex.Pattern;
026
027import org.apache.hadoop.classification.InterfaceAudience;
028import org.apache.hadoop.classification.InterfaceStability;
029import org.apache.hadoop.conf.Configuration;
030import org.apache.hadoop.security.UserGroupInformation;
031import org.apache.hadoop.util.MachineList;
032
033import com.google.common.annotations.VisibleForTesting;
034
035@InterfaceStability.Unstable
036@InterfaceAudience.Public
037public class DefaultImpersonationProvider implements ImpersonationProvider {
038  private static final String CONF_HOSTS = ".hosts";
039  private static final String CONF_USERS = ".users";
040  private static final String CONF_GROUPS = ".groups";
041  // acl and list of hosts per proxyuser
042  private Map<String, AccessControlList> proxyUserAcl = 
043    new HashMap<String, AccessControlList>();
044  private Map<String, MachineList> proxyHosts =
045    new HashMap<String, MachineList>();
046  private Configuration conf;
047
048
049  private static DefaultImpersonationProvider testProvider;
050
051  public static synchronized DefaultImpersonationProvider getTestProvider() {
052    if (testProvider == null) {
053      testProvider = new DefaultImpersonationProvider();
054      testProvider.setConf(new Configuration());
055      testProvider.init(ProxyUsers.CONF_HADOOP_PROXYUSER);
056    }
057    return testProvider;
058  }
059
060  @Override
061  public void setConf(Configuration conf) {
062    this.conf = conf;
063  }
064
065  private String configPrefix;
066
067  @Override
068  public void init(String configurationPrefix) {
069    configPrefix = configurationPrefix +
070        (configurationPrefix.endsWith(".") ? "" : ".");
071    
072    // constructing regex to match the following patterns:
073    //   $configPrefix.[ANY].users
074    //   $configPrefix.[ANY].groups
075    //   $configPrefix.[ANY].hosts
076    //
077    String prefixRegEx = configPrefix.replace(".", "\\.");
078    String usersGroupsRegEx = prefixRegEx + "[^.]*(" +
079        Pattern.quote(CONF_USERS) + "|" + Pattern.quote(CONF_GROUPS) + ")";
080    String hostsRegEx = prefixRegEx + "[^.]*" + Pattern.quote(CONF_HOSTS);
081
082  // get list of users and groups per proxyuser
083    Map<String,String> allMatchKeys = 
084        conf.getValByRegex(usersGroupsRegEx);
085    for(Entry<String, String> entry : allMatchKeys.entrySet()) {  
086      String aclKey = getAclKey(entry.getKey());
087      if (!proxyUserAcl.containsKey(aclKey)) {
088        proxyUserAcl.put(aclKey, new AccessControlList(
089            allMatchKeys.get(aclKey + CONF_USERS) ,
090            allMatchKeys.get(aclKey + CONF_GROUPS)));
091      }
092    }
093
094    // get hosts per proxyuser
095    allMatchKeys = conf.getValByRegex(hostsRegEx);
096    for(Entry<String, String> entry : allMatchKeys.entrySet()) {
097      proxyHosts.put(entry.getKey(),
098          new MachineList(entry.getValue()));
099    }
100  }
101
102  @Override
103  public Configuration getConf() {
104    return conf;
105  }
106
107  @Override
108  public void authorize(UserGroupInformation user, 
109      String remoteAddress) throws AuthorizationException {
110    
111    if (user == null) {
112      throw new IllegalArgumentException("user is null.");
113    }
114
115    UserGroupInformation realUser = user.getRealUser();
116    if (realUser == null) {
117      return;
118    }
119    
120    AccessControlList acl = proxyUserAcl.get(configPrefix +
121        realUser.getShortUserName());
122    if (acl == null || !acl.isUserAllowed(user)) {
123      throw new AuthorizationException("User: " + realUser.getUserName()
124          + " is not allowed to impersonate " + user.getUserName());
125    }
126
127    MachineList MachineList = proxyHosts.get(
128        getProxySuperuserIpConfKey(realUser.getShortUserName()));
129
130    if(MachineList == null || !MachineList.includes(remoteAddress)) {
131      throw new AuthorizationException("Unauthorized connection for super-user: "
132          + realUser.getUserName() + " from IP " + remoteAddress);
133    }
134  }
135  
136  private String getAclKey(String key) {
137    int endIndex = key.lastIndexOf(".");
138    if (endIndex != -1) {
139      return key.substring(0, endIndex); 
140    }
141    return key;
142  }
143  
144  /**
145   * Returns configuration key for effective usergroups allowed for a superuser
146   * 
147   * @param userName name of the superuser
148   * @return configuration key for superuser usergroups
149   */
150  public String getProxySuperuserUserConfKey(String userName) {
151    return configPrefix + userName + CONF_USERS;
152  }
153
154  /**
155   * Returns configuration key for effective groups allowed for a superuser
156   * 
157   * @param userName name of the superuser
158   * @return configuration key for superuser groups
159   */
160  public String getProxySuperuserGroupConfKey(String userName) {
161    return configPrefix + userName + CONF_GROUPS;
162  }
163
164  /**
165   * Return configuration key for superuser ip addresses
166   * 
167   * @param userName name of the superuser
168   * @return configuration key for superuser ip-addresses
169   */
170  public String getProxySuperuserIpConfKey(String userName) {
171    return configPrefix + userName + CONF_HOSTS;
172  }
173
174  @VisibleForTesting
175  public Map<String, Collection<String>> getProxyGroups() {
176     Map<String,Collection<String>> proxyGroups = new HashMap<String,Collection<String>>();
177     for(Entry<String, AccessControlList> entry : proxyUserAcl.entrySet()) {
178       proxyGroups.put(entry.getKey() + CONF_GROUPS, entry.getValue().getGroups());
179     }
180     return proxyGroups;
181  }
182
183  @VisibleForTesting
184  public Map<String, Collection<String>> getProxyHosts() {
185    Map<String, Collection<String>> tmpProxyHosts = 
186        new HashMap<String, Collection<String>>();
187    for (Map.Entry<String, MachineList> proxyHostEntry :proxyHosts.entrySet()) {
188      tmpProxyHosts.put(proxyHostEntry.getKey(), 
189          proxyHostEntry.getValue().getCollection());
190    }
191    return tmpProxyHosts;
192  }
193}