blob: 63e599a98ea8c6694079f2ac7e00e04dadee91fd [file] [log] [blame]
/**
* 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.falcon.security;
import org.apache.commons.lang3.StringUtils;
import org.apache.falcon.service.ProxyUserService;
import org.apache.falcon.service.Services;
import org.apache.hadoop.security.UserGroupInformation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Current authenticated user via REST. Also captures the proxy user from authorized entity
* and doles out proxied UserGroupInformation. Caches proxied users.
*/
public final class CurrentUser {
private static final Logger LOG = LoggerFactory.getLogger(CurrentUser.class);
private static final Logger AUDIT = LoggerFactory.getLogger("AUDIT");
private final String authenticatedUser;
private String proxyUser;
private CurrentUser(String authenticatedUser) {
this.authenticatedUser = authenticatedUser;
this.proxyUser = authenticatedUser;
}
private static final ThreadLocal<CurrentUser> CURRENT_USER = new ThreadLocal<CurrentUser>();
/**
* Captures the authenticated user.
*
* @param user authenticated user
*/
public static void authenticate(final String user) {
if (StringUtils.isEmpty(user)) {
throw new IllegalStateException("Bad user name sent for authentication");
}
LOG.info("Logging in {}", user);
CurrentUser currentUser = new CurrentUser(user);
CURRENT_USER.set(currentUser);
}
/**
* Proxies doAs user.
*
* @param doAsUser doAs user
* @param proxyHost proxy host
*/
public static void proxyDoAsUser(final String doAsUser, final String proxyHost) {
if (!isAuthenticated()) {
throw new IllegalStateException("Authentication not done");
}
String currentUser = CURRENT_USER.get().authenticatedUser;
if (StringUtils.isNotEmpty(doAsUser) && !doAsUser.equalsIgnoreCase(currentUser)) {
if (StringUtils.isEmpty(proxyHost)) {
throw new IllegalArgumentException("proxy host cannot be null or empty");
}
ProxyUserService proxyUserService = Services.get().getService(ProxyUserService.SERVICE_NAME);
try {
proxyUserService.validate(currentUser, proxyHost, doAsUser);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
CurrentUser user = CURRENT_USER.get();
LOG.info("Authenticated user {} is proxying doAs user {} from host {}",
user.authenticatedUser, doAsUser, proxyHost);
AUDIT.info("Authenticated user {} is proxying doAs user {} from host {}",
user.authenticatedUser, doAsUser, proxyHost);
user.proxyUser = doAsUser;
}
}
/**
* Captures the entity owner if authenticated user is a super user.
*
* @param aclOwner entity acl owner
* @param aclGroup entity acl group
*/
public static void proxy(final String aclOwner, final String aclGroup) {
if (!isAuthenticated() || StringUtils.isEmpty(aclOwner)) {
throw new IllegalStateException("Authentication not done or Bad user name");
}
CurrentUser user = CURRENT_USER.get();
LOG.info("Authenticated user {} is proxying entity owner {}/{}",
user.authenticatedUser, aclOwner, aclGroup);
AUDIT.info("Authenticated user {} is proxying entity owner {}/{}",
user.authenticatedUser, aclOwner, aclGroup);
user.proxyUser = aclOwner;
}
/**
* Clears the context.
*/
public static void clear() {
CURRENT_USER.remove();
}
/**
* Checks if the authenticate method is already called.
*
* @return true if authenticated user is set else false
*/
public static boolean isAuthenticated() {
CurrentUser user = CURRENT_USER.get();
return user != null && user.authenticatedUser != null;
}
/**
* Returns authenticated user.
*
* @return logged in authenticated user.
*/
public static String getAuthenticatedUser() {
CurrentUser user = CURRENT_USER.get();
if (user == null || user.authenticatedUser == null) {
throw new IllegalStateException("No user logged into the system");
} else {
return user.authenticatedUser;
}
}
/**
* Dole out a UGI object for the current authenticated user if authenticated
* else return current user.
*
* @return UGI object
* @throws java.io.IOException
*/
public static UserGroupInformation getAuthenticatedUGI() throws IOException {
return CurrentUser.isAuthenticated()
? createProxyUGI(getAuthenticatedUser()) : UserGroupInformation.getCurrentUser();
}
/**
* Returns the proxy user.
*
* @return proxy user
*/
public static String getUser() {
CurrentUser user = CURRENT_USER.get();
if (user == null || user.proxyUser == null) {
throw new IllegalStateException("No user logged into the system");
} else {
return user.proxyUser;
}
}
private static ConcurrentMap<String, UserGroupInformation> userUgiMap =
new ConcurrentHashMap<String, UserGroupInformation>();
/**
* Create a proxy UGI object for the proxy user.
*
* @param proxyUser logged in user
* @return UGI object
* @throws IOException
*/
public static UserGroupInformation createProxyUGI(String proxyUser) throws IOException {
UserGroupInformation proxyUgi = userUgiMap.get(proxyUser);
if (proxyUgi == null) {
// taking care of a race condition, the latest UGI will be discarded
proxyUgi = UserGroupInformation.createProxyUser(
proxyUser, UserGroupInformation.getLoginUser());
userUgiMap.putIfAbsent(proxyUser, proxyUgi);
}
return proxyUgi;
}
/**
* Dole out a proxy UGI object for the current authenticated user if authenticated
* else return current user.
*
* @return UGI object
* @throws java.io.IOException
*/
public static UserGroupInformation getProxyUGI() throws IOException {
return CurrentUser.isAuthenticated()
? createProxyUGI(getUser()) : UserGroupInformation.getCurrentUser();
}
/**
* Gets a collection of group names the proxy user belongs to.
*
* @return group names
* @throws IOException
*/
public static Set<String> getGroupNames() throws IOException {
HashSet<String> s = new HashSet<String>(Arrays.asList(getProxyUGI().getGroupNames()));
return Collections.unmodifiableSet(s);
}
/**
* Returns the primary group name for the proxy user.
*
* @return primary group name for the proxy user
*/
public static String getPrimaryGroupName() {
try {
String[] groups = getProxyUGI().getGroupNames();
if (groups.length > 0) {
return groups[0];
}
} catch (IOException ignore) {
// ignored
}
return "unknown"; // this can only happen in tests
}
}