diff --git a/pom.xml b/pom.xml
index 5a44018..2d9ad9c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -53,6 +53,12 @@
2.3.0
+
+ org.apache.sshd
+ sshd-contrib
+ 2.3.0
+
+
org.apache.sshd
sshd-common
diff --git a/src/main/java/com/ryanmichela/sshd/ConfigPasswordAuthenticator.java b/src/main/java/com/ryanmichela/sshd/ConfigPasswordAuthenticator.java
index d054204..51566d6 100644
--- a/src/main/java/com/ryanmichela/sshd/ConfigPasswordAuthenticator.java
+++ b/src/main/java/com/ryanmichela/sshd/ConfigPasswordAuthenticator.java
@@ -20,7 +20,7 @@ public class ConfigPasswordAuthenticator implements PasswordAuthenticator {
{
// Depending on our hash type, we have to try and figure out what we're doing.
String HashType = SshdPlugin.instance.getConfig().getString("PasswordType");
- String ConfigHash = SshdPlugin.instance.getConfig().getString("Credentials." + username.trim());
+ String ConfigHash = SshdPlugin.instance.getConfig().getString("Credentials." + username.trim() + ".password");
if (ConfigHash == null)
SshdPlugin.instance.getLogger().warning("Config has no such user: " + username);
diff --git a/src/main/java/com/ryanmichela/sshd/SshdPlugin.java b/src/main/java/com/ryanmichela/sshd/SshdPlugin.java
index d82da58..b822e5e 100644
--- a/src/main/java/com/ryanmichela/sshd/SshdPlugin.java
+++ b/src/main/java/com/ryanmichela/sshd/SshdPlugin.java
@@ -1,9 +1,13 @@
package com.ryanmichela.sshd;
import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
+import org.apache.sshd.common.session.helpers.AbstractSession;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
+import org.apache.sshd.server.subsystem.sftp.SimpleAccessControlSftpEventListener;
+import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.plugin.java.JavaPlugin;
import com.ryanmichela.sshd.ConsoleShellFactory;
@@ -14,18 +18,35 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
import java.util.logging.Level;
+import java.util.stream.Stream;
/**
* Copyright 2013 Ryan Michela
*/
-public
-class SshdPlugin extends JavaPlugin
+public class SshdPlugin extends JavaPlugin
{
+ private SshServer sshd;
+ public static SshdPlugin instance;
- private SshServer sshd;
- public static SshdPlugin instance;
+ public static List GetSections(ConfigurationSection source)
+ {
+ if (source == null)
+ return null;
+
+ List nodes = new ArrayList();
+ for (String key : source.getKeys(false))
+ {
+ if (source.isConfigurationSection(key))
+ nodes.add(source.getConfigurationSection(key));
+ }
+ return nodes;
+ }
@Override public void onLoad()
{
@@ -73,9 +94,90 @@ class SshdPlugin extends JavaPlugin
if (getConfig().getBoolean("EnableSFTP"))
{
- sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
- sshd.setFileSystemFactory(
- new VirtualFileSystemFactory(FileSystems.getDefault().getPath(getDataFolder().getAbsolutePath()).getParent().getParent()));
+ // Handle access control for SFTP.
+ SftpSubsystemFactory.Builder builder = new SftpSubsystemFactory.Builder();
+ builder.addSftpEventListener(new SimpleAccessControlSftpEventListener() {
+ protected boolean isAccessAllowed(ServerSession session, String remote, Path localpath)
+ {
+ try
+ {
+ ConfigurationSection UsernameNamespace = getConfig().getConfigurationSection("Credentials." + session.getUsername() + ".sftp");
+
+ // They don't have SFTP enabled so deny them.
+ if (UsernameNamespace == null || !UsernameNamespace.getBoolean("enabled"))
+ return false;
+
+
+ List rules = GetSections(UsernameNamespace.getConfigurationSection("rules"));
+ if (rules != null)
+ {
+ for (ConfigurationSection path : rules)
+ {
+ // Check if the requesting path matches
+ if (localpath.toString().matches(path.getName()))
+ {
+ // Check if they have read permissions
+ if (path.getBoolean("readable"))
+ return true;
+
+ getLogger().info(String.format("Denied %s read access to \"%s\" matching rule \"%s\"", session.getUsername(), localpath.toString(), path.getName()));
+ return false;
+ }
+ }
+ }
+
+ return UsernameNamespace.getString("default").equalsIgnoreCase("allow");
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ // Automatically deny.
+ return false;
+ }
+ }
+
+ protected boolean isModificationAllowed(ServerSession session, String remote, Path localpath)
+ {
+ try
+ {
+ ConfigurationSection UsernameNamespace = getConfig().getConfigurationSection("Credentials." + session.getUsername() + ".sftp");
+
+ // They don't have SFTP enabled so deny them.
+ if (UsernameNamespace == null || !UsernameNamespace.getBoolean("enabled"))
+ return false;
+
+ // Check a list of files against a path trying to be accessed.
+ List rules = GetSections(UsernameNamespace.getConfigurationSection("rules"));
+ if (rules != null)
+ {
+ for (ConfigurationSection path : rules)
+ {
+ // Check if the requesting path matches
+ if (localpath.toString().matches(path.getName()))
+ {
+ // Check if they have read permissions
+ if (path.getBoolean("writeable"))
+ return true;
+
+ getLogger().info(String.format("Denied %s modifications to \"%s\" matching rule \"%s\"", session.getUsername(), localpath.toString(), path.getName()));
+ return false;
+ }
+ }
+ }
+
+ return UsernameNamespace.getString("default").equalsIgnoreCase("allow");
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ // Automatically deny.
+ return false;
+ }
+ }
+ });
+
+ sshd.setSubsystemFactories(Collections.singletonList(builder.build()));
+ sshd.setFileSystemFactory(new VirtualFileSystemFactory(FileSystems.getDefault().getPath(getDataFolder().getAbsolutePath()).getParent().getParent()));
}
this.getCommand("mkpasswd").setExecutor(new MkpasswdCommand());
@@ -95,11 +197,16 @@ class SshdPlugin extends JavaPlugin
{
try
{
- sshd.stop();
+ // Terminate any active sessions
+ for (AbstractSession as : sshd.getActiveSessions())
+ as.close(true);
+ // Pass "true" to stop immediately!
+ sshd.stop(true);
}
catch (Exception e)
{
// do nothing
+ e.printStackTrace();
}
}
}
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 5c3e115..69bf66a 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -37,5 +37,34 @@ PasswordType: bcrypt
# Associate each username with a password hash (or the password if the PasswordType is set to PLAIN)
Credentials:
-# user1: password1
-# user2: password2
+ # Username (should match SSH key if using key-based authentication)
+ justasic:
+ # Password hash from /mkpasswd command
+ password: $2a$10$Oqk83FrypRrMF35EDeoQDuidJOQEWBE0joEQ7MJFi/Oeg26wQ3fm2
+ # Whether they can read, write, or have read/write permissions to console.
+ console: RW
+ # SFTP access for this user.
+ sftp:
+ # Whether SFTP is enabled for this user.
+ enabled: true
+ # Whether to deny by default or allow by default
+ default: allow
+ # Rules regarding their SFTP access.
+ # These rules are relative to the server root.
+ # This acts as a chroot for the server root.
+ # Each path can be an absolute path or a regular expression.
+ rules:
+ "/path/to/file":
+ # Whether the user can read the file over SFTP
+ readable: true
+ # Whether the user can write/modify the file over SFTP
+ writeable: true
+ "/path/to/regex/*":
+ readable: true
+ writeable: false
+ "/path/to/directory/":
+ readable: false
+ writeable: true
+ "/another/example/path":
+ readable: false
+ writeable: false