Merge branch '1.7'

Project: http://git-wip-us.apache.org/repos/asf/accumulo/repo
Commit: http://git-wip-us.apache.org/repos/asf/accumulo/commit/4b88ec84
Tree: http://git-wip-us.apache.org/repos/asf/accumulo/tree/4b88ec84
Diff: http://git-wip-us.apache.org/repos/asf/accumulo/diff/4b88ec84

Branch: refs/heads/master
Commit: 4b88ec84421b56339d7a8fd5edaac588192b938f
Parents: 3c023a3 a7e45df
Author: Josh Elser <els...@apache.org>
Authored: Wed Feb 17 14:03:07 2016 -0500
Committer: Josh Elser <els...@apache.org>
Committed: Wed Feb 17 14:03:07 2016 -0500

----------------------------------------------------------------------
 .../server/security/handler/KerberosAuthenticator.java  |  9 +++++----
 .../org/apache/accumulo/test/functional/KerberosIT.java | 12 ++++++++++++
 2 files changed, 17 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/accumulo/blob/4b88ec84/test/src/main/java/org/apache/accumulo/test/functional/KerberosIT.java
----------------------------------------------------------------------
diff --cc test/src/main/java/org/apache/accumulo/test/functional/KerberosIT.java
index f7a151e,0000000..e636daa
mode 100644,000000..100644
--- a/test/src/main/java/org/apache/accumulo/test/functional/KerberosIT.java
+++ b/test/src/main/java/org/apache/accumulo/test/functional/KerberosIT.java
@@@ -1,644 -1,0 +1,656 @@@
 +/*
 + * 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.test.functional;
 +
 +import static org.junit.Assert.assertEquals;
 +import static org.junit.Assert.assertFalse;
 +import static org.junit.Assert.assertNotNull;
 +import static org.junit.Assert.assertTrue;
 +import static org.junit.Assert.fail;
 +
 +import java.io.File;
 +import java.lang.reflect.UndeclaredThrowableException;
 +import java.security.PrivilegedExceptionAction;
 +import java.util.Arrays;
 +import java.util.Collections;
 +import java.util.HashSet;
 +import java.util.Iterator;
 +import java.util.Map;
 +import java.util.Map.Entry;
 +import java.util.concurrent.TimeUnit;
 +
 +import org.apache.accumulo.cluster.ClusterUser;
 +import org.apache.accumulo.core.client.AccumuloException;
 +import org.apache.accumulo.core.client.AccumuloSecurityException;
 +import org.apache.accumulo.core.client.BatchScanner;
 +import org.apache.accumulo.core.client.BatchWriter;
 +import org.apache.accumulo.core.client.BatchWriterConfig;
 +import org.apache.accumulo.core.client.Connector;
 +import org.apache.accumulo.core.client.Scanner;
 +import org.apache.accumulo.core.client.TableExistsException;
 +import org.apache.accumulo.core.client.TableNotFoundException;
 +import org.apache.accumulo.core.client.admin.CompactionConfig;
 +import org.apache.accumulo.core.client.admin.DelegationTokenConfig;
 +import org.apache.accumulo.core.client.impl.AuthenticationTokenIdentifier;
 +import org.apache.accumulo.core.client.impl.DelegationTokenImpl;
 +import org.apache.accumulo.core.client.security.tokens.AuthenticationToken;
 +import org.apache.accumulo.core.client.security.tokens.KerberosToken;
 +import org.apache.accumulo.core.client.security.tokens.PasswordToken;
 +import org.apache.accumulo.core.conf.Property;
 +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.metadata.MetadataTable;
 +import org.apache.accumulo.core.metadata.RootTable;
 +import org.apache.accumulo.core.security.Authorizations;
 +import org.apache.accumulo.core.security.ColumnVisibility;
 +import org.apache.accumulo.core.security.SystemPermission;
 +import org.apache.accumulo.core.security.TablePermission;
 +import org.apache.accumulo.harness.AccumuloITBase;
 +import org.apache.accumulo.harness.MiniClusterConfigurationCallback;
 +import org.apache.accumulo.harness.MiniClusterHarness;
 +import org.apache.accumulo.harness.TestingKdc;
 +import org.apache.accumulo.minicluster.ServerType;
 +import org.apache.accumulo.minicluster.impl.MiniAccumuloClusterImpl;
 +import org.apache.accumulo.minicluster.impl.MiniAccumuloConfigImpl;
 +import org.apache.hadoop.conf.Configuration;
 +import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
 +import org.apache.hadoop.minikdc.MiniKdc;
 +import org.apache.hadoop.security.UserGroupInformation;
 +import org.junit.After;
 +import org.junit.AfterClass;
 +import org.junit.Before;
 +import org.junit.BeforeClass;
 +import org.junit.Test;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +
 +import com.google.common.collect.Iterables;
 +import com.google.common.collect.Sets;
 +
 +/**
 + * MAC test which uses {@link MiniKdc} to simulate ta secure environment. Can 
be used as a sanity check for Kerberos/SASL testing.
 + */
 +public class KerberosIT extends AccumuloITBase {
 +  private static final Logger log = LoggerFactory.getLogger(KerberosIT.class);
 +
 +  private static TestingKdc kdc;
 +  private static String krbEnabledForITs = null;
 +  private static ClusterUser rootUser;
 +
 +  @BeforeClass
 +  public static void startKdc() throws Exception {
 +    kdc = new TestingKdc();
 +    kdc.start();
 +    krbEnabledForITs = 
System.getProperty(MiniClusterHarness.USE_KERBEROS_FOR_IT_OPTION);
 +    if (null == krbEnabledForITs || !Boolean.parseBoolean(krbEnabledForITs)) {
 +      System.setProperty(MiniClusterHarness.USE_KERBEROS_FOR_IT_OPTION, 
"true");
 +    }
 +    rootUser = kdc.getRootUser();
 +  }
 +
 +  @AfterClass
 +  public static void stopKdc() throws Exception {
 +    if (null != kdc) {
 +      kdc.stop();
 +    }
 +    if (null != krbEnabledForITs) {
 +      System.setProperty(MiniClusterHarness.USE_KERBEROS_FOR_IT_OPTION, 
krbEnabledForITs);
 +    }
 +    UserGroupInformation.setConfiguration(new Configuration(false));
 +  }
 +
 +  @Override
 +  public int defaultTimeoutSeconds() {
 +    return 60 * 5;
 +  }
 +
 +  private MiniAccumuloClusterImpl mac;
 +
 +  @Before
 +  public void startMac() throws Exception {
 +    MiniClusterHarness harness = new MiniClusterHarness();
 +    mac = harness.create(this, new PasswordToken("unused"), kdc, new 
MiniClusterConfigurationCallback() {
 +
 +      @Override
 +      public void configureMiniCluster(MiniAccumuloConfigImpl cfg, 
Configuration coreSite) {
 +        Map<String,String> site = cfg.getSiteConfig();
 +        site.put(Property.INSTANCE_ZK_TIMEOUT.getKey(), "15s");
 +        cfg.setSiteConfig(site);
 +      }
 +
 +    });
 +
 +    mac.getConfig().setNumTservers(1);
 +    mac.start();
 +    // Enabled kerberos auth
 +    Configuration conf = new Configuration(false);
 +    conf.set(CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION, 
"kerberos");
 +    UserGroupInformation.setConfiguration(conf);
 +  }
 +
 +  @After
 +  public void stopMac() throws Exception {
 +    if (null != mac) {
 +      mac.stop();
 +    }
 +  }
 +
 +  @Test
 +  public void testAdminUser() throws Exception {
 +    // Login as the client (provided to `accumulo init` as the "root" user)
 +    UserGroupInformation ugi = 
UserGroupInformation.loginUserFromKeytabAndReturnUGI(rootUser.getPrincipal(), 
rootUser.getKeytab().getAbsolutePath());
 +    ugi.doAs(new PrivilegedExceptionAction<Void>() {
 +      @Override
 +      public Void run() throws Exception {
 +        final Connector conn = mac.getConnector(rootUser.getPrincipal(), new 
KerberosToken());
 +
 +        // The "root" user should have all system permissions
 +        for (SystemPermission perm : SystemPermission.values()) {
 +          assertTrue("Expected user to have permission: " + perm, 
conn.securityOperations().hasSystemPermission(conn.whoami(), perm));
 +        }
 +
 +        // and the ability to modify the root and metadata tables
 +        for (String table : Arrays.asList(RootTable.NAME, 
MetadataTable.NAME)) {
 +          
assertTrue(conn.securityOperations().hasTablePermission(conn.whoami(), table, 
TablePermission.ALTER_TABLE));
 +        }
 +        return null;
 +      }
 +    });
 +  }
 +
 +  @Test
 +  public void testNewUser() throws Exception {
 +    String newUser = testName.getMethodName();
 +    final File newUserKeytab = new File(kdc.getKeytabDir(), newUser + 
".keytab");
 +    if (newUserKeytab.exists() && !newUserKeytab.delete()) {
 +      log.warn("Unable to delete {}", newUserKeytab);
 +    }
 +
 +    // Create a new user
 +    kdc.createPrincipal(newUserKeytab, newUser);
 +
 +    final String newQualifiedUser = kdc.qualifyUser(newUser);
 +    final HashSet<String> users = Sets.newHashSet(rootUser.getPrincipal());
 +
 +    // Login as the "root" user
 +    UserGroupInformation ugi = 
UserGroupInformation.loginUserFromKeytabAndReturnUGI(rootUser.getPrincipal(), 
rootUser.getKeytab().getAbsolutePath());
 +    log.info("Logged in as {}", rootUser.getPrincipal());
 +
 +    ugi.doAs(new PrivilegedExceptionAction<Void>() {
 +      @Override
 +      public Void run() throws Exception {
 +        Connector conn = mac.getConnector(rootUser.getPrincipal(), new 
KerberosToken());
 +        log.info("Created connector as {}", rootUser.getPrincipal());
 +        assertEquals(rootUser.getPrincipal(), conn.whoami());
 +
 +        // Make sure the system user doesn't exist -- this will force some 
RPC to happen server-side
 +        createTableWithDataAndCompact(conn);
 +
 +        assertEquals(users, conn.securityOperations().listLocalUsers());
 +
 +        return null;
 +      }
 +    });
 +    // Switch to a new user
 +    ugi = 
UserGroupInformation.loginUserFromKeytabAndReturnUGI(newQualifiedUser, 
newUserKeytab.getAbsolutePath());
 +    log.info("Logged in as {}", newQualifiedUser);
 +    ugi.doAs(new PrivilegedExceptionAction<Void>() {
 +      @Override
 +      public Void run() throws Exception {
 +        Connector conn = mac.getConnector(newQualifiedUser, new 
KerberosToken());
 +        log.info("Created connector as {}", newQualifiedUser);
 +        assertEquals(newQualifiedUser, conn.whoami());
 +
 +        // The new user should have no system permissions
 +        for (SystemPermission perm : SystemPermission.values()) {
 +          
assertFalse(conn.securityOperations().hasSystemPermission(newQualifiedUser, 
perm));
 +        }
 +
 +        users.add(newQualifiedUser);
 +
 +        // Same users as before, plus the new user we just created
 +        assertEquals(users, conn.securityOperations().listLocalUsers());
 +        return null;
 +      }
 +
 +    });
 +  }
 +
 +  @Test
 +  public void testUserPrivilegesThroughGrant() throws Exception {
 +    String user1 = testName.getMethodName();
 +    final File user1Keytab = new File(kdc.getKeytabDir(), user1 + ".keytab");
 +    if (user1Keytab.exists() && !user1Keytab.delete()) {
 +      log.warn("Unable to delete {}", user1Keytab);
 +    }
 +
 +    // Create some new users
 +    kdc.createPrincipal(user1Keytab, user1);
 +
 +    final String qualifiedUser1 = kdc.qualifyUser(user1);
 +
 +    // Log in as user1
 +    UserGroupInformation ugi = 
UserGroupInformation.loginUserFromKeytabAndReturnUGI(user1, 
user1Keytab.getAbsolutePath());
 +    log.info("Logged in as {}", user1);
 +    ugi.doAs(new PrivilegedExceptionAction<Void>() {
 +      @Override
 +      public Void run() throws Exception {
 +        // Indirectly creates this user when we use it
 +        Connector conn = mac.getConnector(qualifiedUser1, new 
KerberosToken());
 +        log.info("Created connector as {}", qualifiedUser1);
 +
 +        // The new user should have no system permissions
 +        for (SystemPermission perm : SystemPermission.values()) {
 +          
assertFalse(conn.securityOperations().hasSystemPermission(qualifiedUser1, 
perm));
 +        }
 +
 +        return null;
 +      }
 +    });
 +
 +    ugi = 
UserGroupInformation.loginUserFromKeytabAndReturnUGI(rootUser.getPrincipal(), 
rootUser.getKeytab().getAbsolutePath());
 +    ugi.doAs(new PrivilegedExceptionAction<Void>() {
 +      @Override
 +      public Void run() throws Exception {
 +        Connector conn = mac.getConnector(rootUser.getPrincipal(), new 
KerberosToken());
 +        conn.securityOperations().grantSystemPermission(qualifiedUser1, 
SystemPermission.CREATE_TABLE);
 +        return null;
 +      }
 +    });
 +
 +    // Switch back to the original user
 +    ugi = UserGroupInformation.loginUserFromKeytabAndReturnUGI(user1, 
user1Keytab.getAbsolutePath());
 +    ugi.doAs(new PrivilegedExceptionAction<Void>() {
 +      @Override
 +      public Void run() throws Exception {
 +        Connector conn = mac.getConnector(qualifiedUser1, new 
KerberosToken());
 +
 +        // Shouldn't throw an exception since we granted the create table 
permission
 +        final String table = testName.getMethodName() + "_user_table";
 +        conn.tableOperations().create(table);
 +
 +        // Make sure we can actually use the table we made
 +        BatchWriter bw = conn.createBatchWriter(table, new 
BatchWriterConfig());
 +        Mutation m = new Mutation("a");
 +        m.put("b", "c", "d");
 +        bw.addMutation(m);
 +        bw.close();
 +
 +        conn.tableOperations().compact(table, new 
CompactionConfig().setWait(true).setFlush(true));
 +        return null;
 +      }
 +    });
 +  }
 +
 +  @Test
 +  public void testUserPrivilegesForTable() throws Exception {
 +    String user1 = testName.getMethodName();
 +    final File user1Keytab = new File(kdc.getKeytabDir(), user1 + ".keytab");
 +    if (user1Keytab.exists() && !user1Keytab.delete()) {
 +      log.warn("Unable to delete {}", user1Keytab);
 +    }
 +
 +    // Create some new users -- cannot contain realm
 +    kdc.createPrincipal(user1Keytab, user1);
 +
 +    final String qualifiedUser1 = kdc.qualifyUser(user1);
 +
 +    // Log in as user1
 +    UserGroupInformation ugi = 
UserGroupInformation.loginUserFromKeytabAndReturnUGI(qualifiedUser1, 
user1Keytab.getAbsolutePath());
 +    log.info("Logged in as {}", user1);
 +    ugi.doAs(new PrivilegedExceptionAction<Void>() {
 +      @Override
 +      public Void run() throws Exception {
 +        // Indirectly creates this user when we use it
 +        Connector conn = mac.getConnector(qualifiedUser1, new 
KerberosToken());
 +        log.info("Created connector as {}", qualifiedUser1);
 +
 +        // The new user should have no system permissions
 +        for (SystemPermission perm : SystemPermission.values()) {
 +          
assertFalse(conn.securityOperations().hasSystemPermission(qualifiedUser1, 
perm));
 +        }
 +        return null;
 +      }
 +
 +    });
 +
 +    final String table = testName.getMethodName() + "_user_table";
 +    final String viz = "viz";
 +
 +    ugi = 
UserGroupInformation.loginUserFromKeytabAndReturnUGI(rootUser.getPrincipal(), 
rootUser.getKeytab().getAbsolutePath());
 +
 +    ugi.doAs(new PrivilegedExceptionAction<Void>() {
 +      @Override
 +      public Void run() throws Exception {
 +        Connector conn = mac.getConnector(rootUser.getPrincipal(), new 
KerberosToken());
 +        conn.tableOperations().create(table);
 +        // Give our unprivileged user permission on the table we made for them
 +        conn.securityOperations().grantTablePermission(qualifiedUser1, table, 
TablePermission.READ);
 +        conn.securityOperations().grantTablePermission(qualifiedUser1, table, 
TablePermission.WRITE);
 +        conn.securityOperations().grantTablePermission(qualifiedUser1, table, 
TablePermission.ALTER_TABLE);
 +        conn.securityOperations().grantTablePermission(qualifiedUser1, table, 
TablePermission.DROP_TABLE);
 +        conn.securityOperations().changeUserAuthorizations(qualifiedUser1, 
new Authorizations(viz));
 +        return null;
 +      }
 +    });
 +
 +    // Switch back to the original user
 +    ugi = 
UserGroupInformation.loginUserFromKeytabAndReturnUGI(qualifiedUser1, 
user1Keytab.getAbsolutePath());
 +    ugi.doAs(new PrivilegedExceptionAction<Void>() {
 +      @Override
 +      public Void run() throws Exception {
 +        Connector conn = mac.getConnector(qualifiedUser1, new 
KerberosToken());
 +
 +        // Make sure we can actually use the table we made
 +
 +        // Write data
 +        final long ts = 1000l;
 +        BatchWriter bw = conn.createBatchWriter(table, new 
BatchWriterConfig());
 +        Mutation m = new Mutation("a");
 +        m.put("b", "c", new ColumnVisibility(viz.getBytes()), ts, "d");
 +        bw.addMutation(m);
 +        bw.close();
 +
 +        // Compact
 +        conn.tableOperations().compact(table, new 
CompactionConfig().setWait(true).setFlush(true));
 +
 +        // Alter
 +        conn.tableOperations().setProperty(table, 
Property.TABLE_BLOOM_ENABLED.getKey(), "true");
 +
 +        // Read (and proper authorizations)
 +        Scanner s = conn.createScanner(table, new Authorizations(viz));
 +        Iterator<Entry<Key,Value>> iter = s.iterator();
 +        assertTrue("No results from iterator", iter.hasNext());
 +        Entry<Key,Value> entry = iter.next();
 +        assertEquals(new Key("a", "b", "c", viz, ts), entry.getKey());
 +        assertEquals(new Value("d".getBytes()), entry.getValue());
 +        assertFalse("Had more results from iterator", iter.hasNext());
 +        return null;
 +      }
 +    });
 +  }
 +
 +  @Test
 +  public void testDelegationToken() throws Exception {
 +    final String tableName = getUniqueNames(1)[0];
 +
 +    // Login as the "root" user
 +    UserGroupInformation root = 
UserGroupInformation.loginUserFromKeytabAndReturnUGI(rootUser.getPrincipal(), 
rootUser.getKeytab().getAbsolutePath());
 +    log.info("Logged in as {}", rootUser.getPrincipal());
 +
 +    final int numRows = 100, numColumns = 10;
 +
 +    // As the "root" user, open up the connection and get a delegation token
 +    final AuthenticationToken delegationToken = root.doAs(new 
PrivilegedExceptionAction<AuthenticationToken>() {
 +      @Override
 +      public AuthenticationToken run() throws Exception {
 +        Connector conn = mac.getConnector(rootUser.getPrincipal(), new 
KerberosToken());
 +        log.info("Created connector as {}", rootUser.getPrincipal());
 +        assertEquals(rootUser.getPrincipal(), conn.whoami());
 +
 +        conn.tableOperations().create(tableName);
 +        BatchWriter bw = conn.createBatchWriter(tableName, new 
BatchWriterConfig());
 +        for (int r = 0; r < numRows; r++) {
 +          Mutation m = new Mutation(Integer.toString(r));
 +          for (int c = 0; c < numColumns; c++) {
 +            String col = Integer.toString(c);
 +            m.put(col, col, col);
 +          }
 +          bw.addMutation(m);
 +        }
 +        bw.close();
 +
 +        return conn.securityOperations().getDelegationToken(new 
DelegationTokenConfig());
 +      }
 +    });
 +
 +    // The above login with keytab doesn't have a way to logout, so make a 
fake user that won't have krb credentials
 +    UserGroupInformation userWithoutPrivs = 
UserGroupInformation.createUserForTesting("fake_user", new String[0]);
 +    int recordsSeen = userWithoutPrivs.doAs(new 
PrivilegedExceptionAction<Integer>() {
 +      @Override
 +      public Integer run() throws Exception {
 +        Connector conn = mac.getConnector(rootUser.getPrincipal(), 
delegationToken);
 +
 +        BatchScanner bs = conn.createBatchScanner(tableName, 
Authorizations.EMPTY, 2);
 +        bs.setRanges(Collections.singleton(new Range()));
 +        int recordsSeen = Iterables.size(bs);
 +        bs.close();
 +        return recordsSeen;
 +      }
 +    });
 +
 +    assertEquals(numRows * numColumns, recordsSeen);
 +  }
 +
 +  @Test
 +  public void testDelegationTokenAsDifferentUser() throws Exception {
 +    // Login as the "root" user
 +    UserGroupInformation ugi = 
UserGroupInformation.loginUserFromKeytabAndReturnUGI(rootUser.getPrincipal(), 
rootUser.getKeytab().getAbsolutePath());
 +    log.info("Logged in as {}", rootUser.getPrincipal());
 +
 +    final AuthenticationToken delegationToken;
 +    try {
 +      delegationToken = ugi.doAs(new 
PrivilegedExceptionAction<AuthenticationToken>() {
 +        @Override
 +        public AuthenticationToken run() throws Exception {
 +          // As the "root" user, open up the connection and get a delegation 
token
 +          Connector conn = mac.getConnector(rootUser.getPrincipal(), new 
KerberosToken());
 +          log.info("Created connector as {}", rootUser.getPrincipal());
 +          assertEquals(rootUser.getPrincipal(), conn.whoami());
 +          return conn.securityOperations().getDelegationToken(new 
DelegationTokenConfig());
 +        }
 +      });
 +    } catch (UndeclaredThrowableException ex) {
 +      throw ex;
 +    }
 +
 +    // make a fake user that won't have krb credentials
 +    UserGroupInformation userWithoutPrivs = 
UserGroupInformation.createUserForTesting("fake_user", new String[0]);
 +    try {
 +      // Use the delegation token to try to log in as a different user
 +      userWithoutPrivs.doAs(new PrivilegedExceptionAction<Void>() {
 +        @Override
 +        public Void run() throws Exception {
 +          mac.getConnector("some_other_user", delegationToken);
 +          return null;
 +        }
 +      });
 +      fail("Using a delegation token as a different user should throw an 
exception");
 +    } catch (UndeclaredThrowableException e) {
 +      Throwable cause = e.getCause();
 +      assertNotNull(cause);
 +      // We should get an AccumuloSecurityException from trying to use a 
delegation token for the wrong user
 +      assertTrue("Expected cause to be AccumuloSecurityException, but was " + 
cause.getClass(), cause instanceof AccumuloSecurityException);
 +    }
 +  }
 +
 +  @Test
 +  public void testGetDelegationTokenDenied() throws Exception {
 +    String newUser = testName.getMethodName();
 +    final File newUserKeytab = new File(kdc.getKeytabDir(), newUser + 
".keytab");
 +    if (newUserKeytab.exists() && !newUserKeytab.delete()) {
 +      log.warn("Unable to delete {}", newUserKeytab);
 +    }
 +
 +    // Create a new user
 +    kdc.createPrincipal(newUserKeytab, newUser);
 +
 +    final String qualifiedNewUser = kdc.qualifyUser(newUser);
 +
 +    // Login as a normal user
 +    UserGroupInformation ugi = 
UserGroupInformation.loginUserFromKeytabAndReturnUGI(qualifiedNewUser, 
newUserKeytab.getAbsolutePath());
 +    try {
 +      ugi.doAs(new PrivilegedExceptionAction<Void>() {
 +        @Override
 +        public Void run() throws Exception {
 +          // As the "root" user, open up the connection and get a delegation 
token
 +          Connector conn = mac.getConnector(qualifiedNewUser, new 
KerberosToken());
 +          log.info("Created connector as {}", qualifiedNewUser);
 +          assertEquals(qualifiedNewUser, conn.whoami());
 +
 +          conn.securityOperations().getDelegationToken(new 
DelegationTokenConfig());
 +          return null;
 +        }
 +      });
 +    } catch (UndeclaredThrowableException ex) {
 +      assertTrue(ex.getCause() instanceof AccumuloSecurityException);
 +    }
 +  }
 +
 +  @Test
 +  public void testRestartedMasterReusesSecretKey() throws Exception {
 +    // Login as the "root" user
 +    UserGroupInformation root = 
UserGroupInformation.loginUserFromKeytabAndReturnUGI(rootUser.getPrincipal(), 
rootUser.getKeytab().getAbsolutePath());
 +    log.info("Logged in as {}", rootUser.getPrincipal());
 +
 +    // As the "root" user, open up the connection and get a delegation token
 +    final AuthenticationToken delegationToken1 = root.doAs(new 
PrivilegedExceptionAction<AuthenticationToken>() {
 +      @Override
 +      public AuthenticationToken run() throws Exception {
 +        Connector conn = mac.getConnector(rootUser.getPrincipal(), new 
KerberosToken());
 +        log.info("Created connector as {}", rootUser.getPrincipal());
 +        assertEquals(rootUser.getPrincipal(), conn.whoami());
 +
 +        AuthenticationToken token = 
conn.securityOperations().getDelegationToken(new DelegationTokenConfig());
 +
 +        assertTrue("Could not get tables with delegation token", 
mac.getConnector(rootUser.getPrincipal(), 
token).tableOperations().list().size() > 0);
 +
 +        return token;
 +      }
 +    });
 +
 +    log.info("Stopping master");
 +    mac.getClusterControl().stop(ServerType.MASTER);
 +    Thread.sleep(5000);
 +    log.info("Restarting master");
 +    mac.getClusterControl().start(ServerType.MASTER);
 +
 +    // Make sure our original token is still good
 +    root.doAs(new PrivilegedExceptionAction<Void>() {
 +      @Override
 +      public Void run() throws Exception {
 +        Connector conn = mac.getConnector(rootUser.getPrincipal(), 
delegationToken1);
 +
 +        assertTrue("Could not get tables with delegation token", 
conn.tableOperations().list().size() > 0);
 +
 +        return null;
 +      }
 +    });
 +
 +    // Get a new token, so we can compare the keyId on the second to the first
 +    final AuthenticationToken delegationToken2 = root.doAs(new 
PrivilegedExceptionAction<AuthenticationToken>() {
 +      @Override
 +      public AuthenticationToken run() throws Exception {
 +        Connector conn = mac.getConnector(rootUser.getPrincipal(), new 
KerberosToken());
 +        log.info("Created connector as {}", rootUser.getPrincipal());
 +        assertEquals(rootUser.getPrincipal(), conn.whoami());
 +
 +        AuthenticationToken token = 
conn.securityOperations().getDelegationToken(new DelegationTokenConfig());
 +
 +        assertTrue("Could not get tables with delegation token", 
mac.getConnector(rootUser.getPrincipal(), 
token).tableOperations().list().size() > 0);
 +
 +        return token;
 +      }
 +    });
 +
 +    // A restarted master should reuse the same secret key after a restart if 
the secret key hasn't expired (1day by default)
 +    DelegationTokenImpl dt1 = (DelegationTokenImpl) delegationToken1;
 +    DelegationTokenImpl dt2 = (DelegationTokenImpl) delegationToken2;
 +    assertEquals(dt1.getIdentifier().getKeyId(), 
dt2.getIdentifier().getKeyId());
 +  }
 +
 +  @Test(expected = AccumuloException.class)
 +  public void testDelegationTokenWithInvalidLifetime() throws Throwable {
 +    // Login as the "root" user
 +    UserGroupInformation root = 
UserGroupInformation.loginUserFromKeytabAndReturnUGI(rootUser.getPrincipal(), 
rootUser.getKeytab().getAbsolutePath());
 +    log.info("Logged in as {}", rootUser.getPrincipal());
 +
 +    // As the "root" user, open up the connection and get a delegation token
 +    try {
 +      root.doAs(new PrivilegedExceptionAction<AuthenticationToken>() {
 +        @Override
 +        public AuthenticationToken run() throws Exception {
 +          Connector conn = mac.getConnector(rootUser.getPrincipal(), new 
KerberosToken());
 +          log.info("Created connector as {}", rootUser.getPrincipal());
 +          assertEquals(rootUser.getPrincipal(), conn.whoami());
 +
 +          // Should fail
 +          return conn.securityOperations().getDelegationToken(new 
DelegationTokenConfig().setTokenLifetime(Long.MAX_VALUE, 
TimeUnit.MILLISECONDS));
 +        }
 +      });
 +    } catch (UndeclaredThrowableException e) {
 +      Throwable cause = e.getCause();
 +      if (null != cause) {
 +        throw cause;
 +      } else {
 +        throw e;
 +      }
 +    }
 +  }
 +
 +  @Test
 +  public void testDelegationTokenWithReducedLifetime() throws Throwable {
 +    // Login as the "root" user
 +    UserGroupInformation root = 
UserGroupInformation.loginUserFromKeytabAndReturnUGI(rootUser.getPrincipal(), 
rootUser.getKeytab().getAbsolutePath());
 +    log.info("Logged in as {}", rootUser.getPrincipal());
 +
 +    // As the "root" user, open up the connection and get a delegation token
 +    final AuthenticationToken dt = root.doAs(new 
PrivilegedExceptionAction<AuthenticationToken>() {
 +      @Override
 +      public AuthenticationToken run() throws Exception {
 +        Connector conn = mac.getConnector(rootUser.getPrincipal(), new 
KerberosToken());
 +        log.info("Created connector as {}", rootUser.getPrincipal());
 +        assertEquals(rootUser.getPrincipal(), conn.whoami());
 +
 +        return conn.securityOperations().getDelegationToken(new 
DelegationTokenConfig().setTokenLifetime(5, TimeUnit.MINUTES));
 +      }
 +    });
 +
 +    AuthenticationTokenIdentifier identifier = ((DelegationTokenImpl) 
dt).getIdentifier();
 +    assertTrue("Expected identifier to expire in no more than 5 minutes: " + 
identifier,
 +        identifier.getExpirationDate() - identifier.getIssueDate() <= (5 * 60 
* 1000));
 +  }
 +
++  @Test(expected = AccumuloSecurityException.class)
++  public void testRootUserHasIrrevocablePermissions() throws Exception {
++    // Login as the client (provided to `accumulo init` as the "root" user)
++    UserGroupInformation.loginUserFromKeytab(rootUser.getPrincipal(), 
rootUser.getKeytab().getAbsolutePath());
++
++    final Connector conn = mac.getConnector(rootUser.getPrincipal(), new 
KerberosToken());
++
++    // The server-side implementation should prevent the revocation of the 
'root' user's systems permissions
++    // because once they're gone, it's possible that they could never be 
restored.
++    conn.securityOperations().revokeSystemPermission(rootUser.getPrincipal(), 
SystemPermission.GRANT);
++  }
++
 +  /**
 +   * Creates a table, adds a record to it, and then compacts the table. A 
simple way to make sure that the system user exists (since the master does an 
RPC to
 +   * the tserver which will create the system user if it doesn't already 
exist).
 +   */
 +  private void createTableWithDataAndCompact(Connector conn) throws 
TableNotFoundException, AccumuloSecurityException, AccumuloException, 
TableExistsException {
 +    final String table = testName.getMethodName() + "_table";
 +    conn.tableOperations().create(table);
 +    BatchWriter bw = conn.createBatchWriter(table, new BatchWriterConfig());
 +    Mutation m = new Mutation("a");
 +    m.put("b", "c", "d");
 +    bw.addMutation(m);
 +    bw.close();
 +    conn.tableOperations().compact(table, new 
CompactionConfig().setFlush(true).setWait(true));
 +  }
 +}

Reply via email to