Skip to content

Commit 0b7aadd

Browse files
Implement optional service windows auth
1 parent a7c7b35 commit 0b7aadd

File tree

7 files changed

+152
-39
lines changed

7 files changed

+152
-39
lines changed

src/Certify.Server/Certify.Server.Core/Certify.Server.Core/Certify.Server.Core.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
</PropertyGroup>
1010
<ItemGroup>
1111
<PackageReference Include="Microsoft.AspNet.SignalR.Client" Version="2.4.3" />
12+
<PackageReference Include="Microsoft.AspNet.WebApi.Core" Version="5.3.0" />
1213
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.3" />
1314
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.3" />
1415
<PackageReference Include="Polly" Version="8.5.2" />

src/Certify.Server/Certify.Server.Core/Certify.Server.Core/Controllers/ControllerBase.cs

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,71 @@
11
using System.Diagnostics;
2+
using System.Security.Claims;
3+
using System.Text.Encodings.Web;
4+
using Microsoft.AspNetCore.Authentication;
25
using Microsoft.AspNetCore.Authorization;
36
using Microsoft.AspNetCore.Mvc;
7+
using Microsoft.Extensions.Options;
48

59
namespace Certify.Service.Controllers
610
{
7-
public class CustomAuthCheckAttribute : AuthorizeAttribute
11+
public class ServiceAuthSchemeOptions : AuthenticationSchemeOptions { }
12+
public class ServiceAuthSchemeHandler : AuthenticationHandler<ServiceAuthSchemeOptions>
813
{
9-
/* protected override bool IsAuthorized(HttpActionContext actionContext)
10-
{
11-
#if DEBUG_NO_AUTH
12-
return true;
13-
#endif
14-
var user = actionContext.RequestContext.Principal as System.Security.Principal.WindowsPrincipal;
15-
if (user.IsInRole(WindowsBuiltInRole.Administrator))
16-
{
17-
return true;
18-
}
14+
public ServiceAuthSchemeHandler(
15+
IOptionsMonitor<ServiceAuthSchemeOptions> options,
16+
ILoggerFactory logger,
17+
UrlEncoder encoder) : base(options, logger, encoder)
18+
{
19+
}
20+
21+
protected async override Task<AuthenticateResult> HandleAuthenticateAsync()
22+
{
23+
// provide and an artificial default identify when this auth scheme is used (for non-window auth)
24+
var claims = new[] { new Claim(ClaimTypes.Name, "service_user") };
25+
var principal = new ClaimsPrincipal(new ClaimsIdentity(claims));
26+
var ticket = new AuthenticationTicket(principal, this.Scheme.Name);
27+
return AuthenticateResult.Success(ticket);
28+
}
29+
}
30+
31+
public class ClaimsTransformer : IClaimsTransformation
32+
{
33+
private bool _requireWindowsAuth;
34+
public ClaimsTransformer(bool requireWindowsAuth = true)
35+
{
36+
_requireWindowsAuth = requireWindowsAuth;
37+
}
38+
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
39+
{
40+
var ci = (ClaimsIdentity)principal.Identity;
1941

20-
if (user.IsInRole(WindowsBuiltInRole.PowerUser))
21-
{
22-
return true;
23-
}
42+
if (_requireWindowsAuth)
43+
{
44+
// on windows, add group claims for the user
2445

25-
return false;
26-
}*/
46+
var isAdmin = ci.Claims
47+
.Where(x => x.Type == ClaimTypes.GroupSid || x.Type == ClaimTypes.PrimaryGroupSid)
48+
.Any(x => x.Value == "S-1-5-32-544"); //Administrator group
49+
50+
if (isAdmin)
51+
{
52+
var roleClaim = new Claim(ClaimTypes.Role, "service_admin");
53+
ci.AddClaim(roleClaim);
54+
}
55+
}
56+
else
57+
{
58+
// auto enable role for the current identity
59+
var roleClaim = new Claim(ClaimTypes.Role, "service_admin");
60+
ci.AddClaim(roleClaim);
61+
}
62+
63+
return Task.FromResult(principal);
64+
}
2765
}
2866

2967
[ApiController]
30-
// [CustomAuthCheck]
68+
[Authorize(Policy = "CertifyServiceAuth")]
3169
public class ControllerBase : Controller
3270
{
3371
internal void DebugLog(string msg = null,

src/Certify.Server/Certify.Server.Core/Certify.Server.Core/Program.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
builder.Services.AddWindowsService();
66
builder.Services.AddHostedService<WindowsBackgroundService>();
7-
builder.Configuration.AddJsonFile("appsettings-core.json", optional: false, reloadOnChange: true);
7+
builder.Configuration.AddJsonFile("appsettings-core.json", optional: true, reloadOnChange: true);
8+
builder.Configuration.AddJsonFile("appsettings-core.Development.json", optional: true, reloadOnChange: true);
89

910
builder.AddServiceDefaults();
1011

src/Certify.Server/Certify.Server.Core/Certify.Server.Core/Properties/launchSettings.json

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,21 @@
1515
"launchBrowser": false,
1616
"environmentVariables": {
1717
"ASPNETCORE_ENVIRONMENT": "Development",
18-
"ASPNETCORE_URLS": "http://127.0.0.2:9695",
18+
"ASPNETCORE_URLS": "http://127.0.0.2:9696",
1919
"CERTIFY_MANAGEMENT_HUB": "https://localhost:44361/api/internal/managementhub",
20-
"CERTIFY_ENABLE_MANAGEMENT_HUB": "true"
20+
"CERTIFY_ENABLE_MANAGEMENT_HUB": "true",
21+
"CERTIFY_SERVICE_AUTH_MODE": "none"
22+
}
23+
},
24+
"Certify.Server.Core (windows auth)": {
25+
"commandName": "Project",
26+
"launchBrowser": false,
27+
"environmentVariables": {
28+
"ASPNETCORE_ENVIRONMENT": "Development",
29+
"ASPNETCORE_URLS": "http://127.0.0.2:9696",
30+
"CERTIFY_MANAGEMENT_HUB": "https://localhost:44361/api/internal/managementhub",
31+
"CERTIFY_ENABLE_MANAGEMENT_HUB": "true",
32+
"CERTIFY_SERVICE_AUTH_MODE": "windows"
2133
}
2234
},
2335
"IIS Express": {

src/Certify.Server/Certify.Server.Core/Certify.Server.Core/Startup.cs

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
using Certify.Management;
1+
using System.Runtime.InteropServices;
2+
using System.Security.Claims;
3+
using Certify.Management;
24
using Certify.Models;
5+
using Certify.Service.Controllers;
6+
using Microsoft.AspNetCore.Authentication;
7+
using Microsoft.AspNetCore.Authentication.Negotiate;
38
using Microsoft.AspNetCore.DataProtection;
49
using Microsoft.AspNetCore.ResponseCompression;
510
using Microsoft.AspNetCore.SignalR;
@@ -49,6 +54,57 @@ public void ConfigureServices(IServiceCollection services)
4954
});
5055
});
5156

57+
// determine whether we require auth via Kerberos/windows auth, can be overridden by env var
58+
var windowsAuthRequired = true;
59+
60+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
61+
{
62+
// windows auth is required by default
63+
if (Environment.GetEnvironmentVariable("CERTIFY_SERVICE_AUTH_MODE") == "none")
64+
{
65+
windowsAuthRequired = false;
66+
}
67+
else if (Configuration["Service:AuthMode"] == "none")
68+
{
69+
windowsAuthRequired = false;
70+
}
71+
}
72+
else
73+
{
74+
// on non-windows platforms we don't support windows auth
75+
windowsAuthRequired = false;
76+
}
77+
78+
services
79+
.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
80+
.AddScheme<ServiceAuthSchemeOptions, ServiceAuthSchemeHandler>("ServiceAuthScheme", opts => { }) // allow custom service auth when windows auth not used
81+
.AddNegotiate(); //add windows auth/kerberos
82+
83+
services.AddAuthorization(options =>
84+
{
85+
// add policy to require admin role claim
86+
87+
if (windowsAuthRequired)
88+
{
89+
// when using windows auth we require the user to be in the admin group which we check via our ClaimsTransformer
90+
options.AddPolicy("CertifyServiceAuth", policy =>
91+
{
92+
policy.AddAuthenticationSchemes(NegotiateDefaults.AuthenticationScheme);
93+
policy.RequireAuthenticatedUser();
94+
policy.RequireClaim(ClaimTypes.Role, new[] { "service_admin" });
95+
});
96+
}
97+
else
98+
{
99+
// when not using windows auth we use our custom service auth scheme
100+
options.AddPolicy("CertifyServiceAuth", policy =>
101+
{
102+
policy.AddAuthenticationSchemes("ServiceAuthScheme");
103+
policy.RequireClaim(ClaimTypes.Role, new[] { "service_admin" });
104+
});
105+
}
106+
});
107+
52108
#if DEBUG
53109
services.AddEndpointsApiExplorer();
54110

@@ -93,7 +149,7 @@ public void ConfigureServices(IServiceCollection services)
93149
});
94150
#endif
95151

96-
var useHttps = bool.Parse(Configuration["API:Service:UseHttps"]);
152+
var useHttps = Configuration["API:Service:UseHttps"] != null ? bool.Parse(Configuration["API:Service:UseHttps"]) : false;
97153

98154
if (useHttps)
99155
{
@@ -104,6 +160,9 @@ public void ConfigureServices(IServiceCollection services)
104160
});
105161
}
106162

163+
// add claims transformation service, this is used to optionally check auth requirements
164+
services.AddSingleton<IClaimsTransformation, ClaimsTransformer>(c => new ClaimsTransformer(windowsAuthRequired));
165+
107166
// inject instance of certify manager
108167
var certifyManager = new Management.CertifyManager();
109168
certifyManager.Init().Wait();
@@ -148,7 +207,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
148207
// set status report context provider
149208
certifyManager.SetStatusReporting(new Service.StatusHubReporting(statusHubContext));
150209

151-
var useHttps = bool.Parse(Configuration["API:Service:UseHttps"]);
210+
var useHttps = Configuration["API:Service:UseHttps"] != null ? bool.Parse(Configuration["API:Service:UseHttps"]) : false;
152211

153212
if (useHttps)
154213
{
@@ -157,10 +216,12 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
157216

158217
app.UseRouting();
159218

160-
app.UseCors();
161-
219+
// enable authentication middleware
220+
app.UseAuthentication();
162221
app.UseAuthorization();
163222

223+
app.UseCors();
224+
164225
app.UseEndpoints(endpoints =>
165226
{
166227
endpoints.MapHub<Service.StatusHub>("/api/status");

src/Certify.Server/Certify.Server.Core/Certify.Server.Core/appsettings-core.Development.json

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
"Microsoft.AspNetCore.Http.Connections": "Debug"
99
}
1010
},
11-
"API": {
12-
"Service": {
13-
"HttpPort": 9695,
14-
"HttpsPort": 9443,
15-
"UseHttps": false,
16-
"BindingIP": "any"
11+
"Service": {
12+
"AuthMode": "windows"
13+
},
14+
"Kestrel": {
15+
"Endpoints": {
16+
"SvcHttpEndpoint": {
17+
"Url": "http://127.0.0.2:9695"
18+
}
1719
}
1820
}
1921
}

src/Certify.Server/Certify.Server.Core/Certify.Server.Core/appsettings-core.json

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,11 @@
88
"Microsoft.AspNetCore.Http.Connections": "Debug"
99
}
1010
},
11-
"API": {
12-
"Service": {
13-
"HttpPort": 9696,
14-
"HttpsPort": 9443,
15-
"UseHttps": false,
16-
"BindingIP": "any"
11+
"Kestrel": {
12+
"Endpoints": {
13+
"SvcHttpEndpoint": {
14+
"Url": "http://127.0.0.2:9696"
15+
}
1716
}
1817
}
19-
2018
}

0 commit comments

Comments
 (0)