Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: basic auth for all api endpoints #22

Merged
merged 4 commits into from
Jun 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 47 additions & 3 deletions Plugin.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Reflection;
using System.Collections.Generic;
using System.Reflection;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using Bloodstone.API;
Expand All @@ -15,18 +17,34 @@ namespace v_rising_discord_bot_companion;
[Reloadable]
public class Plugin : BasePlugin {

public static ManualLogSource Logger = null!;
public static ManualLogSource Logger { get; private set; } = null!;
public static Plugin Instance { get; private set; } = null!;
private Harmony? _harmony;
private Component? _queryDispatcher;

private PluginConfig? _pluginConfig;
private ConfigEntry<string> _basicAuthUsers;

public Plugin() {

Instance = this;
Logger = Log;

_basicAuthUsers = Config.Bind(
"Authentication",
"BasicAuthUsers",
"",
"A list of comma separated username:password entries that are allowed to query the HTTP API."
);
}

public override void Load() {

if (!VWorld.IsServer) {
Log.LogInfo($"Plugin {MyPluginInfo.PLUGIN_GUID} must be installed on the server side.");
return;
}

Logger = Log;

// Plugin startup logic
Log.LogInfo($"Plugin {MyPluginInfo.PLUGIN_GUID} version {MyPluginInfo.PLUGIN_VERSION} is loaded!");
Expand All @@ -45,6 +63,32 @@ public override bool Unload() {
if (_queryDispatcher != null) {
Object.Destroy(_queryDispatcher);
}
_pluginConfig = null;
return true;
}

public PluginConfig GetPluginConfig() {
_pluginConfig ??= ParsePluginConfig();
return (PluginConfig) _pluginConfig;
}

private PluginConfig ParsePluginConfig() {

var basicAuthUsers = new List<BasicAuthUser>();
foreach (var basicAuthUser in _basicAuthUsers.Value.Split(",")) {
var parts = basicAuthUser.Split(":");
if (parts.Length == 2) {
basicAuthUsers.Add(
new BasicAuthUser(
Username: parts[0].Trim(),
Password: parts[1].Trim()
)
);
}
}

return new PluginConfig(
BasicAuthUsers: basicAuthUsers
);
}
}
12 changes: 12 additions & 0 deletions PluginConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Collections.Generic;

namespace v_rising_discord_bot_companion;

public readonly record struct PluginConfig(
List<BasicAuthUser> BasicAuthUsers
);

public readonly record struct BasicAuthUser(
string Username,
string Password
);
64 changes: 64 additions & 0 deletions command/HttpReceiveServicePatches.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System.Linq;
using HarmonyLib;
using Il2CppSystem.Net;
using Il2CppSystem.Security.Principal;
using ProjectM.Network;

namespace v_rising_discord_bot_companion.command;

[HarmonyPatch(typeof(HttpServiceReceiveThread))]
public class HttpReceiveServicePatches {

[HarmonyPrefix]
[HarmonyPatch("IsAllowed")]
public static bool IsAllowed(HttpListenerContext context, ref bool __result) {

var pluginConfig = Plugin.Instance.GetPluginConfig();

if (pluginConfig.BasicAuthUsers.Count <= 0) {
return true;
}

var currentBasicAuthUser = ParseBasicAuthUser(context);
if (!currentBasicAuthUser.HasValue) {
__result = false;
return false;
}

__result = IsAuthorized((BasicAuthUser) currentBasicAuthUser);
return __result;
}

private static BasicAuthUser? ParseBasicAuthUser(HttpListenerContext context) {

context.ParseAuthentication(AuthenticationSchemes.Basic);

if (context.user == null) {
return null;
}

var principal = context.user.TryCast<GenericPrincipal>();
var identity = principal?.m_identity.TryCast<HttpListenerBasicIdentity>();

if (identity == null) {
return null;
}

var username = identity.Name;
var password = identity.password;

if (username == null || password == null) {
return null;
}

return new BasicAuthUser(
Username: username,
Password: password
);
}

private static bool IsAuthorized(BasicAuthUser currentBasicAuthUser) {
return Plugin.Instance.GetPluginConfig().BasicAuthUsers
.Count(it => it.Username.Equals(currentBasicAuthUser.Username) && it.Password.Equals(currentBasicAuthUser.Password)) == 1;
}
}
3 changes: 3 additions & 0 deletions command/ServerWebAPISystemPatches.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class ServerWebAPISystemPatches {
public static void OnCreate(ServerWebAPISystem __instance) {

if (!SettingsManager.ServerHostSettings.API.Enabled) {
Plugin.Logger.LogInfo($"HTTP API is not enabled.");
return;
}

Expand All @@ -50,6 +51,8 @@ public static void OnCreate(ServerWebAPISystem __instance) {
"GET",
BuildAdapter(_ => VampireDownedServerEventSystemPatches.getPvpKills())
));

Plugin.Logger.LogInfo($"Added v-rising-discord-bot endpoints.");
}

private static HttpServiceReceiveThread.RequestHandler BuildAdapter(Func<HttpListenerContext, object> commandHandler) {
Expand Down
28 changes: 28 additions & 0 deletions http-requests.http
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,31 @@ GET http://localhost:25570/v-rising-discord-bot/player-activities

### Get pvp kills
GET http://localhost:25570/v-rising-discord-bot/pvp-kills

### Metrics
GET http://localhost:25570/metrics

### Console
GET http://localhost:25570/console/v1?input=sendserverannouncement some message

### Messages
POST http://localhost:25570/api/message/v1
Content-Type: application/json

{
"message": "the actual message"
}

### Shutdown
POST http://localhost:25570/api/shutdown/v1
Content-Type: application/json

{
"shutdown": true
}

### Save
POST http://localhost:25570/api/save/v1
Content-Type: application/json

{}
Loading