2022-05-09 07:07:35 -04:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
2023-02-07 07:59:36 -05:00
|
|
|
using System;
|
2022-05-09 07:07:35 -04:00
|
|
|
using System.Security.Claims;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using EpicGames.Horde.Storage;
|
|
|
|
|
using Jupiter.Common;
|
|
|
|
|
using Microsoft.AspNetCore.Authorization;
|
|
|
|
|
using Microsoft.AspNetCore.Http;
|
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
|
using Microsoft.Extensions.Options;
|
2023-03-03 09:07:19 -05:00
|
|
|
using Microsoft.Extensions.Primitives;
|
2022-11-14 10:12:52 -05:00
|
|
|
using OpenTelemetry.Trace;
|
2022-05-09 07:07:35 -04:00
|
|
|
|
2022-10-12 06:36:30 -04:00
|
|
|
namespace Jupiter.Controllers;
|
2022-05-09 07:07:35 -04:00
|
|
|
|
2023-06-29 14:45:59 -04:00
|
|
|
public class RequestHelper : IRequestHelper
|
2022-05-09 07:07:35 -04:00
|
|
|
{
|
2023-07-27 11:20:47 -04:00
|
|
|
private readonly IAuthorizationService _authorizationService;
|
|
|
|
|
private readonly INamespacePolicyResolver _namespacePolicyResolver;
|
|
|
|
|
private readonly IOptionsMonitor<JupiterSettings> _settings;
|
|
|
|
|
private readonly Tracer _tracer;
|
2022-05-09 07:07:35 -04:00
|
|
|
|
2023-07-27 11:20:47 -04:00
|
|
|
public RequestHelper(IAuthorizationService authorizationService, INamespacePolicyResolver namespacePolicyResolver, IOptionsMonitor<JupiterSettings> settings, Tracer tracer)
|
|
|
|
|
{
|
|
|
|
|
_authorizationService = authorizationService;
|
|
|
|
|
_namespacePolicyResolver = namespacePolicyResolver;
|
|
|
|
|
_settings = settings;
|
|
|
|
|
_tracer = tracer;
|
|
|
|
|
}
|
2022-05-09 07:07:35 -04:00
|
|
|
|
2023-08-10 23:09:40 -04:00
|
|
|
public async Task<ActionResult?> HasAccessToNamespaceAsync(ClaimsPrincipal user, HttpRequest request, NamespaceId ns, JupiterAclAction[] aclActions)
|
2023-07-27 11:20:47 -04:00
|
|
|
{
|
|
|
|
|
using TelemetrySpan _ = _tracer.StartActiveSpan("authorize").SetAttribute("operation.name", "authorize");
|
|
|
|
|
AuthorizationResult authorizationResult = await _authorizationService.AuthorizeAsync(user, new NamespaceAccessRequest
|
|
|
|
|
{
|
|
|
|
|
Namespace = ns,
|
|
|
|
|
Actions = aclActions
|
|
|
|
|
}, NamespaceAccessRequirement.Name);
|
2022-05-09 07:07:35 -04:00
|
|
|
|
2023-07-27 11:20:47 -04:00
|
|
|
if (!authorizationResult.Succeeded)
|
|
|
|
|
{
|
|
|
|
|
return new ForbidResult();
|
|
|
|
|
}
|
2022-05-09 07:07:35 -04:00
|
|
|
|
2023-07-27 11:20:47 -04:00
|
|
|
bool isPublicNamespace = _namespacePolicyResolver.GetPoliciesForNs(ns).IsPublicNamespace;
|
2022-05-09 07:07:35 -04:00
|
|
|
|
2023-07-27 11:20:47 -04:00
|
|
|
// public namespaces are always accessible
|
|
|
|
|
if (isPublicNamespace)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2022-05-09 07:07:35 -04:00
|
|
|
|
2024-04-23 08:00:22 -04:00
|
|
|
// namespace is a restricted namespace, check which port it is being accessed on
|
|
|
|
|
bool isPublicPort = IsPublicPort(request.HttpContext);
|
|
|
|
|
|
|
|
|
|
if (isPublicPort)
|
|
|
|
|
{
|
|
|
|
|
// trying to access restricted namespace on a public port, this is not allowed
|
|
|
|
|
return new ForbidResult();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// restricted namespace in corp or internal port, this is okay
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<ActionResult?> HasAccessForGlobalOperationsAsync(ClaimsPrincipal user, JupiterAclAction[] aclActions)
|
|
|
|
|
{
|
|
|
|
|
using TelemetrySpan _ = _tracer.StartActiveSpan("authorize").SetAttribute("operation.name", "authorize");
|
|
|
|
|
AuthorizationResult authorizationResult = await _authorizationService.AuthorizeAsync(user, new GlobalAccessRequest
|
|
|
|
|
{
|
|
|
|
|
Actions = aclActions
|
|
|
|
|
}, GlobalAccessRequirement.Name);
|
|
|
|
|
|
|
|
|
|
if (!authorizationResult.Succeeded)
|
|
|
|
|
{
|
|
|
|
|
return new ForbidResult();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool IsPublicPort(HttpContext context)
|
|
|
|
|
{
|
2023-07-27 11:20:47 -04:00
|
|
|
string? portHeaderValue = null;
|
|
|
|
|
if (context.Request.Headers.TryGetValue("X-Jupiter-Port", out StringValues values))
|
|
|
|
|
{
|
|
|
|
|
portHeaderValue = values.ToString();
|
|
|
|
|
}
|
2023-03-03 09:07:19 -05:00
|
|
|
|
2023-07-27 11:20:47 -04:00
|
|
|
// unit tests do not run on ports, we consider them always on the internal port
|
|
|
|
|
bool isLocalConnection = context.Connection.LocalPort == 0 && context.Connection.LocalIpAddress == null;
|
|
|
|
|
// public port is either running on the public port, or if using domain sockets we check the header that is passed along instead
|
|
|
|
|
bool isPublicPort = _settings!.CurrentValue.PublicApiPorts.Contains(context.Connection.LocalPort);
|
2023-03-03 09:07:19 -05:00
|
|
|
|
2023-07-27 11:20:47 -04:00
|
|
|
if (isLocalConnection && _settings.CurrentValue.AssumeLocalConnectionsHasFullAccess)
|
|
|
|
|
{
|
|
|
|
|
// local connection so granting it full access
|
|
|
|
|
isPublicPort = false;
|
|
|
|
|
}
|
2023-03-03 09:07:19 -05:00
|
|
|
|
2023-07-27 11:20:47 -04:00
|
|
|
if (isLocalConnection && portHeaderValue != null)
|
|
|
|
|
{
|
|
|
|
|
if (string.Equals(portHeaderValue, "Public", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
isPublicPort = true;
|
|
|
|
|
}
|
|
|
|
|
else if (string.Equals(portHeaderValue, "Corp", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
isPublicPort = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-03 09:07:19 -05:00
|
|
|
|
2024-04-23 08:00:22 -04:00
|
|
|
return isPublicPort;
|
2023-07-27 11:20:47 -04:00
|
|
|
}
|
2022-05-09 07:07:35 -04:00
|
|
|
}
|