/* * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0 * (the "License"). You may not use this work except in compliance with the License, which is * available at www.apache.org/licenses/LICENSE-2.0 * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied, as more fully set forth in the License. * * See the NOTICE file distributed with this work for information regarding copyright ownership. */ package alluxio.security.authorization; import alluxio.Configuration; import alluxio.Constants; import alluxio.PropertyKey; import alluxio.annotation.PublicApi; import alluxio.exception.ExceptionMessage; import com.google.common.base.Preconditions; import javax.annotation.concurrent.NotThreadSafe; import javax.annotation.concurrent.ThreadSafe; /** * POSIX style file/directory access mode. */ @PublicApi @NotThreadSafe public final class Mode { private static final Mode FILE_UMASK = new Mode(Constants.FILE_DIR_PERMISSION_DIFF); private Bits mOwnerBits; private Bits mGroupBits; private Bits mOtherBits; /** * Gets the default mode. * * @return the default {@link Mode} */ public static Mode defaults() { return new Mode(Constants.DEFAULT_FILE_SYSTEM_MODE); } /** * Creates the "no access" mode. * * @return the none {@link Mode} */ public static Mode createNoAccess() { return new Mode(); } /** * Creates the "full access" mode. * * @return the none {@link Mode} */ public static Mode createFullAccess() { return new Mode(Bits.ALL, Bits.ALL, Bits.ALL); } /** * Default constructor for {@link Mode}. Default constructor is required for equality testing * and JSON deserialization. */ private Mode() { mOwnerBits = Bits.NONE; mGroupBits = Bits.NONE; mOtherBits = Bits.NONE; } /** * Constructs an instance of {@link Mode} with the given {@link Bits}. * * @param ownerBits the owner {@link Bits} * @param groupBits the group {@link Bits} * @param otherBits the other {@link Bits} */ public Mode(Bits ownerBits, Bits groupBits, Bits otherBits) { set(ownerBits, groupBits, otherBits); } /** * Constructs an instance of {@link Mode} with the given mode. * * @param mode the digital representation of a {@link Mode} * @see #toShort() */ public Mode(short mode) { fromShort(mode); } /** * Copy constructor. * * @param mode another {@link Mode} */ public Mode(Mode mode) { set(mode.mOwnerBits, mode.mGroupBits, mode.mOtherBits); } /** * @return the owner {@link Bits} */ public Bits getOwnerBits() { return mOwnerBits; } /** * @param mode the digital representation of a {@link Mode} * @return the owner {@link Bits} */ public static Bits extractOwnerBits(short mode) { return Bits.values()[(mode >>> 6) & 7]; } /** * Sets owner bits. * * @param bits the owner bits to set */ public void setOwnerBits(Bits bits) { mOwnerBits = bits; } /** * @return the group {@link Bits} */ public Bits getGroupBits() { return mGroupBits; } /** * @param mode the digital representation of a {@link Mode} * @return the group {@link Bits} */ public static Bits extractGroupBits(short mode) { return Bits.values()[(mode >>> 3) & 7]; } /** * Sets group bits. * * @param bits the group bits to set */ public void setGroupBits(Bits bits) { mGroupBits = bits; } /** * @return the other {@link Bits} */ public Bits getOtherBits() { return mOtherBits; } /** * @param mode the digital representation of a {@link Mode} * @return the other {@link Bits} */ public static Bits extractOtherBits(short mode) { return Bits.values()[mode & 7]; } /** * Sets other bits. * * @param bits the other bits to set */ public void setOtherBits(Bits bits) { mOtherBits = bits; } private void set(Bits u, Bits g, Bits o) { mOwnerBits = u; mGroupBits = g; mOtherBits = o; } /** * Sets {@link Mode} bits using a digital representation. * * @param n the digital representation of a {@link Mode} */ public void fromShort(short n) { Bits[] v = Bits.values(); set(v[(n >>> 6) & 7], v[(n >>> 3) & 7], v[n & 7]); } /** * Encodes the object as a short. * * @return the digital representation of this {@link Mode} */ public short toShort() { int s = (mOwnerBits.ordinal() << 6) | (mGroupBits.ordinal() << 3) | mOtherBits.ordinal(); return (short) s; } @Override public boolean equals(Object obj) { if (obj instanceof Mode) { Mode that = (Mode) obj; return mOwnerBits == that.mOwnerBits && mGroupBits == that.mGroupBits && mOtherBits == that.mOtherBits; } return false; } @Override public int hashCode() { return toShort(); } @Override public String toString() { return mOwnerBits.toString() + mGroupBits.toString() + mOtherBits.toString(); } /** * Applies the default umask for newly created files to this mode. * * @return the updated object */ public Mode applyFileUMask() { return applyUMask(Mode.getUMask()).applyUMask(FILE_UMASK); } /** * Applies the default umask for newly created directories to this mode. * * @return the updated object */ public Mode applyDirectoryUMask() { return applyUMask(Mode.getUMask()); } /** * Applies the given umask {@link Mode} to this mode. * * @param umask the umask to apply * @return the updated object */ private Mode applyUMask(Mode umask) { mOwnerBits = mOwnerBits.and(umask.mOwnerBits.not()); mGroupBits = mGroupBits.and(umask.mGroupBits.not()); mOtherBits = mOtherBits.and(umask.mOtherBits.not()); return this; } /** * Gets the file / directory creation umask. * * @return the umask {@link Mode} */ private static Mode getUMask() { int umask = Constants.DEFAULT_FILE_SYSTEM_UMASK; String confUmask = Configuration.get(PropertyKey.SECURITY_AUTHORIZATION_PERMISSION_UMASK); if (confUmask != null) { if ((confUmask.length() > 4) || !isValid(confUmask)) { throw new IllegalArgumentException(ExceptionMessage.INVALID_CONFIGURATION_VALUE .getMessage(confUmask, PropertyKey.SECURITY_AUTHORIZATION_PERMISSION_UMASK)); } int newUmask = 0; int lastIndex = confUmask.length() - 1; for (int i = 0; i <= lastIndex; i++) { newUmask += (confUmask.charAt(i) - '0') << 3 * (lastIndex - i); } umask = newUmask; } return new Mode((short) umask); } private static boolean isValid(String value) { try { Integer.parseInt(value); return true; } catch (NumberFormatException e) { return false; } } /** * Mode bits. */ @PublicApi @ThreadSafe public enum Bits { NONE("---"), EXECUTE("--x"), WRITE("-w-"), WRITE_EXECUTE("-wx"), READ("r--"), READ_EXECUTE("r-x"), READ_WRITE("rw-"), ALL("rwx"), ; /** String representation of the bits. */ private final String mString; /** Retain reference to the values array. */ private static final Bits[] SVALS = values(); Bits(String s) { mString = s; } /** * Creates a {@link Bits} from a short. * * @param bits {@link Bits} in short * @return the {@link Bits} created */ public static Bits fromShort(short bits) { return SVALS[bits]; } @Override public String toString() { return mString; } /** * Checks whether these bits imply the given bits. * * @param that mode bits * @return true when these bits imply the given bits */ public boolean imply(Bits that) { if (that != null) { return (ordinal() & that.ordinal()) == that.ordinal(); } return false; } /** * @param that mode bits * @return the intersection of thes bits and the given bits */ public Bits and(Bits that) { Preconditions.checkNotNull(that); return SVALS[ordinal() & that.ordinal()]; } /** * @param that mode bits * @return the union of thes bits and the given bits */ public Bits or(Bits that) { Preconditions.checkNotNull(that); return SVALS[ordinal() | that.ordinal()]; } /** * @return the complement of these bits */ public Bits not() { return SVALS[7 - ordinal()]; } } }