| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.hadoop.lib.service.security; |
| |
| import org.apache.hadoop.lib.lang.XException; |
| import org.apache.hadoop.lib.server.BaseService; |
| import org.apache.hadoop.lib.server.ServiceException; |
| import org.apache.hadoop.lib.service.Groups; |
| import org.apache.hadoop.lib.service.ProxyUser; |
| import org.apache.hadoop.lib.util.Check; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.io.IOException; |
| import java.net.InetAddress; |
| import java.security.AccessControlException; |
| import java.text.MessageFormat; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| public class ProxyUserService extends BaseService implements ProxyUser { |
| private static Logger LOG = LoggerFactory.getLogger(ProxyUserService.class); |
| |
| public enum ERROR implements XException.ERROR { |
| PRXU01("Could not normalize host name [{0}], {1}"), |
| PRXU02("Missing [{0}] property"); |
| |
| private String template; |
| |
| ERROR(String template) { |
| this.template = template; |
| } |
| |
| @Override |
| public String getTemplate() { |
| return template; |
| } |
| } |
| |
| private static final String PREFIX = "proxyuser"; |
| private static final String GROUPS = ".groups"; |
| private static final String HOSTS = ".hosts"; |
| |
| private Map<String, Set<String>> proxyUserHosts = new HashMap<String, Set<String>>(); |
| private Map<String, Set<String>> proxyUserGroups = new HashMap<String, Set<String>>(); |
| |
| public ProxyUserService() { |
| super(PREFIX); |
| } |
| |
| @Override |
| public Class getInterface() { |
| return ProxyUser.class; |
| } |
| |
| @Override |
| public Class[] getServiceDependencies() { |
| return new Class[]{Groups.class}; |
| } |
| |
| @Override |
| protected void init() throws ServiceException { |
| for (Map.Entry<String, String> entry : getServiceConfig()) { |
| String key = entry.getKey(); |
| if (key.endsWith(GROUPS)) { |
| String proxyUser = key.substring(0, key.lastIndexOf(GROUPS)); |
| if (getServiceConfig().get(proxyUser + HOSTS) == null) { |
| throw new ServiceException(ERROR.PRXU02, getPrefixedName(proxyUser + HOSTS)); |
| } |
| String value = entry.getValue().trim(); |
| LOG.info("Loading proxyuser settings [{}]=[{}]", key, value); |
| Set<String> values = null; |
| if (!value.equals("*")) { |
| values = new HashSet<String>(Arrays.asList(value.split(","))); |
| } |
| proxyUserGroups.put(proxyUser, values); |
| } |
| if (key.endsWith(HOSTS)) { |
| String proxyUser = key.substring(0, key.lastIndexOf(HOSTS)); |
| if (getServiceConfig().get(proxyUser + GROUPS) == null) { |
| throw new ServiceException(ERROR.PRXU02, getPrefixedName(proxyUser + GROUPS)); |
| } |
| String value = entry.getValue().trim(); |
| LOG.info("Loading proxyuser settings [{}]=[{}]", key, value); |
| Set<String> values = null; |
| if (!value.equals("*")) { |
| String[] hosts = value.split(","); |
| for (int i = 0; i < hosts.length; i++) { |
| String originalName = hosts[i]; |
| try { |
| hosts[i] = normalizeHostname(originalName); |
| } catch (Exception ex) { |
| throw new ServiceException(ERROR.PRXU01, originalName, ex.getMessage(), ex); |
| } |
| LOG.info(" Hostname, original [{}], normalized [{}]", originalName, hosts[i]); |
| } |
| values = new HashSet<String>(Arrays.asList(hosts)); |
| } |
| proxyUserHosts.put(proxyUser, values); |
| } |
| } |
| } |
| |
| @Override |
| public void validate(String proxyUser, String proxyHost, String doAsUser) throws IOException, |
| AccessControlException { |
| Check.notEmpty(proxyUser, "proxyUser"); |
| Check.notEmpty(proxyHost, "proxyHost"); |
| Check.notEmpty(doAsUser, "doAsUser"); |
| LOG.debug("Authorization check proxyuser [{}] host [{}] doAs [{}]", |
| new Object[]{proxyUser, proxyHost, doAsUser}); |
| if (proxyUserHosts.containsKey(proxyUser)) { |
| proxyHost = normalizeHostname(proxyHost); |
| validateRequestorHost(proxyUser, proxyHost, proxyUserHosts.get(proxyUser)); |
| validateGroup(proxyUser, doAsUser, proxyUserGroups.get(proxyUser)); |
| } else { |
| throw new AccessControlException(MessageFormat.format("User [{0}] not defined as proxyuser", proxyUser)); |
| } |
| } |
| |
| private void validateRequestorHost(String proxyUser, String hostname, Set<String> validHosts) |
| throws IOException, AccessControlException { |
| if (validHosts != null) { |
| if (!validHosts.contains(hostname) && !validHosts.contains(normalizeHostname(hostname))) { |
| throw new AccessControlException(MessageFormat.format("Unauthorized host [{0}] for proxyuser [{1}]", |
| hostname, proxyUser)); |
| } |
| } |
| } |
| |
| private void validateGroup(String proxyUser, String user, Set<String> validGroups) throws IOException, |
| AccessControlException { |
| if (validGroups != null) { |
| List<String> userGroups = getServer().get(Groups.class).getGroups(user); |
| for (String g : validGroups) { |
| if (userGroups.contains(g)) { |
| return; |
| } |
| } |
| throw new AccessControlException( |
| MessageFormat.format("Unauthorized proxyuser [{0}] for user [{1}], not in proxyuser groups", |
| proxyUser, user)); |
| } |
| } |
| |
| private String normalizeHostname(String name) { |
| try { |
| InetAddress address = InetAddress.getByName(name); |
| return address.getCanonicalHostName(); |
| } catch (IOException ex) { |
| throw new AccessControlException(MessageFormat.format("Could not resolve host [{0}], {1}", name, |
| ex.getMessage())); |
| } |
| } |
| |
| } |