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