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.hbase.security.access;
020
021import java.io.DataInput;
022import java.io.DataOutput;
023import java.io.IOException;
024import java.util.Arrays;
025import java.util.Map;
026
027import org.apache.yetus.audience.InterfaceAudience;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030import org.apache.hadoop.hbase.util.Bytes;
031import org.apache.hadoop.io.VersionedWritable;
032
033import org.apache.hbase.thirdparty.com.google.common.collect.Maps;
034
035/**
036 * Base permissions instance representing the ability to perform a given set
037 * of actions.
038 *
039 * @see TablePermission
040 */
041@InterfaceAudience.Public
042public class Permission extends VersionedWritable {
043  protected static final byte VERSION = 0;
044
045  @InterfaceAudience.Public
046  public enum Action {
047    READ('R'), WRITE('W'), EXEC('X'), CREATE('C'), ADMIN('A');
048
049    private final byte code;
050    Action(char code) {
051      this.code = (byte)code;
052    }
053
054    public byte code() { return code; }
055  }
056
057  private static final Logger LOG = LoggerFactory.getLogger(Permission.class);
058  protected static final Map<Byte,Action> ACTION_BY_CODE = Maps.newHashMap();
059
060  protected Action[] actions;
061
062  static {
063    for (Action a : Action.values()) {
064      ACTION_BY_CODE.put(a.code(), a);
065    }
066  }
067
068  /** Empty constructor for Writable implementation.  <b>Do not use.</b> */
069  public Permission() {
070    super();
071  }
072
073  public Permission(Action... assigned) {
074    if (assigned != null && assigned.length > 0) {
075      actions = Arrays.copyOf(assigned, assigned.length);
076    }
077  }
078
079  public Permission(byte[] actionCodes) {
080    if (actionCodes != null) {
081      Action acts[] = new Action[actionCodes.length];
082      int j = 0;
083      for (int i=0; i<actionCodes.length; i++) {
084        byte b = actionCodes[i];
085        Action a = ACTION_BY_CODE.get(b);
086        if (a == null) {
087          LOG.error("Ignoring unknown action code '"+
088              Bytes.toStringBinary(new byte[]{b})+"'");
089          continue;
090        }
091        acts[j++] = a;
092      }
093      this.actions = Arrays.copyOf(acts, j);
094    }
095  }
096
097  public Action[] getActions() {
098    return actions;
099  }
100
101  public boolean implies(Action action) {
102    if (this.actions != null) {
103      for (Action a : this.actions) {
104        if (a == action) {
105          return true;
106        }
107      }
108    }
109
110    return false;
111  }
112
113  public void setActions(Action[] assigned) {
114    if (assigned != null && assigned.length > 0) {
115      actions = Arrays.copyOf(assigned, assigned.length);
116    }
117  }
118
119  @Override
120  public boolean equals(Object obj) {
121    if (!(obj instanceof Permission)) {
122      return false;
123    }
124    Permission other = (Permission)obj;
125    // check actions
126    if (actions == null && other.getActions() == null) {
127      return true;
128    } else if (actions != null && other.getActions() != null) {
129      Action[] otherActions = other.getActions();
130      if (actions.length != otherActions.length) {
131        return false;
132      }
133
134      outer:
135      for (Action a : actions) {
136        for (Action oa : otherActions) {
137          if (a == oa) continue outer;
138        }
139        return false;
140      }
141      return true;
142    }
143
144    return false;
145  }
146
147  @Override
148  public int hashCode() {
149    final int prime = 37;
150    int result = 23;
151    for (Action a : actions) {
152      result = prime * result + a.code();
153    }
154    return result;
155  }
156
157  @Override
158  public String toString() {
159    StringBuilder str = new StringBuilder("[Permission: ")
160        .append("actions=");
161    if (actions != null) {
162      for (int i=0; i<actions.length; i++) {
163        if (i > 0)
164          str.append(",");
165        if (actions[i] != null)
166          str.append(actions[i].toString());
167        else
168          str.append("NULL");
169      }
170    }
171    str.append("]");
172
173    return str.toString();
174  }
175
176  /** @return the object version number */
177  @Override
178  public byte getVersion() {
179    return VERSION;
180  }
181
182  @Override
183  public void readFields(DataInput in) throws IOException {
184    super.readFields(in);
185    int length = (int)in.readByte();
186    if (length > 0) {
187      actions = new Action[length];
188      for (int i = 0; i < length; i++) {
189        byte b = in.readByte();
190        Action a = ACTION_BY_CODE.get(b);
191        if (a == null) {
192          throw new IOException("Unknown action code '"+
193              Bytes.toStringBinary(new byte[]{b})+"' in input");
194        }
195        this.actions[i] = a;
196      }
197    } else {
198      actions = new Action[0];
199    }
200  }
201
202  @Override
203  public void write(DataOutput out) throws IOException {
204    super.write(out);
205    out.writeByte(actions != null ? actions.length : 0);
206    if (actions != null) {
207      for (Action a: actions) {
208        out.writeByte(a.code());
209      }
210    }
211  }
212}