218 lines
5.8 KiB
C#
218 lines
5.8 KiB
C#
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Http.Extensions;
|
|
using Microsoft.Extensions.Logging;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.Linq;
|
|
using System.Security.Claims;
|
|
using System.Threading.Tasks;
|
|
using zero.Core.Caches.Internal;
|
|
using zero.Core.Database;
|
|
using zero.Core.Entities;
|
|
using zero.Core.Extensions;
|
|
using zero.Core.Handlers;
|
|
using zero.Core.Options;
|
|
|
|
namespace zero.Core
|
|
{
|
|
public class ApplicationResolver : IApplicationResolver
|
|
{
|
|
ApplicationCache _applicationCache;
|
|
BackofficeUserCache _backofficeUserCache;
|
|
|
|
protected IZeroOptions Options { get; private set; }
|
|
|
|
protected ILogger<ApplicationResolver> Logger { get; private set; }
|
|
|
|
protected IHandlerHolder Handler { get; private set; }
|
|
|
|
|
|
|
|
public ApplicationResolver(IZeroStore store, IZeroOptions options, ILogger<ApplicationResolver> logger, IHandlerHolder handler = null)
|
|
{
|
|
_applicationCache = new ApplicationCache(store);
|
|
_backofficeUserCache = new BackofficeUserCache(store);
|
|
Options = options;
|
|
Logger = logger;
|
|
Handler = handler;
|
|
}
|
|
|
|
|
|
/// <inheritdoc />
|
|
public async Task<Application> Resolve(HttpContext context, ClaimsPrincipal user)
|
|
{
|
|
if (context?.Request == null)
|
|
{
|
|
Logger.LogWarning("Could not resolve application as HTTP request is null");
|
|
return null;
|
|
}
|
|
|
|
Application app;
|
|
|
|
if (context.IsBackofficeRequest(Options.BackofficePath))
|
|
{
|
|
app = await ResolveFromUser(user);
|
|
}
|
|
else
|
|
{
|
|
app = await ResolveFromRequest(context);
|
|
}
|
|
|
|
if (app == null)
|
|
{
|
|
//Logger.LogWarning("Could not resolve application for host {host}", context.Request.Host);
|
|
app = _applicationCache.All.FirstOrDefault();
|
|
}
|
|
|
|
return app;
|
|
}
|
|
|
|
|
|
/// <inheritdoc />
|
|
public async Task<Application> ResolveFromUser(ClaimsPrincipal user)
|
|
{
|
|
BackofficeUserCache.Result userEntity = GetBackofficeUser(user);
|
|
return userEntity != null ? await ResolveFromUser(new BackofficeUser()
|
|
{
|
|
Id = userEntity.Id,
|
|
AppId = userEntity.AppId,
|
|
CurrentAppId = userEntity.CurrentAppId,
|
|
Claims = userEntity.Claims,
|
|
IsSuper = userEntity.IsSuper
|
|
}) : null;
|
|
}
|
|
|
|
|
|
/// <inheritdoc />
|
|
public Task<Application> ResolveFromUser(BackofficeUser user)
|
|
{
|
|
if (user == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
string appId;
|
|
string[] allowedAppIds = user.GetAllowedAppIds();
|
|
|
|
if (!user.CurrentAppId.IsNullOrEmpty())
|
|
{
|
|
if (user.IsSuper || allowedAppIds.Contains(user.CurrentAppId, StringComparer.InvariantCultureIgnoreCase))
|
|
{
|
|
appId = user.CurrentAppId;
|
|
}
|
|
else
|
|
{
|
|
appId = user.AppId;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
appId = user.AppId;
|
|
}
|
|
|
|
if (appId.IsNullOrEmpty())
|
|
{
|
|
throw new Exception($"User entity ${user.Id} needs a valid AppId");
|
|
}
|
|
|
|
return Task.FromResult(_applicationCache.ById(appId));
|
|
}
|
|
|
|
|
|
/// <inheritdoc />
|
|
public async Task<Application> ResolveFromRequest(HttpContext context)
|
|
{
|
|
return Handler.Get<IApplicationResolverHandler>()?.Resolve(context.Request, _applicationCache.All) ?? await ResolveFromUri(context.Request.GetEncodedUrl());
|
|
}
|
|
|
|
|
|
/// <inheritdoc />
|
|
public Task<Application> ResolveFromUri(string uriString)
|
|
{
|
|
return Task.FromResult(ResolveFromUriInternal(new Uri(uriString, UriKind.Absolute), _applicationCache.All));
|
|
}
|
|
|
|
|
|
/// <inheritdoc />
|
|
public Task<Application> ResolveFromUri(Uri uri)
|
|
{
|
|
return Task.FromResult(ResolveFromUriInternal(uri, _applicationCache.All));
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Get matching application from an URI
|
|
/// </summary>
|
|
Application ResolveFromUriInternal(Uri uri, IEnumerable<Application> apps)
|
|
{
|
|
foreach (Application app in apps)
|
|
{
|
|
if (app.Domains?.Length < 1)
|
|
{
|
|
Logger.LogWarning("No domains specified for app {app}", app.Id);
|
|
continue;
|
|
}
|
|
|
|
foreach (Uri domain in app.Domains)
|
|
{
|
|
int compareResult = Uri.Compare(uri, domain, UriComponents.HostAndPort, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase);
|
|
if (compareResult == 0)
|
|
{
|
|
return app;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Get backoffice user from claims principal
|
|
/// </summary>
|
|
BackofficeUserCache.Result GetBackofficeUser(ClaimsPrincipal user)
|
|
{
|
|
string userId = user.FindFirstValue(Constants.Auth.Claims.UserId);
|
|
return _backofficeUserCache.ById(userId);
|
|
}
|
|
}
|
|
|
|
|
|
public interface IApplicationResolver
|
|
{
|
|
/// <summary>
|
|
/// Resolves the current application from either the backoffice user (in case it is backoffice request)
|
|
/// or the domain (in case it is frontend request).
|
|
/// </summary>
|
|
Task<Application> Resolve(HttpContext context, ClaimsPrincipal user);
|
|
|
|
/// <summary>
|
|
/// Resolves the current application from the request path
|
|
/// </summary>
|
|
Task<Application> ResolveFromRequest(HttpContext context);
|
|
|
|
/// <summary>
|
|
/// Get matching application from an URI string
|
|
/// </summary>
|
|
Task<Application> ResolveFromUri(string uriString);
|
|
|
|
/// <summary>
|
|
/// Get matching application from an URI
|
|
/// </summary>
|
|
Task<Application> ResolveFromUri(Uri uri);
|
|
|
|
/// <summary>
|
|
/// Resolves the current application from the logged-in backoffice user.
|
|
/// This method won't return apps the user has no access to.
|
|
/// </summary>
|
|
Task<Application> ResolveFromUser(ClaimsPrincipal user);
|
|
|
|
/// <summary>
|
|
/// Resolves the current application from a user.
|
|
/// This method won't return apps the user has no access to.
|
|
/// </summary>
|
|
Task<Application> ResolveFromUser(BackofficeUser user);
|
|
}
|
|
}
|