accumulo-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ctubb...@apache.org
Subject [46/54] [partial] ACCUMULO-658, ACCUMULO-656 Split server into separate modules
Date Fri, 01 Nov 2013 00:56:25 GMT
http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/base/src/main/java/org/apache/accumulo/server/problems/ProblemReport.java
----------------------------------------------------------------------
diff --git a/server/base/src/main/java/org/apache/accumulo/server/problems/ProblemReport.java b/server/base/src/main/java/org/apache/accumulo/server/problems/ProblemReport.java
new file mode 100644
index 0000000..fec4e55
--- /dev/null
+++ b/server/base/src/main/java/org/apache/accumulo/server/problems/ProblemReport.java
@@ -0,0 +1,221 @@
+/*
+ * 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.accumulo.server.problems;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Map.Entry;
+
+import org.apache.accumulo.core.Constants;
+import org.apache.accumulo.core.data.Key;
+import org.apache.accumulo.core.data.Mutation;
+import org.apache.accumulo.core.data.Value;
+import org.apache.accumulo.core.util.Encoding;
+import org.apache.accumulo.core.zookeeper.ZooUtil;
+import org.apache.accumulo.fate.zookeeper.ZooUtil.NodeExistsPolicy;
+import org.apache.accumulo.fate.zookeeper.ZooUtil.NodeMissingPolicy;
+import org.apache.accumulo.server.client.HdfsZooInstance;
+import org.apache.accumulo.server.security.SystemCredentials;
+import org.apache.accumulo.server.util.MetadataTableUtil;
+import org.apache.accumulo.server.zookeeper.ZooReaderWriter;
+import org.apache.hadoop.io.Text;
+
+public class ProblemReport {
+  private String tableName;
+  private ProblemType problemType;
+  private String resource;
+  private String exception;
+  private String server;
+  private long creationTime;
+  
+  public ProblemReport(String table, ProblemType problemType, String resource, String server, Throwable e) {
+    this.tableName = table;
+    
+    this.problemType = problemType;
+    this.resource = resource;
+    
+    if (e != null) {
+      this.exception = e.getMessage();
+    }
+    
+    if (server == null) {
+      try {
+        server = InetAddress.getLocalHost().getHostAddress();
+      } catch (UnknownHostException e1) {
+        
+      }
+    }
+    
+    this.server = server;
+    this.creationTime = System.currentTimeMillis();
+  }
+  
+  public ProblemReport(String table, ProblemType problemType, String resource, Throwable e) {
+    this(table, problemType, resource, null, e);
+  }
+  
+  private ProblemReport(String table, ProblemType problemType, String resource, byte enc[]) throws IOException {
+    this.tableName = table;
+    this.problemType = problemType;
+    this.resource = resource;
+    
+    decode(enc);
+  }
+  
+  private byte[] encode() throws IOException {
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    DataOutputStream dos = new DataOutputStream(baos);
+    
+    dos.writeLong(creationTime);
+    
+    dos.writeBoolean(server != null);
+    if (server != null) {
+      dos.writeUTF(server);
+    }
+    
+    dos.writeBoolean(exception != null);
+    if (exception != null) {
+      dos.writeUTF(exception);
+    }
+    
+    dos.close();
+    baos.close();
+    
+    return baos.toByteArray();
+  }
+  
+  private void decode(byte enc[]) throws IOException {
+    ByteArrayInputStream bais = new ByteArrayInputStream(enc);
+    DataInputStream dis = new DataInputStream(bais);
+    
+    creationTime = dis.readLong();
+    
+    if (dis.readBoolean()) {
+      server = dis.readUTF();
+    } else {
+      server = null;
+    }
+    
+    if (dis.readBoolean()) {
+      exception = dis.readUTF();
+    } else {
+      exception = null;
+    }
+  }
+  
+  void removeFromMetadataTable() throws Exception {
+    Mutation m = new Mutation(new Text("~err_" + tableName));
+    m.putDelete(new Text(problemType.name()), new Text(resource));
+    MetadataTableUtil.getMetadataTable(SystemCredentials.get()).update(m);
+  }
+  
+  void saveToMetadataTable() throws Exception {
+    Mutation m = new Mutation(new Text("~err_" + tableName));
+    m.put(new Text(problemType.name()), new Text(resource), new Value(encode()));
+    MetadataTableUtil.getMetadataTable(SystemCredentials.get()).update(m);
+  }
+  
+  void removeFromZooKeeper() throws Exception {
+    String zpath = getZPath();
+    ZooReaderWriter.getInstance().recursiveDelete(zpath, NodeMissingPolicy.SKIP);
+  }
+  
+  void saveToZooKeeper() throws Exception {
+    ZooReaderWriter.getInstance().putPersistentData(getZPath(), encode(), NodeExistsPolicy.OVERWRITE);
+  }
+  
+  private String getZPath() throws IOException {
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    DataOutputStream dos = new DataOutputStream(baos);
+    dos.writeUTF(getTableName());
+    dos.writeUTF(getProblemType().name());
+    dos.writeUTF(getResource());
+    dos.close();
+    baos.close();
+    
+    String zpath = ZooUtil.getRoot(HdfsZooInstance.getInstance()) + Constants.ZPROBLEMS + "/" + Encoding.encodeAsBase64FileName(new Text(baos.toByteArray()));
+    return zpath;
+  }
+  
+  static ProblemReport decodeZooKeeperEntry(String node) throws Exception {
+    byte bytes[] = Encoding.decodeBase64FileName(node);
+    
+    ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+    DataInputStream dis = new DataInputStream(bais);
+    
+    String tableName = dis.readUTF();
+    String problemType = dis.readUTF();
+    String resource = dis.readUTF();
+    
+    String zpath = ZooUtil.getRoot(HdfsZooInstance.getInstance()) + Constants.ZPROBLEMS + "/" + node;
+    byte[] enc = ZooReaderWriter.getInstance().getData(zpath, null);
+    
+    return new ProblemReport(tableName, ProblemType.valueOf(problemType), resource, enc);
+    
+  }
+  
+  public static ProblemReport decodeMetadataEntry(Entry<Key,Value> entry) throws IOException {
+    String tableName = entry.getKey().getRow().toString().substring("~err_".length());
+    String problemType = entry.getKey().getColumnFamily().toString();
+    String resource = entry.getKey().getColumnQualifier().toString();
+    
+    return new ProblemReport(tableName, ProblemType.valueOf(problemType), resource, entry.getValue().get());
+  }
+  
+  public String getTableName() {
+    return tableName;
+  }
+  
+  public ProblemType getProblemType() {
+    return problemType;
+  }
+  
+  public String getResource() {
+    return resource;
+  }
+  
+  public String getException() {
+    return exception;
+  }
+  
+  public String getServer() {
+    return server;
+  }
+  
+  public long getTime() {
+    return creationTime;
+  }
+  
+  @Override
+  public int hashCode() {
+    return tableName.hashCode() + problemType.hashCode() + resource.hashCode();
+  }
+  
+  @Override
+  public boolean equals(Object o) {
+    if (o instanceof ProblemReport) {
+      ProblemReport opr = (ProblemReport) o;
+      return tableName.equals(opr.tableName) && problemType.equals(opr.problemType) && resource.equals(opr.resource);
+    }
+    return false;
+  }
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/base/src/main/java/org/apache/accumulo/server/problems/ProblemReportingIterator.java
----------------------------------------------------------------------
diff --git a/server/base/src/main/java/org/apache/accumulo/server/problems/ProblemReportingIterator.java b/server/base/src/main/java/org/apache/accumulo/server/problems/ProblemReportingIterator.java
new file mode 100644
index 0000000..69d73e1
--- /dev/null
+++ b/server/base/src/main/java/org/apache/accumulo/server/problems/ProblemReportingIterator.java
@@ -0,0 +1,116 @@
+/*
+ * 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.accumulo.server.problems;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.accumulo.core.data.ByteSequence;
+import org.apache.accumulo.core.data.Key;
+import org.apache.accumulo.core.data.Range;
+import org.apache.accumulo.core.data.Value;
+import org.apache.accumulo.core.iterators.IteratorEnvironment;
+import org.apache.accumulo.core.iterators.SortedKeyValueIterator;
+import org.apache.accumulo.core.iterators.system.InterruptibleIterator;
+
+public class ProblemReportingIterator implements InterruptibleIterator {
+  private SortedKeyValueIterator<Key,Value> source;
+  private boolean sawError = false;
+  private boolean continueOnError;
+  private String resource;
+  private String table;
+  
+  public ProblemReportingIterator(String table, String resource, boolean continueOnError, SortedKeyValueIterator<Key,Value> source) {
+    this.table = table;
+    this.resource = resource;
+    this.continueOnError = continueOnError;
+    this.source = source;
+  }
+  
+  @Override
+  public SortedKeyValueIterator<Key,Value> deepCopy(IteratorEnvironment env) {
+    return new ProblemReportingIterator(table, resource, continueOnError, source.deepCopy(env));
+  }
+  
+  @Override
+  public Key getTopKey() {
+    return source.getTopKey();
+  }
+  
+  @Override
+  public Value getTopValue() {
+    return source.getTopValue();
+  }
+  
+  @Override
+  public boolean hasTop() {
+    if (sawError) {
+      return false;
+    }
+    return source.hasTop();
+  }
+  
+  @Override
+  public void init(SortedKeyValueIterator<Key,Value> source, Map<String,String> options, IteratorEnvironment env) throws IOException {
+    throw new UnsupportedOperationException();
+  }
+  
+  @Override
+  public void next() throws IOException {
+    try {
+      source.next();
+    } catch (IOException ioe) {
+      sawError = true;
+      ProblemReports.getInstance().report(new ProblemReport(table, ProblemType.FILE_READ, resource, ioe));
+      if (!continueOnError) {
+        throw ioe;
+      }
+    }
+  }
+  
+  @Override
+  public void seek(Range range, Collection<ByteSequence> columnFamilies, boolean inclusive) throws IOException {
+    if (continueOnError && sawError) {
+      return;
+    }
+    
+    try {
+      source.seek(range, columnFamilies, inclusive);
+    } catch (IOException ioe) {
+      sawError = true;
+      ProblemReports.getInstance().report(new ProblemReport(table, ProblemType.FILE_READ, resource, ioe));
+      if (!continueOnError) {
+        throw ioe;
+      }
+    }
+  }
+  
+  public boolean sawError() {
+    return sawError;
+  }
+  
+  public String getResource() {
+    return resource;
+  }
+  
+  @Override
+  public void setInterruptFlag(AtomicBoolean flag) {
+    ((InterruptibleIterator) source).setInterruptFlag(flag);
+  }
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/base/src/main/java/org/apache/accumulo/server/problems/ProblemReports.java
----------------------------------------------------------------------
diff --git a/server/base/src/main/java/org/apache/accumulo/server/problems/ProblemReports.java b/server/base/src/main/java/org/apache/accumulo/server/problems/ProblemReports.java
new file mode 100644
index 0000000..a68a3cd
--- /dev/null
+++ b/server/base/src/main/java/org/apache/accumulo/server/problems/ProblemReports.java
@@ -0,0 +1,320 @@
+/*
+ * 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.accumulo.server.problems;
+
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.TreeMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.accumulo.core.Constants;
+import org.apache.accumulo.core.client.Connector;
+import org.apache.accumulo.core.client.IteratorSetting;
+import org.apache.accumulo.core.client.Scanner;
+import org.apache.accumulo.core.data.Key;
+import org.apache.accumulo.core.data.Mutation;
+import org.apache.accumulo.core.data.Range;
+import org.apache.accumulo.core.data.Value;
+import org.apache.accumulo.core.iterators.SortedKeyIterator;
+import org.apache.accumulo.core.metadata.MetadataTable;
+import org.apache.accumulo.core.metadata.RootTable;
+import org.apache.accumulo.core.security.Authorizations;
+import org.apache.accumulo.core.util.LoggingRunnable;
+import org.apache.accumulo.core.util.NamingThreadFactory;
+import org.apache.accumulo.core.zookeeper.ZooUtil;
+import org.apache.accumulo.fate.zookeeper.IZooReaderWriter;
+import org.apache.accumulo.server.client.HdfsZooInstance;
+import org.apache.accumulo.server.security.SystemCredentials;
+import org.apache.accumulo.server.util.MetadataTableUtil;
+import org.apache.accumulo.server.zookeeper.ZooReaderWriter;
+import org.apache.commons.collections.map.LRUMap;
+import org.apache.hadoop.io.Text;
+import org.apache.log4j.Logger;
+
+public class ProblemReports implements Iterable<ProblemReport> {
+  
+  private static final Logger log = Logger.getLogger(ProblemReports.class);
+  
+  private final LRUMap problemReports = new LRUMap(1000);
+  
+  /*
+   * use a thread pool so that reporting a problem never blocks
+   * 
+   * make the thread pool use a bounded queue to avoid the case where problem reports are not being processed because the whole system is in a really bad state
+   * (like HDFS is down) and everything is reporting lots of problems, but problem reports can not be processed
+   */
+  private ExecutorService reportExecutor = new ThreadPoolExecutor(0, 1, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(500), new NamingThreadFactory(
+      "acu-problem-reporter"));
+  
+  public void report(final ProblemReport pr) {
+    
+    synchronized (problemReports) {
+      if (problemReports.containsKey(pr)) {
+        return;
+      }
+      
+      problemReports.put(pr, System.currentTimeMillis());
+    }
+    
+    Runnable r = new Runnable() {
+      
+      @Override
+      public void run() {
+        
+        log.debug("Filing problem report " + pr.getTableName() + " " + pr.getProblemType() + " " + pr.getResource());
+        
+        try {
+          if (pr.getTableName().equals(MetadataTable.ID) || pr.getTableName().equals(RootTable.ID)) {
+            // file report in zookeeper
+            pr.saveToZooKeeper();
+          } else {
+            // file report in metadata table
+            pr.saveToMetadataTable();
+          }
+        } catch (Exception e) {
+          log.error("Failed to file problem report " + pr.getTableName() + " " + pr.getProblemType() + " " + pr.getResource(), e);
+        }
+      }
+      
+    };
+    
+    try {
+      reportExecutor.execute(new LoggingRunnable(log, r));
+    } catch (RejectedExecutionException ree) {
+      log.error("Failed to report problem " + pr.getTableName() + " " + pr.getProblemType() + " " + pr.getResource() + "  " + ree.getMessage());
+    }
+    
+  }
+  
+  public void printProblems() throws Exception {
+    for (ProblemReport pr : this) {
+      System.out.println(pr.getTableName() + " " + pr.getProblemType() + " " + pr.getResource() + " " + pr.getException());
+    }
+  }
+  
+  public void deleteProblemReport(String table, ProblemType pType, String resource) {
+    final ProblemReport pr = new ProblemReport(table, pType, resource, null);
+    
+    Runnable r = new Runnable() {
+      
+      @Override
+      public void run() {
+        try {
+          if (pr.getTableName().equals(MetadataTable.ID)) {
+            // file report in zookeeper
+            pr.removeFromZooKeeper();
+          } else {
+            // file report in metadata table
+            pr.removeFromMetadataTable();
+          }
+        } catch (Exception e) {
+          log.error("Failed to delete problem report " + pr.getTableName() + " " + pr.getProblemType() + " " + pr.getResource(), e);
+        }
+      }
+    };
+    
+    try {
+      reportExecutor.execute(new LoggingRunnable(log, r));
+    } catch (RejectedExecutionException ree) {
+      log.error("Failed to delete problem report " + pr.getTableName() + " " + pr.getProblemType() + " " + pr.getResource() + "  " + ree.getMessage());
+    }
+  }
+  
+  private static ProblemReports instance;
+  
+  public void deleteProblemReports(String table) throws Exception {
+    
+    if (MetadataTable.ID.equals(table)) {
+      Iterator<ProblemReport> pri = iterator(table);
+      while (pri.hasNext()) {
+        pri.next().removeFromZooKeeper();
+      }
+      return;
+    }
+    
+    Connector connector = HdfsZooInstance.getInstance().getConnector(SystemCredentials.get().getPrincipal(), SystemCredentials.get().getToken());
+    Scanner scanner = connector.createScanner(MetadataTable.NAME, Authorizations.EMPTY);
+    scanner.addScanIterator(new IteratorSetting(1, "keys-only", SortedKeyIterator.class));
+    
+    if (table == null) {
+      scanner.setRange(new Range(new Text("~err_"), false, new Text("~err`"), false));
+    } else {
+      scanner.setRange(new Range(new Text("~err_" + table)));
+    }
+    
+    Mutation delMut = new Mutation(new Text("~err_" + table));
+    
+    boolean hasProblems = false;
+    for (Entry<Key,Value> entry : scanner) {
+      hasProblems = true;
+      delMut.putDelete(entry.getKey().getColumnFamily(), entry.getKey().getColumnQualifier());
+    }
+    
+    if (hasProblems)
+      MetadataTableUtil.getMetadataTable(SystemCredentials.get()).update(delMut);
+  }
+  
+  public Iterator<ProblemReport> iterator(final String table) {
+    try {
+      
+      return new Iterator<ProblemReport>() {
+        
+        IZooReaderWriter zoo = ZooReaderWriter.getInstance();
+        private int iter1Count = 0;
+        private Iterator<String> iter1;
+        
+        private Iterator<String> getIter1() {
+          if (iter1 == null) {
+            try {
+              List<String> children;
+              if (table == null || table.equals(MetadataTable.ID)) {
+                children = zoo.getChildren(ZooUtil.getRoot(HdfsZooInstance.getInstance()) + Constants.ZPROBLEMS);
+              } else {
+                children = Collections.emptyList();
+              }
+              iter1 = children.iterator();
+            } catch (Exception e) {
+              throw new RuntimeException(e);
+            }
+          }
+          
+          return iter1;
+        }
+        
+        private Iterator<Entry<Key,Value>> iter2;
+        
+        private Iterator<Entry<Key,Value>> getIter2() {
+          if (iter2 == null) {
+            try {
+              if ((table == null || !table.equals(MetadataTable.ID)) && iter1Count == 0) {
+                Connector connector = HdfsZooInstance.getInstance().getConnector(SystemCredentials.get().getPrincipal(), SystemCredentials.get().getToken());
+                Scanner scanner = connector.createScanner(MetadataTable.NAME, Authorizations.EMPTY);
+                
+                scanner.setTimeout(3, TimeUnit.SECONDS);
+                
+                if (table == null) {
+                  scanner.setRange(new Range(new Text("~err_"), false, new Text("~err`"), false));
+                } else {
+                  scanner.setRange(new Range(new Text("~err_" + table)));
+                }
+                
+                iter2 = scanner.iterator();
+                
+              } else {
+                Map<Key,Value> m = Collections.emptyMap();
+                iter2 = m.entrySet().iterator();
+              }
+            } catch (Exception e) {
+              throw new RuntimeException(e);
+            }
+          }
+          
+          return iter2;
+        }
+        
+        @Override
+        public boolean hasNext() {
+          if (getIter1().hasNext()) {
+            return true;
+          }
+          
+          if (getIter2().hasNext()) {
+            return true;
+          }
+          
+          return false;
+        }
+        
+        @Override
+        public ProblemReport next() {
+          try {
+            if (getIter1().hasNext()) {
+              iter1Count++;
+              return ProblemReport.decodeZooKeeperEntry(getIter1().next());
+            }
+            
+            if (getIter2().hasNext()) {
+              return ProblemReport.decodeMetadataEntry(getIter2().next());
+            }
+          } catch (Exception e) {
+            throw new RuntimeException(e);
+          }
+          
+          throw new NoSuchElementException();
+        }
+        
+        @Override
+        public void remove() {
+          throw new UnsupportedOperationException();
+        }
+        
+      };
+      
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
+  
+  @Override
+  public Iterator<ProblemReport> iterator() {
+    return iterator(null);
+  }
+  
+  public static synchronized ProblemReports getInstance() {
+    if (instance == null) {
+      instance = new ProblemReports();
+    }
+    
+    return instance;
+  }
+  
+  public static void main(String args[]) throws Exception {
+    getInstance().printProblems();
+  }
+  
+  public Map<String,Map<ProblemType,Integer>> summarize() {
+    
+    TreeMap<String,Map<ProblemType,Integer>> summary = new TreeMap<String,Map<ProblemType,Integer>>();
+    
+    for (ProblemReport pr : this) {
+      Map<ProblemType,Integer> tableProblems = summary.get(pr.getTableName());
+      if (tableProblems == null) {
+        tableProblems = new EnumMap<ProblemType,Integer>(ProblemType.class);
+        summary.put(pr.getTableName(), tableProblems);
+      }
+      
+      Integer count = tableProblems.get(pr.getProblemType());
+      if (count == null) {
+        count = 0;
+      }
+      
+      tableProblems.put(pr.getProblemType(), count + 1);
+    }
+    
+    return summary;
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/base/src/main/java/org/apache/accumulo/server/problems/ProblemType.java
----------------------------------------------------------------------
diff --git a/server/base/src/main/java/org/apache/accumulo/server/problems/ProblemType.java b/server/base/src/main/java/org/apache/accumulo/server/problems/ProblemType.java
new file mode 100644
index 0000000..27af759
--- /dev/null
+++ b/server/base/src/main/java/org/apache/accumulo/server/problems/ProblemType.java
@@ -0,0 +1,21 @@
+/*
+ * 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.accumulo.server.problems;
+
+public enum ProblemType {
+  FILE_READ, FILE_WRITE, TABLET_LOAD
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/base/src/main/java/org/apache/accumulo/server/security/AuditedSecurityOperation.java
----------------------------------------------------------------------
diff --git a/server/base/src/main/java/org/apache/accumulo/server/security/AuditedSecurityOperation.java b/server/base/src/main/java/org/apache/accumulo/server/security/AuditedSecurityOperation.java
new file mode 100644
index 0000000..ee337a5
--- /dev/null
+++ b/server/base/src/main/java/org/apache/accumulo/server/security/AuditedSecurityOperation.java
@@ -0,0 +1,436 @@
+/*
+ * 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.accumulo.server.security;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.accumulo.core.client.TableNotFoundException;
+import org.apache.accumulo.core.client.impl.Tables;
+import org.apache.accumulo.core.client.impl.Translator;
+import org.apache.accumulo.core.client.impl.thrift.ThriftSecurityException;
+import org.apache.accumulo.core.data.Column;
+import org.apache.accumulo.core.data.KeyExtent;
+import org.apache.accumulo.core.data.Range;
+import org.apache.accumulo.core.data.thrift.IterInfo;
+import org.apache.accumulo.core.data.thrift.TColumn;
+import org.apache.accumulo.core.data.thrift.TKeyExtent;
+import org.apache.accumulo.core.data.thrift.TRange;
+import org.apache.accumulo.core.master.thrift.TableOperation;
+import org.apache.accumulo.core.metadata.MetadataTable;
+import org.apache.accumulo.core.security.Authorizations;
+import org.apache.accumulo.core.security.Credentials;
+import org.apache.accumulo.core.security.SystemPermission;
+import org.apache.accumulo.core.security.TablePermission;
+import org.apache.accumulo.core.security.thrift.TCredentials;
+import org.apache.accumulo.core.util.ByteBufferUtil;
+import org.apache.accumulo.server.client.HdfsZooInstance;
+import org.apache.accumulo.server.security.handler.Authenticator;
+import org.apache.accumulo.server.security.handler.Authorizor;
+import org.apache.accumulo.server.security.handler.PermissionHandler;
+import org.apache.hadoop.io.Text;
+import org.apache.log4j.Logger;
+
+/**
+ *
+ */
+public class AuditedSecurityOperation extends SecurityOperation {
+  
+  public static final String AUDITLOG = "Audit";
+  public static final Logger audit = Logger.getLogger(AUDITLOG);
+  
+  public AuditedSecurityOperation(Authorizor author, Authenticator authent, PermissionHandler pm, String instanceId) {
+    super(author, authent, pm, instanceId);
+  }
+  
+  public static synchronized SecurityOperation getInstance() {
+    String instanceId = HdfsZooInstance.getInstance().getInstanceID();
+    return getInstance(instanceId, false);
+  }
+  
+  public static synchronized SecurityOperation getInstance(String instanceId, boolean initialize) {
+    if (instance == null) {
+      instance = new AuditedSecurityOperation(getAuthorizor(instanceId, initialize), getAuthenticator(instanceId, initialize), getPermHandler(instanceId,
+          initialize), instanceId);
+    }
+    return instance;
+  }
+  
+  private static String getTableName(String tableId) {
+    try {
+      return Tables.getTableName(HdfsZooInstance.getInstance(), tableId);
+    } catch (TableNotFoundException e) {
+      return "Unknown Table with ID " + tableId;
+    }
+  }
+  
+  public static StringBuilder getAuthString(List<ByteBuffer> authorizations) {
+    StringBuilder auths = new StringBuilder();
+    for (ByteBuffer bb : authorizations) {
+      auths.append(ByteBufferUtil.toString(bb)).append(",");
+    }
+    return auths;
+  }
+  
+  private static boolean shouldAudit(TCredentials credentials, String tableId) {
+    return !tableId.equals(MetadataTable.ID) && shouldAudit(credentials);
+  }
+  
+  // Is INFO the right level to check? Do we even need that check?
+  private static boolean shouldAudit(TCredentials credentials) {
+    return !SystemCredentials.get().getToken().getClass().getName().equals(credentials.getTokenClassName());
+  }
+  
+  /*
+   * Three auditing methods try to capture the 4 states we might have here. audit is in response to a thrown exception, the operation failed (perhaps due to
+   * insufficient privs, or some other reason) audit(credentials, template, args) is a successful operation audit(credentials, permitted, template, args) is a
+   * privileges check that is either permitted or denied. We don't know if the operation went on to be successful or not at this point, we would have to go
+   * digging through loads of other code to find it.
+   */
+  private void audit(TCredentials credentials, ThriftSecurityException ex, String template, Object... args) {
+    audit.warn("operation: failed; user: " + credentials.getPrincipal() + "; " + String.format(template, args) + "; exception: " + ex.toString());
+  }
+  
+  private void audit(TCredentials credentials, String template, Object... args) {
+    if (shouldAudit(credentials)) {
+      audit.info("operation: success; user: " + credentials.getPrincipal() + ": " + String.format(template, args));
+    }
+  }
+  
+  private void audit(TCredentials credentials, boolean permitted, String template, Object... args) {
+    if (shouldAudit(credentials)) {
+      String prefix = permitted ? "permitted" : "denied";
+      audit.info("operation: " + prefix + "; user: " + credentials.getPrincipal() + "; " + String.format(template, args));
+    }
+  }
+  
+  public static final String CAN_SCAN_AUDIT_TEMPLATE = "action: scan; targetTable: %s; authorizations: %s; range: %s; columns: %s; iterators: %s; iteratorOptions: %s;";
+  private static final int MAX_ELEMENTS_TO_LOG = 10;
+  
+  private static List<String> truncate(Collection<? extends Object> list) {
+    List<String> result = new ArrayList<String>();
+    int i = 0;
+    for (Object obj : list) {
+      if (i > MAX_ELEMENTS_TO_LOG) {
+        result.add(" and " + (list.size() - MAX_ELEMENTS_TO_LOG) + " more ");
+        break;
+      }
+      result.add(obj.toString());
+    }
+    return result;
+  }
+  
+  @Override
+  public boolean canScan(TCredentials credentials, String tableId, TRange range, List<TColumn> columns, List<IterInfo> ssiList,
+      Map<String,Map<String,String>> ssio, List<ByteBuffer> authorizations) throws ThriftSecurityException {
+    if (shouldAudit(credentials, tableId)) {
+      Range convertedRange = new Range(range);
+      List<String> convertedColumns = truncate(Translator.translate(columns, new Translator.TColumnTranslator()));
+      String tableName = getTableName(tableId);
+      
+      try {
+        boolean canScan = super.canScan(credentials, tableId);
+        audit(credentials, canScan, CAN_SCAN_AUDIT_TEMPLATE, tableName, getAuthString(authorizations), convertedRange, convertedColumns, ssiList, ssio);
+        
+        return canScan;
+      } catch (ThriftSecurityException ex) {
+        audit(credentials, ex, CAN_SCAN_AUDIT_TEMPLATE, getAuthString(authorizations), tableId, convertedRange, convertedColumns, ssiList, ssio);
+        throw ex;
+      }
+    } else {
+      return super.canScan(credentials, tableId);
+    }
+  }
+  
+  public static final String CAN_SCAN_BATCH_AUDIT_TEMPLATE = "action: scan; targetTable: %s; authorizations: %s; range: %s; columns: %s; iterators: %s; iteratorOptions: %s;";
+  
+  @Override
+  public boolean canScan(TCredentials credentials, String tableId, Map<TKeyExtent,List<TRange>> tbatch, List<TColumn> tcolumns, List<IterInfo> ssiList,
+      Map<String,Map<String,String>> ssio, List<ByteBuffer> authorizations) throws ThriftSecurityException {
+    if (shouldAudit(credentials, tableId)) {
+      @SuppressWarnings({"unchecked", "rawtypes"})
+      Map<KeyExtent,List<Range>> convertedBatch = Translator.translate(tbatch, new Translator.TKeyExtentTranslator(), new Translator.ListTranslator(
+          new Translator.TRangeTranslator()));
+      Map<KeyExtent, List<String>> truncated = new HashMap<KeyExtent, List<String>>();
+      for (Entry<KeyExtent,List<Range>> entry : convertedBatch.entrySet()) {
+          truncated.put(entry.getKey(), truncate(entry.getValue()));
+      }
+      List<Column> convertedColumns = Translator.translate(tcolumns, new Translator.TColumnTranslator());
+      String tableName = getTableName(tableId);
+      
+      try {
+        boolean canScan = super.canScan(credentials, tableId);
+        audit(credentials, canScan, CAN_SCAN_BATCH_AUDIT_TEMPLATE, tableName, getAuthString(authorizations), convertedBatch, convertedColumns, ssiList, ssio);
+        
+        return canScan;
+      } catch (ThriftSecurityException ex) {
+        audit(credentials, ex, CAN_SCAN_BATCH_AUDIT_TEMPLATE, getAuthString(authorizations), tableId, convertedBatch, convertedColumns, ssiList, ssio);
+        throw ex;
+      }
+    } else {
+      return super.canScan(credentials, tableId);
+    }
+  }
+  
+  public static final String CHANGE_AUTHORIZATIONS_AUDIT_TEMPLATE = "action: changeAuthorizations; targetUser: %s; authorizations: %s";
+  
+  @Override
+  public void changeAuthorizations(TCredentials credentials, String user, Authorizations authorizations) throws ThriftSecurityException {
+    try {
+      super.changeAuthorizations(credentials, user, authorizations);
+      audit(credentials, CHANGE_AUTHORIZATIONS_AUDIT_TEMPLATE, user, authorizations);
+    } catch (ThriftSecurityException ex) {
+      audit(credentials, ex, CHANGE_AUTHORIZATIONS_AUDIT_TEMPLATE, user, authorizations);
+      throw ex;
+    }
+  }
+  
+  public static final String CHANGE_PASSWORD_AUDIT_TEMPLATE = "action: changePassword; targetUser: %s;";
+  
+  @Override
+  public void changePassword(TCredentials credentials, Credentials newInfo) throws ThriftSecurityException {
+    try {
+      super.changePassword(credentials, newInfo);
+      audit(credentials, CHANGE_PASSWORD_AUDIT_TEMPLATE, newInfo.getPrincipal());
+    } catch (ThriftSecurityException ex) {
+      audit(credentials, ex, CHANGE_PASSWORD_AUDIT_TEMPLATE, newInfo.getPrincipal());
+      throw ex;
+    }
+  }
+  
+  public static final String CREATE_USER_AUDIT_TEMPLATE = "action: createUser; targetUser: %s; Authorizations: %s;";
+  
+  @Override
+  public void createUser(TCredentials credentials, Credentials newUser, Authorizations authorizations) throws ThriftSecurityException {
+    try {
+      super.createUser(credentials, newUser, authorizations);
+      audit(credentials, CREATE_USER_AUDIT_TEMPLATE, newUser.getPrincipal(), authorizations);
+    } catch (ThriftSecurityException ex) {
+      audit(credentials, ex, CREATE_USER_AUDIT_TEMPLATE, newUser.getPrincipal(), authorizations);
+      throw ex;
+    }
+  }
+  
+  public static final String CAN_CREATE_TABLE_AUDIT_TEMPLATE = "action: createTable; targetTable: %s;";
+  
+  @Override
+  public boolean canCreateTable(TCredentials c, String tableName) throws ThriftSecurityException {
+    try {
+      boolean result = super.canCreateTable(c);
+      audit(c, result, CAN_CREATE_TABLE_AUDIT_TEMPLATE, tableName);
+      return result;
+    } catch (ThriftSecurityException ex) {
+      audit(c, ex, CAN_CREATE_TABLE_AUDIT_TEMPLATE, tableName);
+      throw ex;
+    }
+  }
+  
+  public static final String CAN_DELETE_TABLE_AUDIT_TEMPLATE = "action: deleteTable; targetTable: %s;";
+  
+  @Override
+  public boolean canDeleteTable(TCredentials c, String tableId) throws ThriftSecurityException {
+    String tableName = getTableName(tableId);
+    try {
+      boolean result = super.canDeleteTable(c, tableId);
+      audit(c, result, CAN_DELETE_TABLE_AUDIT_TEMPLATE, tableName, tableId);
+      return result;
+    } catch (ThriftSecurityException ex) {
+      audit(c, ex, CAN_DELETE_TABLE_AUDIT_TEMPLATE, tableName, tableId);
+      throw ex;
+    }
+  }
+  
+  public static final String CAN_RENAME_TABLE_AUDIT_TEMPLATE = "action: renameTable; targetTable: %s; newTableName: %s;";
+  
+  @Override
+  public boolean canRenameTable(TCredentials c, String tableId, String oldTableName, String newTableName) throws ThriftSecurityException {
+    try {
+      boolean result = super.canRenameTable(c, tableId, oldTableName, newTableName);
+      audit(c, result, CAN_RENAME_TABLE_AUDIT_TEMPLATE, oldTableName, newTableName);
+      return result;
+    } catch (ThriftSecurityException ex) {
+      audit(c, ex, CAN_RENAME_TABLE_AUDIT_TEMPLATE, oldTableName, newTableName);
+      throw ex;
+    }
+  }
+  
+  public static final String CAN_CLONE_TABLE_AUDIT_TEMPLATE = "action: cloneTable; targetTable: %s; newTableName: %s";
+  
+  @Override
+  public boolean canCloneTable(TCredentials c, String tableId, String tableName) throws ThriftSecurityException {
+    String oldTableName = getTableName(tableId);
+    try {
+      boolean result = super.canCloneTable(c, tableId, tableName);
+      audit(c, result, CAN_CLONE_TABLE_AUDIT_TEMPLATE, oldTableName, tableName);
+      return result;
+    } catch (ThriftSecurityException ex) {
+      audit(c, ex, CAN_CLONE_TABLE_AUDIT_TEMPLATE, oldTableName, tableName);
+      throw ex;
+    }
+  }
+  
+  public static final String CAN_DELETE_RANGE_AUDIT_TEMPLATE = "action: deleteData; targetTable: %s; startRange: %s; endRange: %s;";
+  
+  @Override
+  public boolean canDeleteRange(TCredentials c, String tableId, String tableName, Text startRow, Text endRow) throws ThriftSecurityException {
+    try {
+      boolean result = super.canDeleteRange(c, tableId, tableName, startRow, endRow);
+      audit(c, result, CAN_DELETE_RANGE_AUDIT_TEMPLATE, tableName, startRow.toString(), endRow.toString());
+      return result;
+    } catch (ThriftSecurityException ex) {
+      audit(c, ex, CAN_DELETE_RANGE_AUDIT_TEMPLATE, tableName, startRow.toString(), endRow.toString());
+      throw ex;
+    }
+  }
+  
+  public static final String CAN_BULK_IMPORT_AUDIT_TEMPLATE = "action: bulkImport; targetTable: %s; dataDir: %s; failDir: %s;";
+  
+  @Override
+  public boolean canBulkImport(TCredentials c, String tableId, String tableName, String dir, String failDir) throws ThriftSecurityException {
+    try {
+      boolean result = super.canBulkImport(c, tableId);
+      audit(c, result, CAN_BULK_IMPORT_AUDIT_TEMPLATE, tableName, dir, failDir);
+      return result;
+    } catch (ThriftSecurityException ex) {
+      audit(c, ex, CAN_BULK_IMPORT_AUDIT_TEMPLATE, tableName, dir, failDir);
+      throw ex;
+    }
+  }
+  
+  public static final String CAN_IMPORT_AUDIT_TEMPLATE = "action: import; targetTable: %s; dataDir: %s;";
+  
+  @Override
+  public boolean canImport(TCredentials credentials, String tableName, String importDir) throws ThriftSecurityException {
+    
+    try {
+      boolean result = super.canImport(credentials, tableName, importDir);
+      audit(credentials, result, CAN_IMPORT_AUDIT_TEMPLATE, tableName, importDir);
+      return result;
+    } catch (ThriftSecurityException ex) {
+      audit(credentials, ex, CAN_IMPORT_AUDIT_TEMPLATE, tableName, importDir);
+      throw ex;
+    }
+  }
+  
+  public static final String CAN_EXPORT_AUDIT_TEMPLATE = "action: export; targetTable: %s; dataDir: %s;";
+  
+  @Override
+  public boolean canExport(TCredentials credentials, String tableId, String tableName, String exportDir) throws ThriftSecurityException {
+    
+    try {
+      boolean result = super.canExport(credentials, tableId, tableName, exportDir);
+      audit(credentials, result, CAN_EXPORT_AUDIT_TEMPLATE, tableName, exportDir);
+      return result;
+    } catch (ThriftSecurityException ex) {
+      audit(credentials, ex, CAN_EXPORT_AUDIT_TEMPLATE, tableName, exportDir);
+      throw ex;
+    }
+  }
+  
+  public static final String DROP_USER_AUDIT_TEMPLATE = "action: dropUser; targetUser: %s;";
+  
+  @Override
+  public void dropUser(TCredentials credentials, String user) throws ThriftSecurityException {
+    try {
+      super.dropUser(credentials, user);
+      audit(credentials, DROP_USER_AUDIT_TEMPLATE, user);
+    } catch (ThriftSecurityException ex) {
+      audit(credentials, ex, DROP_USER_AUDIT_TEMPLATE, user);
+      throw ex;
+    }
+  }
+  
+  public static final String GRANT_SYSTEM_PERMISSION_AUDIT_TEMPLATE = "action: grantSystemPermission; permission: %s; targetUser: %s;";
+  
+  @Override
+  public void grantSystemPermission(TCredentials credentials, String user, SystemPermission permission) throws ThriftSecurityException {
+    try {
+      super.grantSystemPermission(credentials, user, permission);
+      audit(credentials, GRANT_SYSTEM_PERMISSION_AUDIT_TEMPLATE, permission, user);
+    } catch (ThriftSecurityException ex) {
+      audit(credentials, ex, GRANT_SYSTEM_PERMISSION_AUDIT_TEMPLATE, permission, user);
+      throw ex;
+    }
+  }
+  
+  public static final String GRANT_TABLE_PERMISSION_AUDIT_TEMPLATE = "action: grantTablePermission; permission: %s; targetTable: %s; targetUser: %s;";
+  
+  @Override
+  public void grantTablePermission(TCredentials credentials, String user, String tableId, TablePermission permission) throws ThriftSecurityException {
+    String tableName = getTableName(tableId);
+    try {
+      super.grantTablePermission(credentials, user, tableId, permission);
+      audit(credentials, GRANT_TABLE_PERMISSION_AUDIT_TEMPLATE, permission, tableName, user);
+    } catch (ThriftSecurityException ex) {
+      audit(credentials, ex, GRANT_TABLE_PERMISSION_AUDIT_TEMPLATE, permission, tableName, user);
+      throw ex;
+    }
+  }
+  
+  public static final String REVOKE_SYSTEM_PERMISSION_AUDIT_TEMPLATE = "action: revokeSystemPermission; permission: %s; targetUser: %s;";
+  
+  @Override
+  public void revokeSystemPermission(TCredentials credentials, String user, SystemPermission permission) throws ThriftSecurityException {
+    
+    try {
+      super.revokeSystemPermission(credentials, user, permission);
+      audit(credentials, REVOKE_SYSTEM_PERMISSION_AUDIT_TEMPLATE, permission, user);
+    } catch (ThriftSecurityException ex) {
+      audit(credentials, ex, REVOKE_SYSTEM_PERMISSION_AUDIT_TEMPLATE, permission, user);
+      throw ex;
+    }
+  }
+  
+  public static final String REVOKE_TABLE_PERMISSION_AUDIT_TEMPLATE = "action: revokeTablePermission; permission: %s; targetTable: %s; targetUser: %s;";
+  
+  @Override
+  public void revokeTablePermission(TCredentials credentials, String user, String tableId, TablePermission permission) throws ThriftSecurityException {
+    String tableName = getTableName(tableId);
+    try {
+      super.revokeTablePermission(credentials, user, tableId, permission);
+      audit(credentials, REVOKE_TABLE_PERMISSION_AUDIT_TEMPLATE, permission, tableName, user);
+    } catch (ThriftSecurityException ex) {
+      audit(credentials, ex, REVOKE_TABLE_PERMISSION_AUDIT_TEMPLATE, permission, tableName, user);
+      throw ex;
+    }
+  }
+  
+  public static final String CAN_ONLINE_OFFLINE_TABLE_AUDIT_TEMPLATE = "action: %s; targetTable: %s;";
+  
+  @Override
+  public boolean canOnlineOfflineTable(TCredentials credentials, String tableId, TableOperation op) throws ThriftSecurityException {
+    String tableName = getTableName(tableId);
+    String operation = null;
+    if (op == TableOperation.ONLINE)
+      operation = "onlineTable";
+    if (op == TableOperation.OFFLINE)
+      operation = "offlineTable";
+    try {
+      boolean result = super.canOnlineOfflineTable(credentials, tableId, op);
+      audit(credentials, result, CAN_ONLINE_OFFLINE_TABLE_AUDIT_TEMPLATE, operation, tableName, tableId);
+      return result;
+    } catch (ThriftSecurityException ex) {
+      audit(credentials, ex, CAN_ONLINE_OFFLINE_TABLE_AUDIT_TEMPLATE, operation, tableName, tableId);
+      throw ex;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/base/src/main/java/org/apache/accumulo/server/security/SecurityOperation.java
----------------------------------------------------------------------
diff --git a/server/base/src/main/java/org/apache/accumulo/server/security/SecurityOperation.java b/server/base/src/main/java/org/apache/accumulo/server/security/SecurityOperation.java
new file mode 100644
index 0000000..f00159c
--- /dev/null
+++ b/server/base/src/main/java/org/apache/accumulo/server/security/SecurityOperation.java
@@ -0,0 +1,646 @@
+/*
+ * 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.accumulo.server.security;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.accumulo.core.Constants;
+import org.apache.accumulo.core.client.AccumuloSecurityException;
+import org.apache.accumulo.core.client.TableNotFoundException;
+import org.apache.accumulo.core.client.admin.SecurityOperationsImpl;
+import org.apache.accumulo.core.client.impl.thrift.SecurityErrorCode;
+import org.apache.accumulo.core.client.impl.thrift.ThriftSecurityException;
+import org.apache.accumulo.core.client.security.tokens.AuthenticationToken;
+import org.apache.accumulo.core.client.security.tokens.AuthenticationToken.AuthenticationTokenSerializer;
+import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.data.thrift.IterInfo;
+import org.apache.accumulo.core.data.thrift.TColumn;
+import org.apache.accumulo.core.data.thrift.TKeyExtent;
+import org.apache.accumulo.core.data.thrift.TRange;
+import org.apache.accumulo.core.master.thrift.TableOperation;
+import org.apache.accumulo.core.metadata.MetadataTable;
+import org.apache.accumulo.core.metadata.RootTable;
+import org.apache.accumulo.core.security.Authorizations;
+import org.apache.accumulo.core.security.Credentials;
+import org.apache.accumulo.core.security.SystemPermission;
+import org.apache.accumulo.core.security.TablePermission;
+import org.apache.accumulo.core.security.thrift.TCredentials;
+import org.apache.accumulo.server.client.HdfsZooInstance;
+import org.apache.accumulo.server.conf.ServerConfiguration;
+import org.apache.accumulo.server.security.handler.Authenticator;
+import org.apache.accumulo.server.security.handler.Authorizor;
+import org.apache.accumulo.server.security.handler.PermissionHandler;
+import org.apache.accumulo.server.security.handler.ZKAuthenticator;
+import org.apache.accumulo.server.security.handler.ZKAuthorizor;
+import org.apache.accumulo.server.security.handler.ZKPermHandler;
+import org.apache.accumulo.server.zookeeper.ZooCache;
+import org.apache.hadoop.io.Text;
+import org.apache.log4j.Logger;
+
+/**
+ * Utility class for performing various security operations with the appropriate checks
+ */
+public class SecurityOperation {
+  private static final Logger log = Logger.getLogger(SecurityOperationsImpl.class);
+  
+  protected Authorizor authorizor;
+  protected Authenticator authenticator;
+  protected PermissionHandler permHandle;
+  private static String rootUserName = null;
+  private final ZooCache zooCache;
+  private final String ZKUserPath;
+  
+  protected static SecurityOperation instance;
+  
+  public static synchronized SecurityOperation getInstance() {
+    String instanceId = HdfsZooInstance.getInstance().getInstanceID();
+    return getInstance(instanceId, false);
+  }
+  
+  public static synchronized SecurityOperation getInstance(String instanceId, boolean initialize) {
+    if (instance == null) {
+      instance = new SecurityOperation(getAuthorizor(instanceId, initialize), getAuthenticator(instanceId, initialize), getPermHandler(instanceId, initialize),
+          instanceId);
+    }
+    return instance;
+  }
+  
+  protected static Authorizor getAuthorizor(String instanceId, boolean initialize) {
+    Authorizor toRet = ServerConfiguration.getSiteConfiguration().instantiateClassProperty(Property.INSTANCE_SECURITY_AUTHORIZOR, Authorizor.class,
+        ZKAuthorizor.getInstance());
+    toRet.initialize(instanceId, initialize);
+    return toRet;
+  }
+  
+  protected static Authenticator getAuthenticator(String instanceId, boolean initialize) {
+    Authenticator toRet = ServerConfiguration.getSiteConfiguration().instantiateClassProperty(Property.INSTANCE_SECURITY_AUTHENTICATOR, Authenticator.class,
+        ZKAuthenticator.getInstance());
+    toRet.initialize(instanceId, initialize);
+    return toRet;
+  }
+  
+  protected static PermissionHandler getPermHandler(String instanceId, boolean initialize) {
+    PermissionHandler toRet = ServerConfiguration.getSiteConfiguration().instantiateClassProperty(Property.INSTANCE_SECURITY_PERMISSION_HANDLER,
+        PermissionHandler.class, ZKPermHandler.getInstance());
+    toRet.initialize(instanceId, initialize);
+    return toRet;
+  }
+  
+  protected SecurityOperation(String instanceId) {
+    ZKUserPath = Constants.ZROOT + "/" + instanceId + "/users";
+    zooCache = new ZooCache();
+  }
+  
+  public SecurityOperation(Authorizor author, Authenticator authent, PermissionHandler pm, String instanceId) {
+    this(instanceId);
+    authorizor = author;
+    authenticator = authent;
+    permHandle = pm;
+    
+    if (!authorizor.validSecurityHandlers(authenticator, pm) || !authenticator.validSecurityHandlers(authorizor, pm)
+        || !permHandle.validSecurityHandlers(authent, author))
+      throw new RuntimeException(authorizor + ", " + authenticator + ", and " + pm
+          + " do not play nice with eachother. Please choose authentication and authorization mechanisms that are compatible with one another.");
+  }
+  
+  public void initializeSecurity(TCredentials credentials, String rootPrincipal, byte[] token) throws AccumuloSecurityException, ThriftSecurityException {
+    authenticate(credentials);
+    
+    if (!isSystemUser(credentials))
+      throw new AccumuloSecurityException(credentials.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED);
+    
+    authenticator.initializeSecurity(credentials, rootPrincipal, token);
+    authorizor.initializeSecurity(credentials, rootPrincipal);
+    permHandle.initializeSecurity(credentials, rootPrincipal);
+    try {
+      permHandle.grantTablePermission(rootPrincipal, MetadataTable.ID, TablePermission.ALTER_TABLE);
+    } catch (TableNotFoundException e) {
+      // Shouldn't happen
+      throw new RuntimeException(e);
+    }
+  }
+  
+  public synchronized String getRootUsername() {
+    if (rootUserName == null)
+      rootUserName = new String(zooCache.get(ZKUserPath));
+    return rootUserName;
+  }
+  
+  public boolean isSystemUser(TCredentials credentials) {
+    return SystemCredentials.get().getToken().getClass().getName().equals(credentials.getTokenClassName());
+  }
+  
+  private void authenticate(TCredentials credentials) throws ThriftSecurityException {
+    if (!credentials.getInstanceId().equals(HdfsZooInstance.getInstance().getInstanceID()))
+      throw new ThriftSecurityException(credentials.getPrincipal(), SecurityErrorCode.INVALID_INSTANCEID);
+    
+    if (isSystemUser(credentials)) {
+      authenticateSystemUser(credentials);
+    } else {
+      try {
+        AuthenticationToken token = AuthenticationTokenSerializer.deserialize(credentials.getTokenClassName(), credentials.getToken());
+        if (!authenticator.authenticateUser(credentials.getPrincipal(), token)) {
+          throw new ThriftSecurityException(credentials.getPrincipal(), SecurityErrorCode.BAD_CREDENTIALS);
+        }
+      } catch (AccumuloSecurityException e) {
+        log.debug(e);
+        throw e.asThriftException();
+      }
+    }
+  }
+  
+  private void authenticateSystemUser(TCredentials credentials) throws ThriftSecurityException {
+    if (SystemCredentials.get().getToken().equals(credentials.getToken()))
+      throw new ThriftSecurityException(credentials.getPrincipal(), SecurityErrorCode.BAD_CREDENTIALS);
+  }
+  
+  public boolean canAskAboutUser(TCredentials credentials, String user) throws ThriftSecurityException {
+    // Authentication done in canPerformSystemActions
+    if (!(canPerformSystemActions(credentials) || credentials.getPrincipal().equals(user)))
+      throw new ThriftSecurityException(credentials.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED);
+    return true;
+  }
+  
+  public boolean authenticateUser(TCredentials credentials, TCredentials toAuth) throws ThriftSecurityException {
+    canAskAboutUser(credentials, toAuth.getPrincipal());
+    // User is already authenticated from canAskAboutUser
+    if (credentials.equals(toAuth))
+      return true;
+    try {
+      AuthenticationToken token = reassembleToken(toAuth);
+      return authenticator.authenticateUser(toAuth.getPrincipal(), token);
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    }
+  }
+  
+  private AuthenticationToken reassembleToken(TCredentials toAuth) throws AccumuloSecurityException {
+    String tokenClass = toAuth.getTokenClassName();
+    if (authenticator.validTokenClass(tokenClass)) {
+      return AuthenticationTokenSerializer.deserialize(toAuth.getTokenClassName(), toAuth.getToken());
+    }
+    throw new AccumuloSecurityException(toAuth.getPrincipal(), SecurityErrorCode.INVALID_TOKEN);
+  }
+  
+  public Authorizations getUserAuthorizations(TCredentials credentials, String user) throws ThriftSecurityException {
+    authenticate(credentials);
+    
+    targetUserExists(user);
+    
+    if (!credentials.getPrincipal().equals(user) && !hasSystemPermission(credentials, SystemPermission.SYSTEM, false))
+      throw new ThriftSecurityException(credentials.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED);
+    
+    try {
+      return authorizor.getCachedUserAuthorizations(user);
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    }
+  }
+  
+  public Authorizations getUserAuthorizations(TCredentials credentials) throws ThriftSecurityException {
+    // system user doesn't need record-level authorizations for the tables it reads
+    if (isSystemUser(credentials)) {
+      authenticate(credentials);
+      return Authorizations.EMPTY;
+    }
+    return getUserAuthorizations(credentials, credentials.getPrincipal());
+  }
+  
+  public boolean userHasAuthorizations(TCredentials credentials, List<ByteBuffer> list) throws ThriftSecurityException {
+    authenticate(credentials);
+    
+    if (isSystemUser(credentials)) {
+      // system user doesn't need record-level authorizations for the tables it reads (for now)
+      return list.isEmpty();
+    }
+    
+    try {
+      return authorizor.isValidAuthorizations(credentials.getPrincipal(), list);
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    }
+  }
+  
+  /**
+   * Checks if a user has a system permission
+   * 
+   * @return true if a user exists and has permission; false otherwise
+   */
+  private boolean hasSystemPermission(TCredentials credentials, SystemPermission permission, boolean useCached) throws ThriftSecurityException {
+    if (isSystemUser(credentials))
+      return true;
+    return _hasSystemPermission(credentials.getPrincipal(), permission, useCached);
+  }
+  
+  /**
+   * Checks if a user has a system permission<br/>
+   * This cannot check if a system user has permission.
+   * 
+   * @return true if a user exists and has permission; false otherwise
+   */
+  private boolean _hasSystemPermission(String user, SystemPermission permission, boolean useCached) throws ThriftSecurityException {
+    if (user.equals(getRootUsername()))
+      return true;
+    
+    targetUserExists(user);
+    
+    try {
+      if (useCached)
+        return permHandle.hasCachedSystemPermission(user, permission);
+      return permHandle.hasSystemPermission(user, permission);
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    }
+  }
+  
+  /**
+   * Checks if a user has a table permission
+   * 
+   * @return true if a user exists and has permission; false otherwise
+   */
+  protected boolean hasTablePermission(TCredentials credentials, String table, TablePermission permission, boolean useCached) throws ThriftSecurityException {
+    if (isSystemUser(credentials))
+      return true;
+    return _hasTablePermission(credentials.getPrincipal(), table, permission, useCached);
+  }
+  
+  /**
+   * Checks if a user has a table permission<br/>
+   * This cannot check if a system user has permission.
+   * 
+   * @return true if a user exists and has permission; false otherwise
+   */
+  protected boolean _hasTablePermission(String user, String table, TablePermission permission, boolean useCached) throws ThriftSecurityException {
+    targetUserExists(user);
+    
+    if ((table.equals(MetadataTable.ID) || table.equals(RootTable.ID)) && permission.equals(TablePermission.READ))
+      return true;
+    
+    try {
+      if (useCached)
+        return permHandle.hasCachedTablePermission(user, table, permission);
+      return permHandle.hasTablePermission(user, table, permission);
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    } catch (TableNotFoundException e) {
+      throw new ThriftSecurityException(user, SecurityErrorCode.TABLE_DOESNT_EXIST);
+    }
+  }
+  
+  // some people just aren't allowed to ask about other users; here are those who can ask
+  private boolean canAskAboutOtherUsers(TCredentials credentials, String user) throws ThriftSecurityException {
+    authenticate(credentials);
+    return credentials.getPrincipal().equals(user) || hasSystemPermission(credentials, SystemPermission.SYSTEM, false)
+        || hasSystemPermission(credentials, SystemPermission.CREATE_USER, false) || hasSystemPermission(credentials, SystemPermission.ALTER_USER, false)
+        || hasSystemPermission(credentials, SystemPermission.DROP_USER, false);
+  }
+  
+  private void targetUserExists(String user) throws ThriftSecurityException {
+    if (user.equals(getRootUsername()))
+      return;
+    try {
+      if (!authenticator.userExists(user))
+        throw new ThriftSecurityException(user, SecurityErrorCode.USER_DOESNT_EXIST);
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    }
+  }
+  
+  public boolean canScan(TCredentials credentials, String table) throws ThriftSecurityException {
+    authenticate(credentials);
+    return hasTablePermission(credentials, table, TablePermission.READ, true);
+  }
+  
+  public boolean canScan(TCredentials credentials, String table, TRange range, List<TColumn> columns, List<IterInfo> ssiList,
+      Map<String,Map<String,String>> ssio, List<ByteBuffer> authorizations) throws ThriftSecurityException {
+    return canScan(credentials, table);
+  }
+  
+  public boolean canScan(TCredentials credentials, String table, Map<TKeyExtent,List<TRange>> tbatch, List<TColumn> tcolumns, List<IterInfo> ssiList,
+      Map<String,Map<String,String>> ssio, List<ByteBuffer> authorizations) throws ThriftSecurityException {
+    return canScan(credentials, table);
+  }
+  
+  public boolean canWrite(TCredentials credentials, String table) throws ThriftSecurityException {
+    authenticate(credentials);
+    return hasTablePermission(credentials, table, TablePermission.WRITE, true);
+  }
+  
+  public boolean canConditionallyUpdate(TCredentials credentials, String tableID, List<ByteBuffer> authorizations) throws ThriftSecurityException {
+    
+    authenticate(credentials);
+    
+    return hasTablePermission(credentials, tableID, TablePermission.WRITE, true) && hasTablePermission(credentials, tableID, TablePermission.READ, true);
+  }
+  
+  public boolean canSplitTablet(TCredentials credentials, String table) throws ThriftSecurityException {
+    authenticate(credentials);
+    return hasSystemPermission(credentials, SystemPermission.ALTER_TABLE, false) || hasSystemPermission(credentials, SystemPermission.SYSTEM, false)
+        || hasTablePermission(credentials, table, TablePermission.ALTER_TABLE, false);
+  }
+  
+  /**
+   * This is the check to perform any system action. This includes tserver's loading of a tablet, shutting the system down, or altering system properties.
+   */
+  public boolean canPerformSystemActions(TCredentials credentials) throws ThriftSecurityException {
+    authenticate(credentials);
+    return hasSystemPermission(credentials, SystemPermission.SYSTEM, false);
+  }
+  
+  public boolean canFlush(TCredentials c, String tableId) throws ThriftSecurityException {
+    authenticate(c);
+    return hasTablePermission(c, tableId, TablePermission.WRITE, false) || hasTablePermission(c, tableId, TablePermission.ALTER_TABLE, false);
+  }
+  
+  public boolean canAlterTable(TCredentials c, String tableId) throws ThriftSecurityException {
+    authenticate(c);
+    return hasTablePermission(c, tableId, TablePermission.ALTER_TABLE, false) || hasSystemPermission(c, SystemPermission.ALTER_TABLE, false);
+  }
+  
+  public boolean canCreateTable(TCredentials c, String tableName) throws ThriftSecurityException {
+    return canCreateTable(c);
+  }
+  
+  public boolean canCreateTable(TCredentials c) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c, SystemPermission.CREATE_TABLE, false);
+  }
+  
+  public boolean canRenameTable(TCredentials c, String tableId, String oldTableName, String newTableName) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c, SystemPermission.ALTER_TABLE, false) || hasTablePermission(c, tableId, TablePermission.ALTER_TABLE, false);
+  }
+  
+  public boolean canCloneTable(TCredentials c, String tableId, String tableName) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c, SystemPermission.CREATE_TABLE, false) && hasTablePermission(c, tableId, TablePermission.READ, false);
+  }
+  
+  public boolean canDeleteTable(TCredentials c, String tableId) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c, SystemPermission.DROP_TABLE, false) || hasTablePermission(c, tableId, TablePermission.DROP_TABLE, false);
+  }
+  
+  public boolean canOnlineOfflineTable(TCredentials c, String tableId, TableOperation op) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c, SystemPermission.SYSTEM, false) || hasSystemPermission(c, SystemPermission.ALTER_TABLE, false)
+        || hasTablePermission(c, tableId, TablePermission.ALTER_TABLE, false);
+  }
+  
+  public boolean canMerge(TCredentials c, String tableId) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c, SystemPermission.SYSTEM, false) || hasSystemPermission(c, SystemPermission.ALTER_TABLE, false)
+        || hasTablePermission(c, tableId, TablePermission.ALTER_TABLE, false);
+  }
+  
+  public boolean canDeleteRange(TCredentials c, String tableId, String tableName, Text startRow, Text endRow) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c, SystemPermission.SYSTEM, false) || hasTablePermission(c, tableId, TablePermission.WRITE, false);
+  }
+  
+  public boolean canBulkImport(TCredentials c, String tableId, String tableName, String dir, String failDir) throws ThriftSecurityException {
+    return canBulkImport(c, tableId);
+  }
+  
+  public boolean canBulkImport(TCredentials c, String tableId) throws ThriftSecurityException {
+    authenticate(c);
+    return hasTablePermission(c, tableId, TablePermission.BULK_IMPORT, false);
+  }
+  
+  public boolean canCompact(TCredentials c, String tableId) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c, SystemPermission.ALTER_TABLE, false) || hasTablePermission(c, tableId, TablePermission.ALTER_TABLE, false)
+        || hasTablePermission(c, tableId, TablePermission.WRITE, false);
+  }
+  
+  public boolean canChangeAuthorizations(TCredentials c, String user) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c, SystemPermission.ALTER_USER, false);
+  }
+  
+  public boolean canChangePassword(TCredentials c, String user) throws ThriftSecurityException {
+    authenticate(c);
+    return c.getPrincipal().equals(user) || hasSystemPermission(c, SystemPermission.ALTER_USER, false);
+  }
+  
+  public boolean canCreateUser(TCredentials c, String user) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c, SystemPermission.CREATE_USER, false);
+  }
+  
+  public boolean canDropUser(TCredentials c, String user) throws ThriftSecurityException {
+    authenticate(c);
+    if (user.equals(getRootUsername()))
+      throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED);
+    return hasSystemPermission(c, SystemPermission.DROP_USER, false);
+  }
+  
+  public boolean canGrantSystem(TCredentials c, String user, SystemPermission sysPerm) throws ThriftSecurityException {
+    authenticate(c);
+    // can't grant GRANT
+    if (sysPerm.equals(SystemPermission.GRANT))
+      throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.GRANT_INVALID);
+    return hasSystemPermission(c, SystemPermission.GRANT, false);
+  }
+  
+  public boolean canGrantTable(TCredentials c, String user, String table) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c, SystemPermission.ALTER_TABLE, false) || hasTablePermission(c, table, TablePermission.GRANT, false);
+  }
+  
+  public boolean canRevokeSystem(TCredentials c, String user, SystemPermission sysPerm) throws ThriftSecurityException {
+    authenticate(c);
+    // can't modify root user
+    if (user.equals(getRootUsername()))
+      throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED);
+    
+    // can't revoke GRANT
+    if (sysPerm.equals(SystemPermission.GRANT))
+      throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.GRANT_INVALID);
+    
+    return hasSystemPermission(c, SystemPermission.GRANT, false);
+  }
+  
+  public boolean canRevokeTable(TCredentials c, String user, String table) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c, SystemPermission.ALTER_TABLE, false) || hasTablePermission(c, table, TablePermission.GRANT, false);
+  }
+  
+  public void changeAuthorizations(TCredentials credentials, String user, Authorizations authorizations) throws ThriftSecurityException {
+    if (!canChangeAuthorizations(credentials, user))
+      throw new ThriftSecurityException(credentials.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED);
+    
+    targetUserExists(user);
+    
+    try {
+      authorizor.changeAuthorizations(user, authorizations);
+      log.info("Changed authorizations for user " + user + " at the request of user " + credentials.getPrincipal());
+    } catch (AccumuloSecurityException ase) {
+      throw ase.asThriftException();
+    }
+  }
+  
+  public void changePassword(TCredentials credentials, Credentials toChange) throws ThriftSecurityException {
+    if (!canChangePassword(credentials, toChange.getPrincipal()))
+      throw new ThriftSecurityException(credentials.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED);
+    try {
+      AuthenticationToken token = toChange.getToken();
+      authenticator.changePassword(toChange.getPrincipal(), token);
+      log.info("Changed password for user " + toChange.getPrincipal() + " at the request of user " + credentials.getPrincipal());
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    }
+  }
+  
+  public void createUser(TCredentials credentials, Credentials newUser, Authorizations authorizations) throws ThriftSecurityException {
+    if (!canCreateUser(credentials, newUser.getPrincipal()))
+      throw new ThriftSecurityException(credentials.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED);
+    try {
+      AuthenticationToken token = newUser.getToken();
+      authenticator.createUser(newUser.getPrincipal(), token);
+      authorizor.initUser(newUser.getPrincipal());
+      permHandle.initUser(newUser.getPrincipal());
+      log.info("Created user " + newUser.getPrincipal() + " at the request of user " + credentials.getPrincipal());
+      if (canChangeAuthorizations(credentials, newUser.getPrincipal()))
+        authorizor.changeAuthorizations(newUser.getPrincipal(), authorizations);
+    } catch (AccumuloSecurityException ase) {
+      throw ase.asThriftException();
+    }
+  }
+  
+  public void dropUser(TCredentials credentials, String user) throws ThriftSecurityException {
+    if (!canDropUser(credentials, user))
+      throw new ThriftSecurityException(credentials.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED);
+    try {
+      authorizor.dropUser(user);
+      authenticator.dropUser(user);
+      permHandle.cleanUser(user);
+      log.info("Deleted user " + user + " at the request of user " + credentials.getPrincipal());
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    }
+  }
+  
+  public void grantSystemPermission(TCredentials credentials, String user, SystemPermission permissionById) throws ThriftSecurityException {
+    if (!canGrantSystem(credentials, user, permissionById))
+      throw new ThriftSecurityException(credentials.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED);
+    
+    targetUserExists(user);
+    
+    try {
+      permHandle.grantSystemPermission(user, permissionById);
+      log.info("Granted system permission " + permissionById + " for user " + user + " at the request of user " + credentials.getPrincipal());
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    }
+  }
+  
+  public void grantTablePermission(TCredentials c, String user, String tableId, TablePermission permission) throws ThriftSecurityException {
+    if (!canGrantTable(c, user, tableId))
+      throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED);
+    
+    targetUserExists(user);
+    
+    try {
+      permHandle.grantTablePermission(user, tableId, permission);
+      log.info("Granted table permission " + permission + " for user " + user + " on the table " + tableId + " at the request of user " + c.getPrincipal());
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    } catch (TableNotFoundException e) {
+      throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.TABLE_DOESNT_EXIST);
+    }
+  }
+  
+  public void revokeSystemPermission(TCredentials credentials, String user, SystemPermission permission) throws ThriftSecurityException {
+    if (!canRevokeSystem(credentials, user, permission))
+      throw new ThriftSecurityException(credentials.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED);
+    
+    targetUserExists(user);
+    
+    try {
+      permHandle.revokeSystemPermission(user, permission);
+      log.info("Revoked system permission " + permission + " for user " + user + " at the request of user " + credentials.getPrincipal());
+      
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    }
+  }
+  
+  public void revokeTablePermission(TCredentials c, String user, String tableId, TablePermission permission) throws ThriftSecurityException {
+    if (!canRevokeTable(c, user, tableId))
+      throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED);
+    
+    targetUserExists(user);
+    
+    try {
+      permHandle.revokeTablePermission(user, tableId, permission);
+      log.info("Revoked table permission " + permission + " for user " + user + " on the table " + tableId + " at the request of user " + c.getPrincipal());
+      
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    } catch (TableNotFoundException e) {
+      throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.TABLE_DOESNT_EXIST);
+    }
+  }
+  
+  public boolean hasSystemPermission(TCredentials credentials, String user, SystemPermission permissionById) throws ThriftSecurityException {
+    if (!canAskAboutOtherUsers(credentials, user))
+      throw new ThriftSecurityException(credentials.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED);
+    return _hasSystemPermission(user, permissionById, false);
+  }
+  
+  public boolean hasTablePermission(TCredentials credentials, String user, String tableId, TablePermission permissionById) throws ThriftSecurityException {
+    if (!canAskAboutOtherUsers(credentials, user))
+      throw new ThriftSecurityException(credentials.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED);
+    return _hasTablePermission(user, tableId, permissionById, false);
+  }
+  
+  public Set<String> listUsers(TCredentials credentials) throws ThriftSecurityException {
+    authenticate(credentials);
+    try {
+      return authenticator.listUsers();
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    }
+  }
+  
+  public void deleteTable(TCredentials credentials, String tableId) throws ThriftSecurityException {
+    if (!canDeleteTable(credentials, tableId))
+      throw new ThriftSecurityException(credentials.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED);
+    try {
+      permHandle.cleanTablePermissions(tableId);
+    } catch (AccumuloSecurityException e) {
+      e.setUser(credentials.getPrincipal());
+      throw e.asThriftException();
+    } catch (TableNotFoundException e) {
+      throw new ThriftSecurityException(credentials.getPrincipal(), SecurityErrorCode.TABLE_DOESNT_EXIST);
+    }
+  }
+  
+  public boolean canExport(TCredentials credentials, String tableId, String tableName, String exportDir) throws ThriftSecurityException {
+    authenticate(credentials);
+    return hasTablePermission(credentials, tableId, TablePermission.READ, false);
+  }
+  
+  public boolean canImport(TCredentials credentials, String tableName, String importDir) throws ThriftSecurityException {
+    authenticate(credentials);
+    return hasSystemPermission(credentials, SystemPermission.CREATE_TABLE, false);
+  }
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/base/src/main/java/org/apache/accumulo/server/security/SystemCredentials.java
----------------------------------------------------------------------
diff --git a/server/base/src/main/java/org/apache/accumulo/server/security/SystemCredentials.java b/server/base/src/main/java/org/apache/accumulo/server/security/SystemCredentials.java
new file mode 100644
index 0000000..9b4931d
--- /dev/null
+++ b/server/base/src/main/java/org/apache/accumulo/server/security/SystemCredentials.java
@@ -0,0 +1,135 @@
+/*
+ * 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.accumulo.server.security;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecurityPermission;
+import java.util.Map.Entry;
+
+import org.apache.accumulo.core.Constants;
+import org.apache.accumulo.core.client.Instance;
+import org.apache.accumulo.core.client.security.tokens.AuthenticationToken;
+import org.apache.accumulo.core.client.security.tokens.PasswordToken;
+import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.security.Credentials;
+import org.apache.accumulo.core.security.thrift.TCredentials;
+import org.apache.accumulo.server.ServerConstants;
+import org.apache.accumulo.server.client.HdfsZooInstance;
+import org.apache.accumulo.server.conf.ServerConfiguration;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.hadoop.io.Writable;
+
+/**
+ * Credentials for the system services.
+ * 
+ * @since 1.6.0
+ */
+public final class SystemCredentials extends Credentials {
+  
+  private static final SecurityPermission SYSTEM_CREDENTIALS_PERMISSION = new SecurityPermission("systemCredentialsPermission");
+  
+  private static SystemCredentials SYSTEM_CREDS = null;
+  private static final String SYSTEM_PRINCIPAL = "!SYSTEM";
+  private static final SystemToken SYSTEM_TOKEN = SystemToken.get();
+  
+  private final TCredentials AS_THRIFT;
+  
+  private SystemCredentials() {
+    super(SYSTEM_PRINCIPAL, SYSTEM_TOKEN);
+    AS_THRIFT = super.toThrift(HdfsZooInstance.getInstance());
+  }
+  
+  public static SystemCredentials get() {
+    SecurityManager sm = System.getSecurityManager();
+    if (sm != null) {
+      sm.checkPermission(SYSTEM_CREDENTIALS_PERMISSION);
+    }
+    if (SYSTEM_CREDS == null) {
+      SYSTEM_CREDS = new SystemCredentials();
+    }
+    return SYSTEM_CREDS;
+  }
+  
+  @Override
+  public TCredentials toThrift(Instance instance) {
+    if (!AS_THRIFT.getInstanceId().equals(instance.getInstanceID()))
+      throw new IllegalArgumentException("Unexpected instance used for " + SystemCredentials.class.getSimpleName() + ": " + instance.getInstanceID());
+    return AS_THRIFT;
+  }
+  
+  /**
+   * An {@link AuthenticationToken} type for Accumulo servers for inter-server communication.
+   * 
+   * @since 1.6.0
+   */
+  public static final class SystemToken extends PasswordToken {
+    
+    /**
+     * A Constructor for {@link Writable}.
+     */
+    public SystemToken() {}
+    
+    private SystemToken(byte[] systemPassword) {
+      super(systemPassword);
+    }
+    
+    private static SystemToken get() {
+      byte[] confChecksum;
+      MessageDigest md;
+      try {
+        md = MessageDigest.getInstance(Constants.PW_HASH_ALGORITHM);
+      } catch (NoSuchAlgorithmException e) {
+        throw new RuntimeException("Failed to compute configuration checksum", e);
+      }
+      
+      // seed the config with the version and instance id, so at least it's not empty
+      md.update(ServerConstants.WIRE_VERSION.toString().getBytes(Constants.UTF8));
+      md.update(HdfsZooInstance.getInstance().getInstanceID().getBytes(Constants.UTF8));
+      
+      for (Entry<String,String> entry : ServerConfiguration.getSiteConfiguration()) {
+        // only include instance properties
+        if (entry.getKey().startsWith(Property.INSTANCE_PREFIX.toString())) {
+          md.update(entry.getKey().getBytes(Constants.UTF8));
+          md.update(entry.getValue().getBytes(Constants.UTF8));
+        }
+      }
+      confChecksum = md.digest();
+      
+      int wireVersion = ServerConstants.WIRE_VERSION;
+      byte[] inst = HdfsZooInstance.getInstance().getInstanceID().getBytes(Constants.UTF8);
+      
+      ByteArrayOutputStream bytes = new ByteArrayOutputStream(3 * (Integer.SIZE / Byte.SIZE) + inst.length + confChecksum.length);
+      DataOutputStream out = new DataOutputStream(bytes);
+      try {
+        out.write(wireVersion * -1);
+        out.write(inst.length);
+        out.write(inst);
+        out.write(confChecksum.length);
+        out.write(confChecksum);
+      } catch (IOException e) {
+        // this is impossible with ByteArrayOutputStream; crash hard if this happens
+        throw new RuntimeException(e);
+      }
+      return new SystemToken(Base64.encodeBase64(bytes.toByteArray()));
+    }
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/base/src/main/java/org/apache/accumulo/server/security/handler/Authenticator.java
----------------------------------------------------------------------
diff --git a/server/base/src/main/java/org/apache/accumulo/server/security/handler/Authenticator.java b/server/base/src/main/java/org/apache/accumulo/server/security/handler/Authenticator.java
new file mode 100644
index 0000000..7012065
--- /dev/null
+++ b/server/base/src/main/java/org/apache/accumulo/server/security/handler/Authenticator.java
@@ -0,0 +1,57 @@
+/*
+ * 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.accumulo.server.security.handler;
+
+import java.util.Set;
+
+import org.apache.accumulo.core.client.AccumuloSecurityException;
+import org.apache.accumulo.core.client.impl.thrift.ThriftSecurityException;
+import org.apache.accumulo.core.client.security.tokens.AuthenticationToken;
+import org.apache.accumulo.core.security.thrift.TCredentials;
+
+/**
+ * This interface is used for the system which will be used for authenticating a user. If the implementation does not support configuration through Accumulo, it
+ * should throw an AccumuloSecurityException with the error code UNSUPPORTED_OPERATION
+ */
+
+public interface Authenticator {
+  
+  public void initialize(String instanceId, boolean initialize);
+  
+  public boolean validSecurityHandlers(Authorizor auth, PermissionHandler pm);
+  
+  public void initializeSecurity(TCredentials credentials, String principal, byte[] token) throws AccumuloSecurityException, ThriftSecurityException;
+  
+  public boolean authenticateUser(String principal, AuthenticationToken token) throws AccumuloSecurityException;
+  
+  public Set<String> listUsers() throws AccumuloSecurityException;
+  
+  public void createUser(String principal, AuthenticationToken token) throws AccumuloSecurityException;
+  
+  public void dropUser(String user) throws AccumuloSecurityException;
+  
+  public void changePassword(String principal, AuthenticationToken token) throws AccumuloSecurityException;
+  
+  public boolean userExists(String user) throws AccumuloSecurityException;
+  
+  public Set<Class<? extends AuthenticationToken>> getSupportedTokenTypes();
+  
+  /**
+   * Returns true if the given token is appropriate for this Authenticator
+   */
+  public boolean validTokenClass(String tokenClass);
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/598821cd/server/base/src/main/java/org/apache/accumulo/server/security/handler/Authorizor.java
----------------------------------------------------------------------
diff --git a/server/base/src/main/java/org/apache/accumulo/server/security/handler/Authorizor.java b/server/base/src/main/java/org/apache/accumulo/server/security/handler/Authorizor.java
new file mode 100644
index 0000000..569d893
--- /dev/null
+++ b/server/base/src/main/java/org/apache/accumulo/server/security/handler/Authorizor.java
@@ -0,0 +1,72 @@
+/*
+ * 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.accumulo.server.security.handler;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+import org.apache.accumulo.core.client.AccumuloSecurityException;
+import org.apache.accumulo.core.client.impl.thrift.ThriftSecurityException;
+import org.apache.accumulo.core.security.Authorizations;
+import org.apache.accumulo.core.security.thrift.TCredentials;
+
+/**
+ * This interface is used for the system which will be used for getting a users Authorizations. If the implementation does not support configuration through
+ * Accumulo, it should throw an AccumuloSecurityException with the error code UNSUPPORTED_OPERATION
+ */
+public interface Authorizor {
+  
+  /**
+   * Sets up the authorizor for a new instance of Accumulo
+   */
+  public void initialize(String instanceId, boolean initialize);
+  
+  /**
+   * Used to validate that the Authorizor, Authenticator, and permission handler can coexist
+   */
+  public boolean validSecurityHandlers(Authenticator auth, PermissionHandler pm);
+  
+  /**
+   * Used to initialize security for the root user
+   */
+  public void initializeSecurity(TCredentials credentials, String rootuser) throws AccumuloSecurityException, ThriftSecurityException;
+  
+  /**
+   * Used to change the authorizations for the user
+   */
+  public void changeAuthorizations(String user, Authorizations authorizations) throws AccumuloSecurityException;
+  
+  /**
+   * Used to get the authorizations for the user
+   */
+  public Authorizations getCachedUserAuthorizations(String user) throws AccumuloSecurityException;
+
+  /**
+   * Used to check if a user has valid auths.
+   */
+  public boolean isValidAuthorizations(String user, List<ByteBuffer> list) throws AccumuloSecurityException;
+  
+  /**
+   * Initializes a new user
+   */
+  public void initUser(String user) throws AccumuloSecurityException;
+  
+  /**
+   * Deletes a user
+   */
+  public void dropUser(String user) throws AccumuloSecurityException;
+}


Mime
View raw message