hockey did an oopsie

This commit is contained in:
Super_ 2019-01-22 07:40:17 -05:00
parent dded19e254
commit a6b278e7cb
15 changed files with 849 additions and 720 deletions

View file

@ -2,7 +2,7 @@
<artifact type="jar" build-on-make="true" name="Capatchafy"> <artifact type="jar" build-on-make="true" name="Capatchafy">
<output-path>$PROJECT_DIR$/target/</output-path> <output-path>$PROJECT_DIR$/target/</output-path>
<root id="archive" name="Capatchafy.jar"> <root id="archive" name="Capatchafy.jar">
<element id="module-output" name="Capatchafy" /> <element id="module-output" name="Captchafy" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/glassfish/grizzly/grizzly-http-server/2.4.0/grizzly-http-server-2.4.0.jar" path-in-jar="/" /> <element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/glassfish/grizzly/grizzly-http-server/2.4.0/grizzly-http-server-2.4.0.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/commons-io/commons-io/2.6/commons-io-2.6.jar" path-in-jar="/" /> <element id="extracted-dir" path="$MAVEN_REPOSITORY$/commons-io/commons-io/2.6/commons-io-2.6.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/javax/annotation/javax.annotation-api/1.2/javax.annotation-api-1.2.jar" path-in-jar="/" /> <element id="extracted-dir" path="$MAVEN_REPOSITORY$/javax/annotation/javax.annotation-api/1.2/javax.annotation-api-1.2.jar" path-in-jar="/" />

View file

@ -6,7 +6,7 @@
<sourceOutputDir name="target/generated-sources/annotations" /> <sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" /> <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" /> <outputRelativeToContentRoot value="true" />
<module name="Capatchafy" /> <module name="Captchafy" />
</profile> </profile>
</annotationProcessing> </annotationProcessing>
</component> </component>

View file

@ -8,7 +8,7 @@
</list> </list>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="9" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
</project> </project>

124
.idea/uiDesigner.xml Normal file
View file

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

6
.idea/vcs.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View file

@ -1,44 +1,44 @@
# Capatchafy - The only spambot solution you'll ever need. # Captchafy - The only spambot solution you'll ever need.
### What is Capatchafy? ### What is Captchafy?
Capatchafy is a Spigot plugin/extension for Minecraft servers. Its sole purpose is to protect against spambot attacks. When a server is under attack, Capatchafy directs players to solve a capatcha before joining. This prevents spambots from flooding and crashing a server. Attacks are automatically detected by Capatchafy, so you can kick back and let Capatchafy do the work. Captchafy is a Spigot plugin/extension for Minecraft servers. Its sole purpose is to protect against spambot attacks. When a server is under attack, Captchafy directs players to solve a captcha before joining. This prevents spambots from flooding and crashing a server. Attacks are automatically detected by Captchafy, so you can kick back and let Captchafy do the work.
### How does Capatchafy work? ### How does Captchafy work?
From a user's standpoint, during an attack, they will be kicked and ask to solve a capatcha at example.com:port/capatcha/. Once the capatcha is solved, their ip is added to a list of authorized IPs and they are allowed to join. From a user's standpoint, during an attack, they will be kicked and ask to solve a captcha at example.com:port/captcha/. Once the captcha is solved, their ip is added to a list of authorized IPs and they are allowed to join.
Capatchafy runs on a lightweight, embedded Grizzly container for Jersey. This allows us to make full use of the jax-rs API. When a player accesses the capatcha URL in their browser, they send an HTTP/GET request to the server, which is processed by Capatchafy. Capatchafy serves the user the capatcha. The player solves the capatcha. Their capatcha data is POSTed back to the server, and is then sent off to Google to be verified. Once Google gives the okay, the player's IP is added to the list of authorized IPs. Captchafy runs on a lightweight, embedded Grizzly container for Jersey. This allows us to make full use of the jax-rs API. When a player accesses the captcha URL in their browser, they send an HTTP/GET request to the server, which is processed by Captchafy. Captchafy serves the user the captcha. The player solves the captcha. Their captcha data is POSTed back to the server, and is then sent off to Google to be verified. Once Google gives the okay, the player's IP is added to the list of authorized IPs.
### Features ### Features
- Capatcha-based Spam-bot Prevention. - Captcha-based Spam-bot Prevention.
- IP Whitelisting - IP Whitelisting
- Automatic Attack Detection - Automatic Attack Detection
- Multiple Security Modes - Multiple Security Modes
- Check the Issues tab for more. - Check the Issues tab for more.
### How do I use Capatchafy? ### How do I use Captchafy?
Capatchafy comes with one simple command: /capatchafy. Captchafy comes with one simple command: /captchafy.
Usage: /capatchafy <on:off> <friendly:moderate:strict> or /capatchafy <on:off> <1:2:3> Usage: /captchafy <on:off> <friendly:moderate:strict> or /captchafy <on:off> <1:2:3>
The on/off parameter will turn on/turn off Capatcha based verification. The second parameter is the security level. The on/off parameter will turn on/turn off Captcha based verification. The second parameter is the security level.
Security levels: Security levels:
1/Friendly - Players will only have to solve a capatcha once. Their IPs will be saved in a config for future logins. 1/Friendly - Players will only have to solve a captcha once. Their IPs will be saved in a config for future logins.
2/Moderate - Players will have to solve one capatcha every time the server reloads. (Recommended) 2/Moderate - Players will have to solve one captcha every time the server reloads. (Recommended)
3/Strict - Players will have to solve a capatcha every time they join. 3/Strict - Players will have to solve a captcha every time they join.
Capatchafy will auto-enable/disable when the server is attacked. You can force Capatchafy to stay off by using the command /capatchafy off -f. Capatchafy will not auto-disable if you enable it using the command. Captchafy will auto-enable/disable when the server is attacked. You can force Captchafy to stay off by using the command /captchafy off -f. Captchafy will not auto-disable if you enable it using the command.
You will need to assign the permission 'capatchafy.command' to anyone who needs to use /capatchafy. It is assigned to OPs by default. You will need to assign the permission 'captchafy.command' to anyone who needs to use /captchafy. It is assigned to OPs by default.
You can also whitelist IPs in the config. These IPs will never have to solve a capatcha. You can also whitelist IPs in the config. These IPs will never have to solve a captcha.
### How do I install Capatchafy? ### How do I install Captchafy?
Installation is fairly straight-forward. Drag Capatchafy to your plugins folder like any other add-on, and start your server. This will allow Capatchafy to generate the necessary config files. Then, edit the config as you please. You will need to obtain a reCapatcha key from Google. You can do that here: https://www.google.com/recaptcha/. Place the keys in the config file, and you're ready to go! Be sure to open a port up in your firewall, as well as port forward if you are creating a server on a home connection. Installation is fairly straight-forward. Drag Captchafy to your plugins folder like any other add-on, and start your server. This will allow Captchafy to generate the necessary config files. Then, edit the config as you please. You will need to obtain a reCaptcha key from Google. You can do that here: https://www.google.com/recaptcha/. Place the keys in the config file, and you're ready to go! Be sure to open a port up in your firewall, as well as port forward if you are creating a server on a home connection.
--- ---
### Compiling ### Compiling
Capatchafy makes use of Maven, so it should compile automatically. If you are using Intellij or Eclipse, you might need to run 'mvn package' in the command line to build the project. Netbeans does this for you. You shouldn't need to add any dependencies or make edits to the POM. However, you MUST use the jar labeled Capatchafy-jar-with-dependencies. If you don't, you will get NoClassDefFound errors. If you have any problems, open an issue. Captchafy makes use of Maven, so it should compile automatically. If you are using Intellij or Eclipse, you might need to run 'mvn package' in the command line to build the project. Netbeans does this for you. You shouldn't need to add any dependencies or make edits to the POM. However, you MUST use the jar labeled Captchafy-jar-with-dependencies. If you don't, you will get NoClassDefFound errors. If you have any problems, open an issue.
### License ### License
Capatchafy is licensed under the GNU General Public License v3.0. You can find a copy of it in the License.txt file. Captchafy is licensed under the GNU General Public License v3.0. You can find a copy of it in the License.txt file.

View file

@ -2,7 +2,7 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>me.hockey</groupId> <groupId>me.hockey</groupId>
<artifactId>Capatchafy</artifactId> <artifactId>Captchafy</artifactId>
<version>1.1.0</version> <version>1.1.0</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<properties> <properties>

View file

@ -14,23 +14,23 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.hockey.capatchafy; package me.hockey.captchafy;
import me.hockey.capatchafy.httpd.HttpdServer; import me.hockey.captchafy.httpd.HttpdServer;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.glassfish.grizzly.http.server.HttpServer; import org.glassfish.grizzly.http.server.HttpServer;
import java.io.IOException; import java.io.IOException;
public class Capatchafy extends JavaPlugin public class Captchafy extends JavaPlugin
{ {
public static Capatchafy plugin; public static Captchafy plugin;
public static Listeners listeners; public static Listeners listeners;
public static Configuration configs; public static Configuration configs;
public static boolean enabled = false; public static boolean enabled = false;
public static boolean forced = false; //Disallows auto-enabling/disabling of capatchafy. public static boolean forced = false; //Disallows auto-enabling/disabling of captchafy.
public static int securityLevel; public static int securityLevel;
public HttpServer server; public HttpServer server;
@ -44,17 +44,17 @@ public class Capatchafy extends JavaPlugin
listeners = new Listeners(); listeners = new Listeners();
listeners.setThrottleSettings(); listeners.setThrottleSettings();
Bukkit.getPluginManager().registerEvents(listeners, this); Bukkit.getPluginManager().registerEvents(listeners, this);
getCommand("capatchafy").setExecutor(new CapatchafyCommand()); getCommand("captchafy").setExecutor(new CaptchafyCommand());
try try
{ {
configs = new Configuration(); configs = new Configuration();
configs.startup(); configs.startup();
if (configs.isIncomplete()) if (configs.isIncomplete())
{ {
Bukkit.getLogger().severe("[Capatchafy] There is information missing in the config. Please make the appropriate changes. " + Bukkit.getLogger().severe("[Captchafy] There is information missing in the config. Please make the appropriate changes. " +
"This is normal on the first run. Reload the server once you have made the correct edits."); "This is normal on the first run. Reload the server once you have made the correct edits.");
Capatchafy.error = true; Captchafy.error = true;
Bukkit.getPluginManager().disablePlugin(Capatchafy.plugin); Bukkit.getPluginManager().disablePlugin(Captchafy.plugin);
return; return;
} }
} }
@ -66,12 +66,12 @@ public class Capatchafy extends JavaPlugin
if (securityLevel > 3 || securityLevel < 1) if (securityLevel > 3 || securityLevel < 1)
{ {
securityLevel = 2; securityLevel = 2;
Bukkit.getLogger().severe("[Capatchafy] The 'security-level' config field was not between 1 and 3. Setting security level to 2."); Bukkit.getLogger().severe("[Captchafy] The 'security-level' config field was not between 1 and 3. Setting security level to 2.");
} }
enabled = configs.config.getBoolean("always-on"); enabled = configs.config.getBoolean("always-on");
server = HttpdServer.getServer(); server = HttpdServer.getServer();
listeners.setURLMessage(); listeners.setURLMessage();
Bukkit.getLogger().info("[Capatchafy] Running in security level " + securityLevel + "."); Bukkit.getLogger().info("[Captchafy] Running in security level " + securityLevel + ".");
} }
@Override @Override

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.hockey.capatchafy; package me.hockey.captchafy;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
@ -22,7 +22,7 @@ import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
public class CapatchafyCommand implements CommandExecutor public class CaptchafyCommand implements CommandExecutor
{ {
//Friendly - One time verification. //Friendly - One time verification.
//Moderate - One time verification for each time the server starts. //Moderate - One time verification for each time the server starts.
@ -32,87 +32,87 @@ public class CapatchafyCommand implements CommandExecutor
{ {
//TODO Implement command permissions with TFM and bukkit permissions. //TODO Implement command permissions with TFM and bukkit permissions.
if (!sender.hasPermission("capatchafy.command")) if (!sender.hasPermission("captchafy.command"))
{ {
sender.sendMessage(ChatColor.RED + "You do not have permission to use this command."); sender.sendMessage(ChatColor.RED + "You do not have permission to use this command.");
return false; return false;
} }
if (Capatchafy.configs.config.getBoolean("always-on")) if (Captchafy.configs.config.getBoolean("always-on"))
{ {
sender.sendMessage(ChatColor.RED + "The server owner has Capatchafy enabled at all times. You are not allowed to turn it off or change the security level."); sender.sendMessage(ChatColor.RED + "The server owner has Captchafy enabled at all times. You are not allowed to turn it off or change the security level.");
return false; return false;
} }
//TODO Fix arguments problem, it will throw errors if the parameters aren't filled out properly. Also, make it show usage when the security level arg is spelled wrong. //TODO Fix arguments problem, it will throw errors if the parameters aren't filled out properly. Also, make it show usage when the security level arg is spelled wrong.
if (args.length < 1) if (args.length < 1)
{ {
sender.sendMessage("Usage: /capatchafy <on:off:status> <friendly:moderate:strict>"); sender.sendMessage("Usage: /captchafy <on:off:status> <friendly:moderate:strict>");
return false; return false;
} }
if (args[0].equalsIgnoreCase("on")) if (args[0].equalsIgnoreCase("on"))
{ {
Capatchafy.enabled = true; Captchafy.enabled = true;
Bukkit.broadcastMessage(ChatColor.DARK_RED + "Capatcha-based verification has been enabled."); Bukkit.broadcastMessage(ChatColor.DARK_RED + "Captcha-based verification has been enabled.");
//TODO Add time based disabling of capatchafy. E.g. when admin that turns capatchafy on leaves, turn capatchafy off. //TODO Add time based disabling of captchafy. E.g. when admin that turns captchafy on leaves, turn captchafy off.
//If it does not match any of these, we just keep the security level as is. //If it does not match any of these, we just keep the security level as is.
//Remember, the security level only changes when we tell it to with this command. //Remember, the security level only changes when we tell it to with this command.
Capatchafy.forced = true; Captchafy.forced = true;
Capatchafy.listeners.numberOfAttacks = 0; Captchafy.listeners.numberOfAttacks = 0;
if (args.length < 2) if (args.length < 2)
{ {
sender.sendMessage("Capatchafy will run in security level " + Capatchafy.securityLevel + ". It will not be auto-disabled."); sender.sendMessage("Captchafy will run in security level " + Captchafy.securityLevel + ". It will not be auto-disabled.");
return true; return true;
} }
if (args[1].equalsIgnoreCase("friendly") || args[1].equalsIgnoreCase("1")) if (args[1].equalsIgnoreCase("friendly") || args[1].equalsIgnoreCase("1"))
{ {
Capatchafy.securityLevel = 1; Captchafy.securityLevel = 1;
} }
else if (args[1].equalsIgnoreCase("moderate") || args[1].equalsIgnoreCase("2")) else if (args[1].equalsIgnoreCase("moderate") || args[1].equalsIgnoreCase("2"))
{ {
Capatchafy.securityLevel = 2; Captchafy.securityLevel = 2;
} }
else if (args[1].equalsIgnoreCase("strict") || args[1].equalsIgnoreCase("3")) else if (args[1].equalsIgnoreCase("strict") || args[1].equalsIgnoreCase("3"))
{ {
Capatchafy.securityLevel = 3; Captchafy.securityLevel = 3;
Capatchafy.configs.ipList.clear(); Captchafy.configs.ipList.clear();
} }
else else
{ {
sender.sendMessage("Usage: /capatchafy <on:off:status> <friendly:moderate:strict>"); sender.sendMessage("Usage: /captchafy <on:off:status> <friendly:moderate:strict>");
return false; return false;
} }
sender.sendMessage("Capatchafy will run in security level " + Capatchafy.securityLevel + ". It will not be auto-disabled."); sender.sendMessage("Captchafy will run in security level " + Captchafy.securityLevel + ". It will not be auto-disabled.");
return true; return true;
} }
else if (args[0].equalsIgnoreCase("off")) else if (args[0].equalsIgnoreCase("off"))
{ {
Capatchafy.enabled = false; Captchafy.enabled = false;
Capatchafy.forced = false; Captchafy.forced = false;
Capatchafy.listeners.numberOfAttacks = 0; Captchafy.listeners.numberOfAttacks = 0;
Bukkit.broadcastMessage(ChatColor.GREEN + "Capatcha-based verification has been disabled."); Bukkit.broadcastMessage(ChatColor.GREEN + "Captcha-based verification has been disabled.");
if (args.length < 2) if (args.length < 2)
return true; return true;
if (args[1].equalsIgnoreCase("-f")) if (args[1].equalsIgnoreCase("-f"))
{ {
Capatchafy.forced = true; Captchafy.forced = true;
sender.sendMessage(ChatColor.YELLOW + "Capatchafy " + ChatColor.RED + "will not" + ChatColor.YELLOW + " automatically enable if the server detects an attack."); sender.sendMessage(ChatColor.YELLOW + "Captchafy " + ChatColor.RED + "will not" + ChatColor.YELLOW + " automatically enable if the server detects an attack.");
return true; return true;
} }
} }
else if (args[0].equalsIgnoreCase("status")) else if (args[0].equalsIgnoreCase("status"))
{ {
sender.sendMessage(ChatColor.GRAY + "Capatchafy is enabled: " + ChatColor.YELLOW + Capatchafy.enabled); sender.sendMessage(ChatColor.GRAY + "Captchafy is enabled: " + ChatColor.YELLOW + Captchafy.enabled);
sender.sendMessage(ChatColor.GRAY + "Capatchafy is set to run in mode: " + ChatColor.YELLOW + Capatchafy.securityLevel); sender.sendMessage(ChatColor.GRAY + "Captchafy is set to run in mode: " + ChatColor.YELLOW + Captchafy.securityLevel);
return true; return true;
} }
else else
{ {
sender.sendMessage("Usage: /capatchafy <on:off:status> <friendly:moderate:strict>"); sender.sendMessage("Usage: /captchafy <on:off:status> <friendly:moderate:strict>");
} }
return false; return false;
} }

View file

@ -14,9 +14,8 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.hockey.capatchafy; package me.hockey.captchafy;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
@ -28,9 +27,9 @@ public class Configuration
{ {
//TODO Add time based authorization expiration. //TODO Add time based authorization expiration.
FileConfiguration ips; FileConfiguration ips;
File ipsFile = new File(Capatchafy.plugin.getDataFolder(), "ips.yml"); File ipsFile = new File(Captchafy.plugin.getDataFolder(), "ips.yml");
FileConfiguration config; FileConfiguration config;
File configFile = new File(Capatchafy.plugin.getDataFolder(), "config.yml"); File configFile = new File(Captchafy.plugin.getDataFolder(), "config.yml");
public List<String> ipList = new ArrayList(); public List<String> ipList = new ArrayList();
public List<String> alwaysAuthorizedList = new ArrayList(); public List<String> alwaysAuthorizedList = new ArrayList();
@ -43,7 +42,7 @@ public class Configuration
public boolean isIncomplete() public boolean isIncomplete()
{ {
return getHostname() == null || getPort() == null || getCapatchaSiteKey() == null || getCapatchaSecret() == null || config.get("always-on") == null || config.get("security-level") == null; return getHostname() == null || getPort() == null || getCaptchaSiteKey() == null || getCaptchaSecret() == null || config.get("always-on") == null || config.get("security-level") == null;
} }
//TODO Cleanup. This actually generates the config, but saves the ips if true. Causes NPE if saved before loadConfig() //TODO Cleanup. This actually generates the config, but saves the ips if true. Causes NPE if saved before loadConfig()
@ -51,11 +50,11 @@ public class Configuration
{ {
if (ipsFile == null) if (ipsFile == null)
{ {
ipsFile = new File(Capatchafy.plugin.getDataFolder(), "ips.yml"); ipsFile = new File(Captchafy.plugin.getDataFolder(), "ips.yml");
} }
if (!ipsFile.exists()) if (!ipsFile.exists())
{ {
Capatchafy.plugin.saveResource("ips.yml", false); Captchafy.plugin.saveResource("ips.yml", false);
} }
if (save) if (save)
{ {
@ -69,11 +68,11 @@ public class Configuration
{ {
if (configFile == null) if (configFile == null)
{ {
configFile = new File(Capatchafy.plugin.getDataFolder(), "config.yml"); configFile = new File(Captchafy.plugin.getDataFolder(), "config.yml");
} }
if (!configFile.exists()) if (!configFile.exists())
{ {
Capatchafy.plugin.saveResource("config.yml", false); Captchafy.plugin.saveResource("config.yml", false);
} }
} }
@ -92,7 +91,7 @@ public class Configuration
return; return;
//We dont remove always authorized ips, because they were added while in the friendly mode, so they should still be added to the ips.yml. //We dont remove always authorized ips, because they were added while in the friendly mode, so they should still be added to the ips.yml.
} }
switch (Capatchafy.securityLevel) switch (Captchafy.securityLevel)
{ {
case 1: case 1:
{ {
@ -116,7 +115,7 @@ public class Configuration
{ {
return true; return true;
} }
switch (Capatchafy.securityLevel) switch (Captchafy.securityLevel)
{ {
case 1: case 1:
{ {
@ -149,13 +148,13 @@ public class Configuration
return config.getString("port"); return config.getString("port");
} }
public String getCapatchaSiteKey() public String getCaptchaSiteKey()
{ {
return config.getString("recapatcha-key"); return config.getString("recaptcha-key");
} }
public String getCapatchaSecret() public String getCaptchaSecret()
{ {
return config.getString("recapatcha-secret"); return config.getString("recaptcha-secret");
} }
} }

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.hockey.capatchafy; package me.hockey.captchafy;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -34,7 +34,7 @@ public class Listeners implements Listener
public int numberOfAttacks; //This is the number of attacks since admin intervention, not necessarily the number of attacks since the server has been online. public int numberOfAttacks; //This is the number of attacks since admin intervention, not necessarily the number of attacks since the server has been online.
public long throttleTime; //The higher, the more often players will be counted as possible spammers. Required time between each login to not be considered an attack. public long throttleTime; //The higher, the more often players will be counted as possible spammers. Required time between each login to not be considered an attack.
public int throttleLogins; //The lower, the quicker the capatchas will auto-enable. Aka max points before capatchafy is enabled. public int throttleLogins; //The lower, the quicker the captchas will auto-enable. Aka max points before captchafy is enabled.
public long removeAllPointsTime = 7L; //Remove all points after x seconds. Default: 7 public long removeAllPointsTime = 7L; //Remove all points after x seconds. Default: 7
public int maxAttacks = 3; //Set to 0 to disable this function. Default: 3 public int maxAttacks = 3; //Set to 0 to disable this function. Default: 3
@ -50,26 +50,26 @@ public class Listeners implements Listener
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerLogin(PlayerLoginEvent event) public void onPlayerLogin(PlayerLoginEvent event)
{ {
if (!Capatchafy.forced && !Capatchafy.configs.config.getBoolean("always-on") && !Capatchafy.enabled) if (!Captchafy.forced && !Captchafy.configs.config.getBoolean("always-on") && !Captchafy.enabled)
{ {
throttleConnections(); throttleConnections();
} }
if (!Capatchafy.enabled) return; if (!Captchafy.enabled) return;
String ip = event.getAddress().toString().replaceAll("/", ""); String ip = event.getAddress().toString().replaceAll("/", "");
if (!Capatchafy.configs.isAuthorized(ip)) if (!Captchafy.configs.isAuthorized(ip))
{ {
if (url == null) if (url == null)
{ {
setURLMessage(); setURLMessage();
} }
event.disallow(PlayerLoginEvent.Result.KICK_OTHER, ChatColor.RED + "" + ChatColor.BOLD + "Yikes, we're under attack! Please solve the capatcha.\n" + event.disallow(PlayerLoginEvent.Result.KICK_OTHER, ChatColor.RED + "" + ChatColor.BOLD + "Yikes, we're under attack! Please solve the captcha.\n" +
ChatColor.WHITE + "Please go to " + ChatColor.GOLD + url + ChatColor.WHITE + " in your web browser and solve the capatcha.\n" + ChatColor.WHITE + "Please go to " + ChatColor.GOLD + url + ChatColor.WHITE + " in your web browser and solve the captcha.\n" +
"Once solved successfully, you will be able to join."); "Once solved successfully, you will be able to join.");
return; return;
} }
if (Capatchafy.securityLevel == 3) if (Captchafy.securityLevel == 3)
{ {
Capatchafy.configs.setAuthorized(ip, false); Captchafy.configs.setAuthorized(ip, false);
} }
} }
@ -92,10 +92,10 @@ public class Listeners implements Listener
points = 0; points = 0;
} }
if (points == throttleLogins && !Capatchafy.enabled) if (points == throttleLogins && !Captchafy.enabled)
{ {
Bukkit.broadcastMessage(ChatColor.DARK_RED + "Capatcha-based verification has been enabled."); Bukkit.broadcastMessage(ChatColor.DARK_RED + "Captcha-based verification has been enabled.");
Capatchafy.enabled = true; Captchafy.enabled = true;
numberOfAttacks++; numberOfAttacks++;
points = 0; points = 0;
@ -104,20 +104,20 @@ public class Listeners implements Listener
@Override @Override
public void run() public void run()
{ {
if (!Capatchafy.forced && Capatchafy.enabled) if (!Captchafy.forced && Captchafy.enabled)
{ {
Bukkit.broadcastMessage(ChatColor.GREEN + "Capatcha-based verification has been disabled."); Bukkit.broadcastMessage(ChatColor.GREEN + "Captcha-based verification has been disabled.");
Capatchafy.enabled = false; Captchafy.enabled = false;
} }
} }
}.runTaskLater(Capatchafy.plugin, 5 * 60 * 20); //Default 5 * 60 * 20 }.runTaskLater(Captchafy.plugin, 5 * 60 * 20); //Default 5 * 60 * 20
} }
if (numberOfAttacks >= maxAttacks && maxAttacks != 0) if (numberOfAttacks >= maxAttacks && maxAttacks != 0)
{ {
Capatchafy.enabled = true; Captchafy.enabled = true;
Capatchafy.forced = true; Captchafy.forced = true;
lastLogin = currentTime; lastLogin = currentTime;
Bukkit.broadcastMessage(ChatColor.DARK_RED + "Capatchafy will not auto-disable."); Bukkit.broadcastMessage(ChatColor.DARK_RED + "Captchafy will not auto-disable.");
return; return;
} }
lastLogin = currentTime; lastLogin = currentTime;
@ -125,12 +125,12 @@ public class Listeners implements Listener
public void setURLMessage() public void setURLMessage()
{ {
if (Capatchafy.configs.getPort().equals("80")) if (Captchafy.configs.getPort().equals("80"))
{ {
url = Capatchafy.configs.getHostname(); url = Captchafy.configs.getHostname();
return; return;
} }
url = Capatchafy.configs.getHostname() + ":" + Capatchafy.configs.getPort(); url = Captchafy.configs.getHostname() + ":" + Captchafy.configs.getPort();
} }
public void setThrottleSettings() public void setThrottleSettings()
@ -145,6 +145,6 @@ public class Listeners implements Listener
throttleTime = defaultThrottleTime; throttleTime = defaultThrottleTime;
throttleLogins = defaultThrottleLogins; throttleLogins = defaultThrottleLogins;
} }
}.runTaskLater(Capatchafy.plugin, 1200); }.runTaskLater(Captchafy.plugin, 1200);
} }
} }

View file

@ -14,9 +14,9 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.hockey.capatchafy.httpd; package me.hockey.captchafy.httpd;
import me.hockey.capatchafy.Capatchafy; import me.hockey.captchafy.Captchafy;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.glassfish.grizzly.http.server.Request; import org.glassfish.grizzly.http.server.Request;
@ -33,13 +33,13 @@ import java.net.URLConnection;
import java.net.URLEncoder; import java.net.URLEncoder;
@Path("/") @Path("/")
public class CapatchaPage public class CaptchaPage
{ {
@GET @GET
@Produces(MediaType.TEXT_HTML) @Produces(MediaType.TEXT_HTML)
public String urlAccessed(@Context Request req) public String urlAccessed(@Context Request req)
{ {
if (Capatchafy.configs.isAuthorized(req.getRemoteAddr()) || !Capatchafy.enabled) if (Captchafy.configs.isAuthorized(req.getRemoteAddr()) || !Captchafy.enabled)
{ {
return "You are already authorized."; return "You are already authorized.";
} }
@ -47,7 +47,7 @@ public class CapatchaPage
"<script src='https://www.google.com/recaptcha/api.js'></script></head>" + "<script src='https://www.google.com/recaptcha/api.js'></script></head>" +
"<script>function callback(){document.getElementById(\"form\").submit();}</script>"+ "<script>function callback(){document.getElementById(\"form\").submit();}</script>"+
"<form id=\"form\"method=\"post\">" + "<form id=\"form\"method=\"post\">" +
"<div class=\"g-recaptcha\" data-callback=\"callback\" data-sitekey=\"" + Capatchafy.configs.getCapatchaSiteKey() + "\"></div>" + "<div class=\"g-recaptcha\" data-callback=\"callback\" data-sitekey=\"" + Captchafy.configs.getCaptchaSiteKey() + "\"></div>" +
"</form>"; "</form>";
} }
@ -57,11 +57,11 @@ public class CapatchaPage
{ {
try try
{ {
//Bukkit.getLogger().info("A capatcha has been recieved from " + req.getRemoteAddr()); //Bukkit.getLogger().info("A captcha has been recieved from " + req.getRemoteAddr());
if (validateCaptcha(Capatchafy.configs.getCapatchaSecret(), content, req.getRemoteAddr())) if (validateCaptcha(Captchafy.configs.getCaptchaSecret(), content, req.getRemoteAddr()))
{ {
//Bukkit.getLogger().info("Capatcha from " + req.getRemoteAddr() + " is valid."); //Bukkit.getLogger().info("Captcha from " + req.getRemoteAddr() + " is valid.");
Capatchafy.configs.setAuthorized(req.getRemoteAddr(), true); Captchafy.configs.setAuthorized(req.getRemoteAddr(), true);
} }
} }
catch (Exception e) catch (Exception e)

View file

@ -14,25 +14,25 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package me.hockey.capatchafy.httpd; package me.hockey.captchafy.httpd;
import java.net.URI; import java.net.URI;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import me.hockey.capatchafy.Capatchafy; import me.hockey.captchafy.Captchafy;
import org.glassfish.grizzly.http.server.HttpServer; import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.ResourceConfig;
public class HttpdServer public class HttpdServer
{ {
public static final String BASE_URI = "http://" + (Capatchafy.configs.getBindingIP().isEmpty() ? Capatchafy.configs.getHostname() : Capatchafy.configs.getBindingIP()) + ":" + Capatchafy.configs.getPort() + "/"; public static final String BASE_URI = "http://" + (Captchafy.configs.getBindingIP().isEmpty() ? Captchafy.configs.getHostname() : Captchafy.configs.getBindingIP()) + ":" + Captchafy.configs.getPort() + "/";
public static HttpServer getServer() public static HttpServer getServer()
{ {
final ResourceConfig rc = new ResourceConfig().packages("me.hockey.capatchafy.httpd"); final ResourceConfig rc = new ResourceConfig().packages("me.hockey.captchafy.httpd");
final Set<Class<?>> classes = new HashSet<Class<?>>(); final Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(CapatchaPage.class); classes.add(CaptchaPage.class);
rc.registerClasses(classes); rc.registerClasses(classes);
return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc); return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
} }

View file

@ -1,11 +1,11 @@
#This is the main configuration file for Capatchafy 1.0, a plugin by James Depp. #This is the main configuration file for Captchafy 1.0, a plugin by James Depp.
#Authorized-IPs are located in ips.yml. If you want to wipe them, delete ips.yml, WHILE THE SERVER IS STOPPED. It will reverse your actions if the server is alive. #Authorized-IPs are located in ips.yml. If you want to wipe them, delete ips.yml, WHILE THE SERVER IS STOPPED. It will reverse your actions if the server is alive.
#If you would like Capatchafy to be enabled all the time, you can specify that here. #If you would like Captchafy to be enabled all the time, you can specify that here.
always-on: false always-on: false
#This is the default security level. This value should be an integer between 1 and 3. #This is the default security level. This value should be an integer between 1 and 3.
#Any security level can be selected with the /capatchafy command. #Any security level can be selected with the /captchafy command.
#1 - Friendly. One time verification, ips will be stored in config. #1 - Friendly. One time verification, ips will be stored in config.
#2 - Moderate (Recommended). You must verify each time the server restarts/reloads. #2 - Moderate (Recommended). You must verify each time the server restarts/reloads.
#3 - Strict. You must verify each time you join. #3 - Strict. You must verify each time you join.
@ -14,23 +14,23 @@ security-level: 2
# IP to bind the httpd to, leave blank to use hostname # IP to bind the httpd to, leave blank to use hostname
binding-ip: '' binding-ip: ''
#Hostname - The ip that you want Capatchafy to listen on goes here. #Hostname - The ip that you want Captchafy to listen on goes here.
#You don't HAVE to change this, but leaving it will cause problems when a player tries to find the link to the capatcha. #You don't HAVE to change this, but leaving it will cause problems when a player tries to find the link to the captcha.
hostname: 127.0.0.1 hostname: 127.0.0.1
#Port - The port that you want Capatchafy to run on goes here. #Port - The port that you want Captchafy to run on goes here.
#Use port 80 if you don't want your players to have to type a port in the address bar. On some machines, port 80 may already be in use. #Use port 80 if you don't want your players to have to type a port in the address bar. On some machines, port 80 may already be in use.
#If you're not sure about which port to use, just pick a random one. If you're running a VPS, be sure to open the port in the firewall. #If you're not sure about which port to use, just pick a random one. If you're running a VPS, be sure to open the port in the firewall.
port: 8080 port: 8080
#Whitelisted IPs - IPs that will never have to go through the capatcha verification process. #Whitelisted IPs - IPs that will never have to go through the captcha verification process.
whitelisted-ips: whitelisted-ips:
- 127.0.0.1 - 127.0.0.1
#This is the tricky part. You will need to obtain api keys for reCapatcha. #This is the tricky part. You will need to obtain api keys for reCapatcha.
#To obtain a reCapatcha key, click on https://www.google.com/recaptcha/intro/index.html and click "Get Started." #To obtain a recaptcha key, click on https://www.google.com/recaptcha/intro/index.html and click "Get Started."
#Enter in a name for your 'site', it can be anything. Then, give the hostname you put here as the url to your capatcha. #Enter in a name for your 'site', it can be anything. Then, give the hostname you put here as the url to your captcha.
#Your site key goes in the 'recapatcha-key' section, your secret key (which you should never give out) goes in the 'recapatcha-secret' section. #Your site key goes in the 'recaptcha-key' section, your secret key (which you should never give out) goes in the 'recaptcha-secret' section.
recapatcha-key: recaptcha-key:
recapatcha-secret: recaptcha-secret:

View file

@ -1,10 +1,10 @@
name: Capatchafy name: Captchafy
version: 1.1.0-TF version: 1.1.0-TF
main: me.hockey.capatchafy.Capatchafy main: me.hockey.captchafy.Captchafy
commands: commands:
capatchafy: captchafy:
capatchafy.command: captchafy.command:
description: Allow a player to use the capatchafy command. description: Allow a player to use the captchafy command.
default: op default: op