Finish the port to Sponge. Things seem to work well.

This commit is contained in:
Justin Crawford 2019-10-13 01:00:39 -07:00
parent 41b561a88c
commit a7918c68aa
No known key found for this signature in database
GPG Key ID: 0D84DEDBB8EF259C
15 changed files with 472 additions and 477 deletions

124
pom.xml
View File

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.ryanmichela</groupId> <groupId>com.ryanmichela</groupId>
<artifactId>Sponge-SSHD</artifactId> <artifactId>sshd</artifactId>
<version>1.3.7</version> <version>1.3.7</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Sponge-SSHD</name> <name>Sponge-SSHD</name>
@ -56,24 +56,6 @@
<version>0.3.0</version> <version>0.3.0</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-core</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.28</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.28</version>
</dependency>
<dependency> <dependency>
<groupId>jline</groupId> <groupId>jline</groupId>
<artifactId>jline</artifactId> <artifactId>jline</artifactId>
@ -83,7 +65,7 @@
<dependency> <dependency>
<groupId>org.apache.logging.log4j</groupId> <groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId> <artifactId>log4j-core</artifactId>
<version>2.0</version> <version>2.1</version>
</dependency> </dependency>
<dependency> <dependency>
@ -91,25 +73,6 @@
<artifactId>log4j-api</artifactId> <artifactId>log4j-api</artifactId>
<version>2.1</version> <version>2.1</version>
</dependency> </dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</dependency>
<!-- include Sponge API --> <!-- include Sponge API -->
<dependency> <dependency>
<groupId>org.spongepowered</groupId> <groupId>org.spongepowered</groupId>
@ -123,7 +86,7 @@
<!-- Set our default goal to be clean and build a package --> <!-- Set our default goal to be clean and build a package -->
<defaultGoal>clean package</defaultGoal> <defaultGoal>clean package</defaultGoal>
<!-- Include our git hash in the final name --> <!-- Include our git hash in the final name -->
<finalName>${project.name}-${project.version}</finalName> <finalName>${project.artifactId}-${project.version}</finalName>
<resources> <resources>
<resource> <resource>
@ -138,60 +101,37 @@
<!-- Plugins --> <!-- Plugins -->
<plugins> <plugins>
<!-- Run the get git info stuff so we have revisions and whatnot --> <plugin>
<plugin> <artifactId>maven-assembly-plugin</artifactId>
<groupId>pl.project13.maven</groupId> <version>3.1.1</version>
<artifactId>git-commit-id-plugin</artifactId> <executions>
<version>2.2.5</version> <execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<finalName>${project.artifactId}-${project.version}</finalName>
<appendAssemblyId>false</appendAssemblyId>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<configuration> <!-- Compile plugin -->
<dotGitDirectory>${project.basedir}/.git</dotGitDirectory> <plugin>
</configuration>
</plugin>
<!-- Shade plugin (resolves Jar dependencies) -->
<plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.2.1</version> <version>3.7.0</version>
<executions> <configuration>
<execution> <source>1.8</source>
<phase>package</phase> <target>1.8</target>
<goals> <showDeprecation>true</showDeprecation>
<goal>shade</goal> </configuration>
</goals> </plugin>
<configuration>
<minimizeJar>true</minimizeJar>
<!-- Remove jar signing from other jars -->
<filters>
<filter>
<artifact>*:*:*:*</artifact>
<excludes>
<exclude>META-INF/*.RSA</exclude>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
</excludes>
</filter>
</filters>
<createDependencyReducedPom>true</createDependencyReducedPom>
<dependencyReducedPomLocation>${java.io.tmpdir}/dependency-reduced-pom.xml</dependencyReducedPomLocation>
</configuration>
</execution>
</executions>
</plugin>
<!-- Maven templates for our PomData file -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>templating-maven-plugin</artifactId>
<version>1.0-alpha-3</version>
<executions>
<execution>
<id>filter-src</id>
<goals>
<goal>filter-sources</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -0,0 +1,222 @@
package com.ryanmichela.sshd;
// The below code was taken from md_5's BungeeCord project - Justin
/*
Copyright (c) 2012, md_5. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
The name of the author may not be used to endorse or promote products derived
from this software without specific prior written permission.
You may not use the software for commercial software hosting services without
written permission from the author.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
/**
* Simplistic enumeration of all supported color values for chat.
*/
public enum ChatColor
{
/**
* Represents black.
*/
BLACK( '0', "black" ),
/**
* Represents dark blue.
*/
DARK_BLUE( '1', "dark_blue" ),
/**
* Represents dark green.
*/
DARK_GREEN( '2', "dark_green" ),
/**
* Represents dark blue (aqua).
*/
DARK_AQUA( '3', "dark_aqua" ),
/**
* Represents dark red.
*/
DARK_RED( '4', "dark_red" ),
/**
* Represents dark purple.
*/
DARK_PURPLE( '5', "dark_purple" ),
/**
* Represents gold.
*/
GOLD( '6', "gold" ),
/**
* Represents gray.
*/
GRAY( '7', "gray" ),
/**
* Represents dark gray.
*/
DARK_GRAY( '8', "dark_gray" ),
/**
* Represents blue.
*/
BLUE( '9', "blue" ),
/**
* Represents green.
*/
GREEN( 'a', "green" ),
/**
* Represents aqua.
*/
AQUA( 'b', "aqua" ),
/**
* Represents red.
*/
RED( 'c', "red" ),
/**
* Represents light purple.
*/
LIGHT_PURPLE( 'd', "light_purple" ),
/**
* Represents yellow.
*/
YELLOW( 'e', "yellow" ),
/**
* Represents white.
*/
WHITE( 'f', "white" ),
/**
* Represents magical characters that change around randomly.
*/
MAGIC( 'k', "obfuscated" ),
/**
* Makes the text bold.
*/
BOLD( 'l', "bold" ),
/**
* Makes a line appear through the text.
*/
STRIKETHROUGH( 'm', "strikethrough" ),
/**
* Makes the text appear underlined.
*/
UNDERLINE( 'n', "underline" ),
/**
* Makes the text italic.
*/
ITALIC( 'o', "italic" ),
/**
* Resets all previous chat colors or formats.
*/
RESET( 'r', "reset" );
/**
* The special character which prefixes all chat colour codes. Use this if
* you need to dynamically convert colour codes from your custom format.
*/
public static final char COLOR_CHAR = '\u00A7';
public static final String ALL_CODES = "0123456789AaBbCcDdEeFfKkLlMmNnOoRr";
/**
* Pattern to remove all colour codes.
*/
public static final Pattern STRIP_COLOR_PATTERN = Pattern.compile( "(?i)" + String.valueOf( COLOR_CHAR ) + "[0-9A-FK-OR]" );
/**
* Colour instances keyed by their active character.
*/
private static final Map<Character, ChatColor> BY_CHAR = new HashMap<Character, ChatColor>();
/**
* The code appended to {@link #COLOR_CHAR} to make usable colour.
*/
private final char code;
/**
* This colour's colour char prefixed by the {@link #COLOR_CHAR}.
*/
private final String toString;
private final String name;
public String getName() { return this.name; }
static
{
for ( ChatColor colour : values() )
{
BY_CHAR.put( colour.code, colour );
}
}
private ChatColor(char code, String name)
{
this.code = code;
this.name = name;
this.toString = new String( new char[]
{
COLOR_CHAR, code
} );
}
@Override
public String toString()
{
return toString;
}
/**
* Strips the given message of all color codes
*
* @param input String to strip of color
* @return A copy of the input string, without any coloring
*/
public static String stripColor(final String input)
{
if ( input == null )
{
return null;
}
return STRIP_COLOR_PATTERN.matcher( input ).replaceAll( "" );
}
public static String translateAlternateColorCodes(char altColorChar, String textToTranslate)
{
char[] b = textToTranslate.toCharArray();
for ( int i = 0; i < b.length - 1; i++ )
{
if ( b[i] == altColorChar && ALL_CODES.indexOf( b[i + 1] ) > -1 )
{
b[i] = ChatColor.COLOR_CHAR;
b[i + 1] = Character.toLowerCase( b[i + 1] );
}
}
return new String( b );
}
/**
* Get the colour represented by the specified code.
*
* @param code the code to search for
* @return the mapped colour, or null if non exists
*/
public static ChatColor getByChar(char code)
{
return BY_CHAR.get( code );
}
}

View File

@ -23,7 +23,7 @@ public class ConfigPasswordAuthenticator implements PasswordAuthenticator {
String ConfigHash = SshdPlugin.GetInstance().config.configNode.getNode("Credentials", username.trim(), "password").getString(); String ConfigHash = SshdPlugin.GetInstance().config.configNode.getNode("Credentials", username.trim(), "password").getString();
if (ConfigHash == null) if (ConfigHash == null)
SshdPlugin.GetLogger().warn("Config has no such user: " + username); SshdPlugin.GetInstance().logger.warn("Config has no such user: " + username);
else else
{ {
try try
@ -69,7 +69,7 @@ public class ConfigPasswordAuthenticator implements PasswordAuthenticator {
} }
} }
SshdPlugin.GetLogger().info("Failed login for " + username + " using " + HashType + "-based password authentication."); SshdPlugin.GetInstance().logger.info("Failed login for " + username + " using " + HashType + "-based password authentication.");
Integer tries = SshdPlugin.GetInstance().LoginRetries; Integer tries = SshdPlugin.GetInstance().LoginRetries;
try try
@ -83,7 +83,7 @@ public class ConfigPasswordAuthenticator implements PasswordAuthenticator {
if (this.FailCounts.get(username) >= tries) if (this.FailCounts.get(username) >= tries)
{ {
this.FailCounts.put(username, 0); this.FailCounts.put(username, 0);
SshdPlugin.GetLogger().info("Too many failures for " + username + ", disconnecting."); SshdPlugin.GetInstance().logger.info("Too many failures for " + username + ", disconnecting.");
ss.close(true); ss.close(true);
} }
} }

View File

@ -6,13 +6,23 @@ package com.ryanmichela.sshd;
import jline.console.completer.Completer; import jline.console.completer.Completer;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.scheduler.SpongeExecutorService;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
public class ConsoleCommandCompleter //implements Completer public class ConsoleCommandCompleter implements Completer
{ {
/* private SpongeExecutorService MinecraftExecutor;
public ConsoleCommandCompleter()
{
super();
this.MinecraftExecutor = Sponge.getScheduler().createSyncExecutor(SshdPlugin.GetInstance());
}
public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) public int complete(final String buffer, final int cursor, final List<CharSequence> candidates)
{ {
Waitable<List<String>> waitable = new Waitable<List<String>>() Waitable<List<String>> waitable = new Waitable<List<String>>()
@ -20,12 +30,11 @@ public class ConsoleCommandCompleter //implements Completer
@Override @Override
protected List<String> evaluate() protected List<String> evaluate()
{ {
CommandMap commandMap = ReflectionUtil.getProtectedValue(Bukkit.getServer(), "commandMap"); return Sponge.getCommandManager().getSuggestions(Sponge.getServer().getConsole(), buffer, null);
return commandMap.tabComplete(Bukkit.getServer().getConsoleSender(), buffer);
} }
}; };
Bukkit.getScheduler().runTask(SshdPlugin.instance, waitable); this.MinecraftExecutor.execute(waitable);
try try
{ {
List<String> offers = waitable.get(); List<String> offers = waitable.get();
@ -47,7 +56,7 @@ public class ConsoleCommandCompleter //implements Completer
} }
catch (ExecutionException e) catch (ExecutionException e)
{ {
SshdPlugin.instance.getLogger().log(Level.WARNING, "Unhandled exception when tab completing", e); SshdPlugin.GetInstance().logger.warn("Unhandled exception when tab completing", e);
} }
catch (InterruptedException e) catch (InterruptedException e)
{ {
@ -55,6 +64,5 @@ public class ConsoleCommandCompleter //implements Completer
} }
return cursor; return cursor;
} }
*/
} }

View File

@ -55,14 +55,14 @@ public class ConsoleCommandFactory implements CommandFactory {
{ {
try try
{ {
SshdPlugin.GetLogger() SshdPlugin.GetInstance().logger
.info("[U: " + environment.getEnv().get(Environment.ENV_USER) + "] " + command); .info("[U: " + environment.getEnv().get(Environment.ENV_USER) + "] " + command);
Sponge.getCommandManager().process(Sponge.getServer().getConsole(), command); Sponge.getCommandManager().process(Sponge.getServer().getConsole(), command);
} }
catch (Exception e) catch (Exception e)
{ {
SshdPlugin.GetLogger().error("Error processing command from SSH -" + e.getMessage()); SshdPlugin.GetInstance().logger.error("Error processing command from SSH -" + e.getMessage());
} }
finally finally
{ {

View File

@ -9,20 +9,12 @@ import org.fusesource.jansi.Ansi;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.Map; import java.util.Map;
import java.util.logging.Formatter;
import java.util.logging.LogRecord;
public class ConsoleLogFormatter extends Formatter { public class ConsoleLogFormatter
{
private SimpleDateFormat dateFormat; private static final Map<ChatColor, String> replacements = new EnumMap<ChatColor, String>(ChatColor.class);
private static final Map<ChatColor, String> replacements = new EnumMap<ChatColor, String>(ChatColor.class);
public ConsoleLogFormatter() {
this.dateFormat = new SimpleDateFormat("HH:mm:ss");
}
public static String ColorizeString(String str) public static String ColorizeString(String str)
{ {
@ -66,43 +58,5 @@ public class ConsoleLogFormatter extends Formatter {
result += Ansi.ansi().reset().toString(); result += Ansi.ansi().reset().toString();
return result; return result;
} }
public String format(LogRecord logrecord)
{
try
{
Class.forName("org.bukkit.craftbukkit.command.ColouredConsoleSender");
}
catch (ClassNotFoundException ignored)
{
// MEANS WE'RE ON PAPER/TACO/OTHER SHIT
colorize(logrecord);
}
StringBuilder stringbuilder = new StringBuilder();
stringbuilder.append(" [");
stringbuilder.append(this.dateFormat.format(logrecord.getMillis())).append(" ");
stringbuilder.append(logrecord.getLevel().getName()).append("]: ");
stringbuilder.append(this.formatMessage(logrecord));
stringbuilder.append('\n');
Throwable throwable = logrecord.getThrown();
if (throwable != null)
{
StringWriter stringwriter = new StringWriter();
throwable.printStackTrace(new PrintWriter(stringwriter));
stringbuilder.append(stringwriter.toString());
}
return stringbuilder.toString().replace("\n", "\r\n");
}
private void colorize(LogRecord logrecord)
{
String result = ColorizeString(logrecord.getMessage());
logrecord.setMessage(result);
}
} }

View File

@ -3,7 +3,6 @@ package com.ryanmichela.sshd;
import com.ryanmichela.sshd.ConsoleCommandCompleter; import com.ryanmichela.sshd.ConsoleCommandCompleter;
import com.ryanmichela.sshd.ConsoleLogFormatter; import com.ryanmichela.sshd.ConsoleLogFormatter;
import com.ryanmichela.sshd.FlushyOutputStream; import com.ryanmichela.sshd.FlushyOutputStream;
import com.ryanmichela.sshd.FlushyStreamHandler;
import com.ryanmichela.sshd.SshTerminal; import com.ryanmichela.sshd.SshTerminal;
import com.ryanmichela.sshd.SshdPlugin; import com.ryanmichela.sshd.SshdPlugin;
import com.ryanmichela.sshd.StreamHandlerAppender; import com.ryanmichela.sshd.StreamHandlerAppender;
@ -34,7 +33,6 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.StreamHandler; import java.util.logging.StreamHandler;
@ -97,10 +95,9 @@ public class ConsoleShellFactory implements ShellFactory {
{ {
this.ConsoleReader = new ConsoleReader(in, new FlushyOutputStream(out), new SshTerminal()); this.ConsoleReader = new ConsoleReader(in, new FlushyOutputStream(out), new SshTerminal());
this.ConsoleReader.setExpandEvents(true); this.ConsoleReader.setExpandEvents(true);
//this.ConsoleReader.addCompleter(new ConsoleCommandCompleter()); this.ConsoleReader.addCompleter(new ConsoleCommandCompleter());
StreamHandler streamHandler = new FlushyStreamHandler(out, new ConsoleLogFormatter(), this.ConsoleReader); this.streamHandlerAppender = new StreamHandlerAppender(this.ConsoleReader);
this.streamHandlerAppender = new StreamHandlerAppender(streamHandler);
((Logger)LogManager.getRootLogger()).addAppender(this.streamHandlerAppender); ((Logger)LogManager.getRootLogger()).addAppender(this.streamHandlerAppender);
@ -132,7 +129,7 @@ public class ConsoleShellFactory implements ShellFactory {
printPreamble(this.ConsoleReader); printPreamble(this.ConsoleReader);
while (true) while (true)
{ {
String command = this.ConsoleReader.readLine("\r>", null); String command = this.ConsoleReader.readLine("\r> ", null);
// The user sent CTRL+D to close the shell, terminate the session. // The user sent CTRL+D to close the shell, terminate the session.
if (command == null) if (command == null)
break; break;
@ -153,7 +150,7 @@ public class ConsoleShellFactory implements ShellFactory {
// Hide the mkpasswd command input from other users. // Hide the mkpasswd command input from other users.
Boolean mkpasswd = command.split(" ")[0].equals("mkpasswd"); Boolean mkpasswd = command.split(" ")[0].equals("mkpasswd");
MinecraftExecutor.schedule(() -> MinecraftExecutor.submit(() ->
{ {
if (SshdPlugin.GetInstance().Mode.equals("RPC") && command.startsWith("rpc")) if (SshdPlugin.GetInstance().Mode.equals("RPC") && command.startsWith("rpc"))
{ {
@ -168,7 +165,7 @@ public class ConsoleShellFactory implements ShellFactory {
// our plugin and the connected client. // our plugin and the connected client.
if (!mkpasswd) if (!mkpasswd)
{ {
SshdPlugin.GetLogger().info("<" + this.Username + "> " + command); SshdPlugin.GetInstance().logger.info("<" + this.Username + "> " + command);
CmdManager.process(Sponge.getServer().getConsole(), command); CmdManager.process(Sponge.getServer().getConsole(), command);
} }
else else
@ -176,16 +173,16 @@ public class ConsoleShellFactory implements ShellFactory {
CmdManager.process(this.SshdCommandSender, command); CmdManager.process(this.SshdCommandSender, command);
} }
} }
}, 0, TimeUnit.SECONDS); });
} }
} }
catch (IOException e) catch (IOException e)
{ {
SshdPlugin.GetLogger().error("Error processing command from SSH", e); SshdPlugin.GetInstance().logger.error("Error processing command from SSH", e);
} }
finally finally
{ {
SshdPlugin.GetLogger().info(this.Username + " disconnected from SSH."); SshdPlugin.GetInstance().logger.info(this.Username + " disconnected from SSH.");
callback.onExit(0); callback.onExit(0);
} }
} }
@ -203,7 +200,7 @@ public class ConsoleShellFactory implements ShellFactory {
} }
catch (FileNotFoundException e) catch (FileNotFoundException e)
{ {
SshdPlugin.GetLogger().warn("Could not open " + f + ": File does not exist."); SshdPlugin.GetInstance().logger.warn("Could not open " + f + ": File does not exist.");
// Not showing the SSH motd is not a fatal failure, let the session continue. // Not showing the SSH motd is not a fatal failure, let the session continue.
} }
@ -222,7 +219,7 @@ public class ConsoleShellFactory implements ShellFactory {
cr.println(str + "\r"); cr.println(str + "\r");
cr.println(ConsoleLogFormatter.ColorizeString(Sponge.getServer().getMotd().toPlain()).replaceAll("\n", "\r\n")); cr.println(ConsoleLogFormatter.ColorizeString(Sponge.getServer().getMotd().toPlain()).replaceAll("\n", "\r\n"));
cr.println("\r"); cr.println("\r");
cr.println("Type 'exit' to exit the shell." + "\r"); cr.println("Type 'exit' or CTRL+D to exit the shell." + "\r");
cr.println("===============================================" + "\r"); cr.println("===============================================" + "\r");
} }
} }

View File

@ -1,49 +0,0 @@
package com.ryanmichela.sshd;
import jline.console.ConsoleReader;
import org.apache.sshd.common.SshException;
import java.io.IOException;
import java.io.OutputStream;
import java.util.logging.*;
/**
* Copyright 2013 Ryan Michela
*/
public class FlushyStreamHandler extends StreamHandler {
private ConsoleReader reader;
public FlushyStreamHandler(OutputStream out, Formatter formatter, ConsoleReader reader) {
super(out, formatter);
this.reader = reader;
setLevel(Level.INFO);
}
@Override
public synchronized void publish(LogRecord record) {
record.setMessage(record.getMessage().replace("\n", "\n\r"));
super.publish(record);
flush();
}
@Override
public synchronized void flush() {
try {
reader.print(ConsoleReader.RESET_LINE + "");
reader.flush();
super.flush();
try {
reader.drawLine();
} catch (Throwable ex) {
reader.getCursorBuffer().clear();
}
reader.flush();
super.flush();
} catch (SshException ex) {
// do nothing
} catch (IOException ex) {
Logger.getLogger(FlushyStreamHandler.class.getName()).log(Level.SEVERE, null, ex);
}
}
}

View File

@ -35,7 +35,7 @@ public class MkpasswdCommand implements CommandExecutor
.executor((CommandSource source, CommandContext args) -> { .executor((CommandSource source, CommandContext args) -> {
try try
{ {
source.sendMessage(Text.of("\u00A79Your Hash: " + Cryptography.PBKDF2_HashPassword(args.<String>getOne("message").get()))); source.sendMessage(Text.of("\u00A79Your Hash: " + Cryptography.PBKDF2_HashPassword(args.<String>getOne("password").get())));
} }
catch (Exception e) catch (Exception e)
{ {
@ -53,7 +53,7 @@ public class MkpasswdCommand implements CommandExecutor
.executor((CommandSource source, CommandContext args) -> { .executor((CommandSource source, CommandContext args) -> {
try try
{ {
source.sendMessage(Text.of("\u00A79Your Hash: " + Cryptography.BCrypt_HashPassword(args.<String>getOne("message").get()))); source.sendMessage(Text.of("\u00A79Your Hash: " + Cryptography.BCrypt_HashPassword(args.<String>getOne("password").get())));
} }
catch (Exception e) catch (Exception e)
{ {
@ -71,7 +71,7 @@ public class MkpasswdCommand implements CommandExecutor
.executor((CommandSource source, CommandContext args) -> { .executor((CommandSource source, CommandContext args) -> {
try try
{ {
source.sendMessage(Text.of("\u00A79Your Hash: " + Cryptography.SHA256_HashPassword(args.<String>getOne("message").get()))); source.sendMessage(Text.of("\u00A79Your Hash: " + Cryptography.SHA256_HashPassword(args.<String>getOne("password").get())));
} }
catch (Exception e) catch (Exception e)
{ {
@ -82,33 +82,22 @@ public class MkpasswdCommand implements CommandExecutor
}) })
.build(); .build();
// The plain text encryption method // The plain text "encryption" method
CommandSpec plain = CommandSpec.builder() CommandSpec plain = CommandSpec.builder()
.description(Text.of("Plain text password (insecure)")) .description(Text.of("Plain text password (insecure)"))
.permission("sshd.mkpasswd.plain") .permission("sshd.mkpasswd.plain")
.arguments(GenericArguments.remainingJoinedStrings(Text.of("password")))
.executor((CommandSource source, CommandContext args) -> { .executor((CommandSource source, CommandContext args) -> {
source.sendMessage(Text.of("Bro... It's literally your unhashed password.")); source.sendMessage(Text.of("Bro... It's literally your unhashed password."));
return CommandResult.success(); return CommandResult.success();
}) })
.build(); .build();
// The "help" command to show syntax usage
CommandSpec helpcmd = CommandSpec.builder()
.description(Text.of("Display's mkpasswd help"))
.permission("sshd.mkpasswd.help")
.executor((CommandSource source, CommandContext args) -> {
source.sendMessage(Text.of("\u00A7a/mkpasswd <help|hash> <password>\u00A7r"));
source.sendMessage(Text.of("\u00A79Supported Hashes: SHA256, PBKDF2, BCRYPT, PLAIN\u00A7r"));
return CommandResult.success();
})
.build();
// the root "mkpasswd" command // the root "mkpasswd" command
cmdspec = CommandSpec.builder() cmdspec = CommandSpec.builder()
.description(Text.of("Create an SSHd password using hashes")) .description(Text.of("Create an SSHd password using hashes"))
.extendedDescription(Text.of("Supported Hashes: SHA256, PBKDF2, BCRYPT, PLAIN")) .extendedDescription(Text.of("Supported Hashes: SHA256, PBKDF2, BCRYPT, PLAIN"))
.permission("sshd.mkpasswd") .permission("sshd.mkpasswd")
.child(helpcmd, "help", "h")
.child(plain, "plain") .child(plain, "plain")
.child(sha256, "sha256") .child(sha256, "sha256")
.child(bcrypt, "bcrypt") .child(bcrypt, "bcrypt")
@ -125,7 +114,7 @@ public class MkpasswdCommand implements CommandExecutor
public CommandResult execute(CommandSource src, CommandContext args) throws CommandException public CommandResult execute(CommandSource src, CommandContext args) throws CommandException
{ {
// This command doesn't do anything. // This command doesn't do anything.
src.sendMessage(Text.of("\u00A7a/mkpasswd <help|hash> <password>\u00A7r")); src.sendMessage(Text.of("\u00A7a/mkpasswd <hash> <password...>\u00A7r"));
src.sendMessage(Text.of("\u00A79Supported Hashes: SHA256, PBKDF2, BCRYPT, PLAIN\u00A7r")); src.sendMessage(Text.of("\u00A79Supported Hashes: SHA256, PBKDF2, BCRYPT, PLAIN\u00A7r"));
return CommandResult.success(); return CommandResult.success();
} }

View File

@ -46,7 +46,7 @@ public class PublicKeyAuthenticator implements PublickeyAuthenticator
} }
else else
{ {
SshdPlugin.GetLogger().info( SshdPlugin.GetInstance().logger.info(
username + " failed authentication via SSH session using key file " + keyFile.getAbsolutePath()); username + " failed authentication via SSH session using key file " + keyFile.getAbsolutePath());
} }
@ -59,7 +59,7 @@ public class PublicKeyAuthenticator implements PublickeyAuthenticator
if (this.FailCounts.get(username) >= tries) if (this.FailCounts.get(username) >= tries)
{ {
this.FailCounts.put(username, 0); this.FailCounts.put(username, 0);
SshdPlugin.GetLogger().info("Too many failures for " + username + ", disconnecting."); SshdPlugin.GetInstance().logger.info("Too many failures for " + username + ", disconnecting.");
session.close(true); session.close(true);
} }
@ -67,12 +67,12 @@ public class PublicKeyAuthenticator implements PublickeyAuthenticator
} }
catch (Exception e) catch (Exception e)
{ {
SshdPlugin.GetLogger().error("Failed to process public key " + keyFile.getAbsolutePath() + " " + e.getMessage()); SshdPlugin.GetInstance().logger.error("Failed to process public key " + keyFile.getAbsolutePath() + " " + e.getMessage());
} }
} }
else else
{ {
SshdPlugin.GetLogger().error("Could not locate public key for " + username SshdPlugin.GetInstance().logger.error("Could not locate public key for " + username
+ ". Make sure the user's key is named the same as their user name " + ". Make sure the user's key is named the same as their user name "
+ "without a file extension."); + "without a file extension.");
} }

View File

@ -1,96 +0,0 @@
package com.ryanmichela.sshd;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* Copyright 2013 Ryan Michela
*/
public class ReflectionUtil {
public static void setProtectedValue(Object o, String field, Object newValue) {
setProtectedValue(o.getClass(), o, field, newValue);
}
public static void setProtectedValue(Class c, String field, Object newValue) {
setProtectedValue(c, null, field, newValue);
}
public static void setProtectedValue(Class c, Object o, String field, Object newValue) {
try {
Field f = c.getDeclaredField(field);
f.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);
f.set(o, newValue);
} catch (NoSuchFieldException | IllegalAccessException ex) {
System.out.println("*** " + c.getName() + ":" + ex);
}
}
public static <T> T getProtectedValue(Object obj, String fieldName) {
try {
Class c = obj.getClass();
while (c != Object.class) {
Field[] fields = c.getDeclaredFields();
for (Field f : fields) {
if (f.getName() == fieldName) {
f.setAccessible(true);
return (T) f.get(obj);
}
}
c = c.getSuperclass();
}
System.out.println("*** " + obj.getClass().getName() + ":No such field");
return null;
} catch (Exception ex) {
System.out.println("*** " + obj.getClass().getName() + ":" + ex);
return null;
}
}
public static <T> T getProtectedValue(Class c, String field) {
try {
Field f = c.getDeclaredField(field);
f.setAccessible(true);
return (T) f.get(c);
} catch (Exception ex) {
System.out.println("*** " + c.getName() + ":" + ex);
return null;
}
}
public static Object invokeProtectedMethod(Class c, String method, Object... args) {
return invokeProtectedMethod(c, null, method, args);
}
public static Object invokeProtectedMethod(Object o, String method, Object... args) {
return invokeProtectedMethod(o.getClass(), o, method, args);
}
public static Object invokeProtectedMethod(Class c, Object o, String method, Object... args) {
try {
Class[] pTypes = new Class[args.length];
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Integer) {
pTypes[i] = int.class;
} else {
pTypes[i] = args[i].getClass();
}
}
Method m = c.getDeclaredMethod(method, pTypes);
m.setAccessible(true);
return m.invoke(o, args);
} catch (Exception ex) {
System.out.println("*** " + c.getName() + "." + method + "(): " + ex);
return null;
}
}
}

View File

@ -51,7 +51,7 @@ public class SshdPlugin
private static SshdPlugin instance; private static SshdPlugin instance;
@Inject @Inject
public static Logger logger; public Logger logger;
@Inject @Inject
@DefaultConfig(sharedRoot = false) @DefaultConfig(sharedRoot = false)
@ -71,6 +71,11 @@ public class SshdPlugin
config = new Config(); config = new Config();
config.setup(); config.setup();
// Make sure our authorized_keys folder exists
File authorizedKeys = new File(this.ConfigDir.toFile(), "authorized_keys");
if (!authorizedKeys.exists())
authorizedKeys.mkdirs();
// Now include it in our dealio here // Now include it in our dealio here
this.Mode = config.configNode.getNode("Mode").getString(); this.Mode = config.configNode.getNode("Mode").getString();
this.PasswordType = config.configNode.getNode("PasswordType").getString(); this.PasswordType = config.configNode.getNode("PasswordType").getString();
@ -99,7 +104,6 @@ public class SshdPlugin
sshd.setHost(this.ListenAddress.equals("all") ? null : this.ListenAddress); sshd.setHost(this.ListenAddress.equals("all") ? null : this.ListenAddress);
File hostKey = new File(this.ConfigDir.toFile(), "hostkey"); File hostKey = new File(this.ConfigDir.toFile(), "hostkey");
File authorizedKeys = new File(this.ConfigDir.toFile(), "authorized_keys");
sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(hostKey.toPath())); sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(hostKey.toPath()));
sshd.setShellFactory(new ConsoleShellFactory()); sshd.setShellFactory(new ConsoleShellFactory());
@ -109,8 +113,7 @@ public class SshdPlugin
if (this.EnableSFTP) if (this.EnableSFTP)
{ {
sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory())); sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
sshd.setFileSystemFactory( sshd.setFileSystemFactory(new VirtualFileSystemFactory(this.ConfigDir.getParent().getParent()));
new VirtualFileSystemFactory(this.ConfigDir.getParent().getParent()));
} }
MkpasswdCommand.BuildCommand(); MkpasswdCommand.BuildCommand();
@ -133,8 +136,8 @@ public class SshdPlugin
return instance; return instance;
} }
public static Logger GetLogger() public Logger GetLogger()
{ {
return logger; return this.logger;
} }
} }

View File

@ -1,48 +1,75 @@
package com.ryanmichela.sshd; package com.ryanmichela.sshd;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.ErrorHandler; import org.apache.logging.log4j.core.ErrorHandler;
import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.layout.PatternLayout;
import jline.console.ConsoleReader;
import org.apache.sshd.common.SshException;
import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.util.UUID; import java.util.UUID;
import java.util.logging.LogRecord; import java.nio.charset.Charset;
import java.util.logging.StreamHandler;
/** /**
* Copyright 2014 Ryan Michela * Copyright 2014 Ryan Michela
*/ */
public class StreamHandlerAppender implements Appender { public class StreamHandlerAppender implements Appender
{
private StreamHandler streamHandler; private ConsoleReader console;
private UUID uuid; private UUID uuid;
private PatternLayout MinecraftLayout = PatternLayout.newBuilder().withPattern("%highlightError{[%d{HH:mm:ss} %level] [%logger]: %minecraftFormatting{%msg}%xEx}").build();
private PatternLayout MojangLayout = PatternLayout.newBuilder().withPattern("%highlightError{[%d{HH:mm:ss} %level]: %minecraftFormatting{%msg}%xEx}").build();
public StreamHandlerAppender(StreamHandler streamHandler) { public StreamHandlerAppender(ConsoleReader console)
this.streamHandler = streamHandler; {
this.console = console;
uuid = UUID.randomUUID(); uuid = UUID.randomUUID();
} }
@Override @Override
public void append(LogEvent logEvent) { public void append(LogEvent logEvent)
java.util.logging.Level level; {
if(true) return;
if (logEvent.getLevel().equals(org.apache.logging.log4j.Level.DEBUG)) {
level = java.util.logging.Level.FINE; if (logEvent.getLevel() == Level.DEBUG || logEvent.getLevel() == Level.TRACE)
} else if (logEvent.getLevel().equals(org.apache.logging.log4j.Level.INFO)) { return;
level = java.util.logging.Level.INFO;
} else if (logEvent.getLevel().equals(org.apache.logging.log4j.Level.WARN)) { try
level = java.util.logging.Level.WARNING; {
} else if (logEvent.getLevel().equals(org.apache.logging.log4j.Level.ERROR)) { // Delete the jline's `> ` character
level = java.util.logging.Level.SEVERE; this.console.print(ConsoleReader.BACKSPACE + "" + ConsoleReader.BACKSPACE);
} else { // Print our message
level = java.util.logging.Level.INFO; if (logEvent.getLoggerName().matches("net\\.minecraft\\..*|com\\.mojang\\..*"))
this.console.println(ConsoleLogFormatter.ColorizeString(this.MojangLayout.toSerializable(logEvent)).replaceAll("\n", "\r\n") + "\r");
else
this.console.println(ConsoleLogFormatter.ColorizeString(this.MinecraftLayout.toSerializable(logEvent)).replaceAll("\n", "\r\n") + "\r");
// Reset the console (colors, formatting, etc)
this.console.print(ConsoleReader.RESET_LINE + "");
try
{
// Attempt to draw new console line
this.console.drawLine();
}
catch (Throwable ex)
{
this.console.getCursorBuffer().clear();
}
// Push it to the end user.
this.console.flush();
}
catch (SshException ex)
{
// do nothing
}
catch (IOException ex)
{
ex.printStackTrace();
} }
String message = logEvent.getMessage().getFormattedMessage();
LogRecord logRecord = new LogRecord(level, message);
streamHandler.publish(logRecord);
} }
@Override @Override

View File

@ -4,6 +4,7 @@ import com.ryanmichela.sshd.SshdPlugin;
import com.ryanmichela.sshd.ConsoleShellFactory; import com.ryanmichela.sshd.ConsoleShellFactory;
import com.ryanmichela.sshd.ConsoleLogFormatter; import com.ryanmichela.sshd.ConsoleLogFormatter;
import org.spongepowered.api.command.source.ConsoleSource; import org.spongepowered.api.command.source.ConsoleSource;
import org.checkerframework.checker.nullness.Opt;
import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.text.Text; import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.channel.MessageChannel; import org.spongepowered.api.text.channel.MessageChannel;
@ -13,10 +14,13 @@ import org.spongepowered.api.service.permission.SubjectData;
import org.spongepowered.api.service.permission.SubjectReference; import org.spongepowered.api.service.permission.SubjectReference;
import org.spongepowered.api.service.context.Context; import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.util.Tristate; import org.spongepowered.api.util.Tristate;
import jline.console.ConsoleReader;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.Optional; import java.util.Optional;
import java.util.List; import java.util.List;
@ -28,17 +32,19 @@ public class SSHDCommandSender implements ConsoleSource
private Subject subjectDelegate; private Subject subjectDelegate;
// Set by the upstream allocating function // Set by the upstream allocating function
public ConsoleShellFactory.ConsoleShell console; public ConsoleShellFactory.ConsoleShell console;
private UUID uuid = UUID.randomUUID();
// This is an override for Sponge to work with the SSH consoles. // This is an override for Sponge to work with the SSH consoles.
@Override
public void sendMessage(Text message) public void sendMessage(Text message)
{ {
this.sendRawMessage(message.toPlain() + "\r"); this.sendRawMessage(message.toPlain());
} }
// Back port from Spigot/BungeeCord-style API calls. // Back port from Spigot/BungeeCord-style API calls.
public void sendMessage(String message) public void sendMessage(String message)
{ {
this.sendRawMessage(message + "\r"); this.sendRawMessage(message);
} }
public void sendRawMessage(String message) public void sendRawMessage(String message)
@ -48,8 +54,9 @@ public class SSHDCommandSender implements ConsoleSource
return; return;
try try
{ {
this.console.ConsoleReader.println(ConsoleLogFormatter.ColorizeString(message).replace("\n", "\n\r")); this.console.ConsoleReader.print(ConsoleReader.BACKSPACE + "" + ConsoleReader.BACKSPACE);
this.console.ConsoleReader.print(this.console.ConsoleReader.RESET_LINE + ""); this.console.ConsoleReader.println(ConsoleLogFormatter.ColorizeString(message).replaceAll("\n", "\n\r") + "\r");
this.console.ConsoleReader.print(ConsoleReader.RESET_LINE + "");
this.console.ConsoleReader.flush(); this.console.ConsoleReader.flush();
try try
{ {
@ -63,7 +70,7 @@ public class SSHDCommandSender implements ConsoleSource
} }
catch (IOException e) catch (IOException e)
{ {
SshdPlugin.GetLogger().error("Error sending message to SSHDCommandSender", e); SshdPlugin.GetInstance().logger.error("Error sending message to SSHDCommandSender", e);
} }
} }
@ -86,75 +93,86 @@ public class SSHDCommandSender implements ConsoleSource
return "SSHD Console"; return "SSHD Console";
} }
@Override @Override
public String getIdentifier() { public String getIdentifier() {
// TODO Auto-generated method stub return uuid.toString();
return null; }
}
@Override
@Override public Set<Context> getActiveContexts()
public Set<Context> getActiveContexts() { {
// TODO Auto-generated method stub // No clue what any of this does but sponge needs it to work with this class. - Justin
return null; Set<Context> set = new HashSet<Context>();
} set.add(new Context(Context.USER_KEY, "SSHD"));
return set;
@Override }
public boolean isSubjectDataPersisted() {
// TODO Auto-generated method stub @Override
return false; public boolean isSubjectDataPersisted()
} {
// TODO Auto-generated method stub
@Override return false;
public boolean isChildOf(Set<Context> contexts, SubjectReference parent) { }
// TODO Auto-generated method stub
return false; @Override
} public boolean isChildOf(Set<Context> contexts, SubjectReference parent)
{
@Override // TODO Auto-generated method stub
public SubjectData getTransientSubjectData() { return false;
// TODO Auto-generated method stub }
return null;
} @Override
public SubjectData getTransientSubjectData()
@Override {
public SubjectData getSubjectData() { // TODO Auto-generated method stub
// TODO Auto-generated method stub return null;
return null; }
}
@Override
@Override public SubjectData getSubjectData()
public Tristate getPermissionValue(Set<Context> contexts, String permission) { {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return null; return null;
} }
@Override @Override
public List<SubjectReference> getParents(Set<Context> contexts) { public Tristate getPermissionValue(Set<Context> contexts, String permission)
// TODO Auto-generated method stub {
return null; // We're allowed to view all permissions.
} return Tristate.TRUE;
}
@Override
public Optional<String> getOption(Set<Context> contexts, String key) { @Override
// TODO Auto-generated method stub public List<SubjectReference> getParents(Set<Context> contexts)
return null; {
} // TODO Auto-generated method stub
return null;
@Override }
public SubjectCollection getContainingCollection() {
// TODO Auto-generated method stub @Override
return null; public Optional<String> getOption(Set<Context> contexts, String key)
} {
return Optional.empty();
@Override }
public Optional<CommandSource> getCommandSource() {
// TODO Auto-generated method stub @Override
return null; public SubjectCollection getContainingCollection()
} {
// TODO Auto-generated method stub
@Override return null;
public SubjectReference asSubjectReference() { }
// TODO Auto-generated method stub
return null; @Override
} public Optional<CommandSource> getCommandSource()
{
// TODO Auto-generated method stub
return Optional.of(this);
}
@Override
public SubjectReference asSubjectReference()
{
// TODO Auto-generated method stub
return null;
}
} }

View File

@ -74,54 +74,36 @@ public class Config
("The IP addresses(s) the SSH server will listen on. Use a comma separated list for multiple addresses.\n" + ("The IP addresses(s) the SSH server will listen on. Use a comma separated list for multiple addresses.\n" +
"Leave as \"all\" for all addresses."); "Leave as \"all\" for all addresses.");
this.configNode.getNode("Port").setValue("1025").setComment( this.configNode.getNode("Port").setValue("1025").setComment(
"# The port the SSH server will listen on. Note that anything above 1024 will require you to run\n" + "The port the SSH server will listen on. Note that anything above 1024 will require you to run\n" +
"# the whole minecraft server with elevated privileges, this is not recommended and you should\n" + "the whole minecraft server with elevated privileges, this is not recommended and you should\n" +
"# use iptables to route packets from a lower port."); "use iptables to route packets from a lower port.");
this.configNode.getNode("Mode").setValue("DEFAULT").setComment("Operational mode. Don't touch if you don't know what you're doing. Can be either DEFAULT or RPC"); this.configNode.getNode("Mode").setValue("DEFAULT").setComment("Operational mode. Don't touch if you don't know what you're doing. Can be either DEFAULT or RPC");
this.configNode.getNode("EnableSFTP").setValue("true").setComment( this.configNode.getNode("EnableSFTP").setValue("true").setComment(
"# Enable built-in SFTP server or not. You'll be able to connect and upload/download files via SFTP protocol.\n" + "Enable built-in SFTP server or not. You'll be able to connect and upload/download files via SFTP protocol.\n" +
"# Might be useful for testing purposes as well , i. e. docker containers."); "Might be useful for testing purposes as well , i. e. docker containers.");
this.configNode.getNode("LoginRetries").setValue("3").setComment( this.configNode.getNode("LoginRetries").setValue("3").setComment(
"# Number of times a person can fail to use an SSH key or enter a password\n" + "Number of times a person can fail to use an SSH key or enter a password\n" +
"# before it terminates the connection."); "before it terminates the connection.");
this.configNode.getNode("PasswordType").setValue("bcrypt").setComment this.configNode.getNode("PasswordType").setValue("bcrypt").setComment
("########################################################################################\n" + ("########################################################################################\n" +
"# By default, only public key authentication is enabled. This is the most secure mode.\n" + "By default, only public key authentication is enabled. This is the most secure mode.\n" +
"# To authorize a user to login with their public key, install their key using the\n" + "To authorize a user to login with their public key, install their key using the\n" +
"# OpenSSH authorized_keys file format in the authorized_users directory. Name the key\n" + "OpenSSH authorized_keys file format in the authorized_users directory. Name the key\n" +
"# file with the user's username and no extension. Note: If you want to let a user have\n" + "file with the user's username and no extension. Note: If you want to let a user have\n" +
"# many keys, you can append the keys to their file in authorized_users.\n" + "many keys, you can append the keys to their file in authorized_users.\n" +
"########################################################################################\n" + "########################################################################################\n" +
"For less secure username and password based authentication, complete the sections below.\n" + "For less secure username and password based authentication, complete the sections below.\n" +
"\n" + "\n" +
"# Type of hashing to use for the passwords below.\n" + "Type of hashing to use for the passwords below.\n" +
"# Options are: PLAIN (insecure), bcrypt, pbkdf2, sha256\n" + "Options are: PLAIN (insecure), bcrypt, pbkdf2, sha256\n" +
"#\n" + "\n" +
"# You can use the console/in-game command `/mkpasswd [hash] PASSWORD` to\n" + "You can use the console/in-game command `/mkpasswd [hash] PASSWORD` to\n" +
"# generate a password hash string then copy it for your passwords below.\n" + "generate a password hash string then copy it for your passwords below.\n" +
"# You can also use `/mkpasswd help` to see what algorithms are supported."); "You can also use `/mkpasswd help` to see what algorithms are supported.");
this.configNode.getNode("Credentials").setComment("# Associate each username with a password hash (or the password if the PasswordType is set to PLAIN)"); this.configNode.getNode("Credentials").setComment("Associate each username with a password hash (or the password if the PasswordType is set to PLAIN)");
this.configNode.getNode("Credentials", "user1", "password").setValue("MySecretPassword"); this.configNode.getNode("Credentials", "user1", "password").setValue("MySecretPassword");
this.configNode.getNode("Credentials", "user2", "password").setValue("MyBestFriendsPassword"); this.configNode.getNode("Credentials", "user2", "password").setValue("MyBestFriendsPassword");
//this.configNode.getNode("").setValue("").setComment("");
/*
this.Mode = config.configNode.getNode("Mode").getString();
this.PasswordType = config.configNode.getNode("PasswordType").getString();
this.ListenAddress = config.configNode.getNode("ListenAddress").getString();
this.Port = config.configNode.getNode("Port").getInt();
this.LoginRetries = config.configNode.getNode("LoginRetries").getInt();
this.EnableSFTP = config.configNode.getNode("EnableSFTP").getBoolean();
this.configNode.getNode("mysql").setComment("MySQL database for Whitelisting");
this.configNode.getNode("mysql", "port").setValue(3306).setComment("MySQL server port");
this.configNode.getNode("mysql", "host").setValue("localhost").setComment("MySQL server to connect to");
this.configNode.getNode("mysql", "database").setValue("WhitelistSync").setComment("MySQL database for Whitelisting");
this.configNode.getNode("mysql", "username").setValue("Whitelist").setComment("MySQL username for the database");
this.configNode.getNode("mysql", "password").setValue("letmein").setComment("MySQL password for the database");
*/
} }
} }