NIFI-10114 This closes #6122. Removed Single Refresh from ShellUserGroupProvider

Signed-off-by: Joe Witt <joewitt@apache.org>
This commit is contained in:
exceptionfactory 2022-06-13 14:25:41 -05:00 committed by Joe Witt
parent 00a96d79e0
commit f91d877043
No known key found for this signature in database
GPG Key ID: 9093BF854F811A1A
13 changed files with 5 additions and 435 deletions

View File

@ -45,40 +45,6 @@ class NssShellCommands implements ShellCommandsProvider {
return String.format("getent group %s | cut -f 4 -d ':'", groupName);
}
/**
* Gets the command for reading a single user by id.
*
* When executed, this command should output a single line, in the format used by `getUsersList`.
*
* @param userId name of user.
* @return Shell command string that will read a single user.
*/
@Override
public String getUserById(String userId) {
return String.format("getent passwd %s | cut -f 1,3,4 -d ':'", userId);
}
/**
* This method reuses `getUserById` because the getent command is the same for
* both uid and username.
*
* @param userName name of user.
* @return Shell command string that will read a single user.
*/
public String getUserByName(String userName) {
return getUserById(userName);
}
/**
* This method supports gid or group name because getent does.
*
* @param groupId name of group.
* @return Shell command string that will read a single group.
*/
public String getGroupById(String groupId) {
return String.format("getent group %s | cut -f 1,3,4 -d ':'", groupId);
}
/**
* This gives exit code 0 on all tested distributions.
*

View File

@ -47,32 +47,6 @@ class OsxShellCommands implements ShellCommandsProvider {
return String.format("dscl . -read /Groups/%s GroupMembership | cut -f 2- -d ' ' | sed 's/\\ /,/g'", groupName);
}
/**
* @param userId name of user.
* @return Shell command string that will read a single user.
*/
@Override
public String getUserById(String userId) {
return String.format("id -P %s | cut -f 1,3,4 -d ':'", userId);
}
/**
* @param userName name of user.
* @return Shell command string that will read a single user.
*/
public String getUserByName(String userName) {
return getUserById(userName); // 'id' command works for both uid/username
}
/**
* @param groupId name of group.
* @return Shell command string that will read a single group.
*/
public String getGroupById(String groupId) {
return String.format(" dscl . -read /Groups/`dscl . -search /Groups gid %s | head -n 1 | cut -f 1` RecordName PrimaryGroupID | awk 'BEGIN { OFS = \":\"; ORS=\"\\n\"; i=0;} " +
"/RecordName: / {name = $2;i = 1;}/PrimaryGroupID: / {gid = $2;}; {if (i==1) {print name,gid,\"\"}}'", groupId);
}
/**
* @return Shell command string that will exit normally (0) on a suitable system.
*/

View File

@ -56,18 +56,6 @@ class RemoteShellCommands implements ShellCommandsProvider {
return String.format(remoteCommand, innerProvider.getGroupMembers(groupName), privateKeyPath, remotePort, remoteHost);
}
public String getUserById(String userId) {
return String.format(remoteCommand, innerProvider.getUserById(userId), privateKeyPath, remotePort, remoteHost);
}
public String getUserByName(String userName) {
return String.format(remoteCommand, innerProvider.getUserByName(userName), privateKeyPath, remotePort, remoteHost);
}
public String getGroupById(String groupId) {
return String.format(remoteCommand, innerProvider.getGroupById(groupId), privateKeyPath, remotePort, remoteHost);
}
public String getSystemCheck() {
return String.format(remoteCommand, innerProvider.getSystemCheck(), privateKeyPath, remotePort, remoteHost);
}

View File

@ -55,39 +55,6 @@ interface ShellCommandsProvider {
*/
String getGroupMembers(String groupName);
/**
* Gets the command for reading a single user by id. Implementations may return null if reading a single
* user by id is not supported.
*
* When executed, this command should output a single line, in the format used by `getUsersList`.
*
* @param userId name of user.
* @return Shell command string that will read a single user.
*/
String getUserById(String userId);
/**
* Gets the command for reading a single user. Implementations may return null if reading a single user by
* username is not supported.
*
* When executed, this command should output a single line, in the format used by `getUsersList`.
*
* @param userName name of user.
* @return Shell command string that will read a single user.
*/
String getUserByName(String userName);
/**
* Gets the command for reading a single group. Implementations may return null if reading a single group
* by name is not supported.
*
* When executed, this command should output a single line, in the format used by `getGroupsList`.
*
* @param groupId name of group.
* @return Shell command string that will read a single group.
*/
String getGroupById(String groupId);
/**
* Gets the command for checking the suitability of the host system.
*

View File

@ -133,11 +133,6 @@ public class ShellUserGroupProvider implements UserGroupProvider {
user = usersByName.get(identity);
}
if (user == null) {
refreshOneUser(selectedShellCommands.getUserByName(identity), "Get Single User by Name");
user = usersByName.get(identity);
}
if (user == null) {
logger.debug("getUser (by name) user not found: " + identity);
} else {
@ -175,11 +170,6 @@ public class ShellUserGroupProvider implements UserGroupProvider {
group = groupsById.get(identifier);
}
if (group == null) {
refreshOneGroup(selectedShellCommands.getGroupById(identifier), "Get Single Group by Id");
group = groupsById.get(identifier);
}
if (group == null) {
logger.debug("getGroup (by id) group not found: " + identifier);
} else {
@ -279,7 +269,7 @@ public class ShellUserGroupProvider implements UserGroupProvider {
// Our next init step is to run the system check from that command set to determine if the other commands
// will work on this host or not.
try {
shellRunner.runShell(commands.getSystemCheck());
shellRunner.runShell(commands.getSystemCheck(), "Supported System Check");
} catch (final Exception e) {
logger.error("initialize exception: " + e + " system check command: " + commands.getSystemCheck());
throw new AuthorizerCreationException(SYS_CHECK_ERROR, e);
@ -412,73 +402,6 @@ public class ShellUserGroupProvider implements UserGroupProvider {
selectedShellCommands = commandsProvider;
}
/**
* Refresh a single user.
*
* @param command Shell command to read a single user. Pre-formatted by caller.
* @param description Shell command description.
*/
private void refreshOneUser(String command, String description) {
if (command != null) {
Map<String, User> idToUser = new HashMap<>();
Map<String, User> usernameToUser = new HashMap<>();
Map<String, User> gidToUser = new HashMap<>();
List<String> userLines;
try {
userLines = shellRunner.runShell(command, description);
rebuildUsers(userLines, idToUser, usernameToUser, gidToUser);
} catch (final IOException ioexc) {
logger.error("refreshOneUser shell exception: " + ioexc);
}
if (idToUser.size() > 0) {
synchronized (usersById) {
usersById.putAll(idToUser);
}
}
if (usernameToUser.size() > 0) {
synchronized (usersByName) {
usersByName.putAll(usernameToUser);
}
}
} else {
logger.info("Get Single User not supported on this system.");
}
}
/**
* Refresh a single group.
*
* @param command Shell command to read a single group. Pre-formatted by caller.
* @param description Shell command description.
*/
private void refreshOneGroup(String command, String description) {
if (command != null) {
Map<String, Group> gidToGroup = new HashMap<>();
List<String> groupLines;
try {
groupLines = shellRunner.runShell(command, description);
rebuildGroups(groupLines, gidToGroup);
} catch (final IOException ioexc) {
logger.error("refreshOneGroup shell exception: " + ioexc);
}
if (gidToGroup.size() > 0) {
synchronized (groupsById) {
groupsById.putAll(gidToGroup);
}
synchronized (groupsByName) {
gidToGroup.values().forEach(g -> groupsByName.put(g.getName(), g));
}
}
} else {
logger.info("Get Single Group not supported on this system.");
}
}
/**
* This is our entry point for user and group refresh. This method runs the top-level
* `getUserList()` and `getGroupsList()` shell commands, then passes those results to the
@ -614,7 +537,7 @@ public class ShellUserGroupProvider implements UserGroupProvider {
try {
String groupMembersCommand = selectedShellCommands.getGroupMembers(groupName);
List<String> memberLines = shellRunner.runShell(groupMembersCommand);
List<String> memberLines = shellRunner.runShell(groupMembersCommand, "Get Group Members");
// Use the first line only, and log if the line count isn't exactly one:
if (!memberLines.isEmpty()) {
String memberLine = memberLines.get(0);

View File

@ -52,10 +52,6 @@ public class ShellRunner {
});
}
public List<String> runShell(String command) throws IOException {
return runShell(command, "<unknown>");
}
public List<String> runShell(String command, String description) throws IOException {
final ProcessBuilder builder = new ProcessBuilder(SHELL, OPTS, command);
builder.redirectErrorStream(true);

View File

@ -101,7 +101,7 @@ public class ShellUserGroupProviderIT {
try {
// NB: this command is a bit perplexing: it works without prompt from the shell, but hangs
// here without the pipe from `yes`:
shellRunner.runShell("yes | ssh-keygen -C '' -N '' -t rsa -f " + sshPrivKeyFile);
shellRunner.runShell("yes | ssh-keygen -C '' -N '' -t rsa -f " + sshPrivKeyFile, "Setup");
} catch (final IOException ioexc) {
systemCheckFailed = true;
logger.error("setupOnce() exception: " + ioexc + "; tests cannot run on this system.");

View File

@ -44,40 +44,6 @@ class NssShellCommands implements ShellCommandsProvider {
return String.format("getent group %s | cut -f 4 -d ':'", groupName);
}
/**
* Gets the command for reading a single user by id.
*
* When executed, this command should output a single line, in the format used by `getUsersList`.
*
* @param userId name of user.
* @return Shell command string that will read a single user.
*/
@Override
public String getUserById(String userId) {
return String.format("getent passwd %s | cut -f 1,3,4 -d ':'", userId);
}
/**
* This method reuses `getUserById` because the getent command is the same for
* both uid and username.
*
* @param userName name of user.
* @return Shell command string that will read a single user.
*/
public String getUserByName(String userName) {
return getUserById(userName);
}
/**
* This method supports gid or group name because getent does.
*
* @param groupId name of group.
* @return Shell command string that will read a single group.
*/
public String getGroupById(String groupId) {
return String.format("getent group %s | cut -f 1,3,4 -d ':'", groupId);
}
/**
* This gives exit code 0 on all tested distributions.
*

View File

@ -46,32 +46,6 @@ class OsxShellCommands implements ShellCommandsProvider {
return String.format("dscl . -read /Groups/%s GroupMembership | cut -f 2- -d ' ' | sed 's/\\ /,/g'", groupName);
}
/**
* @param userId name of user.
* @return Shell command string that will read a single user.
*/
@Override
public String getUserById(String userId) {
return String.format("id -P %s | cut -f 1,3,4 -d ':'", userId);
}
/**
* @param userName name of user.
* @return Shell command string that will read a single user.
*/
public String getUserByName(String userName) {
return getUserById(userName); // 'id' command works for both uid/username
}
/**
* @param groupId name of group.
* @return Shell command string that will read a single group.
*/
public String getGroupById(String groupId) {
return String.format(" dscl . -read /Groups/`dscl . -search /Groups gid %s | head -n 1 | cut -f 1` RecordName PrimaryGroupID | awk 'BEGIN { OFS = \":\"; ORS=\"\\n\"; i=0;} " +
"/RecordName: / {name = $2;i = 1;}/PrimaryGroupID: / {gid = $2;}; {if (i==1) {print name,gid,\"\"}}'", groupId);
}
/**
* @return Shell command string that will exit normally (0) on a suitable system.
*/

View File

@ -1,73 +0,0 @@
/*
* 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.nifi.registry.security.authorization.shell;
class RemoteShellCommands implements ShellCommandsProvider {
// Carefully crafted command replacement string:
private final static String remoteCommand = "ssh " +
"-o 'StrictHostKeyChecking no' " +
"-o 'PasswordAuthentication no' " +
"-o \"RemoteCommand %s\" " +
"-i %s -p %s -l root %s";
private ShellCommandsProvider innerProvider;
private String privateKeyPath;
private String remoteHost;
private Integer remotePort;
private RemoteShellCommands() {
}
public static ShellCommandsProvider wrapOtherProvider(ShellCommandsProvider otherProvider, String keyPath, String host, Integer port) {
RemoteShellCommands remote = new RemoteShellCommands();
remote.innerProvider = otherProvider;
remote.privateKeyPath = keyPath;
remote.remoteHost = host;
remote.remotePort = port;
return remote;
}
public String getUsersList() {
return String.format(remoteCommand, innerProvider.getUsersList(), privateKeyPath, remotePort, remoteHost);
}
public String getGroupsList() {
return String.format(remoteCommand, innerProvider.getGroupsList(), privateKeyPath, remotePort, remoteHost);
}
public String getGroupMembers(String groupName) {
return String.format(remoteCommand, innerProvider.getGroupMembers(groupName), privateKeyPath, remotePort, remoteHost);
}
public String getUserById(String userId) {
return String.format(remoteCommand, innerProvider.getUserById(userId), privateKeyPath, remotePort, remoteHost);
}
public String getUserByName(String userName) {
return String.format(remoteCommand, innerProvider.getUserByName(userName), privateKeyPath, remotePort, remoteHost);
}
public String getGroupById(String groupId) {
return String.format(remoteCommand, innerProvider.getGroupById(groupId), privateKeyPath, remotePort, remoteHost);
}
public String getSystemCheck() {
return String.format(remoteCommand, innerProvider.getSystemCheck(), privateKeyPath, remotePort, remoteHost);
}
}

View File

@ -55,39 +55,6 @@ interface ShellCommandsProvider {
*/
String getGroupMembers(String groupName);
/**
* Gets the command for reading a single user by id. Implementations may return null if reading a single
* user by id is not supported.
*
* When executed, this command should output a single line, in the format used by `getUsersList`.
*
* @param userId name of user.
* @return Shell command string that will read a single user.
*/
String getUserById(String userId);
/**
* Gets the command for reading a single user. Implementations may return null if reading a single user by
* username is not supported.
*
* When executed, this command should output a single line, in the format used by `getUsersList`.
*
* @param userName name of user.
* @return Shell command string that will read a single user.
*/
String getUserByName(String userName);
/**
* Gets the command for reading a single group. Implementations may return null if reading a single group
* by name is not supported.
*
* When executed, this command should output a single line, in the format used by `getGroupsList`.
*
* @param groupId name of group.
* @return Shell command string that will read a single group.
*/
String getGroupById(String groupId);
/**
* Gets the command for checking the suitability of the host system.
*

View File

@ -52,10 +52,6 @@ public class ShellRunner {
});
}
public List<String> runShell(String command) throws IOException {
return runShell(command, "<unknown>");
}
public List<String> runShell(String command, String description) throws IOException {
final ProcessBuilder builder = new ProcessBuilder(SHELL, OPTS, command);
builder.redirectErrorStream(true);

View File

@ -135,11 +135,6 @@ public class ShellUserGroupProvider implements UserGroupProvider {
user = usersByName.get(identity);
}
if (user == null) {
refreshOneUser(selectedShellCommands.getUserByName(identity), "Get Single User by Name");
user = usersByName.get(identity);
}
if (user == null) {
logger.debug("getUser (by name) user not found: " + identity);
} else {
@ -177,11 +172,6 @@ public class ShellUserGroupProvider implements UserGroupProvider {
group = groupsById.get(identifier);
}
if (group == null) {
refreshOneGroup(selectedShellCommands.getGroupById(identifier), "Get Single Group by Id");
group = groupsById.get(identifier);
}
if (group == null) {
logger.debug("getGroup (by id) group not found: " + identifier);
} else {
@ -265,7 +255,7 @@ public class ShellUserGroupProvider implements UserGroupProvider {
// Our next init step is to run the system check from that command set to determine if the other commands
// will work on this host or not.
try {
shellRunner.runShell(commands.getSystemCheck());
shellRunner.runShell(commands.getSystemCheck(), "Supported System Check");
} catch (final Exception e) {
logger.error("initialize exception: " + e + " system check command: " + commands.getSystemCheck());
throw new SecurityProviderCreationException(SYS_CHECK_ERROR, e);
@ -395,70 +385,6 @@ public class ShellUserGroupProvider implements UserGroupProvider {
selectedShellCommands = commandsProvider;
}
/**
* Refresh a single user.
*
* @param command Shell command to read a single user. Pre-formatted by caller.
* @param description Shell command description.
*/
private void refreshOneUser(String command, String description) {
if (command != null) {
Map<String, User> idToUser = new HashMap<>();
Map<String, User> usernameToUser = new HashMap<>();
Map<String, User> gidToUser = new HashMap<>();
List<String> userLines;
try {
userLines = shellRunner.runShell(command, description);
rebuildUsers(userLines, idToUser, usernameToUser, gidToUser);
} catch (final IOException ioexc) {
logger.error("refreshOneUser shell exception: " + ioexc);
}
if (idToUser.size() > 0) {
synchronized (usersById) {
usersById.putAll(idToUser);
}
}
if (usernameToUser.size() > 0) {
synchronized (usersByName) {
usersByName.putAll(usernameToUser);
}
}
} else {
logger.info("Get Single User not supported on this system.");
}
}
/**
* Refresh a single group.
*
* @param command Shell command to read a single group. Pre-formatted by caller.
* @param description Shell command description.
*/
private void refreshOneGroup(String command, String description) {
if (command != null) {
Map<String, Group> gidToGroup = new HashMap<>();
List<String> groupLines;
try {
groupLines = shellRunner.runShell(command, description);
rebuildGroups(groupLines, gidToGroup);
} catch (final IOException ioexc) {
logger.error("refreshOneGroup shell exception: " + ioexc);
}
if (gidToGroup.size() > 0) {
synchronized (groupsById) {
groupsById.putAll(gidToGroup);
}
}
} else {
logger.info("Get Single Group not supported on this system.");
}
}
/**
* This is our entry point for user and group refresh. This method runs the top-level
* `getUserList()` and `getGroupsList()` shell commands, then passes those results to the
@ -588,7 +514,7 @@ public class ShellUserGroupProvider implements UserGroupProvider {
try {
String groupMembersCommand = selectedShellCommands.getGroupMembers(groupName);
List<String> memberLines = shellRunner.runShell(groupMembersCommand);
List<String> memberLines = shellRunner.runShell(groupMembersCommand, "Get Group Members");
// Use the first line only, and log if the line count isn't exactly one:
if (!memberLines.isEmpty()) {
String memberLine = memberLines.get(0);