diff --git a/src/main/java/dev/plex/HTTPDModule.java b/src/main/java/dev/plex/HTTPDModule.java index 08f64eb..2862830 100644 --- a/src/main/java/dev/plex/HTTPDModule.java +++ b/src/main/java/dev/plex/HTTPDModule.java @@ -1,35 +1,26 @@ package dev.plex; +import dev.plex.authentication.AuthenticationManager; import dev.plex.cache.FileCache; import dev.plex.config.ModuleConfig; import dev.plex.module.PlexModule; import dev.plex.request.AbstractServlet; import dev.plex.request.SchematicUploadServlet; -import dev.plex.request.impl.CommandsEndpoint; -import dev.plex.request.impl.IndefBansEndpoint; -import dev.plex.request.impl.IndexEndpoint; -import dev.plex.request.impl.ListEndpoint; -import dev.plex.request.impl.PunishmentsEndpoint; -import dev.plex.request.impl.SchematicDownloadEndpoint; -import dev.plex.request.impl.SchematicUploadEndpoint; +import dev.plex.request.impl.*; import dev.plex.util.PlexLog; import jakarta.servlet.MultipartConfigElement; -import java.io.File; -import java.util.concurrent.atomic.AtomicReference; import lombok.Getter; import net.milkbowl.vault.permission.Permission; import org.bukkit.Bukkit; import org.bukkit.plugin.RegisteredServiceProvider; -import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.server.ForwardedRequestCustomizer; -import org.eclipse.jetty.server.HttpConfiguration; -import org.eclipse.jetty.server.HttpConnectionFactory; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.*; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; +import java.io.File; +import java.util.concurrent.atomic.AtomicReference; + public class HTTPDModule extends PlexModule { public static ServletContextHandler context; @@ -45,6 +36,8 @@ public class HTTPDModule extends PlexModule public static final String template = AbstractServlet.readFileReal(HTTPDModule.class.getResourceAsStream("/httpd/template.html")); + private AuthenticationManager authenticationManager; + @Override public void load() { @@ -61,6 +54,18 @@ public class HTTPDModule extends PlexModule { throw new RuntimeException("Plex-HTTPD requires the 'Vault' plugin as well as a Permissions plugin that hooks into 'Vault'. We recommend LuckPerms!"); } + + this.authenticationManager = new AuthenticationManager(); + if (this.authenticationManager.provider() != null) + { + PlexLog.debug(this.authenticationManager.provider().generateLogin()); + } + else + { + PlexLog.debug("Provider was not found for Authentication so disabled"); + } + + serverThread = new Thread(() -> { Server server = new Server(); diff --git a/src/main/java/dev/plex/authentication/AuthenticatedUser.java b/src/main/java/dev/plex/authentication/AuthenticatedUser.java new file mode 100644 index 0000000..494dbd5 --- /dev/null +++ b/src/main/java/dev/plex/authentication/AuthenticatedUser.java @@ -0,0 +1,24 @@ +package dev.plex.authentication; + +import com.google.common.collect.Lists; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.time.ZonedDateTime; +import java.util.LinkedList; +import java.util.List; + +/** + * @author Taah + * @since 6:37 PM [03-05-2024] + */ + +@Data +@Accessors(fluent = true) +public class AuthenticatedUser +{ + private final String ip; + private final ZonedDateTime lastAuthenticated; + private final LinkedList roles = Lists.newLinkedList(); + private final UserType userType = UserType.UNKNOWN; +} diff --git a/src/main/java/dev/plex/authentication/AuthenticationManager.java b/src/main/java/dev/plex/authentication/AuthenticationManager.java new file mode 100644 index 0000000..ef08eb6 --- /dev/null +++ b/src/main/java/dev/plex/authentication/AuthenticationManager.java @@ -0,0 +1,57 @@ +package dev.plex.authentication; + +import dev.plex.HTTPDModule; +import dev.plex.authentication.impl.DiscordOAuth2Provider; +import dev.plex.util.PlexLog; +import org.apache.commons.lang3.NotImplementedException; + +/** + * @author Taah + * @since 7:08 PM [03-05-2024] + */ +public class AuthenticationManager +{ + private final OAuth2Provider provider; + + public AuthenticationManager() + { + final boolean enabled = HTTPDModule.moduleConfig.getBoolean("authentication.enabled", false); + if (!enabled) + { + provider = null; + return; + } + + PlexLog.debug("[HTTPD] Auth is enabled"); + + final String providerName = HTTPDModule.moduleConfig.getString("authentication.provider.name", ""); + if (providerName.isEmpty()) + { + PlexLog.error("OAuth2 Authentication is enabled but no provider was given!"); + provider = null; + return; + } + + PlexLog.debug("[HTTPD] Provider name is {0}", providerName); + + switch (providerName.toLowerCase()) + { + case "discord" -> { + provider = new DiscordOAuth2Provider(); + } + case "xenforo" -> { + throw new NotImplementedException("XenForo OAuth2 is not implemented yet!"); + } + default -> { + provider = null; + } + } + + PlexLog.log("Using {0} provider for authentication", providerName); + } + + public OAuth2Provider provider() + { + return this.provider; + } +} diff --git a/src/main/java/dev/plex/authentication/OAuth2Provider.java b/src/main/java/dev/plex/authentication/OAuth2Provider.java new file mode 100644 index 0000000..7f98a75 --- /dev/null +++ b/src/main/java/dev/plex/authentication/OAuth2Provider.java @@ -0,0 +1,21 @@ +package dev.plex.authentication; + +import org.eclipse.jetty.server.Response; + +import java.util.HashMap; + +/** + * @author Taah + * @since 6:36 PM [03-05-2024] + */ +public interface OAuth2Provider +{ + HashMap sessions(); + + AuthenticatedUser login(Response response, UserType type); + + String[] roles(AuthenticatedUser user); + + String generateLogin(); + +} diff --git a/src/main/java/dev/plex/authentication/UserType.java b/src/main/java/dev/plex/authentication/UserType.java new file mode 100644 index 0000000..7cb821e --- /dev/null +++ b/src/main/java/dev/plex/authentication/UserType.java @@ -0,0 +1,10 @@ +package dev.plex.authentication; + +/** + * @author Taah + * @since 6:37 PM [03-05-2024] + */ +public enum UserType +{ + DISCORD, UNKNOWN +} diff --git a/src/main/java/dev/plex/authentication/impl/DiscordOAuth2Provider.java b/src/main/java/dev/plex/authentication/impl/DiscordOAuth2Provider.java new file mode 100644 index 0000000..b5c2397 --- /dev/null +++ b/src/main/java/dev/plex/authentication/impl/DiscordOAuth2Provider.java @@ -0,0 +1,81 @@ +package dev.plex.authentication.impl; + +import com.google.common.collect.Maps; +import dev.plex.HTTPDModule; +import dev.plex.authentication.AuthenticatedUser; +import dev.plex.authentication.OAuth2Provider; +import dev.plex.authentication.UserType; +import dev.plex.util.PlexLog; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.server.Response; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; + +/** + * @author Taah + * @since 6:41 PM [03-05-2024] + */ +public class DiscordOAuth2Provider implements OAuth2Provider +{ + private final HashMap sessions = Maps.newHashMap(); + + private final String token; + private final String clientId; + private final String redirectUri; + + public DiscordOAuth2Provider() + { + token = System.getenv("BOT_TOKEN").isEmpty() ? HTTPDModule.moduleConfig.getString("authentication.provider.discord.token", System.getProperty("BOT_TOKEN", "")) : System.getenv("BOT_TOKEN"); + clientId = HTTPDModule.moduleConfig.getString("authentication.provider.discord.clientId", ""); + redirectUri = URLEncoder.encode(HTTPDModule.moduleConfig.getString("authentication.provider.redirectUri", ""), StandardCharsets.UTF_8); + + PlexLog.debug("[HTTPD] Client ID: {0}, Redirect URL: {1}", clientId, redirectUri); + + if (redirectUri.isEmpty()) + { + PlexLog.error("Provided authentication redirect url was empty for HTTPD!"); + return; + } + + if (token.isEmpty()) + { + PlexLog.error("Provided discord authentication token was empty for HTTPD!"); + return; + } + + if (clientId.isEmpty()) + { + PlexLog.error("Provided discord client ID was empty for HTTPD!"); + } + + } + + @Override + public HashMap sessions() + { + return sessions; + } + + @Override + public AuthenticatedUser login(Response response, UserType type) + { + return null; + } + + @Override + public String[] roles(AuthenticatedUser user) + { + return new String[0]; + } + + @Override + public String generateLogin() + { + return String.format("https://discord.com/oauth2/authorize?client_id=%s&scope=%s&redirect_uri=%s", + clientId, + "identify%20guilds%20guilds.members.read", + redirectUri); + } +} diff --git a/src/main/java/dev/plex/request/impl/AuthenticationEndpoint.java b/src/main/java/dev/plex/request/impl/AuthenticationEndpoint.java new file mode 100644 index 0000000..17f2e4c --- /dev/null +++ b/src/main/java/dev/plex/request/impl/AuthenticationEndpoint.java @@ -0,0 +1,27 @@ +package dev.plex.request.impl; + +import dev.plex.command.PlexCommand; +import dev.plex.command.annotation.CommandPermissions; +import dev.plex.request.AbstractServlet; +import dev.plex.request.GetMapping; +import dev.plex.util.PlexLog; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandMap; +import org.bukkit.command.PluginIdentifiableCommand; + +import java.util.*; + +public class AuthenticationEndpoint extends AbstractServlet +{ + + + @GetMapping(endpoint = "/oauth2") + public String login(HttpServletRequest request, HttpServletResponse response) + { + // TODO: Nuh uh + return ""; + } +} diff --git a/src/main/resources/httpd/config.yml b/src/main/resources/httpd/config.yml index 3dae029..708e8c2 100644 --- a/src/main/resources/httpd/config.yml +++ b/src/main/resources/httpd/config.yml @@ -1,4 +1,15 @@ server: bind-address: 0.0.0.0 port: 27192 - logging: false \ No newline at end of file + logging: false + +authentication: + enabled: false + + # Providers: discord + provider: + name: discord + redirectUri: "" + discord: # Fill if using discord provider + clientId: "" + token: "" # Can also use environment variable or system property BOT_TOKEN \ No newline at end of file