Skip to content

Commit

Permalink
Merge pull request #3 from hermesdj/dev
Browse files Browse the repository at this point in the history
HTTP Security added !
  • Loading branch information
hermesdj authored Jun 13, 2023
2 parents 50d7b4c + 49f84ac commit 339bada
Show file tree
Hide file tree
Showing 22 changed files with 367 additions and 161 deletions.
16 changes: 16 additions & 0 deletions .github/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
changelog:
exclude:
labels:
- ignore-for-release
categories:
- title: Breaking Changes 🛠
labels:
- Semver-Major
- breaking-change
- title: Exciting New Features 🎉
labels:
- Semver-Minor
- enhancement
- title: Other Changes
labels:
- "*"
1 change: 1 addition & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,6 @@ jobs:
name: v${{ env.GitVersion_SemVer }}
files: ./bin/Release/net6.0/VRisingServerApiPlugin.dll
fail_on_unmatched_files: true
generate_release_notes: true
prerelease: true
tag_name: v${{ env.GitVersion_SemVer }}
100 changes: 100 additions & 0 deletions ApiPlugin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#nullable enable
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime.Injection;
using UnityEngine;
using VRisingServerApiPlugin.command;
using VRisingServerApiPlugin.query;

namespace VRisingServerApiPlugin;

[BepInPlugin(MyPluginInfo.PLUGIN_GUID, MyPluginInfo.PLUGIN_NAME, MyPluginInfo.PLUGIN_VERSION)]
public class ApiPlugin : BasePlugin
{
private Harmony? _harmony;
private Component? _queryDispatcher;

# nullable disable
internal static ManualLogSource Logger { get; private set; }
public static ApiPlugin Instance { get; private set; }
#nullable enable

private ConfigEntry<string> _authorizedUsers;

public ApiPlugin() : base()
{
Instance = this;
Logger = Log;

_authorizedUsers = Config.Bind("Authentication", "AuthorizedUsers", "",
"A list of comma separated username:password entries that defines the accounts allowed to query the API");
}

public override void Load()
{
if (!ServerWorld.IsServer)
{
Log.LogInfo($"Plugin {MyPluginInfo.PLUGIN_GUID} must be installed server side");
return;
}

CommandRegistry.RegisterAll();

ClassInjector.RegisterTypeInIl2Cpp<QueryDispatcher>();
_queryDispatcher = AddComponent<QueryDispatcher>();

_harmony = new Harmony(MyPluginInfo.PLUGIN_GUID);
_harmony.PatchAll(Assembly.GetExecutingAssembly());

Log.LogInfo($"Plugin {MyPluginInfo.PLUGIN_GUID} is loaded!");
}

public override bool Unload()
{
_harmony?.UnpatchSelf();
if (_queryDispatcher != null)
{
Object.Destroy(_queryDispatcher);
}

return true;
}

public List<AuthorizedUser> GetAuthorizedUserList()
{
return AuthorizedUser.ParseConfig(this._authorizedUsers.Value);
}

public bool CheckAuthenticationOfUser(string username, string password)
{
return GetAuthorizedUserList()
.Count(user => user.Username.Equals(username) && user.Password.Equals(password)) == 1;
}

public class AuthorizedUser
{
public string Username { get; set; }
public string Password { get; set; }

private AuthorizedUser(string username, string password)
{
Username = username;
Password = password;
}

public static List<AuthorizedUser> ParseConfig(string authorizedUsers)
{
return (from user in authorizedUsers.Split(",")
select user.Split(':')
into parts
where parts.Length == 2
select new AuthorizedUser(parts[0].Trim(), parts[1].Trim())).ToList();
}
}
}
52 changes: 0 additions & 52 deletions Plugin.cs

This file was deleted.

36 changes: 22 additions & 14 deletions ServerWorld.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static class ServerWorld

public static EntityManager EntityManager = Server.EntityManager;
public static GameDataSystem GameDataSystem = Server.GetExistingSystem<GameDataSystem>();

public static bool IsServer => Application.productName == "VRisingServer";

private static World? GetWorld(string name)
Expand All @@ -40,39 +40,47 @@ public static class ServerWorld
private static Player ConvertEntityToPlayer(Entity userEntity)
{
var user = EntityManager.GetComponentData<User>(userEntity);
return new Player(user,

PlayerCharacter? playerCharacter = EntityManager.HasComponent<PlayerCharacter>(user.LocalCharacter._Entity)
? EntityManager.GetComponentData<PlayerCharacter>(user.LocalCharacter._Entity)
: null;

return new Player(user,
userEntity,
EntityManager.GetComponentData<PlayerCharacter>(user.LocalCharacter._Entity),
user.LocalCharacter._Entity);
playerCharacter,
user.LocalCharacter._Entity
);
}

public static IEnumerable<Player> GetAllPlayerCharacters()
{
return ListUtils.Convert(
EntityManager.CreateEntityQuery(ComponentType.ReadOnly<User>())
.ToEntityArray(Allocator.Temp)
EntityManager.CreateEntityQuery(ComponentType.ReadOnly<User>())
.ToEntityArray(Allocator.Temp)
)
.Where(userEntity => EntityManager.GetComponentData<User>(userEntity).LocalCharacter._Entity != Entity.Null)
.Select(ConvertEntityToPlayer)
.ToList();
.ToList();
}

public static Player? GetPlayer(int userIndex)
{
Entity? userEntity = ListUtils
Entity? entity = ListUtils
.Convert(EntityManager.CreateEntityQuery(ComponentType.ReadOnly<User>())
.ToEntityArray(Allocator.Temp))
.FirstOrDefault(userEntity => EntityManager.GetComponentData<User>(userEntity).Index == userIndex);
.FirstOrDefault(e => EntityManager.GetComponentData<User>(e).Index == userIndex);

return userEntity.HasValue ? ConvertEntityToPlayer(userEntity.Value) : null;
return entity.HasValue
? ConvertEntityToPlayer(entity.Value)
: null;
}

public static IEnumerable<ClanTeam> GetAllClans()
{
return ListUtils.Convert(
EntityManager.CreateEntityQuery(ComponentType.ReadOnly<ClanTeam>())
.ToEntityArray(Allocator.Temp)
)
EntityManager.CreateEntityQuery(ComponentType.ReadOnly<ClanTeam>())
.ToEntityArray(Allocator.Temp)
)
.Select(clanEntity => EntityManager.GetComponentData<ClanTeam>(clanEntity))
.ToList();
}
Expand All @@ -88,7 +96,7 @@ public static IEnumerable<Entity> GetAllClanEntities()
public readonly record struct Player(
User User,
Entity UserEntity,
PlayerCharacter Character,
PlayerCharacter? Character,
Entity CharacterEntity
);
}
3 changes: 2 additions & 1 deletion VRisingServerApiPlugin.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@
</ItemGroup>

<ItemGroup>
<Content Include=".github\release.yaml" />
<Content Include=".github\workflows\build.yaml" />
<Content Include=".github\workflows\release.yaml" />
<None Include="README.md" Pack="true" PackagePath="\"/>
<None Include="README.md" Pack="true" PackagePath="\" />
</ItemGroup>

<Target Name="CopyDLLsToTargetServer" AfterTargets="Build">
Expand Down
7 changes: 5 additions & 2 deletions attributes/HttpHandlerAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ namespace VRisingServerApiPlugin.attributes;
public class HttpHandlerAttribute : Attribute
{
public string BasePath { get; set; }

public HttpHandlerAttribute(string basePath = "/")

public bool AllRouteProtected { get; set; }

public HttpHandlerAttribute(string basePath = "/", bool allRouteProtected = false)
{
BasePath = basePath;
AllRouteProtected = allRouteProtected;
}
}
7 changes: 5 additions & 2 deletions attributes/methods/HttpAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ public class HttpAttribute : Attribute
public string Pattern { get; set; }

public string Method { get; set; }

public HttpAttribute(string pattern, string method)

public bool Protected { get; set; }

public HttpAttribute(string pattern, string method, bool isProtected = false)
{
Pattern = pattern;
Method = method;
Protected = isProtected;
}
}
4 changes: 4 additions & 0 deletions attributes/methods/HttpGet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@ public class HttpGet : HttpAttribute
public HttpGet(string pattern) : base(pattern, "GET")
{
}

public HttpGet(string pattern, bool isProtected) : base(pattern, "GET", isProtected)
{
}
}
4 changes: 4 additions & 0 deletions attributes/methods/HttpPost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@ public class HttpPost : HttpAttribute
public HttpPost(string pattern) : base(pattern, "POST")
{
}

public HttpPost(string pattern, bool isProtected) : base(pattern, "POST", isProtected)
{
}
}
5 changes: 4 additions & 1 deletion command/Command.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ public class Command
public string Pattern { get; }
public string Method { get; }

public bool IsProtected { get; }

public Func<HttpRequest, object?> CommandHandler { get; }

public Command(string pattern, string method, Func<HttpRequest, object?> commandHandler){
public Command(string pattern, string method, Func<HttpRequest, object?> commandHandler, bool isProtected = false){
Pattern = pattern;
Method = method;
CommandHandler = commandHandler;
IsProtected = isProtected;
}
}
25 changes: 19 additions & 6 deletions command/CommandRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,22 @@ private static void RegisterMethod(object? container, MethodInfo method,
if (method.GetCustomAttribute(typeof(HttpAttribute), true) is not HttpAttribute httpAttribute) return;

var pattern = $"^{httpHandlerAttribute.BasePath}{httpAttribute.Pattern}$";
var command = new Command(pattern: pattern, method: httpAttribute.Method, request =>

var isProtected = httpAttribute.Protected || httpHandlerAttribute.AllRouteProtected;

var command = new Command(
pattern: pattern,
method: httpAttribute.Method,
commandHandler: GenerateHandler(container, method),
isProtected: isProtected
);

Commands.Add(command);
}

private static Func<HttpRequest, object?> GenerateHandler(object? container, MethodBase method)
{
return request =>
{
var args = new List<object?>();
var parameters = method.GetParameters();
Expand Down Expand Up @@ -84,7 +99,7 @@ private static void RegisterMethod(object? container, MethodInfo method,

if (args.Count != parameters.Length)
{
Plugin.Logger?.LogInfo($"parameters are {parameters} and parsed args are {args}");
ApiPlugin.Logger?.LogInfo($"parameters are {parameters} and parsed args are {args}");
throw new HttpException(400, "Invalid parameters !");
}

Expand All @@ -96,13 +111,11 @@ private static void RegisterMethod(object? container, MethodInfo method,
{
if (ex.InnerException == null) throw;

Plugin.Logger?.LogError(
ApiPlugin.Logger?.LogError(
$"Inner Exception {ex.InnerException?.Message}, stack: {ex.InnerException?.StackTrace}");
throw ex.InnerException!;
}
});

Commands.Add(command);
};
}

private static object? ParseUrlOrQueryArg(Dictionary<string, string> dictionary, string name,
Expand Down
Loading

0 comments on commit 339bada

Please sign in to comment.