diff options
author | vnugent <public@vaughnnugent.com> | 2023-01-12 17:47:40 -0500 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2023-01-12 17:47:40 -0500 |
commit | b75668b164d398b99ee942beced06aa27ef65a50 (patch) | |
tree | c1faf6df3caa78083dcc38eb1a7247e456bbe754 /plugins/ObjectCacheServer/src/Endpoints/BrokerHeartBeat.cs | |
parent | cea64e619e714f6dbe51d37ca8329b58d8c271fb (diff) |
Large project reorder and consolidation
Diffstat (limited to 'plugins/ObjectCacheServer/src/Endpoints/BrokerHeartBeat.cs')
-rw-r--r-- | plugins/ObjectCacheServer/src/Endpoints/BrokerHeartBeat.cs | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/plugins/ObjectCacheServer/src/Endpoints/BrokerHeartBeat.cs b/plugins/ObjectCacheServer/src/Endpoints/BrokerHeartBeat.cs new file mode 100644 index 0000000..bd1233e --- /dev/null +++ b/plugins/ObjectCacheServer/src/Endpoints/BrokerHeartBeat.cs @@ -0,0 +1,130 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: ObjectCacheServer +* File: BrokerHeartBeat.cs +* +* BrokerHeartBeat.cs is part of ObjectCacheServer which is part of the larger +* VNLib collection of libraries and utilities. +* +* ObjectCacheServer 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. +* +* ObjectCacheServer 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 this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System; +using System.Net; +using System.Linq; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; + +using VNLib.Hashing.IdentityUtility; +using VNLib.Plugins.Essentials.Endpoints; +using VNLib.Plugins.Essentials.Extensions; +using VNLib.Plugins.Extensions.Loading; + +namespace VNLib.Plugins.Essentials.Sessions.Server.Endpoints +{ + internal sealed class BrokerHeartBeat : ResourceEndpointBase + { + public override string Path => "/heartbeat"; + + private readonly Func<string> Token; + private readonly ManualResetEvent KeepaliveSet; + private readonly Task<IPAddress[]> BrokerIpList; + private readonly PluginBase Pbase; + + protected override ProtectionSettings EndpointProtectionSettings { get; } = new() + { + DisableBrowsersOnly = true, + DisableSessionsRequired = true, + DisableVerifySessionCors = true + }; + + public BrokerHeartBeat(Func<string> token, ManualResetEvent keepaliveSet, Uri brokerUri, PluginBase pbase) + { + Token = token; + KeepaliveSet = keepaliveSet; + BrokerIpList = Dns.GetHostAddressesAsync(brokerUri.DnsSafeHost); + + this.Pbase = pbase; + } + + private async Task<ReadOnlyJsonWebKey> GetBrokerPubAsync() + { + return await Pbase.TryGetSecretAsync("broker_public_key").ToJsonWebKey() ?? throw new KeyNotFoundException("Missing required secret : broker_public_key"); + } + + protected override async ValueTask<VfReturnType> GetAsync(HttpEntity entity) + { + //If-not loopback then verify server address + if (!entity.Server.IsLoopBack()) + { + //Load and verify the broker's ip address matches with an address we have stored + IPAddress[] addresses = await BrokerIpList; + if (!addresses.Contains(entity.TrustedRemoteIp)) + { + //Token invalid + entity.CloseResponse(HttpStatusCode.Forbidden); + return VfReturnType.VirtualSkip; + } + } + //Get the authorization jwt + string? jwtAuth = entity.Server.Headers[HttpRequestHeader.Authorization]; + + if (string.IsNullOrWhiteSpace(jwtAuth)) + { + //Token invalid + entity.CloseResponse(HttpStatusCode.Forbidden); + return VfReturnType.VirtualSkip; + } + + //Parse the jwt + using JsonWebToken jwt = JsonWebToken.Parse(jwtAuth); + + //Verify the jwt using the broker's public key certificate + using (ReadOnlyJsonWebKey cert = await GetBrokerPubAsync()) + { + //Verify the jwt + if (!jwt.VerifyFromJwk(cert)) + { + //Token invalid + entity.CloseResponse(HttpStatusCode.Forbidden); + return VfReturnType.VirtualSkip; + } + } + + string? auth; + //Recover the auth token from the jwt + using (JsonDocument doc = jwt.GetPayload()) + { + auth = doc.RootElement.GetProperty("token").GetString(); + } + + //Verify token + if(Token().Equals(auth, StringComparison.Ordinal)) + { + //Signal keepalive + KeepaliveSet.Set(); + entity.CloseResponse(HttpStatusCode.OK); + return VfReturnType.VirtualSkip; + } + + //Token invalid + entity.CloseResponse(HttpStatusCode.Forbidden); + return VfReturnType.VirtualSkip; + } + } +} |