/**
 * 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.hdfs.server.namenode;

import java.util.List;

import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.fs.XAttr;
import org.apache.hadoop.hdfs.XAttrHelper;
import org.apache.hadoop.security.AccessControlException;

import com.google.common.collect.Lists;
import com.google.common.base.Preconditions;

import static org.apache.hadoop.hdfs.server.common.HdfsServerConstants.SECURITY_XATTR_UNREADABLE_BY_SUPERUSER;

/**
 * There are four types of extended attributes <XAttr> defined by the
 * following namespaces:
 * <br>
 * USER - extended user attributes: these can be assigned to files and
 * directories to store arbitrary additional information. The access
 * permissions for user attributes are defined by the file permission
 * bits. For sticky directories, only the owner and privileged user can 
 * write attributes.
 * <br>
 * TRUSTED - trusted extended attributes: these are visible/accessible
 * only to/by the super user.
 * <br>
 * SECURITY - extended security attributes: these are used by the HDFS
 * core for security purposes and are not available through admin/user
 * API.
 * <br>
 * SYSTEM - extended system attributes: these are used by the HDFS
 * core and are not available through admin/user API.
 * <br>
 * RAW - extended system attributes: these are used for internal system
 *   attributes that sometimes need to be exposed. Like SYSTEM namespace
 *   attributes they are not visible to the user except when getXAttr/getXAttrs
 *   is called on a file or directory in the /.reserved/raw HDFS directory
 *   hierarchy. These attributes can only be accessed by the superuser.
 * </br>
 */
@InterfaceAudience.Private
public class XAttrPermissionFilter {
  
  static void checkPermissionForApi(FSPermissionChecker pc, XAttr xAttr,
      boolean isRawPath)
      throws AccessControlException {
    final boolean isSuperUser = pc.isSuperUser();
    if (xAttr.getNameSpace() == XAttr.NameSpace.USER || 
        (xAttr.getNameSpace() == XAttr.NameSpace.TRUSTED && isSuperUser)) {
      return;
    }
    if (xAttr.getNameSpace() == XAttr.NameSpace.RAW &&
        isRawPath && isSuperUser) {
      return;
    }
    if (XAttrHelper.getPrefixName(xAttr).
        equals(SECURITY_XATTR_UNREADABLE_BY_SUPERUSER)) {
      if (xAttr.getValue() != null) {
        throw new AccessControlException("Attempt to set a value for '" +
            SECURITY_XATTR_UNREADABLE_BY_SUPERUSER +
            "'. Values are not allowed for this xattr.");
      }
      return;
    }
    throw new AccessControlException("User doesn't have permission for xattr: "
        + XAttrHelper.getPrefixName(xAttr));
  }

  static void checkPermissionForApi(FSPermissionChecker pc,
      List<XAttr> xAttrs, boolean isRawPath) throws AccessControlException {
    Preconditions.checkArgument(xAttrs != null);
    if (xAttrs.isEmpty()) {
      return;
    }

    for (XAttr xAttr : xAttrs) {
      checkPermissionForApi(pc, xAttr, isRawPath);
    }
  }

  static List<XAttr> filterXAttrsForApi(FSPermissionChecker pc,
      List<XAttr> xAttrs, boolean isRawPath) {
    assert xAttrs != null : "xAttrs can not be null";
    if (xAttrs.isEmpty()) {
      return xAttrs;
    }
    
    List<XAttr> filteredXAttrs = Lists.newArrayListWithCapacity(xAttrs.size());
    final boolean isSuperUser = pc.isSuperUser();
    for (XAttr xAttr : xAttrs) {
      if (xAttr.getNameSpace() == XAttr.NameSpace.USER) {
        filteredXAttrs.add(xAttr);
      } else if (xAttr.getNameSpace() == XAttr.NameSpace.TRUSTED && 
          isSuperUser) {
        filteredXAttrs.add(xAttr);
      } else if (xAttr.getNameSpace() == XAttr.NameSpace.RAW &&
          isSuperUser && isRawPath) {
        filteredXAttrs.add(xAttr);
      } else if (XAttrHelper.getPrefixName(xAttr).
          equals(SECURITY_XATTR_UNREADABLE_BY_SUPERUSER)) {
        filteredXAttrs.add(xAttr);
      }
    }
    
    return filteredXAttrs;
  }
}