Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,30 @@ public static void authenticate(String principal, AuthenticationToken token, Sta
String targetProp = props.getProperty("target");
boolean success = Boolean.parseBoolean(props.getProperty("valid"));

try (AccumuloClient client = env.createClient(principal, token)) {
String target;

String target;
if (targetProp.equals("table")) {
target = WalkingSecurity.get(state, env).getTabUserName();
} else {
target = WalkingSecurity.get(state, env).getSysUserName();
}
boolean exists = WalkingSecurity.get(state, env).userExists(target);
// Copy so if failed it doesn't mess with the password stored in state
byte[] password = Arrays.copyOf(WalkingSecurity.get(state, env).getUserPassword(target),
WalkingSecurity.get(state, env).getUserPassword(target).length);

if (targetProp.equals("table")) {
target = WalkingSecurity.get(state, env).getTabUserName();
} else {
target = WalkingSecurity.get(state, env).getSysUserName();
String actor = principal;
AuthenticationToken actorToken = token;
if (principal.equals(target) && !success) {
String rootUser = WalkingSecurity.get(state, env).getRootUserName();
AuthenticationToken rootToken = WalkingSecurity.get(state, env).getRootToken();
if (rootUser != null && rootToken != null) {
actor = rootUser;
actorToken = rootToken;
}
boolean exists = WalkingSecurity.get(state, env).userExists(target);
// Copy so if failed it doesn't mess with the password stored in state
byte[] password = Arrays.copyOf(WalkingSecurity.get(state, env).getUserPassword(target),
WalkingSecurity.get(state, env).getUserPassword(target).length);
}

try (AccumuloClient client = env.createClient(actor, actorToken)) {
boolean hasPermission =
client.securityOperations().hasSystemPermission(principal, SystemPermission.SYSTEM)
|| principal.equals(target);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public void setUp(State state, RandWalkEnv env) throws Exception {

WalkingSecurity.get(state, env).setTableName(secTableName);
WalkingSecurity.get(state, env).setNamespaceName(secNamespaceName);
state.set("rootUserPass", env.getToken());
WalkingSecurity.get(state, env).setRootUserCredentials(client.whoami(), env.getToken());

WalkingSecurity.get(state, env).setSysUserName(systemUserName);
WalkingSecurity.get(state, env).createUser(systemUserName, sysUserPass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;

import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.SortedSet;
Expand All @@ -40,6 +41,7 @@
import org.apache.accumulo.core.client.rfile.RFile;
import org.apache.accumulo.core.client.rfile.RFileWriter;
import org.apache.accumulo.core.client.security.SecurityErrorCode;
import org.apache.accumulo.core.client.summary.Summary;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Mutation;
import org.apache.accumulo.core.data.Value;
Expand Down Expand Up @@ -80,10 +82,11 @@ public void visit(State state, RandWalkEnv env, Properties props) throws Excepti
try {
canRead = secOps.hasTablePermission(tablePrincipal, tableName, TablePermission.READ);
} catch (AccumuloSecurityException ase) {
if (tableExists)
throw new AccumuloException("Table didn't exist when it should have: " + tableName,
ase);
return;
if (handlePermissionCheckException(ase, tableExists, tableName, state, env,
tablePrincipal)) {
return;
}
throw ase;
}
Authorizations auths = secOps.getUserAuthorizations(tablePrincipal);
boolean ambiguousZone =
Expand Down Expand Up @@ -171,10 +174,11 @@ public void visit(State state, RandWalkEnv env, Properties props) throws Excepti
try {
canWrite = secOps.hasTablePermission(tablePrincipal, tableName, TablePermission.WRITE);
} catch (AccumuloSecurityException ase) {
if (tableExists)
throw new AccumuloException("Table didn't exist when it should have: " + tableName,
ase);
return;
if (handlePermissionCheckException(ase, tableExists, tableName, state, env,
tablePrincipal)) {
return;
}
throw ase;
}

String key = WalkingSecurity.get(state, env).getLastKey() + "1";
Expand Down Expand Up @@ -223,6 +227,17 @@ public void visit(State state, RandWalkEnv env, Properties props) throws Excepti
}
break;
case BULK_IMPORT:
boolean canBulkImportBefore;
try {
canBulkImportBefore =
secOps.hasTablePermission(tablePrincipal, tableName, TablePermission.BULK_IMPORT);
} catch (AccumuloSecurityException ase) {
if (handlePermissionCheckException(ase, tableExists, tableName, state, env,
tablePrincipal)) {
return;
}
throw ase;
}
key = WalkingSecurity.get(state, env).getLastKey() + "1";
SortedSet<Key> keys = new TreeSet<>();
for (String s : WalkingSecurity.get(state, env).getAuthsArray()) {
Expand All @@ -247,35 +262,112 @@ public void visit(State state, RandWalkEnv env, Properties props) throws Excepti
return;
} catch (AccumuloSecurityException ae) {
if (ae.getSecurityErrorCode().equals(SecurityErrorCode.PERMISSION_DENIED)) {
if (secOps.hasTablePermission(tablePrincipal, tableName, TablePermission.BULK_IMPORT))
throw new AccumuloException(
"Bulk Import failed when it should have worked: " + tableName);
try {
boolean canBulkImportAfter = secOps.hasTablePermission(tablePrincipal, tableName,
TablePermission.BULK_IMPORT);
if (canBulkImportBefore && canBulkImportAfter) {
if (WalkingSecurity.get(state, env).inAmbiguousZone(tablePrincipal,
TablePermission.BULK_IMPORT)) {
log.info("Bulk import denied while permission propagating; ignoring.");
return;
}
throw new AccumuloException(
"Bulk Import failed when it should have worked: " + tableName, ae);
}
} catch (AccumuloSecurityException ase) {
if (handlePermissionCheckException(ase, tableExists, tableName, state, env,
tablePrincipal)) {
return;
}
throw ase;
}
return;
} else if (ae.getSecurityErrorCode().equals(SecurityErrorCode.BAD_CREDENTIALS)) {
if (WalkingSecurity.get(state, env).userPassTransient(client.whoami()))
return;
}
throw new AccumuloException("Unexpected exception!", ae);
} catch (AccumuloException ae) {
Throwable cause = ae.getCause();
if (cause instanceof AccumuloSecurityException) {
AccumuloSecurityException ase = (AccumuloSecurityException) cause;
if (ase.getSecurityErrorCode().equals(SecurityErrorCode.PERMISSION_DENIED)) {
try {
boolean canBulkImportAfter = secOps.hasTablePermission(tablePrincipal, tableName,
TablePermission.BULK_IMPORT);
if (canBulkImportBefore && canBulkImportAfter) {
if (WalkingSecurity.get(state, env).inAmbiguousZone(tablePrincipal,
TablePermission.BULK_IMPORT)) {
log.info("Bulk import denied while permission propagating; ignoring.");
return;
}
throw new AccumuloException(
"Bulk Import failed when it should have worked: " + tableName, ase);
}
} catch (AccumuloSecurityException inner) {
if (handlePermissionCheckException(inner, tableExists, tableName, state, env,
tablePrincipal)) {
return;
}
throw inner;
}
return;
} else if (ase.getSecurityErrorCode().equals(SecurityErrorCode.BAD_CREDENTIALS)) {
if (WalkingSecurity.get(state, env).userPassTransient(client.whoami())) {
return;
}
}
}
throw ae;
}
for (String s : WalkingSecurity.get(state, env).getAuthsArray())
WalkingSecurity.get(state, env).increaseAuthMap(s, 1);
fs.delete(dir, true);
fs.delete(fail, true);

if (!secOps.hasTablePermission(tablePrincipal, tableName, TablePermission.BULK_IMPORT))
throw new AccumuloException(
"Bulk Import succeeded when it should have failed: " + dir + " table " + tableName);
try {
boolean canBulkImportAfter =
secOps.hasTablePermission(tablePrincipal, tableName, TablePermission.BULK_IMPORT);
if (!canBulkImportAfter) {
if (!canBulkImportBefore) {
if (WalkingSecurity.get(state, env).inAmbiguousZone(tablePrincipal,
TablePermission.BULK_IMPORT)) {
log.info("Bulk import succeeded before permission fully propagated; ignoring.");
return;
}
throw new AccumuloException("Bulk Import succeeded when it should have failed: "
+ dir + " table " + tableName);
}
return;
}
if (!canBulkImportBefore && canBulkImportAfter) {
if (WalkingSecurity.get(state, env).inAmbiguousZone(tablePrincipal,
TablePermission.BULK_IMPORT)) {
log.info("Bulk import succeeded during permission propagation; ignoring.");
return;
}
throw new AccumuloException("Bulk Import succeeded when it should have failed: " + dir
+ " table " + tableName);
}
} catch (AccumuloSecurityException ase) {
if (handlePermissionCheckException(ase, tableExists, tableName, state, env,
tablePrincipal)) {
return;
}
throw ase;
}
break;
case ALTER_TABLE:
boolean tablePerm;
try {
tablePerm =
secOps.hasTablePermission(tablePrincipal, tableName, TablePermission.ALTER_TABLE);
} catch (AccumuloSecurityException ase) {
if (tableExists)
throw new AccumuloException("Table didn't exist when it should have: " + tableName,
ase);
return;
if (handlePermissionCheckException(ase, tableExists, tableName, state, env,
tablePrincipal)) {
return;
}
throw ase;
}
AlterTable.renameTable(client, state, env, tableName, tableName + "plus", tablePerm,
tableExists);
Expand All @@ -294,9 +386,68 @@ public void visit(State state, RandWalkEnv env, Properties props) throws Excepti
DropTable.dropTable(state, env, props);
break;

case GET_SUMMARIES:
throw new UnsupportedOperationException("GET_SUMMARIES not implemented");
case GET_SUMMARIES: {
boolean canSummarize;
try {
canSummarize =
secOps.hasTablePermission(tablePrincipal, tableName, TablePermission.GET_SUMMARIES);
} catch (AccumuloSecurityException ase) {
if (handlePermissionCheckException(ase, tableExists, tableName, state, env,
tablePrincipal)) {
return;
}
throw ase;
}
try {
List<Summary> summaries = tableOps.summaries(tableName).retrieve();
if (!canSummarize) {
throw new AccumuloException(
"Was able to retrieve summaries when permission should be denied: " + tableName);
}
if (summaries == null) {
throw new AccumuloException(
"Summary list was unexpectedly null for table " + tableName);
}
} catch (TableNotFoundException tnfe) {
if (tableExists)
throw new AccumuloException("Table didn't exist when it should have: " + tableName,
tnfe);
return;
} catch (AccumuloSecurityException ae) {
if (ae.getSecurityErrorCode().equals(SecurityErrorCode.PERMISSION_DENIED)) {
if (canSummarize)
throw new AccumuloException(
"Get summaries failed when it should have succeeded: " + tableName, ae);
return;
} else if (ae.getSecurityErrorCode().equals(SecurityErrorCode.BAD_CREDENTIALS)) {
if (WalkingSecurity.get(state, env).userPassTransient(client.whoami()))
return;
}
throw new AccumuloException("Unexpected security exception retrieving summaries", ae);
}
break;
}
}
}
}

private boolean handlePermissionCheckException(AccumuloSecurityException ase, boolean tableExists,
String tableName, State state, RandWalkEnv env, String principal)
throws AccumuloException, AccumuloSecurityException {
SecurityErrorCode code = ase.getSecurityErrorCode();
switch (code) {
case TABLE_DOESNT_EXIST:
case NAMESPACE_DOESNT_EXIST:
if (tableExists)
throw new AccumuloException("Table didn't exist when it should have: " + tableName, ase);
return true;
case BAD_CREDENTIALS:
if (WalkingSecurity.get(state, env).userPassTransient(principal))
return true;
break;
default:
break;
}
throw ase;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class WalkingSecurity {
private static final String tableName = "SecurityTableName";
private static final String namespaceName = "SecurityNamespaceName";
private static final String userName = "UserName";
private static final String rootUserName = "RootUserName";

private static final String userPass = "UserPass";
private static final String userExists = "UserExists";
Expand Down Expand Up @@ -152,8 +153,7 @@ private void setTabPerm(State state, String userName, TablePermission tp, String
log.debug((value ? "Gave" : "Took") + " the table permission " + tp.name()
+ (value ? " to" : " from") + " user " + userName);
state.set("Tab-" + userName + '-' + tp.name(), Boolean.toString(value));
if (tp.equals(TablePermission.READ) || tp.equals(TablePermission.WRITE))
state.set("Tab-" + userName + '-' + tp.name() + '-' + "time", System.currentTimeMillis());
state.set("Tab-" + userName + '-' + tp.name() + '-' + "time", System.currentTimeMillis());
}

public void revokeTablePermission(String user, String table, TablePermission permission)
Expand Down Expand Up @@ -200,6 +200,21 @@ public void setSysUserName(String name) {
state.set("system" + userName, name);
}

public void setRootUserCredentials(String name, AuthenticationToken token) {
state.set(rootUserName, name);
state.set("rootUserPass", token);
state.set(name + userPass + "time", System.currentTimeMillis());
}

public String getRootUserName() {
return state.getString(rootUserName);
}

public AuthenticationToken getRootToken() {
Object token = state.getOkIfAbsent("rootUserPass");
return token instanceof AuthenticationToken ? (AuthenticationToken) token : null;
}

public String getTableName() {
return state.getString(tableName);
}
Expand Down Expand Up @@ -271,13 +286,9 @@ public String[] getAuthsArray() {
}

public boolean inAmbiguousZone(String userName, TablePermission tp) {
if (tp.equals(TablePermission.READ) || tp.equals(TablePermission.WRITE)) {
Long setTime = state.getLong("Tab-" + userName + '-' + tp.name() + '-' + "time");
if (setTime == null)
throw new RuntimeException("Tab-" + userName + '-' + tp.name() + '-' + "time is null");
if (System.currentTimeMillis() < (setTime + 1000))
return true;
}
Long setTime = state.getLong("Tab-" + userName + '-' + tp.name() + '-' + "time");
if (setTime != null && System.currentTimeMillis() < (setTime + 1000))
return true;
return false;
}

Expand Down