com.skelril.aurora.authentication.AuthComponent.java Source code

Java tutorial

Introduction

Here is the source code for com.skelril.aurora.authentication.AuthComponent.java

Source

/*
 * Copyright (c) 2014 Wyatt Childers.
 *
 * This file is part of Aurora.
 *
 * Aurora is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Aurora is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with Aurora.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.skelril.aurora.authentication;

import com.sk89q.commandbook.CommandBook;
import com.sk89q.minecraft.util.commands.*;
import com.skelril.aurora.util.ChatUtil;
import com.zachsthings.libcomponents.ComponentInformation;
import com.zachsthings.libcomponents.bukkit.BukkitComponent;
import com.zachsthings.libcomponents.config.ConfigurationBase;
import com.zachsthings.libcomponents.config.Setting;
import net.milkbowl.vault.permission.Permission;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.logging.Logger;

import static org.bukkit.event.player.AsyncPlayerPreLoginEvent.Result;

@ComponentInformation(friendlyName = "Auth", desc = "Authentication System for Shivtr")
public class AuthComponent extends BukkitComponent implements Listener, Runnable {

    private final CommandBook inst = CommandBook.inst();
    private final Server server = CommandBook.server();
    private final Logger log = inst.getLogger();

    private static Permission permission = null;
    private LocalConfiguration config;
    //private ConcurrentHashMap<String, Character> characters = new ConcurrentHashMap<>();
    private Set<String> characters = new CopyOnWriteArraySet<>();

    @Override
    public void enable() {

        config = configure(new LocalConfiguration());

        registerCommands(Commands.class);
        if (config.whitelist) {
            //noinspection AccessStaticViaInstance
            inst.registerEvents(new WhiteList());
        } else {
            //noinspection AccessStaticViaInstance
            inst.registerEvents(new GreyList());
            setupPermissions();
        }
        if (config.updateFrequency < 30) {
            config.updateFrequency = 30;
            log.warning("The update frequency was set at: " + config.updateFrequency + " minutes"
                    + " and must be at least 30 minutes.");
        }
        server.getScheduler().runTaskTimerAsynchronously(inst, this, 0, 20 * 60 * config.updateFrequency);
    }

    @Override
    public void reload() {

        super.reload();
        configure(config);
    }

    private static class LocalConfiguration extends ConfigurationBase {

        @Setting("white-list")
        public boolean whitelist = true;
        @Setting("website-url")
        public String websiteUrl = "http://example.shivtr.com/";
        @Setting("update-frequency")
        public int updateFrequency = 30;
        @Setting("grey-list-group")
        public String greyListGroup = "Member";
    }

    @Override
    public synchronized void run() {

        JSONArray charactersL = getFrom("characters.json");

        log.info("Testing the connection to " + config.websiteUrl + "...");
        if (charactersL != null) {
            log.info("Connection test successful.");
            if (!charactersL.isEmpty()) {
                updateWhiteList(charactersL);
            } else {
                log.warning("No characters could be downloaded!");
                log.info("Your website could be under maintenance or contain no characters.");
            }
        } else {
            log.warning("Connection test failed!");
        }

        if (characters.isEmpty()) {
            log.info("Attempting to load offline files...");
            loadBackupWhiteList();
        }

        log.info(characters.size() + " characters have been loaded.");
    }

    private boolean setupPermissions() {

        RegisteredServiceProvider<Permission> permissionProvider = server.getServicesManager()
                .getRegistration(net.milkbowl.vault.permission.Permission.class);
        if (permissionProvider != null)
            permission = permissionProvider.getProvider();

        return (permission != null);
    }

    public class WhiteList implements Listener {

        @EventHandler(priority = EventPriority.LOW)
        public void onPlayerLogin(AsyncPlayerPreLoginEvent event) {

            try {
                if (!isListed(event.getName()) && event.getLoginResult().equals(Result.ALLOWED)) {
                    event.disallow(Result.KICK_WHITELIST,
                            "You must register on " + "your account on " + config.websiteUrl + ".");
                }
            } catch (Exception e) {
                event.disallow(Result.KICK_WHITELIST, "An error has occurred please try again in a few minutes.");
            }
        }
    }

    public class GreyList implements Listener {

        @EventHandler(priority = EventPriority.HIGHEST)
        public void onPlayerLogin(PlayerJoinEvent event) {

            Player player = event.getPlayer();
            try {
                if (permission == null || inst.hasPermission(player, "aurora.auth.member"))
                    return;

                if (isListed(player.getName())) {
                    permission.playerAddGroup((World) null, player.getName(), config.greyListGroup);
                    ChatUtil.send(player, "Thank you for registering your account.");
                } else {
                    ChatUtil.sendWarning(player, "You are currently a probationary player.");
                    ChatUtil.sendWarning(player,
                            "To get full access please register your account at: " + config.websiteUrl + ".");
                }
            } catch (Exception e) {
                player.kickPlayer("An error has occurred please try again in a few minutes.");
            }
        }
    }

    public class Commands {

        @Command(aliases = { "auth" }, desc = "Commands for the Authentication System.", min = 0, max = 0)
        @NestedCommand({ AuthCommands.class })
        public void authCmd(CommandContext args, CommandSender sender) {

        }

    }

    public class AuthCommands {

        /*
        @Command(aliases = {"login"}, desc = "Verify with Shivtr",
            min = 2, max = 2)
        public void authLoginCmd(CommandContext args, CommandSender sender) throws CommandException{
            
        Player player = PlayerUtil.checkPlayer(sender);
            
            
        String authToke = authenticate(sender.getName(), args.getString(0), args.getString(1));
            
        if (!authToke.equals("401")) {
            ChatUtil.send(sender, "Successfully authenticated with shivtr.");
            
            AuthenticatedShivtrCharacter shivtrCharacter
                    = new AuthenticatedShivtrCharacter(sender.getName(), authToke);
            playerAuth.put(sender.getName(), shivtrCharacter);
            
            if (promotedPlayers.contains(sender)) promotedPlayers.remove(sender);
        } else if (promotedPlayers.contains(sender) && config.kickOnFailedPrompt){
            ((Player) sender).kickPlayer("Failed authentication.");
        } else {
            ChatUtil.sendWarning(sender, "Authentication failed.");
        }
        }
        */

        @Command(aliases = { "update" }, desc = "Update the auth file", min = 0, max = 0)
        @CommandPermissions("aurora.auth.update")
        public void authUpdateCmd(CommandContext args, CommandSender sender) throws CommandException {

            run();
            if (sender instanceof Player)
                ChatUtil.send(sender, "The Characters List(s) is now being updated.");
        }
    }

    public synchronized boolean isListed(String playerName) {

        return characters.contains(playerName.trim().toLowerCase());
    }

    public synchronized JSONArray getFrom(String subAddress) {

        JSONArray objective = new JSONArray();
        HttpURLConnection connection = null;
        BufferedReader reader = null;

        try {
            JSONParser parser = new JSONParser();
            for (int i = 1; true; i++) {

                try {
                    // Establish the connection
                    URL url = new URL(config.websiteUrl + subAddress + "?page=" + i);
                    connection = (HttpURLConnection) url.openConnection();
                    connection.setConnectTimeout(1500);
                    connection.setReadTimeout(1500);

                    // Check response codes return if invalid
                    if (connection.getResponseCode() < 200 || connection.getResponseCode() >= 300)
                        return null;

                    // Begin to read results
                    reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                    StringBuilder builder = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        builder.append(line);
                    }

                    // Parse Data
                    JSONObject o = (JSONObject) parser.parse(builder.toString());
                    JSONArray ao = (JSONArray) o.get("characters");
                    if (ao.isEmpty())
                        break;
                    Collections.addAll(objective, (JSONObject[]) ao.toArray(new JSONObject[ao.size()]));
                } catch (ParseException e) {
                    break;
                } finally {
                    if (connection != null)
                        connection.disconnect();

                    if (reader != null) {
                        try {
                            reader.close();
                        } catch (IOException ignored) {
                        }
                    }
                }
            }
        } catch (IOException e) {
            return null;
        }

        return objective;
    }

    private static final FilenameFilter filenameFilter = (dir, name) -> name.startsWith("character")
            && name.endsWith(".json");

    public synchronized void updateWhiteList(JSONArray object) {

        // Load the storage directory
        File charactersDirectory = new File(inst.getDataFolder().getPath() + "/characters");
        if (!charactersDirectory.exists())
            charactersDirectory.mkdir();
        log.info("Updating white list...");

        if (!loadCharacters((JSONObject[]) object.toArray(new JSONObject[object.size()]))) {
            log.info("No changes detected.");
            return;
        }

        // Remove outdated JSON backup files
        for (File file : charactersDirectory.listFiles(filenameFilter)) {
            file.delete();
        }

        // Create new JSON backup file
        BufferedWriter out = null;

        File characterList = new File(charactersDirectory, "character-list.json");
        try {
            if (characterList.createNewFile()) {
                out = new BufferedWriter(new FileWriter(characterList));
                out.write(object.toJSONString());
            } else {
                log.warning("Could not create the new character list offline file!");
            }
        } catch (IOException ignored) {
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException ignored) {
                }
            }
        }

        log.info("The white list has updated successfully.");
    }

    private synchronized void loadBackupWhiteList() {

        File charactersDirectory = new File(inst.getDataFolder().getPath() + "/characters");
        File characterFile = new File(charactersDirectory, "character-list.json");
        if (!characterFile.exists()) {
            log.warning("No offline file found!");
            return;
        }

        BufferedReader reader = null;
        JSONParser parser = new JSONParser();

        try {
            reader = new BufferedReader(new FileReader(characterFile));
            StringBuilder builder = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                builder.append(line);
            }

            JSONArray characterArray = ((JSONArray) parser.parse(builder.toString()));
            loadCharacters((JSONObject[]) characterArray.toArray(new JSONObject[characterArray.size()]));
        } catch (IOException e) {
            log.warning("Could not read file: " + characterFile.getName() + ".");
        } catch (ParseException p) {
            log.warning("Could not parse file: " + characterFile.getName() + ".");
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException ignored) {
                }
            }
        }

        log.info("The offline file has been loaded.");
    }

    public synchronized boolean loadCharacters(JSONObject[] chars) {

        boolean diff = false;
        Set<String> localChars = new HashSet<>();
        for (JSONObject aChar : chars) {
            String targ = aChar.get("name").toString().trim().toLowerCase();
            if (!characters.contains(targ)) {
                diff = true;
            }
            localChars.add(targ);
        }

        diff = diff || characters.size() != localChars.size();

        if (diff) {
            // Clear all old chars
            characters.clear();

            // Add all new Characters
            characters.addAll(localChars);
            return true;
        }
        return false;
    }
}