Newer
Older
KeeperJerry_Launcher / LaunchServer / source / auth / provider / FileAuthProvider.java
@KeeperJerry KeeperJerry on 27 Jun 2020 3 KB Рефактор кода
package launchserver.auth.provider;

import launcher.helper.*;
import launcher.serialize.config.ConfigObject;
import launcher.serialize.config.TextConfigReader;
import launcher.serialize.config.entry.BlockConfigEntry;
import launcher.serialize.config.entry.ConfigEntry;
import launcher.serialize.config.entry.ConfigEntry.Type;
import launcher.serialize.config.entry.StringConfigEntry;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public final class FileAuthProvider extends DigestAuthProvider
{
    private final Path file;

    // Cache
    private final Map<String, Entry> entries = new HashMap<>(256);
    private final Object cacheLock = new Object();
    private FileTime cacheLastModified;

    FileAuthProvider(BlockConfigEntry block)
    {
        super(block);
        file = IOHelper.toPath(block.getEntryValue("file", StringConfigEntry.class));

        // Try to update cache
        try
        {
            updateCache();
        }
        catch (IOException e)
        {
            LogHelper.error(e);
        }
    }

    @Override
    public AuthProviderResult auth(String login, String password, String ip) throws IOException
    {
        Entry entry;
        synchronized (cacheLock)
        {
            updateCache();
            entry = entries.get(CommonHelper.low(login));
        }

        // Verify digest and return true username
        verifyDigest(entry == null ? null : entry.password, password);
        if (entry == null || entry.ip != null && !entry.ip.equals(ip))
        {
            authError("Authentication from this IP is not allowed");
        }

        // We're done
        return new AuthProviderResult(entry.username, SecurityHelper.randomStringToken());
    }

    @Override
    public void close()
    {
        // Do nothing
    }

    private void updateCache() throws IOException
    {
        FileTime lastModified = IOHelper.readAttributes(file).lastModifiedTime();
        if (lastModified.equals(cacheLastModified))
        {
            return; // Not modified, so cache is up-to-date
        }

        // Read file
        LogHelper.info("Recaching auth provider file: '%s'", file);
        BlockConfigEntry authFile;
        try (BufferedReader reader = IOHelper.newReader(file))
        {
            authFile = TextConfigReader.read(reader, false);
        }

        // Read entries from config block
        entries.clear();
        Set<Map.Entry<String, ConfigEntry<?>>> entrySet = authFile.getValue().entrySet();
        for (Map.Entry<String, ConfigEntry<?>> entry : entrySet)
        {
            String login = entry.getKey();
            ConfigEntry<?> value = VerifyHelper.verify(entry.getValue(), v -> v.getType() == Type.BLOCK,
                    String.format("Illegal config entry type: '%s'", login));

            // Add auth entry
            Entry auth = new Entry((BlockConfigEntry) value);
            VerifyHelper.putIfAbsent(entries, CommonHelper.low(login), auth,
                    String.format("Duplicate login: '%s'", login));
        }

        // Update last modified time
        cacheLastModified = lastModified;
    }

    private static final class Entry extends ConfigObject
    {
        private final String username;
        private final String password;
        private final String ip;

        private Entry(BlockConfigEntry block)
        {
            super(block);
            username = VerifyHelper.verifyUsername(block.getEntryValue("username", StringConfigEntry.class));
            password = VerifyHelper.verify(block.getEntryValue("password", StringConfigEntry.class),
                    VerifyHelper.NOT_EMPTY, String.format("Password can't be empty: '%s'", username));
            ip = block.hasEntry("ip") ? VerifyHelper.verify(block.getEntryValue("ip", StringConfigEntry.class),
                    VerifyHelper.NOT_EMPTY, String.format("IP can't be empty: '%s'", username)) : null;
        }
    }
}