Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added role based security plumbing #14

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 1 addition & 36 deletions Quartzmin.sln
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCoreSelfHost", "Source\E
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinFormSelfHost", "Source\Examples\WinFormSelfHost\WinFormSelfHost.csproj", "{C54F4403-F40A-497C-B3A7-D1F1E1830D52}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCoreDocker", "Source\Examples\AspNetCoreDocker\AspNetCoreDocker.csproj", "{68D816F2-21BF-4376-ABF4-70390E4783C5}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCoreDocker", "Source\Examples\AspNetCoreDocker\AspNetCoreDocker.csproj", "{68D816F2-21BF-4376-ABF4-70390E4783C5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -73,39 +73,4 @@ Global
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0357313D-BD09-4C5D-AF0D-439B3BD33B5A}
EndGlobalSection
GlobalSection(TeamFoundationVersionControl) = preSolution
SccNumberOfProjects = 9
SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}
SccTeamFoundationServer = http://tfs/defaultcollection
SccLocalPath0 = .
SccProjectUniqueName1 = Source\\Quartzmin\\Quartzmin.csproj
SccProjectName1 = Source/Quartzmin
SccLocalPath1 = Source\\Quartzmin
SccProjectUniqueName2 = Source\\Quartz.Plugins.RecentHistory\\Quartz.Plugins.RecentHistory.csproj
SccProjectName2 = Source/Quartz.Plugins.RecentHistory
SccLocalPath2 = Source\\Quartz.Plugins.RecentHistory
SccProjectUniqueName3 = Source\\Quartzmin.SelfHost\\Quartzmin.SelfHost.csproj
SccProjectName3 = Source/Quartzmin.SelfHost
SccLocalPath3 = Source\\Quartzmin.SelfHost
SccProjectUniqueName4 = Source\\Examples\\NetCoreSelfHost\\NetCoreSelfHost.csproj
SccProjectTopLevelParentUniqueName4 = Quartzmin.sln
SccProjectName4 = Source/Examples/NetCoreSelfHost
SccLocalPath4 = Source\\Examples\\NetCoreSelfHost
SccProjectUniqueName5 = Source\\Examples\\AspNetCoreHost\\AspNetCoreHost.csproj
SccProjectTopLevelParentUniqueName5 = Quartzmin.sln
SccProjectName5 = Source/Examples/AspNetCoreHost
SccLocalPath5 = Source\\Examples\\AspNetCoreHost
SccProjectUniqueName6 = Source\\Examples\\AspNetWebHost\\AspNetWebHost.csproj
SccProjectTopLevelParentUniqueName6 = Quartzmin.sln
SccProjectName6 = Source/Examples/AspNetWebHost
SccLocalPath6 = Source\\Examples\\AspNetWebHost
SccProjectUniqueName7 = Source\\Examples\\WinFormSelfHost\\WinFormSelfHost.csproj
SccProjectTopLevelParentUniqueName7 = Quartzmin.sln
SccProjectName7 = Source/Examples/WinFormSelfHost
SccLocalPath7 = Source\\Examples\\WinFormSelfHost
SccProjectUniqueName8 = Source\\Examples\\AspNetCoreDocker\\AspNetCoreDocker.csproj
SccProjectTopLevelParentUniqueName8 = Quartzmin.sln
SccProjectName8 = Source/Examples/AspNetCoreDocker
SccLocalPath8 = Source\\Examples\\AspNetCoreDocker
EndGlobalSection
EndGlobal
2 changes: 1 addition & 1 deletion Source/Quartzmin/ApplicationBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public static void UseQuartzmin(this IApplicationBuilder app, QuartzminOptions o
await context.Response.WriteAsync(services.ViewEngine.ErrorPage(ex));
});
});

app.UseMvc(routes =>
{
routes.MapRoute(
Expand Down
11 changes: 11 additions & 0 deletions Source/Quartzmin/Controllers/CalendarsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Quartzmin.Security;

#region Target-Specific Directives
#if NETSTANDARD
Expand All @@ -21,6 +22,7 @@ namespace Quartzmin.Controllers
public class CalendarsController : PageControllerBase
{
[HttpGet]
[AuthorizeUser(UserPermissions.ViewCalendars)]
public async Task<IActionResult> Index()
{
var calendarNames = await Scheduler.GetCalendarNames();
Expand All @@ -37,6 +39,7 @@ public async Task<IActionResult> Index()
}

[HttpGet]
[AuthorizeUser(UserPermissions.CreateNewCalendars)]
public IActionResult New()
{
ViewBag.IsNew = true;
Expand All @@ -49,6 +52,7 @@ public IActionResult New()
}

[HttpGet]
[AuthorizeUser(UserPermissions.ViewCalendars)]
public async Task<IActionResult> Edit(string name)
{
var calendar = await Scheduler.GetCalendar(name);
Expand All @@ -75,6 +79,12 @@ private void RemoveLastEmpty(List<string> list)
[HttpPost, JsonErrorResponse]
public async Task<IActionResult> Save([FromBody] CalendarViewModel[] chain, bool isNew)
{
if ((isNew && !UserHasPermissions(UserPermissions.CreateNewCalendars)) ||
!UserHasPermissions(UserPermissions.EditCalendars))
{
return Unauthorized();
}

var result = new ValidationResult();

if (chain.Length == 0 || string.IsNullOrEmpty(chain[0].Name))
Expand Down Expand Up @@ -137,6 +147,7 @@ public class DeleteArgs


[HttpPost, JsonErrorResponse]
[AuthorizeUser(UserPermissions.DeleteCalendars)]
public async Task<IActionResult> Delete([FromBody] DeleteArgs args)
{
if (!await Scheduler.DeleteCalendar(args.Name))
Expand Down
3 changes: 3 additions & 0 deletions Source/Quartzmin/Controllers/ExecutionsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Globalization;
using Quartzmin.Security;

#region Target-Specific Directives
#if NETSTANDARD
Expand All @@ -23,6 +24,7 @@ namespace Quartzmin.Controllers
public class ExecutionsController : PageControllerBase
{
[HttpGet]
[AuthorizeUser(UserPermissions.ViewExecutions)]
public async Task<IActionResult> Index()
{
var currentlyExecutingJobs = await Scheduler.GetCurrentlyExecutingJobs();
Expand Down Expand Up @@ -53,6 +55,7 @@ public class InterruptArgs
}

[HttpPost, JsonErrorResponse]
[AuthorizeUser(UserPermissions.InterruptExecutions)]
public async Task<IActionResult> Interrupt([FromBody] InterruptArgs args)
{
if (!await Scheduler.Interrupt(args.Id))
Expand Down
2 changes: 2 additions & 0 deletions Source/Quartzmin/Controllers/HistoryController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using Quartzmin.Security;

#region Target-Specific Directives
#if NETSTANDARD
Expand All @@ -19,6 +20,7 @@ namespace Quartzmin.Controllers
public class HistoryController : PageControllerBase
{
[HttpGet]
[AuthorizeUser(UserPermissions.ViewHistory)]
public async Task<IActionResult> Index()
{
var store = Scheduler.Context.GetExecutionHistoryStore();
Expand Down
3 changes: 3 additions & 0 deletions Source/Quartzmin/Controllers/JobDataMapController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using Quartzmin.Security;

#region Target-Specific Directives
#if NETSTANDARD
Expand All @@ -25,6 +26,7 @@ namespace Quartzmin.Controllers
public class JobDataMapController : PageControllerBase
{
[HttpPost, JsonErrorResponse]
[AuthorizeUser(UserPermissions.ViewJobs)]
public async Task<IActionResult> ChangeType()
{
var formData = await Request.GetFormData();
Expand Down Expand Up @@ -78,6 +80,7 @@ private class BadRequestResult : IActionResult
#endif

[HttpGet, ActionName("TypeHandlers.js")]
[AuthorizeUser(UserPermissions.ViewJobs)]
public IActionResult TypeHandlersScript()
{
var etag = Services.TypeHandlers.LastModified.ETag();
Expand Down
17 changes: 16 additions & 1 deletion Source/Quartzmin/Controllers/JobsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Quartzmin.Security;

#region Target-Specific Directives
#if NETSTANDARD
Expand All @@ -23,6 +24,7 @@ namespace Quartzmin.Controllers
public class JobsController : PageControllerBase
{
[HttpGet]
[AuthorizeUser(UserPermissions.ViewJobs)]
public async Task<IActionResult> Index()
{
var keys = (await Scheduler.GetJobKeys(GroupMatcher<JobKey>.AnyGroup())).OrderBy(x => x.ToString());
Expand Down Expand Up @@ -55,6 +57,7 @@ public async Task<IActionResult> Index()
}

[HttpGet]
[AuthorizeUser(UserPermissions.CreateNewJobs)]
public async Task<IActionResult> New()
{
var job = new JobPropertiesViewModel() { IsNew = true };
Expand All @@ -68,6 +71,7 @@ public async Task<IActionResult> New()
}

[HttpGet]
[AuthorizeUser(UserPermissions.TriggerJobs)]
public async Task<IActionResult> Trigger(string name, string group)
{
if (!EnsureValidKey(name, group)) return BadRequest();
Expand All @@ -85,6 +89,7 @@ public async Task<IActionResult> Trigger(string name, string group)
}

[HttpPost, ActionName("Trigger"), JsonErrorResponse]
[AuthorizeUser(UserPermissions.TriggerJobs)]
public async Task<IActionResult> PostTrigger(string name, string group)
{
if (!EnsureValidKey(name, group)) return BadRequest();
Expand All @@ -104,6 +109,7 @@ public async Task<IActionResult> PostTrigger(string name, string group)
}

[HttpGet]
[AuthorizeUser(UserPermissions.ViewJobs)]
public async Task<IActionResult> Edit(string name, string group, bool clone = false)
{
if (!EnsureValidKey(name, group)) return BadRequest();
Expand Down Expand Up @@ -142,11 +148,17 @@ private async Task<IJobDetail> GetJobDetail(JobKey key)
throw new InvalidOperationException("Job " + key + " not found.");

return job;
}
}

[HttpPost, JsonErrorResponse]
public async Task<IActionResult> Save([FromForm] JobViewModel model, bool trigger)
{
if ((model.Job.IsNew && !UserHasPermissions(UserPermissions.CreateNewJobs)) ||
!UserHasPermissions(UserPermissions.EditJobs))
{
return Unauthorized();
}

var jobModel = model.Job;
var jobDataMap = (await Request.GetJobDataMapForm()).GetModel(Services);

Expand Down Expand Up @@ -188,6 +200,7 @@ IJobDetail BuildJob(JobBuilder builder)
}

[HttpPost, JsonErrorResponse]
[AuthorizeUser(UserPermissions.DeleteJobs)]
public async Task<IActionResult> Delete([FromBody] KeyModel model)
{
if (!EnsureValidKey(model)) return BadRequest();
Expand All @@ -201,6 +214,7 @@ public async Task<IActionResult> Delete([FromBody] KeyModel model)
}

[HttpGet, JsonErrorResponse]
[AuthorizeUser(UserPermissions.ViewJobs)]
public async Task<IActionResult> AdditionalData()
{
var keys = await Scheduler.GetJobKeys(GroupMatcher<JobKey>.AnyGroup());
Expand All @@ -226,6 +240,7 @@ public async Task<IActionResult> AdditionalData()
}

[HttpGet]
[AuthorizeUser(UserPermissions.CreateNewJobs)]
public Task<IActionResult> Duplicate(string name, string group)
{
return Edit(name, group, clone: true);
Expand Down
47 changes: 43 additions & 4 deletions Source/Quartzmin/Controllers/PageControllerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Threading.Tasks;
using Quartzmin.Models;
using Quartz;
using Quartzmin.Security;

namespace Quartzmin.Controllers
{
Expand Down Expand Up @@ -35,6 +36,16 @@ protected IEnumerable<string> GetHeader(string key)
var values = Request.Headers[key];
return values == StringValues.Empty ? (IEnumerable<string>)null : values;
}

protected bool UserHasPermissions(params UserPermissions[] userPermissions)
{
return HttpContext.DoesUserHavePermissions(userPermissions);
}

protected UserPermissions[] GetUserPermissions()
{
return HttpContext.GetUserPermissions();
}
}
#endif
#if NETFRAMEWORK
Expand All @@ -57,15 +68,15 @@ private class ContentResult : IActionResult

public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var msg = new HttpResponseMessage() { Content = new StringContent(Content) };
var msg = new HttpResponseMessage() {Content = new StringContent(Content)};
msg.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(ContentType);

if (!string.IsNullOrEmpty(ETag))
msg.Headers.ETag = new System.Net.Http.Headers.EntityTagHeaderValue(ETag);

if (LastModified != null)
msg.Content.Headers.LastModified = LastModified;

return Task.FromResult(msg);
}
}
Expand All @@ -81,6 +92,15 @@ protected IEnumerable<string> GetHeader(string key)
return null;
}

protected bool UserHasPermissions(params UserPermissions[] userPermissions)
{
return Request.GetOwinContext().DoesUserHavePermissions(userPermissions);
}

protected UserPermissions[] GetUserPermissions()
{
return Request.GetOwinContext().GetUserPermissions();
}
}
#endif
#endregion
Expand All @@ -89,6 +109,8 @@ public abstract partial class PageControllerBase
{
protected IScheduler Scheduler => Services.Scheduler;

protected IAuthorizationProvider AuthorizationProvider => Services.AuthorizationProvider;

protected dynamic ViewBag { get; } = new ExpandoObject();

internal class Page
Expand All @@ -105,10 +127,27 @@ internal class Page

public object Model { get; set; }

public Page(PageControllerBase controller, object model = null)
public object UserPermissions { get; private set; }

public Page(PageControllerBase controller, object model = null, UserPermissions[] userPermissions = null)
{
_controller = controller;
Model = model;

SetUserPermissions(userPermissions);
}

private void SetUserPermissions(UserPermissions[] userPermissions = null)
{
dynamic userPermissionsBag = new ExpandoObject();

var properties = userPermissionsBag as IDictionary<String, object>;
foreach (var userPermission in Enum.GetValues(typeof(UserPermissions)).Cast<UserPermissions>())
{
properties["Can" + userPermission] = userPermissions?.Contains(userPermission) ?? true;
}

UserPermissions = userPermissionsBag;
}
}

Expand All @@ -119,7 +158,7 @@ protected IActionResult View(object model)

protected IActionResult View(string viewName, object model)
{
string content = Services.ViewEngine.Render($"{GetRouteData("controller")}/{viewName}.hbs", new Page(this, model));
string content = Services.ViewEngine.Render($"{GetRouteData("controller")}/{viewName}.hbs", new Page(this, model, GetUserPermissions()));
return Html(content);
}

Expand Down
Loading