Skip to content

Commit

Permalink
NMS-15702: Only members of ROLE_ADMIN can view/edit users.xml
Browse files Browse the repository at this point in the history
  • Loading branch information
christianpape committed Jun 26, 2023
1 parent 7be4758 commit f2caf7d
Showing 1 changed file with 24 additions and 8 deletions.
Expand Up @@ -65,6 +65,7 @@
import org.apache.commons.io.IOUtils;
import org.apache.cxf.jaxrs.ext.multipart.Attachment;
import org.apache.cxf.jaxrs.ext.multipart.Multipart;
import org.opennms.core.utils.ConfigFileConstants;
import org.opennms.web.api.Authentication;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -88,6 +89,15 @@ public class FilesystemRestService {
"groovy",
"bsh",
"dcb");
private static final java.nio.file.Path USERS_XML;

static {
try {
USERS_XML = ConfigFileConstants.getFile(ConfigFileConstants.USERS_CONF_FILE_NAME).toPath();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private final java.nio.file.Path etcFolder = Paths.get(System.getProperty("opennms.home"), "etc");
private final java.nio.file.Path etcPristineFolder = Paths.get(System.getProperty("opennms.home"), "share", "etc-pristine");
Expand All @@ -102,17 +112,18 @@ public List<String> getFiles(@QueryParam("changedFilesOnly") boolean changedFile

try {
return Files.find(etcFolder, 4, (path, basicFileAttributes) -> isSupportedExtension(path), FileVisitOption.FOLLOW_LINKS)
.filter(p -> !p.equals(USERS_XML) || securityContext.isUserInRole(Authentication.ROLE_ADMIN))
.map(p -> etcFolder.relativize(p).toString())
.filter(p -> !changedFilesOnly || !doesFileExistAndMatchContentsWithEtcPristine(p))
.filter(p -> !changedFilesOnly || !doesFileExistAndMatchContentsWithEtcPristine(p, securityContext))
.sorted()
.collect(Collectors.toList());
} catch (IOException e) {
throw new RuntimeException("Failed to enumerate files in path: " + etcFolder, e);
}
}

public boolean doesFileExistAndMatchContentsWithEtcPristine(String file) {
final java.nio.file.Path etcPath = ensureFileIsAllowed(file);
public boolean doesFileExistAndMatchContentsWithEtcPristine(String file, final SecurityContext securityContext) {
final java.nio.file.Path etcPath = ensureFileIsAllowed(file, securityContext);
final java.nio.file.Path etcPristinePath = etcPristineFolder.resolve(file);
if (!Files.exists(etcPristinePath)) {
return false;
Expand All @@ -133,7 +144,7 @@ public InputStream getFileHelp(@QueryParam("f") String fileName, @Context Securi
if (!securityContext.isUserInRole(Authentication.ROLE_FILESYSTEM_EDITOR)) {
throw new ForbiddenException("FILESYSTEM EDITOR role is required for retrieving help.");
}
ensureFileIsAllowed(fileName);
ensureFileIsAllowed(fileName, securityContext);
return this.getClass().getResourceAsStream("/help/" + fileName + ".md");
}

Expand All @@ -155,7 +166,7 @@ public Response getFileContents(@QueryParam("f") String fileName, @Context Secur
if (!securityContext.isUserInRole(Authentication.ROLE_FILESYSTEM_EDITOR)) {
throw new ForbiddenException("FILESYSTEM EDITOR role is required for reading files.");
}
return fileContents(ensureFileIsAllowed(fileName));
return fileContents(ensureFileIsAllowed(fileName, securityContext));
}

@POST
Expand All @@ -168,7 +179,7 @@ public String uploadFile(@QueryParam("f") String fileName,
if (!securityContext.isUserInRole(Authentication.ROLE_FILESYSTEM_EDITOR)) {
throw new ForbiddenException("FILESYSTEM EDITOR role is required for uploading file contents.");
}
final java.nio.file.Path targetPath = ensureFileIsAllowed(fileName);
final java.nio.file.Path targetPath = ensureFileIsAllowed(fileName, securityContext);

// Write the contents a temporary file
final File tempFile = File.createTempFile("upload-", targetPath.getFileName().toString());
Expand Down Expand Up @@ -200,7 +211,7 @@ public String deleteFile(@QueryParam("f") String fileName,
if (!securityContext.isUserInRole(Authentication.ROLE_FILESYSTEM_EDITOR)) {
throw new ForbiddenException("FILESYSTEM EDITOR role is required for deleting file contents.");
}
final java.nio.file.Path targetPath = ensureFileIsAllowed(fileName);
final java.nio.file.Path targetPath = ensureFileIsAllowed(fileName, securityContext);
Files.delete(targetPath);
return String.format("Successfully deleted to '%s'.", targetPath);
}
Expand All @@ -225,9 +236,14 @@ private static boolean isSupportedExtension(java.nio.file.Path path) {
return SUPPORTED_FILE_EXTENSIONS.contains(FilenameUtils.getExtension(path.getFileName().toString()));
}

private java.nio.file.Path ensureFileIsAllowed(String fileName) {
private java.nio.file.Path ensureFileIsAllowed(String fileName, SecurityContext securityContext) {
final java.nio.file.Path etcFolderNormalized = etcFolder.normalize();
final java.nio.file.Path fileNormalized = etcFolder.resolve(fileName).normalize();

if (fileNormalized.equals(USERS_XML) && !securityContext.isUserInRole(Authentication.ROLE_ADMIN)) {
throw new ForbiddenException("ADMIN role is required for accessing users.xml file contents.");
}

if (!(fileNormalized.getNameCount() > etcFolderNormalized.getNameCount() && fileNormalized.startsWith(etcFolderNormalized))) {
throw new BadRequestException("Cannot access files outside of folder! Filename given: " + fileName);
}
Expand Down

0 comments on commit f2caf7d

Please sign in to comment.