mirror of
https://github.com/fergalmoran/SilkierQuartz.git
synced 2025-12-22 09:37:56 +00:00
Add simple authentication feature
This commit is contained in:
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" href= "/SilkierQuartz">SilkierQuartz</a>
|
||||
<a class="nav-link text-dark" href= "/SilkierQuartz/Authenticate/Login">SilkierQuartz</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
|
||||
|
||||
@@ -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
|
||||
//<2F><><EFBFBD>ɵ<EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD>Ĺ滮Job<6F>Ĵ<EFBFBD><C4B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֲ<EFBFBD><D6B2><EFBFBD>ݵ<EFBFBD>ʾ<EFBFBD><CABE>
|
||||
// app.SchedulerJobs();
|
||||
// app.SchedulerJobs();
|
||||
|
||||
|
||||
#region <EFBFBD><EFBFBD>ʹ<EFBFBD><EFBFBD> SilkierQuartzAttribe <EFBFBD><EFBFBD><EFBFBD>ԵĽ<EFBFBD><EFBFBD><EFBFBD>ע<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD>õ<EFBFBD>IJob<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͨ<EFBFBD><EFBFBD>UseQuartzJob<EFBFBD><EFBFBD>IJob<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ConfigureServices<EFBFBD><EFBFBD><EFBFBD><EFBFBD>AddQuartzJob
|
||||
|
||||
@@ -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
|
||||
/// <param name="app"></param>
|
||||
/// <param name="schedName"></param>
|
||||
/// <returns></returns>
|
||||
public static IScheduler GetScheduler(this IApplicationBuilder app,string schedName)
|
||||
public static IScheduler GetScheduler(this IApplicationBuilder app, string schedName)
|
||||
{
|
||||
return app.ApplicationServices.GetRequiredService<ISchedulerFactory>().GetScheduler(schedName ).Result;
|
||||
return app.ApplicationServices.GetRequiredService<ISchedulerFactory>().GetScheduler(schedName).Result;
|
||||
}
|
||||
/// <summary>
|
||||
/// 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<SilkierQuartzAttribute>();
|
||||
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<NameValueCollection> stdSchedulerFactoryOptions = null,Func<List<Assembly>> jobsasmlist=null)
|
||||
public static IServiceCollection AddSilkierQuartz(this IServiceCollection services, Action<NameValueCollection> stdSchedulerFactoryOptions = null, Func<List<Assembly>> 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<SilkierQuartzAttribute>();
|
||||
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<Type> _silkierQuartzJobs = null;
|
||||
private static List<Type> GetSilkierQuartzJobs(List<Assembly> lists=null)
|
||||
private static List<Type> GetSilkierQuartzJobs(List<Assembly> lists = null)
|
||||
{
|
||||
if (_silkierQuartzJobs == null)
|
||||
{
|
||||
@@ -200,6 +230,21 @@ namespace SilkierQuartz
|
||||
}
|
||||
return _silkierQuartzJobs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <see cref="SilkierQuartzAuthenticationMiddleware"/> to the specified <see cref="IApplicationBuilder"/>, which enables simple authentication for silkier Quartz.
|
||||
/// </summary>
|
||||
/// <param name="app">The <see cref="IApplicationBuilder"/> to add the middleware to.</param>
|
||||
/// <returns>A reference to this instance after the operation has completed.</returns>
|
||||
public static IApplicationBuilder AddSilkierQuartzAuthentication(this IApplicationBuilder app)
|
||||
{
|
||||
if (app == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(app));
|
||||
}
|
||||
|
||||
return app.UseMiddleware<SilkierQuartzAuthenticationMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
110
src/SilkierQuartz/Controllers/AuthenticateController.cs
Normal file
110
src/SilkierQuartz/Controllers/AuthenticateController.cs
Normal file
@@ -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<IActionResult> 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<Claim>
|
||||
{
|
||||
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<IActionResult> 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<Claim>
|
||||
{
|
||||
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<IActionResult> Logout()
|
||||
{
|
||||
await HttpContext.SignOutAsync(SilkierQuartzAuthenticateConfig.AuthScheme);
|
||||
return RedirectToAction(nameof(Login));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 });
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Middleware that performs authentication.
|
||||
/// </summary>
|
||||
public class SilkierQuartzAuthenticationMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="Microsoft.AspNetCore.Authentication.AuthenticationMiddleware"/>.
|
||||
/// </summary>
|
||||
/// <param name="next">The next item in the middleware pipeline.</param>
|
||||
/// <param name="schemes">The <see cref="IAuthenticationSchemeProvider"/>.</param>
|
||||
public SilkierQuartzAuthenticationMiddleware(RequestDelegate next, IAuthenticationSchemeProvider schemes)
|
||||
{
|
||||
_next = next ?? throw new ArgumentNullException(nameof(next));
|
||||
Schemes = schemes ?? throw new ArgumentNullException(nameof(schemes));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IAuthenticationSchemeProvider"/>.
|
||||
/// </summary>
|
||||
public IAuthenticationSchemeProvider Schemes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the middleware performing authentication.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="HttpContext"/>.</param>
|
||||
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<IAuthenticationFeature>(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<IAuthenticationHandlerProvider>();
|
||||
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<T>
|
||||
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<IHttpRequestFeature>()?.RawTarget;
|
||||
if (string.IsNullOrEmpty(requestPath))
|
||||
{
|
||||
requestPath = httpContext.Request.Path.ToString();
|
||||
}
|
||||
|
||||
return requestPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/SilkierQuartz/Models/AuthenticateViewModel.cs
Normal file
16
src/SilkierQuartz/Models/AuthenticateViewModel.cs
Normal file
@@ -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; }
|
||||
}
|
||||
}
|
||||
14
src/SilkierQuartz/SilkierQuartzAuthenticateOptions.cs
Normal file
14
src/SilkierQuartz/SilkierQuartzAuthenticateOptions.cs
Normal file
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
|
||||
/// <summary>
|
||||
/// Supported value types in job data map.
|
||||
/// </summary>
|
||||
|
||||
44
src/SilkierQuartz/Views/Authenticate/Login.hbs
Normal file
44
src/SilkierQuartz/Views/Authenticate/Login.hbs
Normal file
@@ -0,0 +1,44 @@
|
||||
{{!<Layout}}
|
||||
{{ViewBag Title='Login'}}
|
||||
|
||||
<div class="ui inverted page dimmer" id="dimmer"><div class="ui loader"></div></div>
|
||||
<form class="ui form" method="post" enctype="multipart/form-data">
|
||||
<div class="ui clearing basic segment" style="padding: 0px" id="header">
|
||||
<h1 class="ui left floated header">
|
||||
Login
|
||||
</h1>
|
||||
</div>
|
||||
{{#with Model}}
|
||||
<div class="ui segment" style="width: 700px; height: 250px;">
|
||||
<div>
|
||||
<div class="field accept-error">
|
||||
<label>Name</label>
|
||||
<input type="text" placeholder="User Name" value="{{UserName}}" name="userName" />
|
||||
</div>
|
||||
<div class="field accept-error">
|
||||
<label>Password</label>
|
||||
<input type="password" placeholder="User Name" value="{{Password}}" name="password" />
|
||||
</div>
|
||||
|
||||
<div class="field accept-error">
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" value="False" {{IsPersist}} />
|
||||
<label>Remember Me</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="float: left; margin-top: 15px;">
|
||||
<div class="ui buttons">
|
||||
<button type="submit" class="ui primary button">Login</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{#if IsLoginError}}
|
||||
<div class="ui negative message">
|
||||
<p>User Name or Password is incorrect!</p>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/with}}
|
||||
</form>
|
||||
|
||||
<script src="Content/Scripts/post-validation.js"></script>
|
||||
@@ -41,25 +41,26 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{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'}}
|
||||
|
||||
<!--
|
||||
<div class="right menu">
|
||||
<div class="ui dropdown item">
|
||||
<i class="user circle large icon"></i>
|
||||
domain\user
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="menu">
|
||||
<a class="item center" href="#">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right menu">
|
||||
<div class="ui dropdown item">
|
||||
<i class="user circle large icon"></i>
|
||||
domain\user
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="menu">
|
||||
<a class="item center" href="#">Logout</a>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user