Skip to content

Commit

Permalink
Create authorization policies for OrganizationAdmin and ServerAdmin. …
Browse files Browse the repository at this point in the history
…Use these within the routing system via Authorize attribute instead of directly checking on each page.
  • Loading branch information
bitbound committed Jun 14, 2023
1 parent 5383343 commit bc690e8
Show file tree
Hide file tree
Showing 18 changed files with 586 additions and 485 deletions.
8 changes: 7 additions & 1 deletion Server/App.razor
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
<div class="jumbotron">
<h3>You are not authorized to access this resource.</h3>
</div>
</NotAuthorized>
</AuthorizeRouteView>
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
Expand Down
8 changes: 8 additions & 0 deletions Server/Auth/OrganizationAdminRequirement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Microsoft.AspNetCore.Authorization;

namespace Remotely.Server.Auth;

public class OrganizationAdminRequirement : IAuthorizationRequirement
{

}
34 changes: 34 additions & 0 deletions Server/Auth/OrganizationAdminRequirementHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Remotely.Shared.Models;
using System.Threading.Tasks;

namespace Remotely.Server.Auth;

public class OrganizationAdminRequirementHandler : AuthorizationHandler<OrganizationAdminRequirement>
{
private readonly UserManager<RemotelyUser> _userManager;

public OrganizationAdminRequirementHandler(UserManager<RemotelyUser> userManager)
{
_userManager = userManager;
}

protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, OrganizationAdminRequirement requirement)
{
if (context.User.Identity?.IsAuthenticated != true)
{
context.Fail();
return;
}

var user = await _userManager.GetUserAsync(context.User);
if (user?.IsAdministrator != true)
{
context.Fail();
return;
}

context.Succeed(requirement);
}
}
8 changes: 8 additions & 0 deletions Server/Auth/PolicyNames.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Remotely.Server.Auth;

public static class PolicyNames
{
public const string TwoFactorRequired = nameof(TwoFactorRequired);
public const string OrganizationAdminRequired = nameof(OrganizationAdminRequired);
public const string ServerAdminRequired = nameof(ServerAdminRequired);
}
7 changes: 7 additions & 0 deletions Server/Auth/ServerAdminRequirement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using Microsoft.AspNetCore.Authorization;

namespace Remotely.Server.Auth;

public class ServerAdminRequirement : IAuthorizationRequirement
{
}
36 changes: 36 additions & 0 deletions Server/Auth/ServerAdminRequirementHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#nullable enable

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Remotely.Shared.Models;
using System.Threading.Tasks;

namespace Remotely.Server.Auth;

public class ServerAdminRequirementHandler : AuthorizationHandler<ServerAdminRequirement>
{
private readonly UserManager<RemotelyUser> _userManager;

public ServerAdminRequirementHandler(UserManager<RemotelyUser> userManager)
{
_userManager = userManager;
}

protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ServerAdminRequirement requirement)
{
if (context.User.Identity?.IsAuthenticated != true)
{
context.Fail();
return;
}

var user = await _userManager.GetUserAsync(context.User);
if (user?.IsServerAdmin != true)
{
context.Fail();
return;
}

context.Succeed(requirement);
}
}
2 changes: 1 addition & 1 deletion Server/Auth/TwoFactorRequiredRequirement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ namespace Remotely.Server.Auth
{
public class TwoFactorRequiredRequirement : IAuthorizationRequirement
{
public const string PolicyName = "TwoFactorRequired";

}
}
9 changes: 3 additions & 6 deletions Server/Components/LoaderHarness.razor
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,10 @@
private bool _loaderShown;
private string _statusMessage = string.Empty;

protected override Task OnAfterRenderAsync(bool firstRender)
protected override Task OnInitializedAsync()
{
if (firstRender)
{
Messenger.Register<ShowLoaderMessage>(this, HandleShowLoaderMessage);
}
return base.OnAfterRenderAsync(firstRender);
Messenger.Register<ShowLoaderMessage>(this, HandleShowLoaderMessage);
return base.OnInitializedAsync();
}

private async Task HandleShowLoaderMessage(ShowLoaderMessage message)
Expand Down
2 changes: 1 addition & 1 deletion Server/Pages/ApiKeys.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@page "/api-keys"
@attribute [Authorize]
@attribute [Authorize(Policy = PolicyNames.OrganizationAdminRequired)]
@inherits AuthComponentBase

@inject IDataService DataService
Expand Down
104 changes: 48 additions & 56 deletions Server/Pages/Branding.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@page "/branding"
@inherits AuthComponentBase
@attribute [Authorize(Policy = PolicyNames.OrganizationAdminRequired)]
@inject IDataService DataService
@inject IToastService ToastService
@inject IJsInterop JsInterop
Expand All @@ -8,70 +9,61 @@

<h3 class="mb-3">Branding</h3>

<AlertBanner Message="@_alertMessage" />

@if (User?.IsAdministrator != true)
{
<h5 class="text-muted">Only organization administrators can view this page.</h5>
}
else
{
<AlertBanner Message="@_alertMessage" />

<div class="row">
<div class="col-sm-8 col-lg-6 col-xl-5">
<div class="form-group">
<label class="mb-1">Branding Areas</label>
<br />
<img class="img-fluid" src="/images/Remotely_Desktop.png" />
</div>
<div class="row">
<div class="col-sm-8 col-lg-6 col-xl-5">
<div class="form-group">
<label class="mb-1">Branding Areas</label>
<br />
<img class="img-fluid" src="/images/Remotely_Desktop.png" />
</div>
</div>
</div>

<div class="row">
<div class="col-sm-8 col-lg-6 col-xl-5">
<EditForm OnValidSubmit="HandleValidSubmit" Model="_inputModel">
<DataAnnotationsValidator />
<div class="row">
<div class="col-sm-8 col-lg-6 col-xl-5">
<EditForm OnValidSubmit="HandleValidSubmit" Model="_inputModel">
<DataAnnotationsValidator />

<div class="form-group mb-5">
<label class="text-info font-weight-bold">Product Name</label>
<InputText class="form-control" @bind-Value="_inputModel.ProductName" />
<ValidationMessage For="() => _inputModel.ProductName" />
</div>
<div class="form-group mb-5">
<label class="text-info font-weight-bold">Product Name</label>
<InputText class="form-control" @bind-Value="_inputModel.ProductName" />
<ValidationMessage For="() => _inputModel.ProductName" />
</div>

@if (!string.IsNullOrWhiteSpace(_base64Icon))
{
<div class="form-group mb-4">
<label class="text-info font-weight-bold">Current Icon</label>
<br />
<img class="img-fluid" src="data:image/png;base64,@_base64Icon" />
</div>
}

<div class="form-group mb-5">
<label class="text-info font-weight-bold">New Icon</label>
<InputFile type="file" accept="image/png" class="form-control" OnChange="IconUploadInputChanged" />
</div>
<div class="form-group mb-5">
<label class="text-info font-weight-bold">Title Foreground</label>
<ColorPicker @bind-Color="_inputModel.TitleForegroundColor" />
@if (!string.IsNullOrWhiteSpace(_base64Icon))
{
<div class="form-group mb-4">
<label class="text-info font-weight-bold">Current Icon</label>
<br />
<img class="img-fluid" src="data:image/png;base64,@_base64Icon" />
</div>
<div class="form-group mb-5">
<label class="text-info font-weight-bold">Title Background</label>
<ColorPicker @bind-Color="_inputModel.TitleBackgroundColor" />
</div>
<div class="form-group mb-5">
<label class="text-info font-weight-bold">Button Color</label>
<ColorPicker @bind-Color="_inputModel.TitleButtonColor" />
</div>
<div class="form-group text-right">
<button class="btn btn-secondary mr-3" type="button" @onclick="ResetBranding">Reset</button>
<button class="btn btn-primary" type="submit">Submit</button>
</div>
</EditForm>
</div>
</div>
}
}

<div class="form-group mb-5">
<label class="text-info font-weight-bold">New Icon</label>
<InputFile type="file" accept="image/png" class="form-control" OnChange="IconUploadInputChanged" />
</div>
<div class="form-group mb-5">
<label class="text-info font-weight-bold">Title Foreground</label>
<ColorPicker @bind-Color="_inputModel.TitleForegroundColor" />
</div>
<div class="form-group mb-5">
<label class="text-info font-weight-bold">Title Background</label>
<ColorPicker @bind-Color="_inputModel.TitleBackgroundColor" />
</div>
<div class="form-group mb-5">
<label class="text-info font-weight-bold">Button Color</label>
<ColorPicker @bind-Color="_inputModel.TitleButtonColor" />
</div>
<div class="form-group text-right">
<button class="btn btn-secondary mr-3" type="button" @onclick="ResetBranding">Reset</button>
<button class="btn btn-primary" type="submit">Submit</button>
</div>
</EditForm>
</div>
</div>
@code {
private string _alertMessage;
private string _base64Icon;
Expand Down
2 changes: 1 addition & 1 deletion Server/Pages/ManageOrganization.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@page "/manage-organization"
@attribute [Authorize]
@attribute [Authorize(Policy = PolicyNames.OrganizationAdminRequired)]
@inherits AuthComponentBase

<h3 class="mb-3">Manage Organization</h3>
Expand Down
1 change: 1 addition & 0 deletions Server/Pages/ScriptsPage.razor
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@page "/scripts/{activeTab?}"
@attribute [Authorize]
@inherits AuthComponentBase
@using System.Collections
@inject IDataService DataService
Expand Down

0 comments on commit bc690e8

Please sign in to comment.