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 */
018package org.apache.hadoop.hbase.security.access;
019
020import java.util.Collection;
021import java.util.HashMap;
022import java.util.Map;
023import java.util.Map.Entry;
024import org.apache.hadoop.hbase.Cell;
025import org.apache.hadoop.hbase.TableName;
026import org.apache.hadoop.hbase.security.User;
027import org.apache.hadoop.hbase.util.Bytes;
028import org.apache.yetus.audience.InterfaceAudience;
029
030import org.apache.hbase.thirdparty.com.google.common.base.Joiner;
031
032/**
033 * Represents the result of an authorization check for logging and error reporting.
034 */
035@InterfaceAudience.Private
036public class AuthResult {
037  private boolean allowed;
038  private final String namespace;
039  private final TableName table;
040  private final Permission.Action action;
041  private final String request;
042  private String reason;
043  private final User user;
044  private AuthResult.Params params;
045
046  // "family" and "qualifier" should only be used if "families" is null.
047  private final byte[] family;
048  private final byte[] qualifier;
049  private final Map<byte[], ? extends Collection<?>> families;
050
051  public AuthResult(boolean allowed, String request, String reason, User user,
052    Permission.Action action, TableName table, byte[] family, byte[] qualifier) {
053    this.allowed = allowed;
054    this.request = request;
055    this.reason = reason;
056    this.user = user;
057    this.table = table;
058    this.family = family;
059    this.qualifier = qualifier;
060    this.action = action;
061    this.families = null;
062    this.namespace = null;
063    this.params = new Params().setTableName(table).setFamily(family).setQualifier(qualifier);
064  }
065
066  public AuthResult(boolean allowed, String request, String reason, User user,
067    Permission.Action action, TableName table, Map<byte[], ? extends Collection<?>> families) {
068    this.allowed = allowed;
069    this.request = request;
070    this.reason = reason;
071    this.user = user;
072    this.table = table;
073    this.family = null;
074    this.qualifier = null;
075    this.action = action;
076    this.families = families;
077    this.namespace = null;
078    this.params = new Params().setTableName(table).setFamilies(families);
079  }
080
081  public AuthResult(boolean allowed, String request, String reason, User user,
082    Permission.Action action, String namespace) {
083    this.allowed = allowed;
084    this.request = request;
085    this.reason = reason;
086    this.user = user;
087    this.namespace = namespace;
088    this.action = action;
089    this.table = null;
090    this.family = null;
091    this.qualifier = null;
092    this.families = null;
093    this.params = new Params().setNamespace(namespace);
094  }
095
096  public boolean isAllowed() {
097    return allowed;
098  }
099
100  public User getUser() {
101    return user;
102  }
103
104  public String getReason() {
105    return reason;
106  }
107
108  public TableName getTableName() {
109    return table;
110  }
111
112  public byte[] getFamily() {
113    return family;
114  }
115
116  public byte[] getQualifier() {
117    return qualifier;
118  }
119
120  public Permission.Action getAction() {
121    return action;
122  }
123
124  public String getRequest() {
125    return request;
126  }
127
128  public Params getParams() {
129    return this.params;
130  }
131
132  public void setAllowed(boolean allowed) {
133    this.allowed = allowed;
134  }
135
136  public void setReason(String reason) {
137    this.reason = reason;
138  }
139
140  private static String toFamiliesString(Map<byte[], ? extends Collection<?>> families,
141    byte[] family, byte[] qual) {
142    StringBuilder sb = new StringBuilder();
143    if (families != null) {
144      boolean first = true;
145      for (Map.Entry<byte[], ? extends Collection<?>> entry : families.entrySet()) {
146        String familyName = Bytes.toString(entry.getKey());
147        if (entry.getValue() != null && !entry.getValue().isEmpty()) {
148          for (Object o : entry.getValue()) {
149            String qualifier;
150            if (o instanceof byte[]) {
151              qualifier = Bytes.toString((byte[]) o);
152            } else if (o instanceof Cell) {
153              Cell c = (Cell) o;
154              qualifier = Bytes.toString(c.getQualifierArray(), c.getQualifierOffset(),
155                c.getQualifierLength());
156            } else {
157              // Shouldn't really reach this?
158              qualifier = o.toString();
159            }
160            if (!first) {
161              sb.append("|");
162            }
163            first = false;
164            sb.append(familyName).append(":").append(qualifier);
165          }
166        } else {
167          if (!first) {
168            sb.append("|");
169          }
170          first = false;
171          sb.append(familyName);
172        }
173      }
174    } else if (family != null) {
175      sb.append(Bytes.toString(family));
176      if (qual != null) {
177        sb.append(":").append(Bytes.toString(qual));
178      }
179    }
180    return sb.toString();
181  }
182
183  public String toContextString() {
184    StringBuilder sb = new StringBuilder();
185    String familiesString = toFamiliesString(families, family, qualifier);
186    sb.append("(user=").append(user != null ? user.getName() : "UNKNOWN").append(", ");
187    sb.append("scope=")
188      .append(namespace != null ? namespace
189        : table == null ? "GLOBAL"
190        : table.getNameWithNamespaceInclAsString())
191      .append(", ");
192    if (namespace == null && familiesString.length() > 0) {
193      sb.append("family=").append(familiesString).append(", ");
194    }
195    String paramsString = params.toString();
196    if (paramsString.length() > 0) {
197      sb.append("params=[").append(paramsString).append("],");
198    }
199    sb.append("action=").append(action != null ? action.toString() : "").append(")");
200    return sb.toString();
201  }
202
203  @Override
204  public String toString() {
205    return "AuthResult" + toContextString();
206  }
207
208  public static AuthResult allow(String request, String reason, User user, Permission.Action action,
209    String namespace) {
210    return new AuthResult(true, request, reason, user, action, namespace);
211  }
212
213  public static AuthResult allow(String request, String reason, User user, Permission.Action action,
214    TableName table, byte[] family, byte[] qualifier) {
215    return new AuthResult(true, request, reason, user, action, table, family, qualifier);
216  }
217
218  public static AuthResult allow(String request, String reason, User user, Permission.Action action,
219    TableName table, Map<byte[], ? extends Collection<?>> families) {
220    return new AuthResult(true, request, reason, user, action, table, families);
221  }
222
223  public static AuthResult deny(String request, String reason, User user, Permission.Action action,
224    String namespace) {
225    return new AuthResult(false, request, reason, user, action, namespace);
226  }
227
228  public static AuthResult deny(String request, String reason, User user, Permission.Action action,
229    TableName table, byte[] family, byte[] qualifier) {
230    return new AuthResult(false, request, reason, user, action, table, family, qualifier);
231  }
232
233  public static AuthResult deny(String request, String reason, User user, Permission.Action action,
234    TableName table, Map<byte[], ? extends Collection<?>> families) {
235    return new AuthResult(false, request, reason, user, action, table, families);
236  }
237
238  public String toFamilyString() {
239    return toFamiliesString(families, family, qualifier);
240  }
241
242  public static class Params {
243    private String namespace = null;
244    private TableName tableName = null;
245    private Map<byte[], ? extends Collection<?>> families = null;
246    byte[] family = null;
247    byte[] qualifier = null;
248    // For extra parameters to be shown in audit log
249    private final Map<String, String> extraParams = new HashMap<String, String>(2);
250
251    public Params addExtraParam(String key, String value) {
252      extraParams.put(key, value);
253      return this;
254    }
255
256    public Params setNamespace(String namespace) {
257      this.namespace = namespace;
258      return this;
259    }
260
261    public Params setTableName(TableName table) {
262      this.tableName = table;
263      return this;
264    }
265
266    public Params setFamilies(Map<byte[], ? extends Collection<?>> families) {
267      this.families = families;
268      return this;
269    }
270
271    public Params setFamily(byte[] family) {
272      this.family = family;
273      return this;
274    }
275
276    public Params setQualifier(byte[] qualifier) {
277      this.qualifier = qualifier;
278      return this;
279    }
280
281    @Override
282    public String toString() {
283      String familiesString = toFamiliesString(families, family, qualifier);
284      String[] params = new String[] { namespace != null ? "namespace=" + namespace : null,
285        tableName != null ? "table=" + tableName.getNameWithNamespaceInclAsString() : null,
286        familiesString.length() > 0 ? "family=" + familiesString : null,
287        extraParams.isEmpty() ? null : concatenateExtraParams() };
288      return Joiner.on(",").skipNulls().join(params);
289    }
290
291    /**
292     * @return extra parameter key/value string
293     */
294    private String concatenateExtraParams() {
295      final StringBuilder sb = new StringBuilder();
296      boolean first = true;
297      for (Entry<String, String> entry : extraParams.entrySet()) {
298        if (entry.getKey() != null && entry.getValue() != null) {
299          if (!first) {
300            sb.append(',');
301          }
302          first = false;
303          sb.append(entry.getKey() + '=');
304          sb.append(entry.getValue());
305        }
306      }
307      return sb.toString();
308    }
309  }
310}