package org.intellimate.izou.security;
import org.intellimate.izou.addon.AddOnModel;
import org.intellimate.izou.main.Main;
import org.intellimate.izou.security.exceptions.IzouPermissionException;
import java.io.File;
import java.io.FilePermission;
import java.io.IOException;
import java.net.URL;
import java.security.Permission;
import java.util.ArrayList;
import java.util.List;
/**
* The FilePermissionModule is used to check the access to files
* @author LeanderK
* @version 1.0
*/
public class FilePermissionModule extends PermissionModule {
private final List<String> forbiddenReadFiles;
private final List<File> allowedWriteDirectories;
private final List<File> forbiddenWriteDirectories;
private final List<String> forbiddenWriteFilesNames;
/**
* Creates a new PermissionModule
*
* @param main the instance of main
*/
FilePermissionModule(Main main, SecurityManager securityManager) {
super(main, securityManager);
forbiddenReadFiles = new ArrayList<>();
forbiddenWriteDirectories = new ArrayList<>();
forbiddenWriteFilesNames = new ArrayList<>();
forbiddenWriteFilesNames.add("addon_config.properties");
allowedWriteDirectories = new ArrayList<>();
allowedWriteDirectories.add(main.getFileSystemManager().getResourceLocation());
allowedWriteDirectories.add(main.getFileSystemManager().getLogsLocation());
allowedWriteDirectories.add(main.getFileSystemManager().getPropertiesLocation());
allowedWriteDirectories.add(main.getFileSystemManager().getResourceLocation());
allowedWriteDirectories.add(main.getFileSystemManager().getLibLocation());
if (getMain().getFileSystemManager().getIzouJarLocation().isDirectory()) {
allowedWriteDirectories.add(new File(getMain().getFileSystemManager().getIzouJarLocation() + File.separator + "META-INF" + File.separator + "services"));
} else {
URL metaInfo = this.getClass().getClassLoader().getResource("META-INF/services");
if (metaInfo != null)
allowedWriteDirectories.add(new File(metaInfo.getFile()));
}
if (Boolean.getBoolean("debug")) {
allowedWriteDirectories.add(new File(System.getProperty("user.home") + File.separator + ".m2"));
allowedWriteDirectories.add(main.getFileSystemManager().getIzouJarLocation());
allowedWriteDirectories.add(new File(System.getProperty("java.home")));
}
}
/**
* returns true if able to check permissions
*
* @param permission the permission to check
* @return true if able to, false if not
*/
@Override
public boolean canCheckPermission(Permission permission) {
return permission instanceof FilePermission;
}
/**
* Checks if the given addOn is allowed to access the requested service and registers them if not yet registered.
*
* @param permission the Permission to check
* @param addon the identifiable to check
* @throws IzouPermissionException thrown if the addOn is not allowed to access its requested service
*/
@Override
public void checkPermission(Permission permission, AddOnModel addon) throws IzouPermissionException {
String canonicalName = permission.getName().intern().toLowerCase();
String canonicalAction = permission.getActions().intern().toLowerCase();
if (canonicalName.contains("all files") || canonicalAction.equals("execute")) {
throw getException(permission.getName());
}
if (canonicalAction.equals("read")) {
fileReadCheck(canonicalName);
}
fileWriteCheck(canonicalName, addon);
}
/**
* Determines if the file at the given file path is safe to read from in all aspects, if so returns true, else false
*
* @param filePath the path to the file to read from
*/
void fileReadCheck(String filePath) {
File potentialFile = new File(filePath);
String canonicalPath;
try {
canonicalPath = potentialFile.getCanonicalPath();
} catch (IOException e) {
error("Error getting canonical path", e);
throw getException(filePath);
}
if (forbiddenReadFiles.stream().anyMatch(canonicalPath::startsWith)) {
throw getException(filePath);
}
}
/**
* Determines if the file at the given file path is safe to write to in all aspects, if so returns true, else false
*
* @param filePath the path to the file to write to
* @param addOnModel the AddonModel
*/
void fileWriteCheck(String filePath, AddOnModel addOnModel) {
File request;
try {
request = new File(filePath).getCanonicalFile();
} catch (IOException e) {
error("Error getting canonical path", e);
throw getException(filePath);
}
isForbidden(request, addOnModel);
boolean success = false;
if (allowedWriteDirectories.stream()
.anyMatch(compare -> request.toPath().startsWith(compare.toPath()))) {
success = true;
}
for (String name : forbiddenWriteFilesNames) {
if (request.getName().equals(name)) {
success = false;
}
}
if (!success) {
throw getException(filePath);
}
if (!getSecurityManager().getSecureAccess().checkForExistingFileOrDirectory(request.toString())
|| getSecurityManager().getSecureAccess().checkForDirectory(request.toString())) {
return;
}
}
/**
* throws an Exception if the
* @param request the requested File
* @param addOnModel the AddonModel
*/
private void isForbidden(File request, AddOnModel addOnModel) {
if (forbiddenWriteDirectories.stream()
.anyMatch(compare -> request.toPath().startsWith(compare.toPath()))) {
throw getException("file: " + request.toString() + " is forbidden. Attempt made by: "
+ addOnModel.getID());
}
}
}