chris cfe3ce76c8 Rename Bridgekeeper project to Mithrandir.
Replaced all instances of "Bridgekeeper" with "Mithrandir" across the solution. Updated project files, folder structures, namespaces, and references to reflect the new naming convention.
2025-05-31 03:28:32 +02:00

163 lines
5.3 KiB
C#

using System.Net;
using System.Net.Http.Headers;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
using Suspectus.Gandalf.Core.Abstractions.DTOs.Internal.Auth;
using Suspectus.Gandalf.Core.Abstractions.Extensions;
using Suspectus.Gandalf.Palantir.Client;
namespace Suspectus.Gandalf.Mithrandir.Api.Controllers;
[ApiController]
[Route("api/[controller]")]
public class AppProxyController : ControllerBase
{
private readonly IDistributedCache _tokenCache;
private readonly IPalantirClient _client;
private readonly HttpClient _httpClient;
public AppProxyController(IDistributedCache tokenCache, IPalantirClient client, IHttpClientFactory httpClientFactory)
{
_tokenCache = tokenCache;
_client = client;
_httpClient = httpClientFactory.CreateClient("test");
}
[HttpGet("{appId}/{*path}")]
public async Task<IActionResult> Get(string appId, string path, CancellationToken cancellationToken)
{
var sessionCookie = Request.Cookies["MithrandirSession"];
if (sessionCookie is null)
{
return Unauthorized("Session expired.");
}
var appInfoResult = await _client.Internal.App.GetInfo(appId);
if (!appInfoResult.IsSuccess)
{
return BadRequest(appInfoResult.Match<string?>(_ => null, e => e.Message));
}
var appInfo = appInfoResult.GetValue();
if (appInfo.IsMaster && path.StartsWith("api/internal"))
{
return NotFound();
}
var token = await GetToken(sessionCookie, appId, cancellationToken);
if (token is null)
{
return Unauthorized("Session expired.");
}
_httpClient.BaseAddress = new Uri(appInfo.BaseAddress);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
try
{
var clientResponse = await _httpClient.GetAsync(path, cancellationToken);
if (!clientResponse.IsSuccessStatusCode)
{
Response.StatusCode = (int)clientResponse.StatusCode;
}
foreach (var header in clientResponse.Content.Headers)
{
Response.Headers[header.Key] = header.Value.ToArray();
}
await Response.WriteAsync(await clientResponse.Content.ReadAsStringAsync(cancellationToken), cancellationToken: cancellationToken);
}
catch (HttpRequestException e)
{
Response.StatusCode = e.StatusCode is null ? (int)HttpStatusCode.InternalServerError : (int)e.StatusCode;
await Response.WriteAsync(e.Message, cancellationToken: cancellationToken);
}
return new EmptyResult();
}
private async Task<string?> GetToken(string subjectId, string appId, CancellationToken cancellationToken)
{
var accessTokenCacheKey = GetCacheKey(subjectId, appId, "access_token");
var accessToken = await _tokenCache.GetStringAsync(accessTokenCacheKey, cancellationToken);
if (accessToken is not null)
{
return accessToken;
}
var refreshTokenCacheKey = GetCacheKey(subjectId, appId, "refresh_token");
var refreshToken = await _tokenCache.GetStringAsync(refreshTokenCacheKey, cancellationToken);
if (refreshToken is null)
{
return null;
}
var tokenRequestCommandResponse = await _client.Internal.Auth.Token(new TokenRequestCommand
{
GrandType = GrandType.RefreshToken,
RefreshToken = refreshToken,
ClientId = appId
});
if (tokenRequestCommandResponse.IsFaulted)
{
return null;
}
var tokenRequestResponse = tokenRequestCommandResponse.GetValue();
if (tokenRequestResponse is null)
{
return null;
}
await _tokenCache.SetStringAsync(
accessTokenCacheKey,
tokenRequestResponse.AccessToken,
new DistributedCacheEntryOptions
{
AbsoluteExpiration = tokenRequestResponse.AccessTokenExpiresAt.AddSeconds(-10),
},
cancellationToken
);
var refreshTokenCacheExpiration = tokenRequestResponse.RefreshTokenExpiresAt.AddSeconds(-10);
await _tokenCache.SetStringAsync(
refreshTokenCacheKey,
tokenRequestResponse.RefreshToken,
new DistributedCacheEntryOptions
{
AbsoluteExpiration = refreshTokenCacheExpiration,
},
cancellationToken
);
UpdateSessionCookie(subjectId, refreshTokenCacheExpiration);
return tokenRequestResponse.AccessToken;
}
private string GetCacheKey(string subjectId, string appId, string tokenType)
{
return $"{subjectId}:{appId}:{tokenType}";
}
private void UpdateSessionCookie(string subjectId, DateTimeOffset refreshTokenCacheExpiration)
{
Response.Cookies.Append("MithrandirSession", subjectId, new CookieOptions
{
Secure = true,
HttpOnly = true,
SameSite = SameSiteMode.Lax,
Expires = refreshTokenCacheExpiration
});
}
}