feat/Update_Controllers_to_Allow_for_Premium_Membership
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace JobTrackerApi.Services;
|
||||
|
||||
public sealed class ExternalOrigin
|
||||
{
|
||||
private static readonly HashSet<string> InternalHealthHosts = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"backend",
|
||||
"localhost",
|
||||
"127.0.0.1",
|
||||
"::1",
|
||||
};
|
||||
|
||||
private readonly Uri _uri;
|
||||
|
||||
private ExternalOrigin(Uri uri)
|
||||
{
|
||||
_uri = uri;
|
||||
BaseUrl = uri.GetComponents(UriComponents.SchemeAndServer, UriFormat.UriEscaped).TrimEnd('/');
|
||||
}
|
||||
|
||||
public string BaseUrl { get; }
|
||||
public bool UsesHttps => _uri.Scheme == Uri.UriSchemeHttps;
|
||||
|
||||
public static ExternalOrigin FromConfiguration(IConfiguration configuration, bool production = false) =>
|
||||
Parse(configuration["App:PublicBaseUrl"], production);
|
||||
|
||||
public static ExternalOrigin Parse(string? value, bool production)
|
||||
{
|
||||
var raw = value?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(raw))
|
||||
{
|
||||
if (production)
|
||||
throw new InvalidOperationException("App:PublicBaseUrl is required in Production.");
|
||||
|
||||
raw = "http://localhost:3000";
|
||||
}
|
||||
|
||||
if (!Uri.TryCreate(raw, UriKind.Absolute, out var uri)
|
||||
|| (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps)
|
||||
|| string.IsNullOrWhiteSpace(uri.Host)
|
||||
|| !string.IsNullOrEmpty(uri.UserInfo)
|
||||
|| uri.AbsolutePath != "/"
|
||||
|| !string.IsNullOrEmpty(uri.Query)
|
||||
|| !string.IsNullOrEmpty(uri.Fragment))
|
||||
{
|
||||
throw new InvalidOperationException("App:PublicBaseUrl must be an absolute HTTP(S) origin without credentials, a path, query, or fragment.");
|
||||
}
|
||||
|
||||
if (production && uri.Scheme != Uri.UriSchemeHttps)
|
||||
throw new InvalidOperationException("App:PublicBaseUrl must use HTTPS in Production.");
|
||||
|
||||
return new ExternalOrigin(uri);
|
||||
}
|
||||
|
||||
public string BuildPath(string pathAndQuery)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pathAndQuery) || pathAndQuery[0] != '/')
|
||||
throw new ArgumentException("External paths must start with '/'.", nameof(pathAndQuery));
|
||||
|
||||
return $"{BaseUrl}{pathAndQuery}";
|
||||
}
|
||||
|
||||
public bool Matches(HostString host)
|
||||
{
|
||||
if (!string.Equals(host.Host, _uri.IdnHost, StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(host.Host, _uri.Host, StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
|
||||
return _uri.IsDefaultPort
|
||||
? host.Port is null || host.Port == _uri.Port
|
||||
: host.Port == _uri.Port;
|
||||
}
|
||||
|
||||
public bool AllowsRequest(HostString host, PathString path) =>
|
||||
Matches(host) || (path == "/health" && IsInternalHealthHost(host));
|
||||
|
||||
public static bool IsInternalHealthHost(HostString host) => InternalHealthHosts.Contains(host.Host);
|
||||
}
|
||||
Reference in New Issue
Block a user