CmdTool: Added options for editing roles and listing users/roles
Two new commands to the command line tool have been added * esusers list: Allows to list all users with their roles or only a single one * esusers roles: Allows to add or remove roles Roles have been configured as to only consist of lowercase alphanumeric characters. Original commit: elastic/x-pack-elasticsearch@6fcb4c56e4
This commit is contained in:
parent
75cf637fed
commit
3ee4d527f3
|
@ -6,10 +6,15 @@
|
|||
package org.elasticsearch.shield.authc.esusers.tool;
|
||||
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.base.Joiner;
|
||||
import org.elasticsearch.common.cli.CliTool;
|
||||
import org.elasticsearch.common.cli.CliToolConfig;
|
||||
import org.elasticsearch.common.cli.Terminal;
|
||||
import org.elasticsearch.common.cli.commons.CommandLine;
|
||||
import org.elasticsearch.common.collect.Lists;
|
||||
import org.elasticsearch.common.collect.Maps;
|
||||
import org.elasticsearch.common.collect.ObjectArrays;
|
||||
import org.elasticsearch.common.collect.Sets;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.shield.authc.esusers.FileUserPasswdStore;
|
||||
|
@ -17,10 +22,8 @@ import org.elasticsearch.shield.authc.esusers.FileUserRolesStore;
|
|||
import org.elasticsearch.shield.authc.support.Hasher;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.elasticsearch.common.cli.CliToolConfig.Builder.cmd;
|
||||
import static org.elasticsearch.common.cli.CliToolConfig.Builder.option;
|
||||
|
@ -31,7 +34,7 @@ import static org.elasticsearch.common.cli.CliToolConfig.Builder.option;
|
|||
public class ESUsersTool extends CliTool {
|
||||
|
||||
private static final CliToolConfig CONFIG = CliToolConfig.config("esusers", ESUsersTool.class)
|
||||
.cmds(Useradd.CMD, Userdel.CMD, Passwd.CMD)
|
||||
.cmds(Useradd.CMD, Userdel.CMD, Passwd.CMD, Roles.CMD, ListUsersAndRoles.CMD)
|
||||
.build();
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
@ -49,9 +52,16 @@ public class ESUsersTool extends CliTool {
|
|||
@Override
|
||||
protected Command parse(String cmdName, CommandLine cli) throws Exception {
|
||||
switch (cmdName.toLowerCase(Locale.ROOT)) {
|
||||
case Useradd.NAME: return Useradd.parse(terminal, cli);
|
||||
case Userdel.NAME: return Userdel.parse(terminal, cli);
|
||||
case Passwd.NAME: return Passwd.parse(terminal, cli);
|
||||
case Useradd.NAME:
|
||||
return Useradd.parse(terminal, cli);
|
||||
case Userdel.NAME:
|
||||
return Userdel.parse(terminal, cli);
|
||||
case Passwd.NAME:
|
||||
return Passwd.parse(terminal, cli);
|
||||
case ListUsersAndRoles.NAME:
|
||||
return ListUsersAndRoles.parse(terminal, cli);
|
||||
case Roles.NAME:
|
||||
return Roles.parse(terminal, cli);
|
||||
default:
|
||||
assert false : "should never get here, if the user enters an unknown command, an error message should be shown before parse is called";
|
||||
return null;
|
||||
|
@ -112,7 +122,7 @@ public class ESUsersTool extends CliTool {
|
|||
users = new HashMap<>();
|
||||
}
|
||||
if (users.containsKey(username)) {
|
||||
terminal.println("User [{}] already exists", username);
|
||||
terminal.println("User [%s] already exists", username);
|
||||
return ExitStatus.CODE_ERROR;
|
||||
}
|
||||
Hasher hasher = Hasher.HTPASSWD;
|
||||
|
@ -229,7 +239,7 @@ public class ESUsersTool extends CliTool {
|
|||
users = new HashMap<>();
|
||||
}
|
||||
if (!users.containsKey(username)) {
|
||||
terminal.println("User [{}] doesn't exist", username);
|
||||
terminal.println("User [%s] doesn't exist", username);
|
||||
return ExitStatus.NO_USER;
|
||||
}
|
||||
Hasher hasher = Hasher.HTPASSWD;
|
||||
|
@ -238,4 +248,138 @@ public class ESUsersTool extends CliTool {
|
|||
return ExitStatus.OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class Roles extends CliTool.Command {
|
||||
|
||||
private static final String NAME = "roles";
|
||||
|
||||
private static final CliToolConfig.Cmd CMD = cmd(NAME, Roles.class)
|
||||
.options(
|
||||
option("a", "add").hasArg(true).required(false),
|
||||
option("r", "remove").hasArg(true).required(false))
|
||||
.build();
|
||||
|
||||
public static Command parse(Terminal terminal, CommandLine cli) {
|
||||
if (cli.getArgs().length == 0) {
|
||||
return exitCmd(ExitStatus.USAGE, terminal, "username is missing");
|
||||
}
|
||||
|
||||
String username = cli.getArgs()[0];
|
||||
String addRolesCsv = cli.getOptionValue("add");
|
||||
String[] addRoles = (addRolesCsv != null) ? addRolesCsv.split(",") : Strings.EMPTY_ARRAY;
|
||||
String removeRolesCsv = cli.getOptionValue("remove");
|
||||
String[] removeRoles = (removeRolesCsv != null) ? removeRolesCsv.split(",") : Strings.EMPTY_ARRAY;
|
||||
|
||||
return new Roles(terminal, username, addRoles, removeRoles);
|
||||
}
|
||||
|
||||
public static final Pattern ROLE_PATTERN = Pattern.compile("[\\w@-]+");
|
||||
final String username;
|
||||
final String[] addRoles;
|
||||
final String[] removeRoles;
|
||||
|
||||
public Roles(Terminal terminal, String username, String[] addRoles, String[] removeRoles) {
|
||||
super(terminal);
|
||||
this.username = username;
|
||||
this.addRoles = addRoles;
|
||||
this.removeRoles = removeRoles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExitStatus execute(Settings settings, Environment env) throws Exception {
|
||||
// check if just need to return data as no write operation happens
|
||||
// Nothing to add, just list the data for a username
|
||||
boolean readOnlyUserListing = removeRoles.length == 0 && addRoles.length == 0;
|
||||
if (readOnlyUserListing) {
|
||||
return new ListUsersAndRoles(terminal, username).execute(settings, env);
|
||||
}
|
||||
|
||||
|
||||
// check for roles if they match
|
||||
String[] allRoles = ObjectArrays.concat(addRoles, removeRoles, String.class);
|
||||
for (String role : allRoles) {
|
||||
if (!ROLE_PATTERN.matcher(role).matches()) {
|
||||
terminal.println("Role name [%s] is not valid. Please use lowercase and numbers only", role);
|
||||
return ExitStatus.DATA_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
Path file = FileUserRolesStore.resolveFile(settings, env);
|
||||
Map<String, String[]> userRoles = FileUserRolesStore.parseFile(file, null);
|
||||
|
||||
if (!userRoles.containsKey(username)) {
|
||||
terminal.println("User [%s] doesn't exist", username);
|
||||
return ExitStatus.NO_USER;
|
||||
}
|
||||
|
||||
List<String> roles = Lists.newArrayList(userRoles.get(username));;
|
||||
roles.addAll(Arrays.asList(addRoles));
|
||||
roles.removeAll(Arrays.asList(removeRoles));
|
||||
|
||||
Map<String, String[]> userRolesToWrite = Maps.newHashMapWithExpectedSize(userRoles.size());
|
||||
userRolesToWrite.putAll(userRoles);
|
||||
userRolesToWrite.put(username, Sets.newLinkedHashSet(roles).toArray(new String[]{}));
|
||||
FileUserRolesStore.writeFile(userRolesToWrite, file);
|
||||
|
||||
return ExitStatus.OK;
|
||||
}
|
||||
}
|
||||
|
||||
static class ListUsersAndRoles extends CliTool.Command {
|
||||
|
||||
private static final String NAME = "list";
|
||||
|
||||
private static final CliToolConfig.Cmd CMD = cmd(NAME, Useradd.class).build();
|
||||
|
||||
public static Command parse(Terminal terminal, CommandLine cli) {
|
||||
if (cli.getArgs().length == 0) {
|
||||
return exitCmd(ExitStatus.USAGE, terminal, "username is missing");
|
||||
}
|
||||
|
||||
String username = cli.getArgs()[0];
|
||||
return new ListUsersAndRoles(terminal, username);
|
||||
}
|
||||
|
||||
String username;
|
||||
|
||||
public ListUsersAndRoles(Terminal terminal, String username) {
|
||||
super(terminal);
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExitStatus execute(Settings settings, Environment env) throws Exception {
|
||||
Path userRolesFilePath = FileUserRolesStore.resolveFile(settings, env);
|
||||
Map<String, String[]> userRoles = FileUserRolesStore.parseFile(userRolesFilePath, null);
|
||||
Path userFilePath = FileUserPasswdStore.resolveFile(settings, env);
|
||||
Set<String> users = FileUserPasswdStore.parseFile(userFilePath, null).keySet();
|
||||
|
||||
if (username != null) {
|
||||
if (!users.contains(username)) {
|
||||
terminal.println("User [%s] doesn't exist", username);
|
||||
return ExitStatus.NO_USER;
|
||||
}
|
||||
|
||||
if (userRoles.containsKey(username)) {
|
||||
terminal.println("%-15s: %s", username, Joiner.on(",").useForNull("-").join(userRoles.get(username)));
|
||||
} else {
|
||||
terminal.println("%-15s: -", username);
|
||||
}
|
||||
} else {
|
||||
for (Map.Entry<String, String[]> entry : userRoles.entrySet()) {
|
||||
terminal.println("%-15s: %s", entry.getKey(), Joiner.on(",").join(entry.getValue()));
|
||||
}
|
||||
// list users without roles
|
||||
Set<String> usersWithoutRoles = Sets.newHashSet(users);
|
||||
if (usersWithoutRoles.removeAll(userRoles.keySet())) {
|
||||
for (String user : usersWithoutRoles) {
|
||||
terminal.println("%-15s: -", user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ExitStatus.OK;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
NAME
|
||||
|
||||
list - List existing users and their corresponding roles
|
||||
|
||||
SYNOPSIS
|
||||
|
||||
esusers list <username>
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
The list command allows to list all existing users and their
|
||||
corresponding roles. Alternatively you can also list just a
|
||||
single user.
|
||||
|
||||
OPTIONS
|
||||
|
||||
-h,--help Shows this message
|
|
@ -0,0 +1,21 @@
|
|||
NAME
|
||||
|
||||
roles - Edit roles of an existing user
|
||||
|
||||
SYNOPSIS
|
||||
|
||||
esusers roles <username> [-a roles] [-r roles]
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
The roles command allows to edit roles for an existing user.
|
||||
corresponding roles. Alternatively you can also list just a
|
||||
single users roles, if you do not specify the -a or the -r parameter.
|
||||
|
||||
OPTIONS
|
||||
|
||||
-h,--help Shows this message
|
||||
|
||||
-a,--add <roles> Adds supplied roles for the specified user
|
||||
|
||||
-r,--remove <roles> Adds supplied roles for the specified user
|
|
@ -5,20 +5,27 @@
|
|||
*/
|
||||
package org.elasticsearch.shield.authc.esusers.tool;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import org.elasticsearch.common.base.Charsets;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.cli.CliTool;
|
||||
import org.elasticsearch.common.cli.CliToolTestCase;
|
||||
import org.elasticsearch.common.cli.Terminal;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.shield.authc.esusers.FileUserRolesStore;
|
||||
import org.elasticsearch.shield.authc.support.Hasher;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
@ -28,6 +35,9 @@ import static org.hamcrest.Matchers.*;
|
|||
*/
|
||||
public class ESUsersToolTests extends CliToolTestCase {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void testUseradd_Parse_AllOptions() throws Exception {
|
||||
ESUsersTool tool = new ESUsersTool();
|
||||
|
@ -67,22 +77,21 @@ public class ESUsersToolTests extends CliToolTestCase {
|
|||
|
||||
@Test
|
||||
public void testUseradd_Cmd_Create() throws Exception {
|
||||
Path dir = Files.createTempDirectory(null);
|
||||
Path users = dir.resolve("users");
|
||||
Path usersRoles = dir.resolve("users_roles");
|
||||
File tmpFolder = temporaryFolder.newFolder();
|
||||
File userFile = new File(tmpFolder, "users");
|
||||
File userRolesFile = new File(tmpFolder, "users_roles");
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users", users.toAbsolutePath())
|
||||
.put("shield.authc.esusers.files.users_roles", usersRoles.toAbsolutePath())
|
||||
.put("shield.authc.esusers.files.users", userFile)
|
||||
.put("shield.authc.esusers.files.users_roles", userRolesFile)
|
||||
.build();
|
||||
Environment env = new Environment(settings);
|
||||
|
||||
ESUsersTool.Useradd cmd = new ESUsersTool.Useradd(new TerminalMock(), "user1", "changeme".toCharArray(), "r1", "r2");
|
||||
|
||||
CliTool.ExitStatus status = cmd.execute(settings, env);
|
||||
CliTool.ExitStatus status = execute(cmd, settings);
|
||||
assertThat(status, is(CliTool.ExitStatus.OK));
|
||||
|
||||
assertThat(Files.exists(users), is(true));
|
||||
List<String> lines = Files.readAllLines(users, Charsets.UTF_8);
|
||||
assertFileExists(userFile);
|
||||
List<String> lines = Files.readLines(userFile, Charsets.UTF_8);
|
||||
assertThat(lines.size(), is(1));
|
||||
// we can't just hash again and compare the lines, as every time we hash a new salt is generated
|
||||
// instead we'll just verify the generated hash against the correct password.
|
||||
|
@ -91,8 +100,8 @@ public class ESUsersToolTests extends CliToolTestCase {
|
|||
String hash = line.substring("user1:".length());
|
||||
assertThat(Hasher.HTPASSWD.verify("changeme".toCharArray(), hash.toCharArray()), is(true));
|
||||
|
||||
assertThat(Files.exists(usersRoles), is(true));
|
||||
lines = Files.readAllLines(usersRoles, Charsets.UTF_8);
|
||||
assertFileExists(userRolesFile);
|
||||
lines = Files.readLines(userRolesFile, Charsets.UTF_8);
|
||||
assertThat(lines.size(), is(1));
|
||||
line = lines.get(0);
|
||||
assertThat(line, equalTo("user1:r1,r2"));
|
||||
|
@ -100,31 +109,20 @@ public class ESUsersToolTests extends CliToolTestCase {
|
|||
|
||||
@Test
|
||||
public void testUseradd_Cmd_Append() throws Exception {
|
||||
Path users = Files.createTempFile(null, null);
|
||||
Path usersRoles = Files.createTempFile(null, null);
|
||||
File userFile = writeFile("user2:hash2");
|
||||
File userRolesFile = writeFile("user2:r3,r4");
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users", users.toAbsolutePath())
|
||||
.put("shield.authc.esusers.files.users_roles", usersRoles.toAbsolutePath())
|
||||
.put("shield.authc.esusers.files.users", userFile)
|
||||
.put("shield.authc.esusers.files.users_roles", userRolesFile)
|
||||
.build();
|
||||
Environment env = new Environment(settings);
|
||||
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(users, Charsets.UTF_8)) {
|
||||
writer.write("user2:hash2");
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(usersRoles, Charsets.UTF_8)) {
|
||||
writer.write("user2:r3,r4");
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
ESUsersTool.Useradd cmd = new ESUsersTool.Useradd(new TerminalMock(), "user1", "changeme".toCharArray(), "r1", "r2");
|
||||
|
||||
CliTool.ExitStatus status = cmd.execute(settings, env);
|
||||
CliTool.ExitStatus status = execute(cmd, settings);
|
||||
assertThat(status, is(CliTool.ExitStatus.OK));
|
||||
|
||||
assertThat(Files.exists(users), is(true));
|
||||
List<String> lines = Files.readAllLines(users, Charsets.UTF_8);
|
||||
assertFileExists(userFile);
|
||||
List<String> lines = Files.readLines(userFile, Charsets.UTF_8);
|
||||
assertThat(lines.size(), is(2));
|
||||
assertThat(lines.get(0), equalTo("user2:hash2"));
|
||||
// we can't just hash again and compare the lines, as every time we hash a new salt is generated
|
||||
|
@ -134,8 +132,8 @@ public class ESUsersToolTests extends CliToolTestCase {
|
|||
String hash = line.substring("user1:".length());
|
||||
assertThat(Hasher.HTPASSWD.verify("changeme".toCharArray(), hash.toCharArray()), is(true));
|
||||
|
||||
assertThat(Files.exists(usersRoles), is(true));
|
||||
lines = Files.readAllLines(usersRoles, Charsets.UTF_8);
|
||||
assertFileExists(userRolesFile);
|
||||
lines = Files.readLines(userRolesFile, Charsets.UTF_8);
|
||||
assertThat(lines.size(), is(2));
|
||||
assertThat(lines.get(0), equalTo("user2:r3,r4"));
|
||||
line = lines.get(1);
|
||||
|
@ -144,22 +142,16 @@ public class ESUsersToolTests extends CliToolTestCase {
|
|||
|
||||
@Test
|
||||
public void testUseradd_Cmd_Append_UserAlreadyExists() throws Exception {
|
||||
Path users = Files.createTempFile(null, null);
|
||||
Path usersRoles = Files.createTempFile(null, null);
|
||||
File userFile = writeFile("user1:hash1");
|
||||
File userRolesFile = temporaryFolder.newFile();
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users", users.toAbsolutePath())
|
||||
.put("shield.authc.esusers.files.users_roles", usersRoles.toAbsolutePath())
|
||||
.put("shield.authc.esusers.files.users", userFile)
|
||||
.put("shield.authc.esusers.files.users_roles", userRolesFile)
|
||||
.build();
|
||||
Environment env = new Environment(settings);
|
||||
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(users, Charsets.UTF_8)) {
|
||||
writer.write("user1:hash1");
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
ESUsersTool.Useradd cmd = new ESUsersTool.Useradd(new TerminalMock(), "user1", "changeme".toCharArray(), "r1", "r2");
|
||||
|
||||
CliTool.ExitStatus status = cmd.execute(settings, env);
|
||||
CliTool.ExitStatus status = execute(cmd, settings);
|
||||
assertThat(status, is(CliTool.ExitStatus.CODE_ERROR));
|
||||
}
|
||||
|
||||
|
@ -183,90 +175,67 @@ public class ESUsersToolTests extends CliToolTestCase {
|
|||
|
||||
@Test
|
||||
public void testUserdel_Cmd() throws Exception {
|
||||
Path users = Files.createTempFile(null, null);
|
||||
Path usersRoles = Files.createTempFile(null, null);
|
||||
File userFile = writeFile("user1:hash2");
|
||||
File userRolesFile = writeFile("user1:r3,r4");
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users", users.toAbsolutePath())
|
||||
.put("shield.authc.esusers.files.users_roles", usersRoles.toAbsolutePath())
|
||||
.put("shield.authc.esusers.files.users", userFile)
|
||||
.put("shield.authc.esusers.files.users_roles", userRolesFile)
|
||||
.build();
|
||||
Environment env = new Environment(settings);
|
||||
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(users, Charsets.UTF_8)) {
|
||||
writer.write("user1:hash2");
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(usersRoles, Charsets.UTF_8)) {
|
||||
writer.write("user1:r3,r4");
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
ESUsersTool.Userdel cmd = new ESUsersTool.Userdel(new TerminalMock(), "user1");
|
||||
|
||||
CliTool.ExitStatus status = cmd.execute(settings, env);
|
||||
CliTool.ExitStatus status = execute(cmd, settings);
|
||||
assertThat(status, is(CliTool.ExitStatus.OK));
|
||||
|
||||
assertThat(Files.exists(users), is(true));
|
||||
List<String> lines = Files.readAllLines(users, Charsets.UTF_8);
|
||||
assertFileExists(userFile);
|
||||
List<String> lines = Files.readLines(userFile, Charsets.UTF_8);
|
||||
assertThat(lines.size(), is(0));
|
||||
|
||||
assertThat(Files.exists(usersRoles), is(true));
|
||||
lines = Files.readAllLines(usersRoles, Charsets.UTF_8);
|
||||
assertFileExists(userRolesFile);
|
||||
lines = Files.readLines(userRolesFile, Charsets.UTF_8);
|
||||
assertThat(lines.size(), is(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserdel_Cmd_MissingUser() throws Exception {
|
||||
Path users = Files.createTempFile(null, null);
|
||||
Path usersRoles = Files.createTempFile(null, null);
|
||||
File userFile = writeFile("user1:hash2");
|
||||
File userRolesFile = writeFile("user1:r3,r4");
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users", users.toAbsolutePath())
|
||||
.put("shield.authc.esusers.files.users_roles", usersRoles.toAbsolutePath())
|
||||
.put("shield.authc.esusers.files.users", userFile)
|
||||
.put("shield.authc.esusers.files.users_roles", userRolesFile)
|
||||
.build();
|
||||
Environment env = new Environment(settings);
|
||||
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(users, Charsets.UTF_8)) {
|
||||
writer.write("user1:hash2");
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(usersRoles, Charsets.UTF_8)) {
|
||||
writer.write("user1:r3,r4");
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
ESUsersTool.Userdel cmd = new ESUsersTool.Userdel(new TerminalMock(), "user2");
|
||||
|
||||
CliTool.ExitStatus status = cmd.execute(settings, env);
|
||||
CliTool.ExitStatus status = execute(cmd, settings);
|
||||
assertThat(status, is(CliTool.ExitStatus.OK));
|
||||
|
||||
assertThat(Files.exists(users), is(true));
|
||||
List<String> lines = Files.readAllLines(users, Charsets.UTF_8);
|
||||
assertFileExists(userFile);
|
||||
List<String> lines = Files.readLines(userFile, Charsets.UTF_8);
|
||||
assertThat(lines.size(), is(1));
|
||||
|
||||
assertThat(Files.exists(usersRoles), is(true));
|
||||
lines = Files.readAllLines(usersRoles, Charsets.UTF_8);
|
||||
assertFileExists(userRolesFile);
|
||||
lines = Files.readLines(userRolesFile, Charsets.UTF_8);
|
||||
assertThat(lines.size(), is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserdel_Cmd_MissingFiles() throws Exception {
|
||||
Path dir = Files.createTempDirectory(null);
|
||||
Path users = dir.resolve("users");
|
||||
Path usersRoles = dir.resolve("users_roles");
|
||||
File dir = temporaryFolder.newFolder();
|
||||
File userFile = new File(dir, "users");
|
||||
File userRolesFile = new File(dir, "users_roles");
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users", users.toAbsolutePath())
|
||||
.put("shield.authc.esusers.files.users_roles", usersRoles.toAbsolutePath())
|
||||
.put("shield.authc.esusers.files.users", userFile)
|
||||
.put("shield.authc.esusers.files.users_roles", userRolesFile)
|
||||
.build();
|
||||
Environment env = new Environment(settings);
|
||||
|
||||
ESUsersTool.Userdel cmd = new ESUsersTool.Userdel(new TerminalMock(), "user2");
|
||||
|
||||
CliTool.ExitStatus status = cmd.execute(settings, env);
|
||||
CliTool.ExitStatus status = execute(cmd, settings);
|
||||
assertThat(status, is(CliTool.ExitStatus.OK));
|
||||
|
||||
assertThat(Files.exists(users), is(false));
|
||||
assertThat(Files.exists(usersRoles), is(false));
|
||||
assertThat(userFile.exists(), is(false));
|
||||
assertThat(userRolesFile.exists(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -309,22 +278,16 @@ public class ESUsersToolTests extends CliToolTestCase {
|
|||
|
||||
@Test
|
||||
public void testPasswd_Cmd() throws Exception {
|
||||
Path users = Files.createTempFile(null, null);
|
||||
File userFile = writeFile("user1:hash2");
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users", users.toAbsolutePath())
|
||||
.put("shield.authc.esusers.files.users", userFile)
|
||||
.build();
|
||||
Environment env = new Environment(settings);
|
||||
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(users, Charsets.UTF_8)) {
|
||||
writer.write("user1:hash2");
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
ESUsersTool.Passwd cmd = new ESUsersTool.Passwd(new TerminalMock(), "user1", "changeme".toCharArray());
|
||||
CliTool.ExitStatus status = cmd.execute(settings, env);
|
||||
CliTool.ExitStatus status = execute(cmd, settings);
|
||||
assertThat(status, is(CliTool.ExitStatus.OK));
|
||||
|
||||
List<String> lines = Files.readAllLines(users, Charsets.UTF_8);
|
||||
List<String> lines = Files.readLines(userFile, Charsets.UTF_8);
|
||||
assertThat(lines.size(), is(1));
|
||||
// we can't just hash again and compare the lines, as every time we hash a new salt is generated
|
||||
// instead we'll just verify the generated hash against the correct password.
|
||||
|
@ -336,34 +299,287 @@ public class ESUsersToolTests extends CliToolTestCase {
|
|||
|
||||
@Test
|
||||
public void testPasswd_Cmd_UnknownUser() throws Exception {
|
||||
Path users = Files.createTempFile(null, null);
|
||||
File userFile = writeFile("user1:hash2");
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users", users.toAbsolutePath())
|
||||
.put("shield.authc.esusers.files.users", userFile)
|
||||
.build();
|
||||
Environment env = new Environment(settings);
|
||||
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(users, Charsets.UTF_8)) {
|
||||
writer.write("user1:hash2");
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
ESUsersTool.Passwd cmd = new ESUsersTool.Passwd(new TerminalMock(), "user2", "changeme".toCharArray());
|
||||
CliTool.ExitStatus status = cmd.execute(settings, env);
|
||||
CliTool.ExitStatus status = execute(cmd, settings);
|
||||
assertThat(status, is(CliTool.ExitStatus.NO_USER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPasswd_Cmd_MissingFiles() throws Exception {
|
||||
Path dir = Files.createTempDirectory(null);
|
||||
Path users = dir.resolve("users");
|
||||
File userFile = temporaryFolder.newFile();
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users", users.toAbsolutePath())
|
||||
.put("shield.authc.esusers.files.users", userFile)
|
||||
.build();
|
||||
Environment env = new Environment(settings);
|
||||
|
||||
ESUsersTool.Passwd cmd = new ESUsersTool.Passwd(new TerminalMock(), "user2", "changeme".toCharArray());
|
||||
CliTool.ExitStatus status = cmd.execute(settings, env);
|
||||
CliTool.ExitStatus status = execute(cmd, settings);
|
||||
assertThat(status, is(CliTool.ExitStatus.NO_USER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoles_Parse_AllOptions() throws Exception {
|
||||
ESUsersTool tool = new ESUsersTool();
|
||||
CliTool.Command command = tool.parse("roles", args("someuser -a test1,test2,test3 -r test4,test5,test6"));
|
||||
assertThat(command, instanceOf(ESUsersTool.Roles.class));
|
||||
ESUsersTool.Roles rolesCommand = (ESUsersTool.Roles) command;
|
||||
assertThat(rolesCommand.username, is("someuser"));
|
||||
assertThat(rolesCommand.addRoles, arrayContaining("test1", "test2", "test3"));
|
||||
assertThat(rolesCommand.removeRoles, arrayContaining("test4", "test5", "test6"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoles_Cmd_validatingRoleNames() throws Exception {
|
||||
ESUsersTool tool = new ESUsersTool();
|
||||
File usersFile = writeFile("admin:hash");
|
||||
File usersRoleFile = writeFile("admin: admin\n");
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users", usersFile)
|
||||
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
|
||||
.build();
|
||||
|
||||
// invalid role names
|
||||
assertThat(execute(tool.parse("roles", args("admin -a r0le!")), settings), is(CliTool.ExitStatus.DATA_ERROR));
|
||||
assertThat(execute(tool.parse("roles", args("admin -a role%")), settings), is(CliTool.ExitStatus.DATA_ERROR));
|
||||
assertThat(execute(tool.parse("roles", args("admin -a role:")), settings), is(CliTool.ExitStatus.DATA_ERROR));
|
||||
assertThat(execute(tool.parse("roles", args("admin -a role>")), settings), is(CliTool.ExitStatus.DATA_ERROR));
|
||||
|
||||
// valid role names
|
||||
assertThat(execute(tool.parse("roles", args("admin -a test01")), settings), is(CliTool.ExitStatus.OK));
|
||||
assertThat(execute(tool.parse("roles", args("admin -a @role")), settings), is(CliTool.ExitStatus.OK));
|
||||
assertThat(execute(tool.parse("roles", args("admin -a _role")), settings), is(CliTool.ExitStatus.OK));
|
||||
assertThat(execute(tool.parse("roles", args("admin -a -role")), settings), is(CliTool.ExitStatus.OK));
|
||||
assertThat(execute(tool.parse("roles", args("admin -a -Role")), settings), is(CliTool.ExitStatus.OK));
|
||||
assertThat(execute(tool.parse("roles", args("admin -a role0")), settings), is(CliTool.ExitStatus.OK));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoles_Cmd_addingRoleWorks() throws Exception {
|
||||
File usersRoleFile = writeFile("admin: admin\nuser:user\n");
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
|
||||
.build();
|
||||
|
||||
ESUsersTool.Roles cmd = new ESUsersTool.Roles(new TerminalMock(), "user", new String[]{"foo"}, Strings.EMPTY_ARRAY);
|
||||
CliTool.ExitStatus status = execute(cmd, settings);
|
||||
|
||||
assertThat(status, is(CliTool.ExitStatus.OK));
|
||||
|
||||
Map<String, String[]> userRoles = FileUserRolesStore.parseFile(usersRoleFile.toPath(), logger);
|
||||
assertThat(userRoles.keySet(), hasSize(2));
|
||||
assertThat(userRoles.keySet(), hasItems("admin", "user"));
|
||||
assertThat(userRoles.get("admin"), arrayContaining("admin"));
|
||||
assertThat(userRoles.get("user"), arrayContaining("user", "foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoles_Cmd_removingRoleWorks() throws Exception {
|
||||
File usersRoleFile = writeFile("admin: admin\nuser:user,foo,bar\n");
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
|
||||
.build();
|
||||
|
||||
ESUsersTool.Roles cmd = new ESUsersTool.Roles(new TerminalMock(), "user", Strings.EMPTY_ARRAY, new String[]{"foo"});
|
||||
CliTool.ExitStatus status = execute(cmd, settings);
|
||||
|
||||
assertThat(status, is(CliTool.ExitStatus.OK));
|
||||
|
||||
Map<String, String[]> userRoles = FileUserRolesStore.parseFile(usersRoleFile.toPath(), logger);
|
||||
assertThat(userRoles.keySet(), hasSize(2));
|
||||
assertThat(userRoles.keySet(), hasItems("admin", "user"));
|
||||
assertThat(userRoles.get("admin"), arrayContaining("admin"));
|
||||
assertThat(userRoles.get("user"), arrayContaining("user", "bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoles_Cmd_addingAndRemovingRoleWorks() throws Exception {
|
||||
File usersRoleFile = writeFile("admin: admin\nuser:user,foo,bar\n");
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
|
||||
.build();
|
||||
|
||||
ESUsersTool.Roles cmd = new ESUsersTool.Roles(new TerminalMock(), "user", new String[]{"newrole"}, new String[]{"foo"});
|
||||
CliTool.ExitStatus status = execute(cmd, settings);
|
||||
|
||||
assertThat(status, is(CliTool.ExitStatus.OK));
|
||||
|
||||
Map<String, String[]> userRoles = FileUserRolesStore.parseFile(usersRoleFile.toPath(), logger);
|
||||
assertThat(userRoles.keySet(), hasSize(2));
|
||||
assertThat(userRoles.keySet(), hasItems("admin", "user"));
|
||||
assertThat(userRoles.get("admin"), arrayContaining("admin"));
|
||||
assertThat(userRoles.get("user"), arrayContaining("user", "bar", "newrole"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoles_Cmd_userNotFound() throws Exception {
|
||||
File usersRoleFile = writeFile("admin: admin\nuser:user,foo,bar\n");
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
|
||||
.build();
|
||||
|
||||
ESUsersTool.Roles cmd = new ESUsersTool.Roles(new TerminalMock(), "does-not-exist", Strings.EMPTY_ARRAY, Strings.EMPTY_ARRAY);
|
||||
CliTool.ExitStatus status = execute(cmd, settings);
|
||||
|
||||
assertThat(status, is(CliTool.ExitStatus.NO_USER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoles_Cmd_testNotAddingOrRemovingRolesShowsListingOfRoles() throws Exception {
|
||||
File usersFile = writeFile("admin:hash\nuser:hash");
|
||||
File usersRoleFile = writeFile("admin: admin\nuser:user,foo,bar\n");
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users", usersFile)
|
||||
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
|
||||
.build();
|
||||
|
||||
LoggingTerminal catchTerminalOutput = new LoggingTerminal();
|
||||
ESUsersTool.Roles cmd = new ESUsersTool.Roles(catchTerminalOutput, "user", Strings.EMPTY_ARRAY, Strings.EMPTY_ARRAY);
|
||||
CliTool.ExitStatus status = execute(cmd, settings);
|
||||
|
||||
assertThat(status, is(CliTool.ExitStatus.OK));
|
||||
assertThat(catchTerminalOutput.getTerminalOutput(), hasItem(allOf(containsString("user"), containsString("user,foo,bar"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListUsersAndRoles_Cmd_parsingWorks() throws Exception {
|
||||
ESUsersTool tool = new ESUsersTool();
|
||||
CliTool.Command command = tool.parse("list", args("someuser"));
|
||||
assertThat(command, instanceOf(ESUsersTool.ListUsersAndRoles.class));
|
||||
ESUsersTool.ListUsersAndRoles listUsersAndRolesCommand = (ESUsersTool.ListUsersAndRoles) command;
|
||||
assertThat(listUsersAndRolesCommand.username, is("someuser"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListUsersAndRoles_Cmd_listAllUsers() throws Exception {
|
||||
File usersRoleFile = writeFile("admin: admin\nuser: user,foo,bar\n");
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
|
||||
.build();
|
||||
|
||||
LoggingTerminal catchTerminalOutput = new LoggingTerminal();
|
||||
ESUsersTool.ListUsersAndRoles cmd = new ESUsersTool.ListUsersAndRoles(catchTerminalOutput, null);
|
||||
CliTool.ExitStatus status = execute(cmd, settings);
|
||||
|
||||
assertThat(status, is(CliTool.ExitStatus.OK));
|
||||
assertThat(catchTerminalOutput.getTerminalOutput(), hasSize(greaterThanOrEqualTo(2)));
|
||||
assertThat(catchTerminalOutput.getTerminalOutput(), hasItem(containsString("admin")));
|
||||
assertThat(catchTerminalOutput.getTerminalOutput(), hasItem(allOf(containsString("user"), containsString("user,foo,bar"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListUsersAndRoles_Cmd_listSingleUser() throws Exception {
|
||||
File usersRoleFile = writeFile("admin: admin\nuser: user,foo,bar\n");
|
||||
File usersFile = writeFile("admin:{plain}changeme\nuser:{plain}changeme\nno-roles-user:{plain}changeme\n");
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
|
||||
.put("shield.authc.esusers.files.users", usersFile)
|
||||
.build();
|
||||
|
||||
LoggingTerminal catchTerminalOutput = new LoggingTerminal();
|
||||
ESUsersTool.ListUsersAndRoles cmd = new ESUsersTool.ListUsersAndRoles(catchTerminalOutput, "admin");
|
||||
CliTool.ExitStatus status = execute(cmd, settings);
|
||||
|
||||
assertThat(status, is(CliTool.ExitStatus.OK));
|
||||
assertThat(catchTerminalOutput.getTerminalOutput(), hasSize(greaterThanOrEqualTo(1)));
|
||||
assertThat(catchTerminalOutput.getTerminalOutput(), hasItem(containsString("admin")));
|
||||
assertThat(catchTerminalOutput.getTerminalOutput(), hasItem(not(containsString("user"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListUsersAndRoles_Cmd_listSingleUserNotFound() throws Exception {
|
||||
File usersRoleFile = writeFile("admin: admin\nuser: user,foo,bar\n");
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
|
||||
.build();
|
||||
|
||||
LoggingTerminal catchTerminalOutput = new LoggingTerminal();
|
||||
ESUsersTool.ListUsersAndRoles cmd = new ESUsersTool.ListUsersAndRoles(catchTerminalOutput, "does-not-exist");
|
||||
CliTool.ExitStatus status = execute(cmd, settings);
|
||||
|
||||
assertThat(status, is(CliTool.ExitStatus.NO_USER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListUsersAndRoles_Cmd_testThatUsersWithoutRolesAreListed() throws Exception {
|
||||
File usersFile = writeFile("admin:{plain}changeme\nuser:{plain}changeme\nno-roles-user:{plain}changeme\n");
|
||||
File usersRoleFile = writeFile("admin: admin\nuser: user,foo,bar\n");
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
|
||||
.put("shield.authc.esusers.files.users", usersFile)
|
||||
.build();
|
||||
|
||||
LoggingTerminal catchTerminalOutput = new LoggingTerminal();
|
||||
ESUsersTool.ListUsersAndRoles cmd = new ESUsersTool.ListUsersAndRoles(catchTerminalOutput, null);
|
||||
CliTool.ExitStatus status = execute(cmd, settings);
|
||||
|
||||
assertThat(status, is(CliTool.ExitStatus.OK));
|
||||
assertThat(catchTerminalOutput.getTerminalOutput(), hasSize(greaterThanOrEqualTo(3)));
|
||||
assertThat(catchTerminalOutput.getTerminalOutput(), hasItem(containsString("admin")));
|
||||
assertThat(catchTerminalOutput.getTerminalOutput(), hasItem(allOf(containsString("user"), containsString("user,foo,bar"))));
|
||||
assertThat(catchTerminalOutput.getTerminalOutput(), hasItem(allOf(containsString("no-roles-user"), containsString("-"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListUsersAndRoles_Cmd_testThatUsersWithoutRolesAreListedForSingleUser() throws Exception {
|
||||
File usersFile = writeFile("admin:{plain}changeme");
|
||||
File usersRoleFile = writeFile("");
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.files.users_roles", usersRoleFile)
|
||||
.put("shield.authc.esusers.files.users", usersFile)
|
||||
.build();
|
||||
|
||||
LoggingTerminal loggingTerminal = new LoggingTerminal();
|
||||
ESUsersTool.ListUsersAndRoles cmd = new ESUsersTool.ListUsersAndRoles(loggingTerminal, "admin");
|
||||
CliTool.ExitStatus status = execute(cmd, settings);
|
||||
|
||||
assertThat(status, is(CliTool.ExitStatus.OK));
|
||||
assertThat(loggingTerminal.getTerminalOutput(), hasSize(greaterThanOrEqualTo(1)));
|
||||
assertThat(loggingTerminal.getTerminalOutput(), hasItem(allOf(containsString("admin"), containsString("-"))));
|
||||
}
|
||||
|
||||
public static class LoggingTerminal extends TerminalMock {
|
||||
|
||||
List<String> terminalOutput = new ArrayList();
|
||||
|
||||
public LoggingTerminal() {
|
||||
super(Verbosity.NORMAL);
|
||||
}
|
||||
|
||||
public LoggingTerminal(Verbosity verbosity) {
|
||||
super(verbosity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPrint(String msg, Object... args) {
|
||||
terminalOutput.add(String.format(Locale.ROOT, msg, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void print(String msg, Object... args) {
|
||||
doPrint(msg, args);
|
||||
}
|
||||
|
||||
public List<String> getTerminalOutput() {
|
||||
return terminalOutput;
|
||||
}
|
||||
}
|
||||
|
||||
private CliTool.ExitStatus execute(CliTool.Command cmd, Settings settings) throws Exception {
|
||||
Environment env = new Environment(settings);
|
||||
return cmd.execute(settings, env);
|
||||
}
|
||||
|
||||
private File writeFile(String content) throws IOException {
|
||||
File file = temporaryFolder.newFile();
|
||||
Files.write(content.getBytes(Charsets.UTF_8), file);
|
||||
return file;
|
||||
}
|
||||
|
||||
private void assertFileExists(File file) {
|
||||
assertThat(String.format(Locale.ROOT, "Expected file [%s] to exist", file), file.exists(), is(true));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue