You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@accumulo.apache.org by vi...@apache.org on 2012/06/05 21:02:31 UTC

svn commit: r1346535 [2/3] - in /accumulo/branches/ACCUMULO-259: core/src/main/java/org/apache/accumulo/core/client/ core/src/main/java/org/apache/accumulo/core/conf/ server/src/main/java/org/apache/accumulo/server/client/ server/src/main/java/org/apac...

Added: accumulo/branches/ACCUMULO-259/server/src/main/java/org/apache/accumulo/server/security/SecurityOperationImpl.java
URL: http://svn.apache.org/viewvc/accumulo/branches/ACCUMULO-259/server/src/main/java/org/apache/accumulo/server/security/SecurityOperationImpl.java?rev=1346535&view=auto
==============================================================================
--- accumulo/branches/ACCUMULO-259/server/src/main/java/org/apache/accumulo/server/security/SecurityOperationImpl.java (added)
+++ accumulo/branches/ACCUMULO-259/server/src/main/java/org/apache/accumulo/server/security/SecurityOperationImpl.java Tue Jun  5 19:02:30 2012
@@ -0,0 +1,817 @@
+/**
+ * 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.Arrays;
+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.ThriftTableOperationException;
+import org.apache.accumulo.core.conf.AccumuloConfiguration;
+import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.security.Authorizations;
+import org.apache.accumulo.core.security.SystemPermission;
+import org.apache.accumulo.core.security.TablePermission;
+import org.apache.accumulo.core.security.thrift.AuthInfo;
+import org.apache.accumulo.core.security.thrift.SecurityErrorCode;
+import org.apache.accumulo.core.security.thrift.ThriftSecurityException;
+import org.apache.accumulo.server.client.HdfsZooInstance;
+import org.apache.accumulo.server.master.Master;
+import org.apache.accumulo.server.zookeeper.ZooCache;
+import org.apache.log4j.Logger;
+
+/**
+ * Utility class for performing various security operations with the appropriate checks
+ */
+public class SecurityOperationImpl implements SecurityOperation {
+  private static final Logger log = Logger.getLogger(SecurityOperationsImpl.class);
+
+  private static Authorizor authorizor;
+  private static Authenticator authenticator;
+  private static String rootUserName = null;
+  private final ZooCache zooCache;
+  private final String ZKUserPath;
+  
+  private static SecurityOperation instance;
+  
+  public static synchronized SecurityOperation getInstance() {
+    String instanceId = HdfsZooInstance.getInstance().getInstanceID();
+    return getInstance(instanceId);
+  }
+  
+  public static synchronized SecurityOperation getInstance(String instanceId) {
+    if (instance == null) {
+      instance = new AuditedSecurityOperation(new SecurityOperationImpl(getAuthorizor(instanceId), getAuthenticator(instanceId), instanceId));
+    }
+    return instance;
+  }
+  
+  @SuppressWarnings("deprecation")
+  private static Authorizor getAuthorizor(String instanceId) {
+    Authorizor toRet = Master.createInstanceFromPropertyName(AccumuloConfiguration.getSiteConfiguration(), Property.INSTANCE_SECURITY_AUTHORIZOR,
+        Authorizor.class, ZKAuthorizor.getInstance());
+    toRet.initialize(instanceId);
+    return toRet;
+  }
+
+  @SuppressWarnings("deprecation")
+  private static Authenticator getAuthenticator(String instanceId) {
+    Authenticator toRet = Master.createInstanceFromPropertyName(AccumuloConfiguration.getSiteConfiguration(), Property.INSTANCE_SECURITY_AUTHENTICATOR,
+        Authenticator.class, ZKAuthenticator.getInstance());
+    toRet.initialize(instanceId);
+    return toRet;
+  }
+
+  public SecurityOperationImpl(Authorizor author, Authenticator authent, String instanceId) {
+    authorizor = author;
+    authenticator = authent;
+    
+    if (!authorizor.validAuthenticator(authenticator) || !authenticator.validAuthorizor(authorizor))
+      throw new RuntimeException(authorizor + " and " + authenticator
+          + " do not play nice with eachother. Please choose authentication and authorization mechanisms that are compatible with one another.");
+    
+    ZKUserPath = Constants.ZROOT + "/" + instanceId + "/users";
+    zooCache = new ZooCache();
+  }
+  
+  public void initializeSecurity(AuthInfo credentials, String rootuser, byte[] rootpass) throws AccumuloSecurityException, ThriftSecurityException {
+    authenticate(credentials);
+
+    if (!credentials.user.equals(SecurityConstants.SYSTEM_USERNAME))
+      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
+
+    authenticator.initializeSecurity(credentials, rootuser, rootpass);
+    authorizor.initializeSecurity(rootuser);
+  }
+
+  public synchronized String getRootUsername() {
+    if (rootUserName == null)
+      rootUserName = new String(zooCache.get(ZKUserPath));
+    return rootUserName;
+  }
+  
+  private void authenticate(String user, ByteBuffer password, String instance) throws ThriftSecurityException {
+    if (!instance.equals(HdfsZooInstance.getInstance().getInstanceID()))
+      throw new ThriftSecurityException(user, SecurityErrorCode.INVALID_INSTANCEID);
+    
+    if (user.equals(SecurityConstants.SYSTEM_USERNAME)) {
+      if (Arrays.equals(SecurityConstants.getSystemCredentials().password.array(), password.array())
+          && instance.equals(SecurityConstants.getSystemCredentials().instanceId))
+        return;
+      else
+        throw new ThriftSecurityException(user, SecurityErrorCode.BAD_CREDENTIALS);
+    }
+    
+    if (!authenticator.authenticateUser(user, password, instance))
+      throw new ThriftSecurityException(user, SecurityErrorCode.BAD_CREDENTIALS);
+  }
+  
+  private void authenticate(AuthInfo credentials) throws ThriftSecurityException {
+    authenticate(credentials.user, credentials.password, credentials.instanceId);
+  }
+
+  /**
+   * @param credentials
+   * @param user
+   * @param password
+   * @return
+   * @throws ThriftSecurityException
+   */
+  public boolean authenticateUser(AuthInfo credentials, String user, ByteBuffer password) throws ThriftSecurityException {
+    authenticate(credentials);
+    
+    if (credentials.user.equals(user))
+      return true;
+    
+    if (!canPerformSystemActions(credentials))
+      throw new ThriftSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
+    
+    return authenticator.authenticateUser(user, password, credentials.instanceId);
+    
+  }
+  
+  /**
+   * @param credentials
+   * @param user
+   * @return The given user's authorizations
+   * @throws ThriftSecurityException
+   */
+  public Authorizations getUserAuthorizations(AuthInfo credentials, String user) throws ThriftSecurityException {
+    authenticate(credentials);
+    
+    targetUserExists(user);
+
+    if (!credentials.user.equals(user) && !hasSystemPermission(credentials.user, SystemPermission.SYSTEM))
+      throw new ThriftSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
+    
+    // system user doesn't need record-level authorizations for the tables it reads (for now)
+    if (user.equals(SecurityConstants.SYSTEM_USERNAME))
+      return Constants.NO_AUTHS;
+    
+    try {
+      return authorizor.getUserAuthorizations(user);
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    }
+  }
+
+  /**
+   * @param credentials
+   * @return
+   * @throws ThriftSecurityException
+   */
+  public Authorizations getUserAuthorizations(AuthInfo credentials) throws ThriftSecurityException {
+    return getUserAuthorizations(credentials, credentials.user);
+  }
+  
+  /**
+   * Checks if a user has a system permission
+   * 
+   * @return true if a user exists and has permission; false otherwise
+   */
+  private boolean hasSystemPermission(String user, SystemPermission permission) throws ThriftSecurityException {
+    if (user.equals(getRootUsername()) || user.equals(SecurityConstants.SYSTEM_USERNAME))
+      return true;
+    
+    targetUserExists(user);
+
+    try {
+      return authorizor.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
+   * @throws ThriftTableOperationException
+   */
+  private boolean hasTablePermission(String user, String table, TablePermission permission) throws ThriftSecurityException {
+    if (user.equals(getRootUsername()) || user.equals(SecurityConstants.SYSTEM_USERNAME))
+      return true;
+    
+    targetUserExists(user);
+
+    if (table.equals(Constants.METADATA_TABLE_ID) && permission.equals(TablePermission.READ))
+      return true;
+
+    try {
+      return authorizor.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(AuthInfo credentials, String user) throws ThriftSecurityException {
+    authenticate(credentials);
+    return credentials.user.equals(user) || hasSystemPermission(credentials.user, SystemPermission.SYSTEM)
+        || hasSystemPermission(credentials, credentials.user, SystemPermission.CREATE_USER)
+        || hasSystemPermission(credentials, credentials.user, SystemPermission.ALTER_USER)
+        || hasSystemPermission(credentials, credentials.user, SystemPermission.DROP_USER);
+  }
+  
+  /**
+   * @param user
+   * @throws ThriftSecurityException
+   */
+  private void targetUserExists(String user) throws ThriftSecurityException {
+    if (user.equals(SecurityConstants.SYSTEM_USERNAME) || user.equals(getRootUsername()))
+      return;
+
+    if (!authenticator.userExists(user))
+      throw new ThriftSecurityException(user, SecurityErrorCode.USER_DOESNT_EXIST);
+  }
+  
+  /**
+   * @param credentials
+   * @param string
+   * @return
+   * @throws ThriftSecurityException
+   * @throws TableNotFoundException
+   */
+  public boolean canScan(AuthInfo credentials, String table) throws ThriftSecurityException {
+    authenticate(credentials);
+    return hasTablePermission(credentials.user, table, TablePermission.READ);
+  }
+
+  /**
+   * @param credentials
+   * @param string
+   * @return
+   * @throws ThriftSecurityException
+   * @throws TableNotFoundException
+   */
+  public boolean canWrite(AuthInfo credentials, String table) throws ThriftSecurityException {
+    authenticate(credentials);
+    return hasTablePermission(credentials.user, table, TablePermission.WRITE);
+  }
+
+  /**
+   * @param credentials
+   * @param string
+   * @return
+   * @throws ThriftSecurityException
+   * @throws TableNotFoundException
+   */
+  public boolean canSplitTablet(AuthInfo credentials, String table) throws ThriftSecurityException {
+    authenticate(credentials);
+    return hasSystemPermission(credentials.user, SystemPermission.ALTER_TABLE) || hasSystemPermission(credentials.user, SystemPermission.SYSTEM)
+        || hasTablePermission(credentials.user, table, TablePermission.ALTER_TABLE);
+  }
+  
+  /**
+   * @param credentials
+   * @return
+   * @throws ThriftSecurityException
+   * 
+   *           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(AuthInfo credentials) throws ThriftSecurityException {
+    authenticate(credentials);
+    return hasSystemPermission(credentials.user, SystemPermission.SYSTEM);
+  }
+
+  /**
+   * @param c
+   * @param tableId
+   * @throws ThriftSecurityException
+   * @throws ThriftTableOperationException
+   */
+  public boolean canFlush(AuthInfo c, String tableId) throws ThriftSecurityException {
+    authenticate(c);
+    return hasTablePermission(c.user, tableId, TablePermission.WRITE) || hasTablePermission(c.user, tableId, TablePermission.ALTER_TABLE);
+  }
+  
+  /**
+   * @param c
+   * @param tableId
+   * @throws ThriftSecurityException
+   * @throws ThriftTableOperationException
+   */
+  public boolean canAlterTable(AuthInfo c, String tableId) throws ThriftSecurityException {
+    authenticate(c);
+    return hasTablePermission(c.user, tableId, TablePermission.ALTER_TABLE) || hasSystemPermission(c.user, SystemPermission.ALTER_TABLE);
+  }
+  
+  /**
+   * @param c
+   * @throws ThriftSecurityException
+   */
+  public boolean canCreateTable(AuthInfo c) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c.user, SystemPermission.CREATE_TABLE);
+  }
+  
+  /**
+   * @param c
+   * @param tableId
+   * @return
+   * @throws TableNotFoundException
+   * @throws ThriftSecurityException
+   */
+  public boolean canRenameTable(AuthInfo c, String tableId) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c.user, SystemPermission.ALTER_TABLE) || hasTablePermission(c.user, tableId, TablePermission.ALTER_TABLE);
+  }
+  
+  /**
+   * @param c
+   * @return
+   * @throws TableNotFoundException
+   * @throws ThriftSecurityException
+   */
+  public boolean canCloneTable(AuthInfo c, String tableId) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c.user, SystemPermission.CREATE_TABLE) && hasTablePermission(c.user, tableId, TablePermission.READ);
+  }
+  
+  /**
+   * @param c
+   * @param tableId
+   * @return
+   * @throws TableNotFoundException
+   * @throws ThriftSecurityException
+   */
+  public boolean canDeleteTable(AuthInfo c, String tableId) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c.user, SystemPermission.DROP_TABLE) || hasTablePermission(c.user, tableId, TablePermission.DROP_TABLE);
+  }
+  
+  /**
+   * @param c
+   * @param tableId
+   * @return
+   * @throws TableNotFoundException
+   * @throws ThriftSecurityException
+   */
+  public boolean canOnlineOfflineTable(AuthInfo c, String tableId) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c.user, SystemPermission.SYSTEM) || hasSystemPermission(c.user, SystemPermission.ALTER_TABLE)
+        || hasTablePermission(c.user, tableId, TablePermission.ALTER_TABLE);
+  }
+  
+  /**
+   * @param c
+   * @param tableId
+   * @return
+   * @throws TableNotFoundException
+   * @throws ThriftSecurityException
+   */
+  public boolean canMerge(AuthInfo c, String tableId) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c.user, SystemPermission.SYSTEM) || hasSystemPermission(c.user, SystemPermission.ALTER_TABLE)
+        || hasTablePermission(c.user, tableId, TablePermission.ALTER_TABLE);
+  }
+  
+  /**
+   * @param c
+   * @param tableId
+   * @return
+   * @throws TableNotFoundException
+   * @throws ThriftSecurityException
+   */
+  public boolean canDeleteRange(AuthInfo c, String tableId) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c.user, SystemPermission.SYSTEM) || hasTablePermission(c.user, tableId, TablePermission.WRITE);
+  }
+  
+  /**
+   * @param c
+   * @param tableId
+   * @return
+   * @throws TableNotFoundException
+   * @throws ThriftSecurityException
+   */
+  public boolean canBulkImport(AuthInfo c, String tableId) throws ThriftSecurityException {
+    authenticate(c);
+    return hasTablePermission(c.user, tableId, TablePermission.BULK_IMPORT);
+  }
+  
+  /**
+   * @param c
+   * @param tableId
+   * @return
+   * @throws TableNotFoundException
+   * @throws ThriftSecurityException
+   */
+  public boolean canCompact(AuthInfo c, String tableId) throws ThriftSecurityException {
+    authenticate(c);
+    return hasSystemPermission(c.user, SystemPermission.ALTER_TABLE) || hasTablePermission(c.user, tableId, TablePermission.ALTER_TABLE)
+        || hasTablePermission(c.user, tableId, TablePermission.WRITE);
+  }
+  
+  /**
+   * @param credentials
+   * @return
+   * @throws ThriftSecurityException
+   */
+  public boolean canChangeAuthorizations(AuthInfo c, String user) throws ThriftSecurityException {
+    authenticate(c);
+    if (user.equals(SecurityConstants.SYSTEM_USERNAME))
+      throw new ThriftSecurityException(c.user, SecurityErrorCode.PERMISSION_DENIED);
+    return hasSystemPermission(c.user, SystemPermission.ALTER_TABLE);
+  }
+
+  /**
+   * @param credentials
+   * @param user
+   * @return
+   * @throws ThriftSecurityException
+   */
+  public boolean canChangePassword(AuthInfo c, String user) throws ThriftSecurityException {
+    authenticate(c);
+    if (user.equals(SecurityConstants.SYSTEM_USERNAME))
+      throw new ThriftSecurityException(c.user, SecurityErrorCode.PERMISSION_DENIED);
+    return c.user.equals(user) || hasSystemPermission(c.user, SystemPermission.ALTER_TABLE);
+  }
+  
+  /**
+   * @param credentials
+   * @param user
+   * @return
+   * @throws ThriftSecurityException
+   */
+  public boolean canCreateUser(AuthInfo c, String user) throws ThriftSecurityException {
+    authenticate(c);
+    
+    // don't allow creating a user with the same name as system user
+    if (user.equals(SecurityConstants.SYSTEM_USERNAME))
+      throw new ThriftSecurityException(user, SecurityErrorCode.PERMISSION_DENIED);
+
+    return hasSystemPermission(c.user, SystemPermission.CREATE_USER);
+  }
+
+  /**
+   * @param credentials
+   * @param user
+   * @return
+   * @throws ThriftSecurityException
+   */
+  public boolean canDropUser(AuthInfo c, String user) throws ThriftSecurityException {
+    authenticate(c);
+    
+    // can't delete root or system users
+    if (user.equals(getRootUsername()) || user.equals(SecurityConstants.SYSTEM_USERNAME))
+      throw new ThriftSecurityException(user, SecurityErrorCode.PERMISSION_DENIED);
+
+    return hasSystemPermission(c.user, SystemPermission.DROP_USER);
+  }
+  
+  /**
+   * @param credentials
+   * @param user
+   * @param sysPerm
+   * @return
+   * @throws ThriftSecurityException
+   */
+  public boolean canGrantSystem(AuthInfo c, String user, SystemPermission sysPerm) throws ThriftSecurityException {
+    authenticate(c);
+    
+    // can't modify system user
+    if (user.equals(SecurityConstants.SYSTEM_USERNAME))
+      throw new ThriftSecurityException(c.user, SecurityErrorCode.PERMISSION_DENIED);
+    
+    // can't grant GRANT
+    if (sysPerm.equals(SystemPermission.GRANT))
+      throw new ThriftSecurityException(c.user, SecurityErrorCode.GRANT_INVALID);
+
+    return hasSystemPermission(c.user, SystemPermission.GRANT);
+  }
+  
+  /**
+   * @param credentials
+   * @param user
+   * @param table
+   * @return
+   * @throws ThriftSecurityException
+   */
+  public boolean canGrantTable(AuthInfo c, String user, String table) throws ThriftSecurityException {
+    authenticate(c);
+    
+    // can't modify system user
+    if (user.equals(SecurityConstants.SYSTEM_USERNAME))
+      throw new ThriftSecurityException(c.user, SecurityErrorCode.PERMISSION_DENIED);
+    
+    return hasSystemPermission(c.user, SystemPermission.ALTER_TABLE) || hasTablePermission(c.user, table, TablePermission.GRANT);
+  }
+  
+  /**
+   * @param credentials
+   * @param user
+   * @param sysPerm
+   * @return
+   * @throws ThriftSecurityException
+   */
+  public boolean canRevokeSystem(AuthInfo c, String user, SystemPermission sysPerm) throws ThriftSecurityException {
+    authenticate(c);
+    
+    // can't modify system or root user
+    if (user.equals(getRootUsername()) || user.equals(SecurityConstants.SYSTEM_USERNAME))
+      throw new ThriftSecurityException(c.user, SecurityErrorCode.PERMISSION_DENIED);
+    
+    // can't revoke GRANT
+    if (sysPerm.equals(SystemPermission.GRANT))
+      throw new ThriftSecurityException(c.user, SecurityErrorCode.GRANT_INVALID);
+    
+    return hasSystemPermission(c.user, SystemPermission.GRANT);
+  }
+  
+  /**
+   * @param credentials
+   * @param user
+   * @param table
+   * @return
+   * @throws ThriftSecurityException
+   */
+  public boolean canRevokeTable(AuthInfo c, String user, String table) throws ThriftSecurityException {
+    authenticate(c);
+    
+    // can't modify system user
+    if (user.equals(SecurityConstants.SYSTEM_USERNAME))
+      throw new ThriftSecurityException(c.user, SecurityErrorCode.PERMISSION_DENIED);
+    
+    return hasSystemPermission(c.user, SystemPermission.ALTER_TABLE) || hasTablePermission(c.user, table, TablePermission.GRANT);
+  }
+  
+  /**
+   * @param credentials
+   * @param user
+   * @param authorizations
+   * @throws ThriftSecurityException
+   */
+  public void changeAuthorizations(AuthInfo credentials, String user, Authorizations authorizations) throws ThriftSecurityException {
+    if (!canChangeAuthorizations(credentials, user))
+      throw new ThriftSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
+    
+    targetUserExists(user);
+
+    try {
+      authorizor.changeAuthorizations(user, authorizations);
+      log.info("Changed authorizations for user " + user + " at the request of user " + credentials.user);
+    } catch (AccumuloSecurityException ase) {
+      throw ase.asThriftException();
+    }
+  }
+  
+  /**
+   * @param credentials
+   * @param user
+   * @param bytes
+   * @throws ThriftSecurityException
+   */
+  public void changePassword(AuthInfo credentials, String user, byte[] pass) throws ThriftSecurityException {
+    if (!canChangePassword(credentials, user))
+      throw new ThriftSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
+    try {
+      authenticator.changePassword(user, pass);
+      log.info("Changed password for user " + user + " at the request of user " + credentials.user);
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    }
+  }
+  
+  /**
+   * @param credentials
+   * @param user
+   * @param bytes
+   * @param authorizations
+   * @throws ThriftSecurityException
+   */
+  public void createUser(AuthInfo credentials, String user, byte[] pass, Authorizations authorizations) throws ThriftSecurityException {
+    if (!canCreateUser(credentials, user))
+      throw new ThriftSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
+    try {
+      authenticator.createUser(user, pass);
+      authorizor.initUser(user);
+      log.info("Created user " + user + " at the request of user " + credentials.user);
+      if (canChangeAuthorizations(credentials, user))
+        authorizor.changeAuthorizations(user, authorizations);
+    } catch (AccumuloSecurityException ase) {
+      throw ase.asThriftException();
+    }
+  }
+  
+  /**
+   * @param credentials
+   * @param user
+   * @throws ThriftSecurityException
+   */
+  public void dropUser(AuthInfo credentials, String user) throws ThriftSecurityException {
+    if (!canDropUser(credentials, user))
+      throw new ThriftSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
+    try {
+      authorizor.dropUser(user);
+      authenticator.dropUser(user);
+      log.info("Deleted user " + user + " at the request of user " + credentials.user);
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    }
+  }
+  
+  /**
+   * @param credentials
+   * @param user
+   * @param permissionById
+   * @throws ThriftSecurityException
+   */
+  public void grantSystemPermission(AuthInfo credentials, String user, SystemPermission permissionById) throws ThriftSecurityException {
+    if (!canGrantSystem(credentials, user, permissionById))
+      throw new ThriftSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
+    
+    targetUserExists(user);
+
+    try {
+      authorizor.grantSystemPermission(user, permissionById);
+      log.info("Granted system permission " + permissionById + " for user " + user + " at the request of user " + credentials.user);
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    }
+  }
+  
+  /**
+   * @param credentials
+   * @param user
+   * @param tableId
+   * @param permission
+   * @throws ThriftSecurityException
+   */
+  public void grantTablePermission(AuthInfo c, String user, String tableId, TablePermission permission) throws ThriftSecurityException {
+    if (!canGrantTable(c, user, tableId))
+      throw new ThriftSecurityException(c.user, SecurityErrorCode.PERMISSION_DENIED);
+    
+    targetUserExists(user);
+
+    try {
+      authorizor.grantTablePermission(user, tableId, permission);
+      log.info("Granted table permission " + permission + " for user " + user + " on the table " + tableId + " at the request of user " + c.user);
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    } catch (TableNotFoundException e) {
+      throw new ThriftSecurityException(c.user, SecurityErrorCode.TABLE_DOESNT_EXIST);
+    }
+  }
+  
+  /**
+   * @param credentials
+   * @param user
+   * @param permission
+   * @throws ThriftSecurityException
+   */
+  public void revokeSystemPermission(AuthInfo credentials, String user, SystemPermission permission) throws ThriftSecurityException {
+    if (!canRevokeSystem(credentials, user, permission))
+      throw new ThriftSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
+    
+    targetUserExists(user);
+
+    try {
+      authorizor.revokeSystemPermission(user, permission);
+      log.info("Revoked system permission " + permission + " for user " + user + " at the request of user " + credentials.user);
+
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    }
+  }
+  
+  /**
+   * @param credentials
+   * @param user
+   * @param tableId
+   * @param permission
+   * @throws ThriftSecurityException
+   */
+  public void revokeTablePermission(AuthInfo c, String user, String tableId, TablePermission permission) throws ThriftSecurityException {
+    if (!canRevokeTable(c, user, tableId))
+      throw new ThriftSecurityException(c.user, SecurityErrorCode.PERMISSION_DENIED);
+    
+    targetUserExists(user);
+
+    try {
+      authorizor.revokeTablePermission(user, tableId, permission);
+      log.info("Revoked table permission " + permission + " for user " + user + " on the table " + tableId + " at the request of user " + c.user);
+
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    } catch (TableNotFoundException e) {
+      throw new ThriftSecurityException(c.user, SecurityErrorCode.TABLE_DOESNT_EXIST);
+    }
+  }
+
+  /**
+   * @param credentials
+   * @param user
+   * @param permissionById
+   * @return
+   * @throws ThriftSecurityException
+   */
+  public boolean hasSystemPermission(AuthInfo credentials, String user, SystemPermission permissionById) throws ThriftSecurityException {
+    if (!canAskAboutOtherUsers(credentials, user))
+      throw new ThriftSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
+    return hasSystemPermission(user, permissionById);
+  }
+  
+  /**
+   * @param credentials
+   * @param user
+   * @param tableId
+   * @param permissionById
+   * @return
+   * @throws ThriftSecurityException
+   */
+  public boolean hasTablePermission(AuthInfo credentials, String user, String tableId, TablePermission permissionById) throws ThriftSecurityException {
+    if (!canAskAboutOtherUsers(credentials, user))
+      throw new ThriftSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
+    return hasTablePermission(user, tableId, permissionById);
+  }
+  
+  /**
+   * @param credentials
+   * @return
+   * @throws ThriftSecurityException
+   */
+  public Set<String> listUsers(AuthInfo credentials) throws ThriftSecurityException {
+    authenticate(credentials);
+    try {
+      return authenticator.listUsers();
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    }
+  }
+  
+  /**
+   * @param systemCredentials
+   * @param tableId
+   * @throws ThriftSecurityException
+   */
+  public void deleteTable(AuthInfo credentials, String tableId) throws ThriftSecurityException {
+    if (!canDeleteTable(credentials, tableId))
+      throw new ThriftSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
+    try {
+      authorizor.cleanTablePermissions(tableId);
+    } catch (AccumuloSecurityException e) {
+      e.setUser(credentials.user);
+      throw e.asThriftException();
+    } catch (TableNotFoundException e) {
+      throw new ThriftSecurityException(credentials.user, SecurityErrorCode.TABLE_DOESNT_EXIST);
+    }
+  }
+  
+  @Override
+  public void clearCache(String user, boolean password, boolean auths, boolean system, Set<String> tables) throws ThriftSecurityException {
+    if (password)
+      authenticator.clearCache(user);
+
+    if (auths || system || tables.size() > 0)
+      try {
+        authorizor.clearCache(user, auths, system, tables);
+      } catch (TableNotFoundException e) {
+        throw new ThriftSecurityException(user, SecurityErrorCode.TABLE_DOESNT_EXIST);
+      } catch (AccumuloSecurityException e) {
+        throw e.asThriftException();
+      }
+    
+  }
+  
+  @Override
+  public void clearCache(String table) throws ThriftSecurityException {
+    try {
+      authorizor.clearTableCache(table);
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    } catch (TableNotFoundException e) {
+      throw new ThriftSecurityException(table, SecurityErrorCode.TABLE_DOESNT_EXIST);
+    }
+  }
+  
+  @Override
+  public boolean cachesToClear() throws ThriftSecurityException {
+    try {
+      return authenticator.cachesToClear() || authorizor.cachesToClear();
+    } catch (AccumuloSecurityException e) {
+      throw e.asThriftException();
+    }
+  }
+}

Propchange: accumulo/branches/ACCUMULO-259/server/src/main/java/org/apache/accumulo/server/security/SecurityOperationImpl.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: accumulo/branches/ACCUMULO-259/server/src/main/java/org/apache/accumulo/server/security/ZKAuthenticator.java
URL: http://svn.apache.org/viewvc/accumulo/branches/ACCUMULO-259/server/src/main/java/org/apache/accumulo/server/security/ZKAuthenticator.java?rev=1346535&r1=1346534&r2=1346535&view=diff
==============================================================================
--- accumulo/branches/ACCUMULO-259/server/src/main/java/org/apache/accumulo/server/security/ZKAuthenticator.java (original)
+++ accumulo/branches/ACCUMULO-259/server/src/main/java/org/apache/accumulo/server/security/ZKAuthenticator.java Tue Jun  5 19:02:30 2012
@@ -16,101 +16,48 @@
  */
 package org.apache.accumulo.server.security;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
 import java.nio.ByteBuffer;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TreeSet;
 
 import org.apache.accumulo.core.Constants;
 import org.apache.accumulo.core.client.AccumuloException;
 import org.apache.accumulo.core.client.AccumuloSecurityException;
-import org.apache.accumulo.core.security.Authorizations;
-import org.apache.accumulo.core.security.SystemPermission;
-import org.apache.accumulo.core.security.TablePermission;
 import org.apache.accumulo.core.security.thrift.AuthInfo;
 import org.apache.accumulo.core.security.thrift.SecurityErrorCode;
 import org.apache.accumulo.core.util.ByteBufferUtil;
 import org.apache.accumulo.core.zookeeper.ZooUtil.NodeExistsPolicy;
 import org.apache.accumulo.core.zookeeper.ZooUtil.NodeMissingPolicy;
-import org.apache.accumulo.server.client.HdfsZooInstance;
 import org.apache.accumulo.server.zookeeper.IZooReaderWriter;
 import org.apache.accumulo.server.zookeeper.ZooCache;
 import org.apache.accumulo.server.zookeeper.ZooReaderWriter;
 import org.apache.log4j.Logger;
 import org.apache.zookeeper.KeeperException;
 
-// Utility class for adding all security info into ZK
+// Utility class for adding all authentication info into ZK
 public final class ZKAuthenticator implements Authenticator {
-  private static final Logger log = Logger.getLogger(ZKAuthenticator.class);
+  static final Logger log = Logger.getLogger(ZKAuthenticator.class);
   private static Authenticator zkAuthenticatorInstance = null;
-  private static String rootUserName = null;
-  
-  private final String ZKUserAuths = "/Authorizations";
-  private final String ZKUserSysPerms = "/System";
-  private final String ZKUserTablePerms = "/Tables";
-  
-  private final String ZKUserPath;
+
+  private String ZKUserPath;
   private final ZooCache zooCache;
   
   public static synchronized Authenticator getInstance() {
     if (zkAuthenticatorInstance == null)
-      zkAuthenticatorInstance = new Auditor(new ZKAuthenticator());
+      zkAuthenticatorInstance = new ZKAuthenticator();
     return zkAuthenticatorInstance;
   }
   
-  private ZKAuthenticator() {
-    this(HdfsZooInstance.getInstance().getInstanceID());
-  }
-  
-  public ZKAuthenticator(String instanceId) {
-    ZKUserPath = Constants.ZROOT + "/" + instanceId + "/users";
+  public ZKAuthenticator() {
     zooCache = new ZooCache();
   }
   
-  /**
-   * Authenticate a user's credentials
-   * 
-   * @return true if username/password combination match existing user; false otherwise
-   * @throws AccumuloSecurityException
-   */
-  private boolean authenticate(AuthInfo credentials) throws AccumuloSecurityException {
-    if (!credentials.instanceId.equals(HdfsZooInstance.getInstance().getInstanceID()))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.INVALID_INSTANCEID);
-    
-    if (credentials.user.equals(SecurityConstants.SYSTEM_USERNAME))
-      return credentials.equals(SecurityConstants.getSystemCredentials());
-    
-    byte[] pass;
-    String zpath = ZKUserPath + "/" + credentials.user;
-    pass = zooCache.get(zpath);
-    boolean result = Tool.checkPass(ByteBufferUtil.toBytes(credentials.password), pass);
-    if (!result) {
-      zooCache.clear(zpath);
-      pass = zooCache.get(zpath);
-      result = Tool.checkPass(ByteBufferUtil.toBytes(credentials.password), pass);
-    }
-    return result;
+  public void initialize(String instanceId) {
+    ZKUserPath = Constants.ZROOT + "/" + instanceId + "/users";
   }
-  
-  /**
-   * Only SYSTEM user can call this method
-   */
+
+  @Override
   public void initializeSecurity(AuthInfo credentials, String rootuser, byte[] rootpass) throws AccumuloSecurityException {
-    if (!credentials.user.equals(SecurityConstants.SYSTEM_USERNAME) || !authenticate(credentials))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
-    
     try {
       // remove old settings from zookeeper first, if any
       IZooReaderWriter zoo = ZooReaderWriter.getRetryingInstance();
@@ -124,16 +71,8 @@ public final class ZKAuthenticator imple
         // prep parent node of users with root username
         zoo.putPersistentData(ZKUserPath, rootuser.getBytes(), NodeExistsPolicy.FAIL);
         
-        // create the root user with all system privileges, no table privileges, and no record-level authorizations
-        Set<SystemPermission> rootPerms = new TreeSet<SystemPermission>();
-        for (SystemPermission p : SystemPermission.values())
-          rootPerms.add(p);
-        Map<String,Set<TablePermission>> tablePerms = new HashMap<String,Set<TablePermission>>();
-        // Allow the root user to flush the !METADATA table
-        tablePerms.put(Constants.METADATA_TABLE_ID, Collections.singleton(TablePermission.ALTER_TABLE));
-        constructUser(rootuser, Tool.createPass(rootpass), rootPerms, tablePerms, Constants.NO_AUTHS);
+        constructUser(rootuser, ZKSecurityTool.createPass(rootpass));
       }
-      log.info("Initialized root user with username: " + rootuser + " at the request of user " + credentials.user);
     } catch (KeeperException e) {
       log.error(e, e);
       throw new RuntimeException(e);
@@ -149,81 +88,27 @@ public final class ZKAuthenticator imple
   /**
    * Sets up the user in ZK for the provided user. No checking for existence is done here, it should be done before calling.
    */
-  private void constructUser(String user, byte[] pass, Set<SystemPermission> sysPerms, Map<String,Set<TablePermission>> tablePerms, Authorizations auths)
+  private void constructUser(String user, byte[] pass)
       throws KeeperException, InterruptedException {
     synchronized (zooCache) {
       zooCache.clear();
       IZooReaderWriter zoo = ZooReaderWriter.getRetryingInstance();
       zoo.putPrivatePersistentData(ZKUserPath + "/" + user, pass, NodeExistsPolicy.FAIL);
-      zoo.putPersistentData(ZKUserPath + "/" + user + ZKUserAuths, Tool.convertAuthorizations(auths), NodeExistsPolicy.FAIL);
-      zoo.putPersistentData(ZKUserPath + "/" + user + ZKUserSysPerms, Tool.convertSystemPermissions(sysPerms), NodeExistsPolicy.FAIL);
-      zoo.putPersistentData(ZKUserPath + "/" + user + ZKUserTablePerms, new byte[0], NodeExistsPolicy.FAIL);
-      for (Entry<String,Set<TablePermission>> entry : tablePerms.entrySet())
-        createTablePerm(user, entry.getKey(), entry.getValue());
-    }
-  }
-  
-  /**
-   * Sets up a new table configuration for the provided user/table. No checking for existance is done here, it should be done before calling.
-   */
-  private void createTablePerm(String user, String table, Set<TablePermission> perms) throws KeeperException, InterruptedException {
-    synchronized (zooCache) {
-      zooCache.clear();
-      ZooReaderWriter.getRetryingInstance().putPersistentData(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + table, Tool.convertTablePermissions(perms),
-          NodeExistsPolicy.FAIL);
     }
   }
   
-  public synchronized String getRootUsername() {
-    if (rootUserName == null)
-      rootUserName = new String(zooCache.get(ZKUserPath));
-    return rootUserName;
-  }
-  
-  public boolean authenticateUser(AuthInfo credentials, String user, ByteBuffer pass) throws AccumuloSecurityException {
-    if (!authenticate(credentials))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.BAD_CREDENTIALS);
-    if (!credentials.user.equals(user) && !hasSystemPermission(credentials, credentials.user, SystemPermission.SYSTEM))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
-    
-    return authenticate(new AuthInfo(user, pass, credentials.instanceId));
-  }
-  
-  public boolean authenticateUser(AuthInfo credentials, String user, byte[] pass) throws AccumuloSecurityException {
-    return authenticateUser(credentials, user, ByteBuffer.wrap(pass));
-  }
-  
   @Override
-  public Set<String> listUsers(AuthInfo credentials) throws AccumuloSecurityException {
-    if (!authenticate(credentials))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.BAD_CREDENTIALS);
-    
+  public Set<String> listUsers() {
     return new TreeSet<String>(zooCache.getChildren(ZKUserPath));
   }
   
   /**
    * Creates a user with no permissions whatsoever
    */
-  public void createUser(AuthInfo credentials, String user, byte[] pass, Authorizations authorizations) throws AccumuloSecurityException {
-    if (!hasSystemPermission(credentials, credentials.user, SystemPermission.CREATE_USER))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
-    
-    if (!hasSystemPermission(credentials, credentials.user, SystemPermission.ALTER_USER)) {
-      Authorizations creatorAuths = getUserAuthorizations(credentials, credentials.user);
-      for (byte[] auth : authorizations.getAuthorizations())
-        if (!creatorAuths.contains(auth)) {
-          log.info("User " + credentials.user + " attempted to create a user " + user + " with authorization " + new String(auth) + " they did not have");
-          throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.BAD_AUTHORIZATIONS);
-        }
-    }
-    
-    // don't allow creating a user with the same name as system user
-    if (user.equals(SecurityConstants.SYSTEM_USERNAME))
-      throw new AccumuloSecurityException(user, SecurityErrorCode.PERMISSION_DENIED);
-    
+  @Override
+  public void createUser(String user, byte[] pass) throws AccumuloSecurityException {
     try {
-      constructUser(user, Tool.createPass(pass), new TreeSet<SystemPermission>(), new HashMap<String,Set<TablePermission>>(), authorizations);
-      log.info("Created user " + user + " at the request of user " + credentials.user);
+      constructUser(user, ZKSecurityTool.createPass(pass));
     } catch (KeeperException e) {
       log.error(e, e);
       if (e.code().equals(KeeperException.Code.NODEEXISTS))
@@ -238,20 +123,13 @@ public final class ZKAuthenticator imple
     }
   }
   
-  public void dropUser(AuthInfo credentials, String user) throws AccumuloSecurityException {
-    if (!hasSystemPermission(credentials, credentials.user, SystemPermission.DROP_USER))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
-    
-    // can't delete root or system users
-    if (user.equals(getRootUsername()) || user.equals(SecurityConstants.SYSTEM_USERNAME))
-      throw new AccumuloSecurityException(user, SecurityErrorCode.PERMISSION_DENIED);
-    
+  @Override
+  public void dropUser(String user) throws AccumuloSecurityException {
     try {
       synchronized (zooCache) {
         zooCache.clear();
         ZooReaderWriter.getRetryingInstance().recursiveDelete(ZKUserPath + "/" + user, NodeMissingPolicy.FAIL);
       }
-      log.info("Deleted user " + user + " at the request of user " + credentials.user);
     } catch (InterruptedException e) {
       log.error(e, e);
       throw new RuntimeException(e);
@@ -263,21 +141,14 @@ public final class ZKAuthenticator imple
     }
   }
   
-  public void changePassword(AuthInfo credentials, String user, byte[] pass) throws AccumuloSecurityException {
-    if (!hasSystemPermission(credentials, credentials.user, SystemPermission.ALTER_USER) && !credentials.user.equals(user))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
-    
-    // can't modify system user
-    if (user.equals(SecurityConstants.SYSTEM_USERNAME))
-      throw new AccumuloSecurityException(user, SecurityErrorCode.PERMISSION_DENIED);
-    
+  @Override
+  public void changePassword(String user, byte[] pass) throws AccumuloSecurityException {
     if (userExists(user)) {
       try {
         synchronized (zooCache) {
-          zooCache.clear();
-          ZooReaderWriter.getRetryingInstance().putPrivatePersistentData(ZKUserPath + "/" + user, Tool.createPass(pass), NodeExistsPolicy.OVERWRITE);
+          zooCache.clear(ZKUserPath + "/" + user);
+          ZooReaderWriter.getRetryingInstance().putPrivatePersistentData(ZKUserPath + "/" + user, ZKSecurityTool.createPass(pass), NodeExistsPolicy.OVERWRITE);
         }
-        log.info("Changed password for user " + user + " at the request of user " + credentials.user);
       } catch (KeeperException e) {
         log.error(e, e);
         throw new AccumuloSecurityException(user, SecurityErrorCode.CONNECTION_ERROR, e);
@@ -295,427 +166,37 @@ public final class ZKAuthenticator imple
   /**
    * Checks if a user exists
    */
-  private boolean userExists(String user) {
-    if (zooCache.get(ZKUserPath + "/" + user) != null)
-      return true;
-    zooCache.clear(ZKUserPath + "/" + user);
+  @Override
+  public boolean userExists(String user) {
     return zooCache.get(ZKUserPath + "/" + user) != null;
   }
   
-  public void changeAuthorizations(AuthInfo credentials, String user, Authorizations authorizations) throws AccumuloSecurityException {
-    if (!hasSystemPermission(credentials, credentials.user, SystemPermission.ALTER_USER))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
-    
-    // can't modify system user
-    if (user.equals(SecurityConstants.SYSTEM_USERNAME))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
-    
-    if (userExists(user)) {
-      try {
-        synchronized (zooCache) {
-          zooCache.clear();
-          ZooReaderWriter.getRetryingInstance().putPersistentData(ZKUserPath + "/" + user + ZKUserAuths, Tool.convertAuthorizations(authorizations),
-              NodeExistsPolicy.OVERWRITE);
-        }
-        log.info("Changed authorizations for user " + user + " at the request of user " + credentials.user);
-      } catch (KeeperException e) {
-        log.error(e, e);
-        throw new AccumuloSecurityException(user, SecurityErrorCode.CONNECTION_ERROR, e);
-      } catch (InterruptedException e) {
-        log.error(e, e);
-        throw new RuntimeException(e);
-      }
-    } else
-      throw new AccumuloSecurityException(user, SecurityErrorCode.USER_DOESNT_EXIST); // user doesn't exist
-  }
-  
-  public Authorizations getUserAuthorizations(AuthInfo credentials, String user) throws AccumuloSecurityException {
-    if (!hasSystemPermission(credentials, credentials.user, SystemPermission.SYSTEM) && !credentials.user.equals(user))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
-    
-    // system user doesn't need record-level authorizations for the tables it reads (for now)
-    if (user.equals(SecurityConstants.SYSTEM_USERNAME))
-      return Constants.NO_AUTHS;
-    
-    if (userExists(user)) {
-      byte[] authsBytes = zooCache.get(ZKUserPath + "/" + user + ZKUserAuths);
-      if (authsBytes != null)
-        return Tool.convertAuthorizations(authsBytes);
-    }
-    throw new AccumuloSecurityException(user, SecurityErrorCode.USER_DOESNT_EXIST); // user doesn't exist
-  }
-  
-  /**
-   * Checks if a user has a system permission
-   * 
-   * @return true if a user exists and has permission; false otherwise
-   */
   @Override
-  public boolean hasSystemPermission(AuthInfo credentials, String user, SystemPermission permission) throws AccumuloSecurityException {
-    if (!authenticate(credentials))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.BAD_CREDENTIALS);
-    
-    // some people just aren't allowed to ask about other users; here are those who can ask
-    if (!credentials.user.equals(user) && !hasSystemPermission(credentials, credentials.user, SystemPermission.SYSTEM)
-        && !hasSystemPermission(credentials, credentials.user, SystemPermission.CREATE_USER)
-        && !hasSystemPermission(credentials, credentials.user, SystemPermission.ALTER_USER)
-        && !hasSystemPermission(credentials, credentials.user, SystemPermission.DROP_USER))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
-    
-    if (user.equals(getRootUsername()) || user.equals(SecurityConstants.SYSTEM_USERNAME))
-      return true;
-    
-    byte[] perms = zooCache.get(ZKUserPath + "/" + user + ZKUserSysPerms);
-    if (perms != null) {
-      if (Tool.convertSystemPermissions(perms).contains(permission))
-        return true;
-      zooCache.clear(ZKUserPath + "/" + user + ZKUserSysPerms);
-      perms = zooCache.get(ZKUserPath + "/" + user + ZKUserSysPerms);
-      if (perms == null)
-        return false;
-      return Tool.convertSystemPermissions(perms).contains(permission);
-    }
-    throw new AccumuloSecurityException(user, SecurityErrorCode.USER_DOESNT_EXIST); // user doesn't exist
+  public void clearCache(String user) {
+    zooCache.clear(ZKUserPath + "/" + user);
   }
-  
+
   @Override
-  public boolean hasTablePermission(AuthInfo credentials, String user, String table, TablePermission permission) throws AccumuloSecurityException {
-    if (!_hasTablePermission(credentials, user, table, permission)) {
-      zooCache.clear(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + table);
-      return _hasTablePermission(credentials, user, table, permission);
-    }
-    
+  public boolean validAuthorizor(Authorizor auth) {
     return true;
   }
   
-  private boolean _hasTablePermission(AuthInfo credentials, String user, String table, TablePermission permission) throws AccumuloSecurityException {
-    if (!authenticate(credentials))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.BAD_CREDENTIALS);
-    
-    // some people just aren't allowed to ask about other users; here are those who can ask
-    if (!credentials.user.equals(user) && !hasSystemPermission(credentials, credentials.user, SystemPermission.SYSTEM)
-        && !hasSystemPermission(credentials, credentials.user, SystemPermission.CREATE_USER)
-        && !hasSystemPermission(credentials, credentials.user, SystemPermission.ALTER_USER)
-        && !hasSystemPermission(credentials, credentials.user, SystemPermission.DROP_USER))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
-    
-    // always allow system user
-    if (user.equals(SecurityConstants.SYSTEM_USERNAME))
-      return true;
-    
-    // Don't let nonexistant users scan
-    if (!userExists(user))
-      throw new AccumuloSecurityException(user, SecurityErrorCode.USER_DOESNT_EXIST); // user doesn't exist
-
-    // allow anybody to read the METADATA table
-    if (table.equals(Constants.METADATA_TABLE_ID) && permission.equals(TablePermission.READ))
-      return true;
-    
-    byte[] serializedPerms = zooCache.get(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + table);
-    if (serializedPerms != null) {
-      return Tool.convertTablePermissions(serializedPerms).contains(permission);
-    }
-    return false;
-  }
-  
   @Override
-  public void grantSystemPermission(AuthInfo credentials, String user, SystemPermission permission) throws AccumuloSecurityException {
-    if (!hasSystemPermission(credentials, credentials.user, SystemPermission.GRANT))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
-    
-    // can't modify system user
-    if (user.equals(SecurityConstants.SYSTEM_USERNAME))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
-    
-    if (permission.equals(SystemPermission.GRANT))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.GRANT_INVALID);
-    
-    if (userExists(user)) {
-      try {
-        byte[] permBytes = zooCache.get(ZKUserPath + "/" + user + ZKUserSysPerms);
-        if (permBytes == null) {
-          throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.USER_DOESNT_EXIST); // user doesn't exist
-        }
-
-        Set<SystemPermission> perms = Tool.convertSystemPermissions(permBytes);
-        if (perms.add(permission)) {
-          synchronized (zooCache) {
-            zooCache.clear();
-            ZooReaderWriter.getRetryingInstance().putPersistentData(ZKUserPath + "/" + user + ZKUserSysPerms, Tool.convertSystemPermissions(perms),
-                NodeExistsPolicy.OVERWRITE);
-          }
-        }
-        log.info("Granted system permission " + permission + " for user " + user + " at the request of user " + credentials.user);
-      } catch (KeeperException e) {
-        log.error(e, e);
-        throw new AccumuloSecurityException(user, SecurityErrorCode.CONNECTION_ERROR, e);
-      } catch (InterruptedException e) {
-        log.error(e, e);
-        throw new RuntimeException(e);
-      }
-    } else
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.USER_DOESNT_EXIST); // user doesn't exist
-  }
-
-  @Override
-  public void grantTablePermission(AuthInfo credentials, String user, String table, TablePermission permission) throws AccumuloSecurityException {
-    if (!hasSystemPermission(credentials, credentials.user, SystemPermission.ALTER_USER)
-        && !hasTablePermission(credentials, credentials.user, table, TablePermission.GRANT))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
-    
-    // can't modify system user
-    if (user.equals(SecurityConstants.SYSTEM_USERNAME))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
-    
-    if (userExists(user)) {
-      Set<TablePermission> tablePerms;
-      byte[] serializedPerms = zooCache.get(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + table);
-      if (serializedPerms != null)
-        tablePerms = Tool.convertTablePermissions(serializedPerms);
-      else
-        tablePerms = new TreeSet<TablePermission>();
-      
-      try {
-        if (tablePerms.add(permission)) {
-          synchronized (zooCache) {
-            zooCache.clear();
-            ZooReaderWriter.getRetryingInstance().putPersistentData(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + table,
-                Tool.convertTablePermissions(tablePerms), NodeExistsPolicy.OVERWRITE);
-          }
-        }
-        log.info("Granted table permission " + permission + " for user " + user + " on the table " + table + " at the request of user " + credentials.user);
-      } catch (KeeperException e) {
-        log.error(e, e);
-        throw new AccumuloSecurityException(user, SecurityErrorCode.CONNECTION_ERROR, e);
-      } catch (InterruptedException e) {
-        log.error(e, e);
-        throw new RuntimeException(e);
-      }
-    } else
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.USER_DOESNT_EXIST); // user doesn't exist
-  }
-
-  @Override
-  public void revokeSystemPermission(AuthInfo credentials, String user, SystemPermission permission) throws AccumuloSecurityException {
-    if (!hasSystemPermission(credentials, credentials.user, SystemPermission.GRANT))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
-    
-    // can't modify system user or revoke permissions from root user
-    if (user.equals(SecurityConstants.SYSTEM_USERNAME) || user.equals(getRootUsername()))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
-    
-    if (permission.equals(SystemPermission.GRANT))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.GRANT_INVALID);
-
-    if (userExists(user)) {
-      byte[] sysPermBytes = zooCache.get(ZKUserPath + "/" + user + ZKUserSysPerms);
-      if (sysPermBytes == null)
-        throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.USER_DOESNT_EXIST);
-
-      Set<SystemPermission> sysPerms = Tool.convertSystemPermissions(sysPermBytes);
-
-      try {
-        if (sysPerms.remove(permission)) {
-          synchronized (zooCache) {
-            zooCache.clear();
-            ZooReaderWriter.getRetryingInstance().putPersistentData(ZKUserPath + "/" + user + ZKUserSysPerms, Tool.convertSystemPermissions(sysPerms),
-                NodeExistsPolicy.OVERWRITE);
-          }
-        }
-        log.info("Revoked system permission " + permission + " for user " + user + " at the request of user " + credentials.user);
-      } catch (KeeperException e) {
-        log.error(e, e);
-        throw new AccumuloSecurityException(user, SecurityErrorCode.CONNECTION_ERROR, e);
-      } catch (InterruptedException e) {
-        log.error(e, e);
-        throw new RuntimeException(e);
-      }
-    } else
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.USER_DOESNT_EXIST);
-  }
-
-  @Override
-  public void revokeTablePermission(AuthInfo credentials, String user, String table, TablePermission permission) throws AccumuloSecurityException {
-    if (!hasSystemPermission(credentials, credentials.user, SystemPermission.ALTER_USER)
-        && !hasTablePermission(credentials, credentials.user, table, TablePermission.GRANT))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
-    
-    // can't modify system user
-    if (user.equals(SecurityConstants.SYSTEM_USERNAME))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
-    
-    if (userExists(user)) {
-      
-      byte[] serializedPerms = zooCache.get(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + table);
-      if (serializedPerms == null)
-        return;
-      Set<TablePermission> tablePerms = Tool.convertTablePermissions(serializedPerms);
-      try {
-        if (tablePerms.remove(permission)) {
-          zooCache.clear();
-          IZooReaderWriter zoo = ZooReaderWriter.getRetryingInstance();
-          if (tablePerms.size() == 0)
-            zoo.recursiveDelete(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + table, NodeMissingPolicy.SKIP);
-          else
-            zoo.putPersistentData(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + table, Tool.convertTablePermissions(tablePerms),
-                NodeExistsPolicy.OVERWRITE);
-        }
-      } catch (KeeperException e) {
-        log.error(e, e);
-        throw new AccumuloSecurityException(user, SecurityErrorCode.CONNECTION_ERROR, e);
-      } catch (InterruptedException e) {
-        log.error(e, e);
-        throw new RuntimeException(e);
-      }
-      log.info("Revoked table permission " + permission + " for user " + user + " on the table " + table + " at the request of user " + credentials.user);
-    } else
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.USER_DOESNT_EXIST);
-  }
-
-  @Override
-  public void deleteTable(AuthInfo credentials, String table) throws AccumuloSecurityException {
-    if (!hasSystemPermission(credentials, credentials.user, SystemPermission.DROP_TABLE)
-        && !hasTablePermission(credentials, credentials.user, table, TablePermission.DROP_TABLE))
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.PERMISSION_DENIED);
-    
-    try {
-      synchronized (zooCache) {
-        zooCache.clear();
-        IZooReaderWriter zoo = ZooReaderWriter.getRetryingInstance();
-        for (String user : zooCache.getChildren(ZKUserPath))
-          zoo.recursiveDelete(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + table, NodeMissingPolicy.SKIP);
-      }
-    } catch (KeeperException e) {
-      log.error(e, e);
-      throw new AccumuloSecurityException(credentials.user, SecurityErrorCode.CONNECTION_ERROR, e);
-    } catch (InterruptedException e) {
-      log.error(e, e);
-      throw new RuntimeException(e);
-    }
-  }
-
-  /**
-   * All the static too methods used for this class, so that we can separate out stuff that isn't using ZooKeeper. That way, we can check the synchronization
-   * model more easily, as we only need to check to make sure zooCache is cleared when things are written to ZooKeeper in methods that might use it. These
-   * won't, and so don't need to be checked.
-   */
-  static class Tool {
-    private static final int SALT_LENGTH = 8;
-    
-    // Generates a byte array salt of length SALT_LENGTH
-    private static byte[] generateSalt() {
-      final SecureRandom random = new SecureRandom();
-      byte[] salt = new byte[SALT_LENGTH];
-      random.nextBytes(salt);
-      return salt;
-    }
-    
-    private static byte[] hash(byte[] raw) throws NoSuchAlgorithmException {
-      MessageDigest md = MessageDigest.getInstance(Constants.PW_HASH_ALGORITHM);
-      md.update(raw);
-      return md.digest();
-    }
-    
-    public static boolean checkPass(byte[] password, byte[] zkData) {
-      if (zkData == null)
-        return false;
-      
-      byte[] salt = new byte[SALT_LENGTH];
-      System.arraycopy(zkData, 0, salt, 0, SALT_LENGTH);
-      byte[] passwordToCheck;
-      try {
-        passwordToCheck = convertPass(password, salt);
-      } catch (NoSuchAlgorithmException e) {
-        log.error("Count not create hashed password", e);
-        return false;
-      }
-      return java.util.Arrays.equals(passwordToCheck, zkData);
-    }
-    
-    public static byte[] createPass(byte[] password) throws AccumuloException {
-      byte[] salt = generateSalt();
-      try {
-        return convertPass(password, salt);
-      } catch (NoSuchAlgorithmException e) {
-        log.error("Count not create hashed password", e);
-        throw new AccumuloException("Count not create hashed password", e);
-      }
-    }
-    
-    private static byte[] convertPass(byte[] password, byte[] salt) throws NoSuchAlgorithmException {
-      byte[] plainSalt = new byte[password.length + SALT_LENGTH];
-      System.arraycopy(password, 0, plainSalt, 0, password.length);
-      System.arraycopy(salt, 0, plainSalt, password.length, SALT_LENGTH);
-      byte[] hashed = hash(plainSalt);
-      byte[] saltedHash = new byte[SALT_LENGTH + hashed.length];
-      System.arraycopy(salt, 0, saltedHash, 0, SALT_LENGTH);
-      System.arraycopy(hashed, 0, saltedHash, SALT_LENGTH, hashed.length);
-      return saltedHash; // contains salt+hash(password+salt)
-    }
-    
-    public static Authorizations convertAuthorizations(byte[] authorizations) {
-      return new Authorizations(authorizations);
-    }
-    
-    public static byte[] convertAuthorizations(Authorizations authorizations) {
-      return authorizations.getAuthorizationsArray();
-    }
-
-    public static byte[] convertSystemPermissions(Set<SystemPermission> systempermissions) {
-      ByteArrayOutputStream bytes = new ByteArrayOutputStream(systempermissions.size());
-      DataOutputStream out = new DataOutputStream(bytes);
-      try {
-        for (SystemPermission sp : systempermissions)
-          out.writeByte(sp.getId());
-      } catch (IOException e) {
-        log.error(e, e);
-        throw new RuntimeException(e); // this is impossible with ByteArrayOutputStream; crash hard if this happens
-      }
-      return bytes.toByteArray();
-    }
-    
-    public static Set<SystemPermission> convertSystemPermissions(byte[] systempermissions) {
-      ByteArrayInputStream bytes = new ByteArrayInputStream(systempermissions);
-      DataInputStream in = new DataInputStream(bytes);
-      Set<SystemPermission> toReturn = new HashSet<SystemPermission>();
-      try {
-        while (in.available() > 0)
-          toReturn.add(SystemPermission.getPermissionById(in.readByte()));
-      } catch (IOException e) {
-        log.error("User database is corrupt; error converting system permissions", e);
-        toReturn.clear();
-      }
-      return toReturn;
-    }
-    
-    public static byte[] convertTablePermissions(Set<TablePermission> tablepermissions) {
-      ByteArrayOutputStream bytes = new ByteArrayOutputStream(tablepermissions.size());
-      DataOutputStream out = new DataOutputStream(bytes);
-      try {
-        for (TablePermission tp : tablepermissions)
-          out.writeByte(tp.getId());
-      } catch (IOException e) {
-        log.error(e, e);
-        throw new RuntimeException(e); // this is impossible with ByteArrayOutputStream; crash hard if this happens
-      }
-      return bytes.toByteArray();
-    }
-    
-    public static Set<TablePermission> convertTablePermissions(byte[] tablepermissions) {
-      Set<TablePermission> toReturn = new HashSet<TablePermission>();
-      for (byte b : tablepermissions)
-        toReturn.add(TablePermission.getPermissionById(b));
-      return toReturn;
+  public boolean authenticateUser(String user, ByteBuffer password, String instanceId) {
+    byte[] pass;
+    String zpath = ZKUserPath + "/" + user;
+    pass = zooCache.get(zpath);
+    boolean result = ZKSecurityTool.checkPass(ByteBufferUtil.toBytes(password), pass);
+    if (!result) {
+      zooCache.clear(zpath);
+      pass = zooCache.get(zpath);
+      result = ZKSecurityTool.checkPass(ByteBufferUtil.toBytes(password), pass);
     }
+    return result;
   }
-
-  @Override
-  public void clearCache(String user) {
-    zooCache.clear(ZKUserPath + "/" + user);
-  }
-
+  
   @Override
-  public void clearCache(String user, String tableId) {
-    zooCache.clear(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + tableId);
+  public boolean cachesToClear() {
+    return true;
   }
 }

Added: accumulo/branches/ACCUMULO-259/server/src/main/java/org/apache/accumulo/server/security/ZKAuthorizor.java
URL: http://svn.apache.org/viewvc/accumulo/branches/ACCUMULO-259/server/src/main/java/org/apache/accumulo/server/security/ZKAuthorizor.java?rev=1346535&view=auto
==============================================================================
--- accumulo/branches/ACCUMULO-259/server/src/main/java/org/apache/accumulo/server/security/ZKAuthorizor.java (added)
+++ accumulo/branches/ACCUMULO-259/server/src/main/java/org/apache/accumulo/server/security/ZKAuthorizor.java Tue Jun  5 19:02:30 2012
@@ -0,0 +1,349 @@
+/**
+ * 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.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.accumulo.core.Constants;
+import org.apache.accumulo.core.client.AccumuloSecurityException;
+import org.apache.accumulo.core.security.Authorizations;
+import org.apache.accumulo.core.security.SystemPermission;
+import org.apache.accumulo.core.security.TablePermission;
+import org.apache.accumulo.core.security.thrift.SecurityErrorCode;
+import org.apache.accumulo.core.zookeeper.ZooUtil.NodeExistsPolicy;
+import org.apache.accumulo.core.zookeeper.ZooUtil.NodeMissingPolicy;
+import org.apache.accumulo.server.zookeeper.IZooReaderWriter;
+import org.apache.accumulo.server.zookeeper.ZooCache;
+import org.apache.accumulo.server.zookeeper.ZooReaderWriter;
+import org.apache.log4j.Logger;
+import org.apache.zookeeper.KeeperException;
+
+public class ZKAuthorizor implements Authorizor {
+  private static final Logger log = Logger.getLogger(ZKAuthorizor.class);
+  private static Authorizor zkAuthorizorInstance = null;
+
+  private final String ZKUserAuths = "/Authorizations";
+  private final String ZKUserSysPerms = "/System";
+  private final String ZKUserTablePerms = "/Tables";
+
+  private String ZKUserPath;
+  private final ZooCache zooCache;
+  
+  public static synchronized Authorizor getInstance() {
+    if (zkAuthorizorInstance == null)
+      zkAuthorizorInstance = new ZKAuthorizor();
+    return zkAuthorizorInstance;
+  }
+
+  public ZKAuthorizor() {
+    zooCache = new ZooCache();
+  }
+  
+  public void initialize(String instanceId) {
+    ZKUserPath = Constants.ZROOT + "/" + instanceId + "/users";
+  }
+
+  public Authorizations getUserAuthorizations(String user) {
+    byte[] authsBytes = zooCache.get(ZKUserPath + "/" + user + ZKUserAuths);
+    if (authsBytes != null)
+      return ZKSecurityTool.convertAuthorizations(authsBytes);
+    return Constants.NO_AUTHS;
+  }
+
+  @Override
+  public boolean hasTablePermission(String user, String table, TablePermission permission) {
+    byte[] serializedPerms = zooCache.get(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + table);
+    if (serializedPerms != null) {
+      return ZKSecurityTool.convertTablePermissions(serializedPerms).contains(permission);
+    }
+    return false;
+  }
+  
+  @Override
+  public void grantSystemPermission(String user, SystemPermission permission) throws AccumuloSecurityException {
+    try {
+      byte[] permBytes = zooCache.get(ZKUserPath + "/" + user + ZKUserSysPerms);
+      Set<SystemPermission> perms;
+      if (permBytes == null) {
+        perms = new TreeSet<SystemPermission>();
+      } else {
+        perms = ZKSecurityTool.convertSystemPermissions(permBytes);
+      }
+      
+      if (perms.add(permission)) {
+        synchronized (zooCache) {
+          zooCache.clear();
+          ZooReaderWriter.getRetryingInstance().putPersistentData(ZKUserPath + "/" + user + ZKUserSysPerms, ZKSecurityTool.convertSystemPermissions(perms),
+              NodeExistsPolicy.OVERWRITE);
+        }
+      }
+    } catch (KeeperException e) {
+      log.error(e, e);
+      throw new AccumuloSecurityException(user, SecurityErrorCode.CONNECTION_ERROR, e);
+    } catch (InterruptedException e) {
+      log.error(e, e);
+      throw new RuntimeException(e);
+    }
+  }
+  
+  @Override
+  public void grantTablePermission(String user, String table, TablePermission permission) throws AccumuloSecurityException {
+    Set<TablePermission> tablePerms;
+    byte[] serializedPerms = zooCache.get(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + table);
+    if (serializedPerms != null)
+      tablePerms = ZKSecurityTool.convertTablePermissions(serializedPerms);
+    else
+      tablePerms = new TreeSet<TablePermission>();
+    
+    try {
+      if (tablePerms.add(permission)) {
+        synchronized (zooCache) {
+          zooCache.clear(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + table);
+          IZooReaderWriter zoo = ZooReaderWriter.getRetryingInstance();
+          zoo.putPersistentData(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + table,
+              ZKSecurityTool.convertTablePermissions(tablePerms), NodeExistsPolicy.OVERWRITE);
+        }
+      }
+    } catch (KeeperException e) {
+      log.error(e, e);
+      throw new AccumuloSecurityException(user, SecurityErrorCode.CONNECTION_ERROR, e);
+    } catch (InterruptedException e) {
+      log.error(e, e);
+      throw new RuntimeException(e);
+    }
+  }
+  
+  @Override
+  public void revokeSystemPermission(String user, SystemPermission permission) throws AccumuloSecurityException {
+    byte[] sysPermBytes = zooCache.get(ZKUserPath + "/" + user + ZKUserSysPerms);
+    
+    // User had no system permission, nothing to revoke.
+    if (sysPermBytes == null)
+      return;
+    
+    Set<SystemPermission> sysPerms = ZKSecurityTool.convertSystemPermissions(sysPermBytes);
+    
+    try {
+      if (sysPerms.remove(permission)) {
+        synchronized (zooCache) {
+          zooCache.clear();
+          ZooReaderWriter.getRetryingInstance().putPersistentData(ZKUserPath + "/" + user + ZKUserSysPerms, ZKSecurityTool.convertSystemPermissions(sysPerms),
+              NodeExistsPolicy.OVERWRITE);
+        }
+      }
+    } catch (KeeperException e) {
+      log.error(e, e);
+      throw new AccumuloSecurityException(user, SecurityErrorCode.CONNECTION_ERROR, e);
+    } catch (InterruptedException e) {
+      log.error(e, e);
+      throw new RuntimeException(e);
+    }
+  }
+  
+  @Override
+  public void revokeTablePermission(String user, String table, TablePermission permission) throws AccumuloSecurityException {
+    byte[] serializedPerms = zooCache.get(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + table);
+    
+    // User had no table permission, nothing to revoke.
+    if (serializedPerms == null)
+      return;
+
+    Set<TablePermission> tablePerms = ZKSecurityTool.convertTablePermissions(serializedPerms);
+    try {
+      if (tablePerms.remove(permission)) {
+        zooCache.clear();
+        IZooReaderWriter zoo = ZooReaderWriter.getRetryingInstance();
+        if (tablePerms.size() == 0)
+          zoo.recursiveDelete(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + table, NodeMissingPolicy.SKIP);
+        else
+          zoo.putPersistentData(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + table, ZKSecurityTool.convertTablePermissions(tablePerms),
+              NodeExistsPolicy.OVERWRITE);
+      }
+    } catch (KeeperException e) {
+      log.error(e, e);
+      throw new AccumuloSecurityException(user, SecurityErrorCode.CONNECTION_ERROR, e);
+    } catch (InterruptedException e) {
+      log.error(e, e);
+      throw new RuntimeException(e);
+    }
+  }
+  
+  @Override
+  public void cleanTablePermissions(String table) throws AccumuloSecurityException {
+    try {
+      synchronized (zooCache) {
+        zooCache.clear();
+        IZooReaderWriter zoo = ZooReaderWriter.getRetryingInstance();
+        for (String user : zooCache.getChildren(ZKUserPath))
+          zoo.recursiveDelete(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + table, NodeMissingPolicy.SKIP);
+      }
+    } catch (KeeperException e) {
+      log.error(e, e);
+      throw new AccumuloSecurityException("unknownUser", SecurityErrorCode.CONNECTION_ERROR, e);
+    } catch (InterruptedException e) {
+      log.error(e, e);
+      throw new RuntimeException(e);
+    }
+
+  }
+  
+  @Override
+  public boolean validAuthenticator(Authenticator auth) {
+    return true;
+  }
+  
+  @Override
+  public void initializeSecurity(String rootuser) throws AccumuloSecurityException {
+    IZooReaderWriter zoo = ZooReaderWriter.getRetryingInstance();
+
+    // create the root user with all system privileges, no table privileges, and no record-level authorizations
+    Set<SystemPermission> rootPerms = new TreeSet<SystemPermission>();
+    for (SystemPermission p : SystemPermission.values())
+      rootPerms.add(p);
+    Map<String,Set<TablePermission>> tablePerms = new HashMap<String,Set<TablePermission>>();
+    // Allow the root user to flush the !METADATA table
+    tablePerms.put(Constants.METADATA_TABLE_ID, Collections.singleton(TablePermission.ALTER_TABLE));
+    
+    try {
+      initUser(rootuser);
+      zoo.putPersistentData(ZKUserPath + "/" + rootuser + ZKUserAuths, ZKSecurityTool.convertAuthorizations(Constants.NO_AUTHS), NodeExistsPolicy.FAIL);
+      zoo.putPersistentData(ZKUserPath + "/" + rootuser + ZKUserSysPerms, ZKSecurityTool.convertSystemPermissions(rootPerms), NodeExistsPolicy.FAIL);
+      zoo.putPersistentData(ZKUserPath + "/" + rootuser + ZKUserTablePerms, new byte[0], NodeExistsPolicy.FAIL);
+      for (Entry<String,Set<TablePermission>> entry : tablePerms.entrySet())
+        createTablePerm(rootuser, entry.getKey(), entry.getValue());
+    } catch (KeeperException e) {
+      log.error(e, e);
+      throw new RuntimeException(e);
+    } catch (InterruptedException e) {
+      log.error(e, e);
+      throw new RuntimeException(e);
+    }
+  }
+  
+  /**
+   * @param user
+   * @throws AccumuloSecurityException
+   */
+  public void initUser(String user) throws AccumuloSecurityException {
+    IZooReaderWriter zoo = ZooReaderWriter.getRetryingInstance();
+    try {
+      zoo.putPersistentData(ZKUserPath + "/" + user, new byte[0], NodeExistsPolicy.SKIP);
+      zoo.putPersistentData(ZKUserPath + "/" + user + ZKUserTablePerms, new byte[0], NodeExistsPolicy.SKIP);
+    } catch (KeeperException e) {
+      log.error(e, e);
+      throw new AccumuloSecurityException(user, SecurityErrorCode.CONNECTION_ERROR, e);
+    } catch (InterruptedException e) {
+      log.error(e, e);
+      throw new RuntimeException(e);
+    }
+  }
+  
+  /**
+   * Sets up a new table configuration for the provided user/table. No checking for existance is done here, it should be done before calling.
+   */
+  private void createTablePerm(String user, String table, Set<TablePermission> perms) throws KeeperException, InterruptedException {
+    synchronized (zooCache) {
+      zooCache.clear();
+      ZooReaderWriter.getRetryingInstance().putPersistentData(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + table,
+          ZKSecurityTool.convertTablePermissions(perms), NodeExistsPolicy.FAIL);
+    }
+  }
+  
+  @Override
+  public void changeAuthorizations(String user, Authorizations authorizations) throws AccumuloSecurityException {
+    try {
+      synchronized (zooCache) {
+        zooCache.clear();
+        ZooReaderWriter.getRetryingInstance().putPersistentData(ZKUserPath + "/" + user + ZKUserAuths, ZKSecurityTool.convertAuthorizations(authorizations),
+            NodeExistsPolicy.OVERWRITE);
+      }
+    } catch (KeeperException e) {
+      log.error(e, e);
+      throw new AccumuloSecurityException(user, SecurityErrorCode.CONNECTION_ERROR, e);
+    } catch (InterruptedException e) {
+      log.error(e, e);
+      throw new RuntimeException(e);
+    }
+  }
+  
+  @Override
+  public boolean hasSystemPermission(String user, SystemPermission permission) throws AccumuloSecurityException {
+    byte[] perms = zooCache.get(ZKUserPath + "/" + user + ZKUserSysPerms);
+    if (perms == null)
+      return false;
+    return ZKSecurityTool.convertSystemPermissions(perms).contains(permission);
+  }
+  
+  @Override
+  public void clearUserCache(String user) {
+    zooCache.clear(ZKUserPath + "/" + user);
+  }
+  
+  @Override
+  public void clearTableCache(String table) {
+    for (String user : zooCache.getChildren(ZKUserPath))
+      zooCache.clear(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + table);
+  }
+  
+  @Override
+  public void clearCache(String user, String tableId) {
+    zooCache.clear(ZKUserPath + "/" + user + ZKUserTablePerms + "/" + tableId);
+  }
+  
+  @Override
+  public boolean cachesToClear() {
+    return true;
+  }
+  
+  @Override
+  public void clearCache(String user, boolean auths, boolean system, Set<String> tables) {
+    if (auths)
+      zooCache.clear(ZKUserPath + "/" + user + ZKUserAuths);
+    
+    if (system)
+      zooCache.clear(ZKUserPath + "/" + user + ZKUserSysPerms);
+    
+    for (String table : tables)
+      clearCache(user, table);
+  }
+
+  @Override
+  public void dropUser(String user) throws AccumuloSecurityException {
+    try {
+      synchronized (zooCache) {
+        zooCache.clear(ZKUserPath + "/" + user);
+        ZooReaderWriter.getRetryingInstance().recursiveDelete(ZKUserPath + "/" + user + ZKUserAuths, NodeMissingPolicy.FAIL);
+        ZooReaderWriter.getRetryingInstance().recursiveDelete(ZKUserPath + "/" + user + ZKUserSysPerms, NodeMissingPolicy.FAIL);
+        ZooReaderWriter.getRetryingInstance().recursiveDelete(ZKUserPath + "/" + user + ZKUserTablePerms, NodeMissingPolicy.FAIL);
+      }
+    } catch (InterruptedException e) {
+      log.error(e, e);
+      throw new RuntimeException(e);
+    } catch (KeeperException e) {
+      log.error(e, e);
+      if (e.code().equals(KeeperException.Code.NONODE))
+        throw new AccumuloSecurityException(user, SecurityErrorCode.USER_DOESNT_EXIST, e);
+      throw new AccumuloSecurityException(user, SecurityErrorCode.CONNECTION_ERROR, e);
+      
+    }
+  }
+}

Propchange: accumulo/branches/ACCUMULO-259/server/src/main/java/org/apache/accumulo/server/security/ZKAuthorizor.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: accumulo/branches/ACCUMULO-259/server/src/main/java/org/apache/accumulo/server/security/ZKSecurityTool.java
URL: http://svn.apache.org/viewvc/accumulo/branches/ACCUMULO-259/server/src/main/java/org/apache/accumulo/server/security/ZKSecurityTool.java?rev=1346535&view=auto
==============================================================================
--- accumulo/branches/ACCUMULO-259/server/src/main/java/org/apache/accumulo/server/security/ZKSecurityTool.java (added)
+++ accumulo/branches/ACCUMULO-259/server/src/main/java/org/apache/accumulo/server/security/ZKSecurityTool.java Tue Jun  5 19:02:30 2012
@@ -0,0 +1,149 @@
+/**
+ * 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.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.accumulo.core.Constants;
+import org.apache.accumulo.core.client.AccumuloException;
+import org.apache.accumulo.core.security.Authorizations;
+import org.apache.accumulo.core.security.SystemPermission;
+import org.apache.accumulo.core.security.TablePermission;
+
+/**
+ * All the static too methods used for this class, so that we can separate out stuff that isn't using ZooKeeper. That way, we can check the synchronization
+ * model more easily, as we only need to check to make sure zooCache is cleared when things are written to ZooKeeper in methods that might use it. These
+ * won't, and so don't need to be checked.
+ */
+class ZKSecurityTool {
+  private static final int SALT_LENGTH = 8;
+  
+  // Generates a byte array salt of length SALT_LENGTH
+  private static byte[] generateSalt() {
+    final SecureRandom random = new SecureRandom();
+    byte[] salt = new byte[SALT_LENGTH];
+    random.nextBytes(salt);
+    return salt;
+  }
+  
+  private static byte[] hash(byte[] raw) throws NoSuchAlgorithmException {
+    MessageDigest md = MessageDigest.getInstance(Constants.PW_HASH_ALGORITHM);
+    md.update(raw);
+    return md.digest();
+  }
+  
+  public static boolean checkPass(byte[] password, byte[] zkData) {
+    if (zkData == null)
+      return false;
+    
+    byte[] salt = new byte[SALT_LENGTH];
+    System.arraycopy(zkData, 0, salt, 0, SALT_LENGTH);
+    byte[] passwordToCheck;
+    try {
+      passwordToCheck = convertPass(password, salt);
+    } catch (NoSuchAlgorithmException e) {
+      ZKAuthenticator.log.error("Count not create hashed password", e);
+      return false;
+    }
+    return java.util.Arrays.equals(passwordToCheck, zkData);
+  }
+  
+  public static byte[] createPass(byte[] password) throws AccumuloException {
+    byte[] salt = generateSalt();
+    try {
+      return convertPass(password, salt);
+    } catch (NoSuchAlgorithmException e) {
+      ZKAuthenticator.log.error("Count not create hashed password", e);
+      throw new AccumuloException("Count not create hashed password", e);
+    }
+  }
+  
+  private static byte[] convertPass(byte[] password, byte[] salt) throws NoSuchAlgorithmException {
+    byte[] plainSalt = new byte[password.length + SALT_LENGTH];
+    System.arraycopy(password, 0, plainSalt, 0, password.length);
+    System.arraycopy(salt, 0, plainSalt, password.length, SALT_LENGTH);
+    byte[] hashed = hash(plainSalt);
+    byte[] saltedHash = new byte[SALT_LENGTH + hashed.length];
+    System.arraycopy(salt, 0, saltedHash, 0, SALT_LENGTH);
+    System.arraycopy(hashed, 0, saltedHash, SALT_LENGTH, hashed.length);
+    return saltedHash; // contains salt+hash(password+salt)
+  }
+  
+  public static Authorizations convertAuthorizations(byte[] authorizations) {
+    return new Authorizations(authorizations);
+  }
+  
+  public static byte[] convertAuthorizations(Authorizations authorizations) {
+    return authorizations.getAuthorizationsArray();
+  }
+
+  public static byte[] convertSystemPermissions(Set<SystemPermission> systempermissions) {
+    ByteArrayOutputStream bytes = new ByteArrayOutputStream(systempermissions.size());
+    DataOutputStream out = new DataOutputStream(bytes);
+    try {
+      for (SystemPermission sp : systempermissions)
+        out.writeByte(sp.getId());
+    } catch (IOException e) {
+      ZKAuthenticator.log.error(e, e);
+      throw new RuntimeException(e); // this is impossible with ByteArrayOutputStream; crash hard if this happens
+    }
+    return bytes.toByteArray();
+  }
+  
+  public static Set<SystemPermission> convertSystemPermissions(byte[] systempermissions) {
+    ByteArrayInputStream bytes = new ByteArrayInputStream(systempermissions);
+    DataInputStream in = new DataInputStream(bytes);
+    Set<SystemPermission> toReturn = new HashSet<SystemPermission>();
+    try {
+      while (in.available() > 0)
+        toReturn.add(SystemPermission.getPermissionById(in.readByte()));
+    } catch (IOException e) {
+      ZKAuthenticator.log.error("User database is corrupt; error converting system permissions", e);
+      toReturn.clear();
+    }
+    return toReturn;
+  }
+  
+  public static byte[] convertTablePermissions(Set<TablePermission> tablepermissions) {
+    ByteArrayOutputStream bytes = new ByteArrayOutputStream(tablepermissions.size());
+    DataOutputStream out = new DataOutputStream(bytes);
+    try {
+      for (TablePermission tp : tablepermissions)
+        out.writeByte(tp.getId());
+    } catch (IOException e) {
+      ZKAuthenticator.log.error(e, e);
+      throw new RuntimeException(e); // this is impossible with ByteArrayOutputStream; crash hard if this happens
+    }
+    return bytes.toByteArray();
+  }
+  
+  public static Set<TablePermission> convertTablePermissions(byte[] tablepermissions) {
+    Set<TablePermission> toReturn = new HashSet<TablePermission>();
+    for (byte b : tablepermissions)
+      toReturn.add(TablePermission.getPermissionById(b));
+    return toReturn;
+  }
+}
\ No newline at end of file

Propchange: accumulo/branches/ACCUMULO-259/server/src/main/java/org/apache/accumulo/server/security/ZKSecurityTool.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain