267 lines
8.9 KiB
C#
267 lines
8.9 KiB
C#
using Microsoft.AspNetCore.Authentication;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
|
using NTDLS.Helpers;
|
|
using System.Security.Claims;
|
|
using TightWiki.Exceptions;
|
|
using TightWiki.Library;
|
|
using TightWiki.Library.Interfaces;
|
|
using TightWiki.Models;
|
|
using TightWiki.Models.DataModels;
|
|
using TightWiki.Repository;
|
|
using static TightWiki.Library.Constants;
|
|
|
|
namespace TightWiki
|
|
{
|
|
public class SessionState : ISessionState
|
|
{
|
|
public IQueryCollection? QueryString { get; set; }
|
|
|
|
#region Authentication.
|
|
|
|
public bool IsAuthenticated { get; set; }
|
|
public IAccountProfile? Profile { get; set; }
|
|
public string Role { get; set; } = string.Empty;
|
|
public Theme UserTheme { get; set; } = new();
|
|
|
|
#endregion
|
|
|
|
#region Current Page.
|
|
|
|
/// <summary>
|
|
/// Custom page title set by a call to @@Title("...")
|
|
/// </summary>
|
|
public string? PageTitle { get; set; }
|
|
public bool ShouldCreatePage { get; set; }
|
|
public string PageNavigation { get; set; } = string.Empty;
|
|
public string PageNavigationEscaped { get; set; } = string.Empty;
|
|
public string PageTags { get; set; } = string.Empty;
|
|
public ProcessingInstructionCollection PageInstructions { get; set; } = new();
|
|
|
|
/// <summary>
|
|
/// The "page" here is more of a "mock page", we use the name for various stuff.
|
|
/// </summary>
|
|
public IPage Page { get; set; } = new Models.DataModels.Page() { Name = GlobalConfiguration.Name };
|
|
|
|
#endregion
|
|
|
|
public SessionState Hydrate(SignInManager<IdentityUser> signInManager, PageModel pageModel)
|
|
{
|
|
QueryString = pageModel.Request.Query;
|
|
|
|
HydrateSecurityContext(pageModel.HttpContext, signInManager, pageModel.User);
|
|
return this;
|
|
}
|
|
|
|
public SessionState Hydrate(SignInManager<IdentityUser> signInManager, Controller controller)
|
|
{
|
|
QueryString = controller.Request.Query;
|
|
PageNavigation = RouteValue("givenCanonical", "Home");
|
|
PageNavigationEscaped = Uri.EscapeDataString(PageNavigation);
|
|
|
|
HydrateSecurityContext(controller.HttpContext, signInManager, controller.User);
|
|
|
|
string RouteValue(string key, string defaultValue = "")
|
|
{
|
|
if (controller.RouteData.Values.ContainsKey(key))
|
|
{
|
|
return controller.RouteData.Values[key]?.ToString() ?? defaultValue;
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
private void HydrateSecurityContext(HttpContext httpContext, SignInManager<IdentityUser> signInManager, ClaimsPrincipal user)
|
|
{
|
|
IsAuthenticated = false;
|
|
|
|
UserTheme = GlobalConfiguration.SystemTheme;
|
|
|
|
if (signInManager.IsSignedIn(user))
|
|
{
|
|
try
|
|
{
|
|
string emailAddress = (user.Claims.First(x => x.Type == ClaimTypes.Email)?.Value).EnsureNotNull();
|
|
|
|
if (user.Identity?.IsAuthenticated == true)
|
|
{
|
|
var userId = Guid.Parse((user.Claims.First(x => x.Type == ClaimTypes.NameIdentifier)?.Value).EnsureNotNull());
|
|
|
|
if (UsersRepository.TryGetBasicProfileByUserId(userId, out var profile))
|
|
{
|
|
Profile = profile;
|
|
Role = Profile.Role;
|
|
UserTheme = ConfigurationRepository.GetAllThemes().SingleOrDefault(o => o.Name == Profile.Theme) ?? GlobalConfiguration.SystemTheme;
|
|
IsAuthenticated = true;
|
|
}
|
|
else
|
|
{
|
|
//User is signed in, but does not have a profile.
|
|
//This likely means that the user has authenticated externally, but has yet to complete the signup process.
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
httpContext.SignOutAsync();
|
|
if (user.Identity != null)
|
|
{
|
|
httpContext.SignOutAsync(user.Identity.AuthenticationType);
|
|
}
|
|
|
|
ExceptionRepository.InsertException(ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the current context pageId and optionally the revision.
|
|
/// </summary>
|
|
/// <param name="pageId"></param>
|
|
/// <param name="revision"></param>
|
|
/// <exception cref="Exception"></exception>
|
|
public void SetPageId(int? pageId, int? revision = null)
|
|
{
|
|
Page = new Models.DataModels.Page();
|
|
PageInstructions = new();
|
|
PageTags = string.Empty;
|
|
|
|
if (pageId != null)
|
|
{
|
|
Page = PageRepository.GetLimitedPageInfoByIdAndRevision((int)pageId, revision)
|
|
?? throw new Exception("Page not found");
|
|
|
|
PageInstructions = PageRepository.GetPageProcessingInstructionsByPageId(Page.Id);
|
|
|
|
if (GlobalConfiguration.IncludeWikiTagsInMeta)
|
|
{
|
|
PageTags = string.Join(",", PageRepository.GetPageTagsById(Page.Id)?.Select(o => o.Tag) ?? []);
|
|
}
|
|
}
|
|
}
|
|
|
|
#region Permissions.
|
|
|
|
public bool IsMemberOf(string role, string[] roles)
|
|
=> roles.Contains(role);
|
|
|
|
public void RequireAuthorizedPermission()
|
|
{
|
|
if (!IsAuthenticated) throw new UnauthorizedException();
|
|
}
|
|
|
|
public void RequireEditPermission()
|
|
{
|
|
if (!CanEdit) throw new UnauthorizedException();
|
|
}
|
|
|
|
public void RequireViewPermission()
|
|
{
|
|
if (!CanView) throw new UnauthorizedException();
|
|
}
|
|
|
|
public void RequireAdminPermission()
|
|
{
|
|
if (!CanAdmin) throw new UnauthorizedException();
|
|
}
|
|
|
|
public void RequireModeratePermission()
|
|
{
|
|
if (!CanModerate) throw new UnauthorizedException();
|
|
}
|
|
|
|
public void RequireCreatePermission()
|
|
{
|
|
if (!CanCreate) throw new UnauthorizedException();
|
|
}
|
|
|
|
public void RequireDeletePermission()
|
|
{
|
|
if (!CanDelete) throw new UnauthorizedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Is the current user (or anonymous) allowed to view?
|
|
/// </summary>
|
|
public bool CanView => true;
|
|
|
|
/// <summary>
|
|
/// Is the current user allowed to edit?
|
|
/// </summary>
|
|
public bool CanEdit
|
|
{
|
|
get
|
|
{
|
|
if (IsAuthenticated)
|
|
{
|
|
if (PageInstructions.Contains(WikiInstruction.Protect))
|
|
{
|
|
return IsMemberOf(Role, [Roles.Administrator, Roles.Moderator]);
|
|
}
|
|
|
|
return IsMemberOf(Role, [Roles.Administrator, Roles.Contributor, Roles.Moderator]);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Is the current user allowed to perform administrative functions?
|
|
/// </summary>
|
|
public bool CanAdmin =>
|
|
IsAuthenticated && IsMemberOf(Role, [Roles.Administrator]);
|
|
|
|
/// <summary>
|
|
/// Is the current user allowed to moderate content (such as delete comments, and view moderation tools)?
|
|
/// </summary>
|
|
public bool CanModerate =>
|
|
IsAuthenticated && IsMemberOf(Role, [Roles.Administrator, Roles.Moderator]);
|
|
|
|
/// <summary>
|
|
/// Is the current user allowed to create pages?
|
|
/// </summary>
|
|
public bool CanCreate =>
|
|
IsAuthenticated && IsMemberOf(Role, [Roles.Administrator, Roles.Contributor, Roles.Moderator]);
|
|
|
|
/// <summary>
|
|
/// Is the current user allowed to delete unprotected pages?
|
|
/// </summary>
|
|
public bool CanDelete
|
|
{
|
|
get
|
|
{
|
|
if (IsAuthenticated)
|
|
{
|
|
if (PageInstructions.Contains(WikiInstruction.Protect))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return IsMemberOf(Role, [Roles.Administrator, Roles.Moderator]);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
public DateTime LocalizeDateTime(DateTime datetime)
|
|
{
|
|
return TimeZoneInfo.ConvertTimeFromUtc(datetime, GetPreferredTimeZone());
|
|
}
|
|
|
|
public TimeZoneInfo GetPreferredTimeZone()
|
|
{
|
|
if (string.IsNullOrEmpty(Profile?.TimeZone))
|
|
{
|
|
return TimeZoneInfo.FindSystemTimeZoneById(GlobalConfiguration.DefaultTimeZone);
|
|
}
|
|
return TimeZoneInfo.FindSystemTimeZoneById(Profile.TimeZone);
|
|
}
|
|
}
|
|
}
|