diff --git a/sample/AppSettings.cs b/sample/AppSettings.cs
index d522721..99ab7eb 100644
--- a/sample/AppSettings.cs
+++ b/sample/AppSettings.cs
@@ -1,9 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-
-namespace SilkierQuartz.Example
+namespace SilkierQuartz.Example
{
public class AppSettings
{
diff --git a/sample/Pages/Privacy.cshtml.cs b/sample/Pages/Privacy.cshtml.cs
index 9a6f096..f8c9934 100644
--- a/sample/Pages/Privacy.cshtml.cs
+++ b/sample/Pages/Privacy.cshtml.cs
@@ -1,9 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace SilkierQuartz.Example.Pages
diff --git a/sample/Pages/Shared/_Layout.cshtml b/sample/Pages/Shared/_Layout.cshtml
index 5dd43f1..1e1b005 100644
--- a/sample/Pages/Shared/_Layout.cshtml
+++ b/sample/Pages/Shared/_Layout.cshtml
@@ -22,7 +22,7 @@
Home
- SilkierQuartz
+ SilkierQuartz
Privacy
diff --git a/sample/Startup.cs b/sample/Startup.cs
index f6e0ccc..714db49 100644
--- a/sample/Startup.cs
+++ b/sample/Startup.cs
@@ -1,15 +1,11 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using SilkierQuartz.Example.Jobs;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Options;
using Quartz;
+using SilkierQuartz.Example.Jobs;
+using System.Collections.Generic;
namespace SilkierQuartz.Example
{
@@ -50,6 +46,8 @@ namespace SilkierQuartz.Example
app.UseStaticFiles();
app.UseRouting();
+ app.UseAuthentication();
+ app.AddSilkierQuartzAuthentication();
app.UseAuthorization();
app.UseSilkierQuartz(
new SilkierQuartzOptions()
@@ -59,18 +57,22 @@ namespace SilkierQuartz.Example
DefaultDateFormat = "yyyy-MM-dd",
DefaultTimeFormat = "HH:mm:ss",
CronExpressionOptions = new CronExpressionDescriptor.Options()
- {
- DayOfWeekStartIndexZero = false //Quartz uses 1-7 as the range
- }
+ {
+ DayOfWeekStartIndexZero = false //Quartz uses 1-7 as the range
+ },
+ AccountName = "admin",
+ AccountPassword = "password",
+ IsAuthenticationPersist = false
}
);
+
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
//How to compatible old code to SilkierQuartz
//½«¾ÉµÄÔÀ´µÄ¹æ»®JobµÄ´úÂë½øÐÐÒÆÖ²¼æÈݵÄʾÀý
- // app.SchedulerJobs();
+ // app.SchedulerJobs();
#region ²»Ê¹Óà SilkierQuartzAttribe ÊôÐԵĽøÐÐ×¢²áºÍʹÓõÄIJob£¬ÕâÀïͨ¹ýUseQuartzJobµÄIJob±ØÐëÔÚ ConfigureServices½øÐÐAddQuartzJob
diff --git a/src/SilkierQuartz/ApplicationBuilderExtensions.cs b/src/SilkierQuartz/ApplicationBuilderExtensions.cs
index 41fafd2..f514dd8 100644
--- a/src/SilkierQuartz/ApplicationBuilderExtensions.cs
+++ b/src/SilkierQuartz/ApplicationBuilderExtensions.cs
@@ -1,14 +1,11 @@

using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc.Formatters;
-using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Quartz;
using Quartz.Impl;
-using SilkierQuartz;
+using SilkierQuartz.Middlewares;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
@@ -34,9 +31,9 @@ namespace SilkierQuartz
///
///
///
- public static IScheduler GetScheduler(this IApplicationBuilder app,string schedName)
+ public static IScheduler GetScheduler(this IApplicationBuilder app, string schedName)
{
- return app.ApplicationServices.GetRequiredService().GetScheduler(schedName ).Result;
+ return app.ApplicationServices.GetRequiredService().GetScheduler(schedName).Result;
}
///
/// Returns handles to all known Schedulers (made by any SchedulerFactory within this app domain.
@@ -73,7 +70,7 @@ namespace SilkierQuartz
{
options.Scheduler = null;
}
- if (options.Scheduler==null)
+ if (options.Scheduler == null)
{
options.Scheduler = StdSchedulerFactory.GetDefaultScheduler().Result;
}
@@ -90,46 +87,54 @@ namespace SilkierQuartz
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(nameof(SilkierQuartz), $"{options.VirtualPathRoot}/{{controller=Scheduler}}/{{action=Index}}");
+
+ SilkierQuartzAuthenticateConfig.VirtualPathRoot = options.VirtualPathRoot;
+ SilkierQuartzAuthenticateConfig.VirtualPathRootUrlEncode = options.VirtualPathRoot.Replace("/", "%2F");
+ SilkierQuartzAuthenticateConfig.UserName = options.AccountName;
+ SilkierQuartzAuthenticateConfig.UserPassword = options.AccountPassword;
+ SilkierQuartzAuthenticateConfig.IsPersist = options.IsAuthenticationPersist;
+ endpoints.MapControllerRoute($"{nameof(SilkierQuartz)}Authenticate",
+ $"{options.VirtualPathRoot}{{controller=Authenticate}}/{{action=Login}}");
});
var types = GetSilkierQuartzJobs();
types.ForEach(t =>
{
var so = t.GetCustomAttribute();
- app.UseQuartzJob( t,() =>
- {
- var tb = TriggerBuilder.Create();
- tb.WithSimpleSchedule(x =>
- {
- x.WithInterval(so.WithInterval);
- if (so.RepeatCount>0)
- {
- x.WithRepeatCount(so.RepeatCount);
-
- }
- else
- {
- x.RepeatForever();
- }
- });
- if (so.StartAt== DateTimeOffset.MinValue)
- {
- tb.StartNow();
- }
- else
- {
- tb.StartAt(so.StartAt);
- }
- var tk = new TriggerKey(!string.IsNullOrEmpty(so.TriggerName) ? so.TriggerName : $"{t.Name}'s Trigger");
- if (!string.IsNullOrEmpty(so.TriggerGroup))
- {
- so.TriggerGroup = so.TriggerGroup;
- }
- tb.WithIdentity(tk);
- tb.WithDescription(so.TriggerDescription ?? $"{t.Name}'s Trigger,full name is {t.FullName}");
- if (so.Priority > 0) tb.WithPriority(so.Priority);
- return tb;
- });
+ app.UseQuartzJob(t, () =>
+ {
+ var tb = TriggerBuilder.Create();
+ tb.WithSimpleSchedule(x =>
+ {
+ x.WithInterval(so.WithInterval);
+ if (so.RepeatCount > 0)
+ {
+ x.WithRepeatCount(so.RepeatCount);
+
+ }
+ else
+ {
+ x.RepeatForever();
+ }
+ });
+ if (so.StartAt == DateTimeOffset.MinValue)
+ {
+ tb.StartNow();
+ }
+ else
+ {
+ tb.StartAt(so.StartAt);
+ }
+ var tk = new TriggerKey(!string.IsNullOrEmpty(so.TriggerName) ? so.TriggerName : $"{t.Name}'s Trigger");
+ if (!string.IsNullOrEmpty(so.TriggerGroup))
+ {
+ so.TriggerGroup = so.TriggerGroup;
+ }
+ tb.WithIdentity(tk);
+ tb.WithDescription(so.TriggerDescription ?? $"{t.Name}'s Trigger,full name is {t.FullName}");
+ if (so.Priority > 0) tb.WithPriority(so.Priority);
+ return tb;
+ });
});
@@ -160,23 +165,48 @@ namespace SilkierQuartz
=> services.AddSilkierQuartz(stdSchedulerFactoryOptions);
- public static IServiceCollection AddSilkierQuartz(this IServiceCollection services, Action stdSchedulerFactoryOptions = null,Func> jobsasmlist=null)
+ public static IServiceCollection AddSilkierQuartz(this IServiceCollection services, Action stdSchedulerFactoryOptions = null, Func> jobsasmlist = null)
{
services.AddControllers()
.AddApplicationPart(Assembly.GetExecutingAssembly())
.AddNewtonsoftJson();
+
+ services.AddAuthentication(SilkierQuartzAuthenticateConfig.AuthScheme).AddCookie(
+ SilkierQuartzAuthenticateConfig.AuthScheme,
+ cfg =>
+ {
+ cfg.Cookie.Name = SilkierQuartzAuthenticateConfig.AuthScheme;
+ cfg.LoginPath = $"{SilkierQuartzAuthenticateConfig.VirtualPathRoot}/Authenticate/Login";
+ cfg.AccessDeniedPath = $"{SilkierQuartzAuthenticateConfig.VirtualPathRoot}/Authenticate/Login";
+ if (SilkierQuartzAuthenticateConfig.IsPersist)
+ {
+ cfg.ExpireTimeSpan = TimeSpan.FromDays(7);
+ cfg.SlidingExpiration = true;
+ }
+ });
+
+ services.AddAuthorization(opts =>
+ {
+ opts.AddPolicy(SilkierQuartzAuthenticateConfig.AuthScheme, authBuilder =>
+ {
+ authBuilder.RequireAuthenticatedUser();
+ authBuilder.RequireClaim(SilkierQuartzAuthenticateConfig.SilkierQuartzSpecificClaim,
+ SilkierQuartzAuthenticateConfig.SilkierQuartzSpecificClaimValue);
+ });
+ });
+
services.UseQuartzHostedService(stdSchedulerFactoryOptions);
-
+
var types = GetSilkierQuartzJobs(jobsasmlist?.Invoke());
types.ForEach(t =>
{
var so = t.GetCustomAttribute();
- services.AddQuartzJob(t, so.Identity??t.Name, so.Desciption??t.FullName);
+ services.AddQuartzJob(t, so.Identity ?? t.Name, so.Desciption ?? t.FullName);
});
return services;
}
private static List _silkierQuartzJobs = null;
- private static List GetSilkierQuartzJobs(List lists=null)
+ private static List GetSilkierQuartzJobs(List lists = null)
{
if (_silkierQuartzJobs == null)
{
@@ -200,6 +230,21 @@ namespace SilkierQuartz
}
return _silkierQuartzJobs;
}
+
+ ///
+ /// Adds the to the specified , which enables simple authentication for silkier Quartz.
+ ///
+ /// The to add the middleware to.
+ /// A reference to this instance after the operation has completed.
+ public static IApplicationBuilder AddSilkierQuartzAuthentication(this IApplicationBuilder app)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ return app.UseMiddleware();
+ }
}
}
diff --git a/src/SilkierQuartz/Controllers/AuthenticateController.cs b/src/SilkierQuartz/Controllers/AuthenticateController.cs
new file mode 100644
index 0000000..79592b2
--- /dev/null
+++ b/src/SilkierQuartz/Controllers/AuthenticateController.cs
@@ -0,0 +1,110 @@
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using SilkierQuartz.Models;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Security.Claims;
+using System.Threading.Tasks;
+
+namespace SilkierQuartz.Controllers
+{
+ [AllowAnonymous]
+ public class AuthenticateController : PageControllerBase
+ {
+ [HttpGet]
+ public async Task Login([FromServices] IAuthenticationSchemeProvider schemes)
+ {
+ var silkierScheme = await schemes.GetSchemeAsync(SilkierQuartzAuthenticateConfig.AuthScheme);
+
+ if (string.IsNullOrEmpty(SilkierQuartzAuthenticateConfig.UserName) ||
+ string.IsNullOrEmpty(SilkierQuartzAuthenticateConfig.UserPassword))
+ {
+ foreach (var userClaim in HttpContext.User.Claims)
+ {
+ Debug.WriteLine($"{userClaim.Type} - {userClaim.Value}");
+ }
+
+ if (HttpContext.User == null || !HttpContext.User.Identity.IsAuthenticated ||
+ !HttpContext.User.HasClaim(SilkierQuartzAuthenticateConfig.SilkierQuartzSpecificClaim,
+ SilkierQuartzAuthenticateConfig.SilkierQuartzSpecificClaimValue))
+ {
+ var claims = new List
+ {
+ new Claim(ClaimTypes.NameIdentifier, string.IsNullOrEmpty(SilkierQuartzAuthenticateConfig.UserName) ? "SilkierQuartzAdmin" : SilkierQuartzAuthenticateConfig.UserName ),
+ new Claim(ClaimTypes.Name, string.IsNullOrEmpty(SilkierQuartzAuthenticateConfig.UserPassword) ? "SilkierQuartzPassword" : SilkierQuartzAuthenticateConfig.UserPassword),
+ new Claim(SilkierQuartzAuthenticateConfig.SilkierQuartzSpecificClaim, SilkierQuartzAuthenticateConfig.SilkierQuartzSpecificClaimValue)
+ };
+
+ var authProperties = new AuthenticationProperties()
+ {
+ IsPersistent = SilkierQuartzAuthenticateConfig.IsPersist
+ };
+
+ var userIdentity = new ClaimsIdentity(claims, SilkierQuartzAuthenticateConfig.AuthScheme);
+ await HttpContext.SignInAsync(SilkierQuartzAuthenticateConfig.AuthScheme, new ClaimsPrincipal(userIdentity),
+ authProperties);
+
+ return RedirectToAction(nameof(SchedulerController.Index), nameof(Scheduler));
+ }
+ else
+ {
+ return RedirectToAction(nameof(SchedulerController.Index), nameof(Scheduler));
+ }
+ }
+ else
+ {
+ if (HttpContext.User == null || !HttpContext.User.Identity.IsAuthenticated ||
+ !HttpContext.User.HasClaim(SilkierQuartzAuthenticateConfig.SilkierQuartzSpecificClaim, "Authorized"))
+ {
+ ViewBag.IsLoginError = false;
+ return View(new AuthenticateViewModel());
+ }
+ else
+ {
+ return RedirectToAction(nameof(SchedulerController.Index), nameof(Scheduler));
+ }
+ }
+ }
+
+ [HttpPost]
+ public async Task Login(AuthenticateViewModel request)
+ {
+ if (string.Compare(request.UserName, SilkierQuartzAuthenticateConfig.UserName,
+ StringComparison.InvariantCulture) != 0 ||
+ string.Compare(request.Password, SilkierQuartzAuthenticateConfig.UserPassword,
+ StringComparison.InvariantCulture) != 0)
+ {
+ request.IsLoginError = true;
+ return View(request);
+ }
+
+ var claims = new List
+ {
+ new Claim(ClaimTypes.NameIdentifier, string.IsNullOrEmpty(SilkierQuartzAuthenticateConfig.UserName) ? "SilkierQuartzAdmin" : SilkierQuartzAuthenticateConfig.UserName ),
+ new Claim(ClaimTypes.Name, string.IsNullOrEmpty(SilkierQuartzAuthenticateConfig.UserPassword) ? "SilkierQuartzPassword" : SilkierQuartzAuthenticateConfig.UserPassword),
+ new Claim(SilkierQuartzAuthenticateConfig.SilkierQuartzSpecificClaim, "Authorized")
+ };
+
+ var authProperties = new AuthenticationProperties()
+ {
+ IsPersistent = request.IsPersist
+ };
+
+ var userIdentity = new ClaimsIdentity(claims, SilkierQuartzAuthenticateConfig.AuthScheme);
+ await HttpContext.SignInAsync(SilkierQuartzAuthenticateConfig.AuthScheme, new ClaimsPrincipal(userIdentity),
+ authProperties);
+
+ return RedirectToAction(nameof(SchedulerController.Index), nameof(Scheduler));
+ }
+
+ [HttpGet]
+ [Authorize(SilkierQuartzAuthenticateConfig.AuthScheme)]
+ public async Task Logout()
+ {
+ await HttpContext.SignOutAsync(SilkierQuartzAuthenticateConfig.AuthScheme);
+ return RedirectToAction(nameof(Login));
+ }
+ }
+}
diff --git a/src/SilkierQuartz/Controllers/CalendarsController.cs b/src/SilkierQuartz/Controllers/CalendarsController.cs
index ade02c7..8093e72 100644
--- a/src/SilkierQuartz/Controllers/CalendarsController.cs
+++ b/src/SilkierQuartz/Controllers/CalendarsController.cs
@@ -1,14 +1,16 @@
-using Quartz;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Quartz;
using SilkierQuartz.Helpers;
using SilkierQuartz.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using Microsoft.AspNetCore.Mvc;
namespace SilkierQuartz.Controllers
{
+ [Authorize(SilkierQuartzAuthenticateConfig.AuthScheme)]
public class CalendarsController : PageControllerBase
{
[HttpGet]
@@ -23,7 +25,7 @@ namespace SilkierQuartz.Controllers
var cal = await Scheduler.GetCalendar(name);
list.Add(new CalendarListItem() { Name = name, Description = cal.Description, Type = cal.GetType() });
}
-
+
return View(list);
}
@@ -81,7 +83,7 @@ namespace SilkierQuartz.Controllers
errors.ForEach(x => x.SegmentIndex = i);
result.Errors.AddRange(errors);
}
-
+
if (result.Success)
{
string name = chain[0].Name;
@@ -107,7 +109,7 @@ namespace SilkierQuartz.Controllers
current = newCal;
existing = existing?.CalendarBase;
}
-
+
if (root == null)
{
result.Errors.Add(new ValidationError() { Field = nameof(CalendarViewModel.Type), Reason = "Cannot create calendar.", SegmentIndex = 0 });
diff --git a/src/SilkierQuartz/Controllers/ExecutionsController.cs b/src/SilkierQuartz/Controllers/ExecutionsController.cs
index 777cbf3..1d0ad58 100644
--- a/src/SilkierQuartz/Controllers/ExecutionsController.cs
+++ b/src/SilkierQuartz/Controllers/ExecutionsController.cs
@@ -1,4 +1,5 @@
-using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
using SilkierQuartz.Helpers;
using System;
using System.Collections.Generic;
@@ -6,6 +7,7 @@ using System.Threading.Tasks;
namespace SilkierQuartz.Controllers
{
+ [Authorize(SilkierQuartzAuthenticateConfig.AuthScheme)]
public class ExecutionsController : PageControllerBase
{
[HttpGet]
diff --git a/src/SilkierQuartz/Controllers/HistoryController.cs b/src/SilkierQuartz/Controllers/HistoryController.cs
index 4416064..3181ae4 100644
--- a/src/SilkierQuartz/Controllers/HistoryController.cs
+++ b/src/SilkierQuartz/Controllers/HistoryController.cs
@@ -1,4 +1,5 @@
-using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
using Quartz.Plugins.RecentHistory;
using System;
using System.Collections.Generic;
@@ -7,6 +8,7 @@ using System.Threading.Tasks;
namespace SilkierQuartz.Controllers
{
+ [Authorize(SilkierQuartzAuthenticateConfig.AuthScheme)]
public class HistoryController : PageControllerBase
{
[HttpGet]
diff --git a/src/SilkierQuartz/Controllers/JobDataMapController.cs b/src/SilkierQuartz/Controllers/JobDataMapController.cs
index 5f52afb..0b6a85b 100644
--- a/src/SilkierQuartz/Controllers/JobDataMapController.cs
+++ b/src/SilkierQuartz/Controllers/JobDataMapController.cs
@@ -1,4 +1,5 @@
-using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SilkierQuartz.Helpers;
@@ -9,6 +10,7 @@ using System.Threading.Tasks;
namespace SilkierQuartz.Controllers
{
+ [Authorize(SilkierQuartzAuthenticateConfig.AuthScheme)]
public class JobDataMapController : PageControllerBase
{
[HttpPost, JsonErrorResponse]
diff --git a/src/SilkierQuartz/Controllers/JobsController.cs b/src/SilkierQuartz/Controllers/JobsController.cs
index 94b1118..aad16e4 100644
--- a/src/SilkierQuartz/Controllers/JobsController.cs
+++ b/src/SilkierQuartz/Controllers/JobsController.cs
@@ -1,4 +1,5 @@
-using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
using Quartz;
using Quartz.Impl.Matchers;
using Quartz.Plugins.RecentHistory;
@@ -11,6 +12,7 @@ using System.Threading.Tasks;
namespace SilkierQuartz.Controllers
{
+ [Authorize(SilkierQuartzAuthenticateConfig.AuthScheme)]
public class JobsController : PageControllerBase
{
[HttpGet]
diff --git a/src/SilkierQuartz/Controllers/PageControllerBase.cs b/src/SilkierQuartz/Controllers/PageControllerBase.cs
index 9ebd490..7c87fa4 100644
--- a/src/SilkierQuartz/Controllers/PageControllerBase.cs
+++ b/src/SilkierQuartz/Controllers/PageControllerBase.cs
@@ -1,4 +1,5 @@
-using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Primitives;
@@ -14,6 +15,7 @@ using System.Text.Json;
namespace SilkierQuartz.Controllers
{
+ [Authorize(SilkierQuartzAuthenticateConfig.AuthScheme)]
public abstract partial class PageControllerBase : ControllerBase
{
private static readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings()
diff --git a/src/SilkierQuartz/Controllers/SchedulerController.cs b/src/SilkierQuartz/Controllers/SchedulerController.cs
index 70e6dd8..64b2543 100644
--- a/src/SilkierQuartz/Controllers/SchedulerController.cs
+++ b/src/SilkierQuartz/Controllers/SchedulerController.cs
@@ -1,4 +1,5 @@
-using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
using Quartz;
using Quartz.Impl.Matchers;
using Quartz.Plugins.RecentHistory;
@@ -12,6 +13,7 @@ using System.Threading.Tasks;
namespace SilkierQuartz.Controllers
{
+ [Authorize(SilkierQuartzAuthenticateConfig.AuthScheme)]
public class SchedulerController : PageControllerBase
{
[HttpGet]
diff --git a/src/SilkierQuartz/Controllers/TriggersController.cs b/src/SilkierQuartz/Controllers/TriggersController.cs
index 7f60f62..373907b 100644
--- a/src/SilkierQuartz/Controllers/TriggersController.cs
+++ b/src/SilkierQuartz/Controllers/TriggersController.cs
@@ -1,4 +1,5 @@
-using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
using Quartz;
using Quartz.Impl.Matchers;
using Quartz.Plugins.RecentHistory;
@@ -11,6 +12,7 @@ using System.Threading.Tasks;
namespace SilkierQuartz.Controllers
{
+ [Authorize(SilkierQuartzAuthenticateConfig.AuthScheme)]
public class TriggersController : PageControllerBase
{
[HttpGet]
diff --git a/src/SilkierQuartz/Middlewares/SilkierQuartzAuthenticationMiddleware.cs b/src/SilkierQuartz/Middlewares/SilkierQuartzAuthenticationMiddleware.cs
new file mode 100644
index 0000000..d66363a
--- /dev/null
+++ b/src/SilkierQuartz/Middlewares/SilkierQuartzAuthenticationMiddleware.cs
@@ -0,0 +1,108 @@
+using System;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace SilkierQuartz.Middlewares
+{
+ ///
+ /// Middleware that performs authentication.
+ ///
+ public class SilkierQuartzAuthenticationMiddleware
+ {
+ private readonly RequestDelegate _next;
+
+ ///
+ /// Initializes a new instance of .
+ ///
+ /// The next item in the middleware pipeline.
+ /// The .
+ public SilkierQuartzAuthenticationMiddleware(RequestDelegate next, IAuthenticationSchemeProvider schemes)
+ {
+ _next = next ?? throw new ArgumentNullException(nameof(next));
+ Schemes = schemes ?? throw new ArgumentNullException(nameof(schemes));
+ }
+
+ ///
+ /// Gets or sets the .
+ ///
+ public IAuthenticationSchemeProvider Schemes { get; set; }
+
+ ///
+ /// Invokes the middleware performing authentication.
+ ///
+ /// The .
+ public async Task Invoke(HttpContext context)
+ {
+ var relativePath = GetRelativeUrlPath(context);
+ if (relativePath.StartsWith(SilkierQuartzAuthenticateConfig.VirtualPathRoot) ||
+ relativePath.StartsWith("?ReturnUrl") &&
+ relativePath.Contains(SilkierQuartzAuthenticateConfig.VirtualPathRootUrlEncode))
+ {
+ await DetailProcess(context, SilkierQuartzAuthenticateConfig.AuthScheme);
+ }
+
+ await _next(context);
+ }
+
+ public async Task DetailProcess(HttpContext httpContext, string authSchemeName = null)
+ {
+ httpContext.Features.Set(new AuthenticationFeature
+ {
+ OriginalPath = httpContext.Request.Path,
+ OriginalPathBase = httpContext.Request.PathBase
+ });
+
+ // Give any IAuthenticationRequestHandler schemes a chance to handle the request
+ var handlers = httpContext.RequestServices.GetRequiredService();
+ foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
+ {
+ if (await handlers.GetHandlerAsync(httpContext, scheme.Name) is IAuthenticationRequestHandler handler &&
+ await handler.HandleRequestAsync())
+ {
+ return;
+ }
+ }
+
+ var authScheme = string.IsNullOrEmpty(authSchemeName)
+ ? await Schemes.GetDefaultAuthenticateSchemeAsync()
+ : await Schemes.GetSchemeAsync(authSchemeName);
+ if (authScheme != null)
+ {
+ var result = await httpContext.AuthenticateAsync(authScheme.Name);
+ if (result.Principal == null || !result.Principal.HasClaim(SilkierQuartzAuthenticateConfig.SilkierQuartzSpecificClaim,
+ SilkierQuartzAuthenticateConfig.SilkierQuartzSpecificClaimValue))
+ {
+ return;
+ }
+ if (result?.Principal != null)
+ {
+ httpContext.User = result.Principal;
+ }
+ }
+ }
+
+ public string GetRelativeUrlPath(HttpContext httpContext)
+ {
+ /*
+ In some cases, like when running integration tests with WebApplicationFactory
+ the RawTarget returns an empty string instead of null, in that case we can't use
+ ?? as fallback.
+ */
+ if (httpContext == null)
+ {
+ return string.Empty;
+ }
+
+ var requestPath = httpContext.Features.Get()?.RawTarget;
+ if (string.IsNullOrEmpty(requestPath))
+ {
+ requestPath = httpContext.Request.Path.ToString();
+ }
+
+ return requestPath;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/SilkierQuartz/Models/AuthenticateViewModel.cs b/src/SilkierQuartz/Models/AuthenticateViewModel.cs
new file mode 100644
index 0000000..dbd61f8
--- /dev/null
+++ b/src/SilkierQuartz/Models/AuthenticateViewModel.cs
@@ -0,0 +1,16 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace SilkierQuartz.Models
+{
+ public class AuthenticateViewModel
+ {
+ [Required]
+ public string UserName { get; set; }
+ [Required]
+ public string Password { get; set; }
+
+ public bool IsPersist { get; set; }
+
+ public bool IsLoginError { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/SilkierQuartz/SilkierQuartzAuthenticateOptions.cs b/src/SilkierQuartz/SilkierQuartzAuthenticateOptions.cs
new file mode 100644
index 0000000..cce2307
--- /dev/null
+++ b/src/SilkierQuartz/SilkierQuartzAuthenticateOptions.cs
@@ -0,0 +1,14 @@
+namespace SilkierQuartz
+{
+ internal class SilkierQuartzAuthenticateConfig
+ {
+ internal static string VirtualPathRoot = string.Empty;
+ internal static string VirtualPathRootUrlEncode = string.Empty;
+ internal static string UserName;
+ internal static string UserPassword;
+ internal static bool IsPersist;
+ internal const string AuthScheme = "SilkierQuartzAuth";
+ internal const string SilkierQuartzSpecificClaim = "SilkierQuartzManage";
+ internal const string SilkierQuartzSpecificClaimValue = "Authorized";
+ }
+}
\ No newline at end of file
diff --git a/src/SilkierQuartz/SilkierQuartzOptions.cs b/src/SilkierQuartz/SilkierQuartzOptions.cs
index a744c9d..ce502ab 100644
--- a/src/SilkierQuartz/SilkierQuartzOptions.cs
+++ b/src/SilkierQuartz/SilkierQuartzOptions.cs
@@ -23,6 +23,12 @@ namespace SilkierQuartz
public IScheduler Scheduler { get; set; }
+ public string AccountName { get; set; }
+
+ public string AccountPassword { get; set; }
+
+ public bool IsAuthenticationPersist { get; set; }
+
///
/// Supported value types in job data map.
///
diff --git a/src/SilkierQuartz/Views/Authenticate/Login.hbs b/src/SilkierQuartz/Views/Authenticate/Login.hbs
new file mode 100644
index 0000000..fe2070a
--- /dev/null
+++ b/src/SilkierQuartz/Views/Authenticate/Login.hbs
@@ -0,0 +1,44 @@
+{{!
+
+
+
diff --git a/src/SilkierQuartz/Views/Layout.hbs b/src/SilkierQuartz/Views/Layout.hbs
index d7fcbe4..22bd618 100644
--- a/src/SilkierQuartz/Views/Layout.hbs
+++ b/src/SilkierQuartz/Views/Layout.hbs
@@ -41,25 +41,26 @@
- {{MenuItemActionLink text='Overview' controller='Scheduler'}}
+ {{MenuItemActionLink title='Overview' controller='Scheduler'}}
{{MenuItemActionLink 'Jobs'}}
{{MenuItemActionLink 'Triggers'}}
{{MenuItemActionLink 'Executions'}}
{{MenuItemActionLink 'History'}}
{{MenuItemActionLink 'Calendars'}}
+ {{MenuItemActionLink title='Logout' controller='Authenticate/Logout'}}
+
+
+ -->