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    UserGroupInformation realUser = user.getRealUser();
112    if (realUser == null) {
113      return;
114    }
115    
116    AccessControlList acl = proxyUserAcl.get(configPrefix +
117        realUser.getShortUserName());
118    if (acl == null || !acl.isUserAllowed(user)) {
119      throw new AuthorizationException("User: " + realUser.getUserName()
120          + " is not allowed to impersonate " + user.getUserName());
121    }
122
123    MachineList MachineList = proxyHosts.get(
124        getProxySuperuserIpConfKey(realUser.getShortUserName()));
125
126    if(MachineList == null || !MachineList.includes(remoteAddress)) {
127      throw new AuthorizationException("Unauthorized connection for super-user: "
128          + realUser.getUserName() + " from IP " + remoteAddress);
129    }
130  }
131  
132  private String getAclKey(String key) {
133    int endIndex = key.lastIndexOf(".");
134    if (endIndex != -1) {
135      return key.substring(0, endIndex); 
136    }
137    return key;
138  }
139  
140  /**
141   * Returns configuration key for effective usergroups allowed for a superuser
142   * 
143   * @param userName name of the superuser
144   * @return configuration key for superuser usergroups
145   */
146  public String getProxySuperuserUserConfKey(String userName) {
147    return configPrefix + userName + CONF_USERS;
148  }
149
150  /**
151   * Returns configuration key for effective groups allowed for a superuser
152   * 
153   * @param userName name of the superuser
154   * @return configuration key for superuser groups
155   */
156  public String getProxySuperuserGroupConfKey(String userName) {
157    return configPrefix + userName + CONF_GROUPS;
158  }
159
160  /**
161   * Return configuration key for superuser ip addresses
162   * 
163   * @param userName name of the superuser
164   * @return configuration key for superuser ip-addresses
165   */
166  public String getProxySuperuserIpConfKey(String userName) {
167    return configPrefix + userName + CONF_HOSTS;
168  }
169
170  @VisibleForTesting
171  public Map<String, Collection<String>> getProxyGroups() {
172     Map<String,Collection<String>> proxyGroups = new HashMap<String,Collection<String>>();
173     for(Entry<String, AccessControlList> entry : proxyUserAcl.entrySet()) {
174       proxyGroups.put(entry.getKey() + CONF_GROUPS, entry.getValue().getGroups());
175     }
176     return proxyGroups;
177  }
178
179  @VisibleForTesting
180  public Map<String, Collection<String>> getProxyHosts() {
181    Map<String, Collection<String>> tmpProxyHosts = 
182        new HashMap<String, Collection<String>>();
183    for (Map.Entry<String, MachineList> proxyHostEntry :proxyHosts.entrySet()) {
184      tmpProxyHosts.put(proxyHostEntry.getKey(), 
185          proxyHostEntry.getValue().getCollection());
186    }
187    return tmpProxyHosts;
188  }
189}