Skip to content

Commit cd53f0e

Browse files
Refactor service startup for clarity
1 parent 148ac26 commit cd53f0e

File tree

1 file changed

+144
-144
lines changed
  • src/Certify.Server/Certify.Server.Core/Certify.Server.Core

1 file changed

+144
-144
lines changed

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

Lines changed: 144 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -13,125 +13,174 @@ namespace Certify.Server.Core
1313
{
1414
public class Startup
1515
{
16+
private const string ServiceAuthScheme = "ServiceAuthScheme";
17+
private const string CertifyServiceAuthPolicy = "CertifyServiceAuth";
18+
private const string SwaggerDocTitle = "Certify Agent Service Internal API";
19+
private const string SwaggerDocVersion = "v1";
20+
private const string SwaggerDocDescription = "Provides a private API for use by the Certify The Web Desktop UI and related components. This internal API changes between versions, you should use the public Hub API when building integrations instead.";
21+
1622
public Startup(IConfiguration configuration)
1723
{
1824
Configuration = configuration;
1925
}
2026

2127
public IConfiguration Configuration { get; }
2228

23-
// This method gets called by the runtime. Use this method to add services to the container.
2429
public void ConfigureServices(IServiceCollection services)
2530
{
2631
services.AddControllers();
2732

28-
services
29-
.AddSignalR()
30-
.AddMessagePackProtocol();
33+
ConfigureSignalR(services);
34+
ConfigureDataProtection(services);
35+
ConfigureResponseCompression(services);
36+
ConfigureCors(services);
37+
ConfigureAuthentication(services);
38+
ConfigureAuthorization(services);
39+
#if DEBUG
40+
ConfigureSwagger(services);
41+
#endif
42+
ConfigureHttpsRedirection(services);
43+
ConfigureClaimsTransformation(services);
44+
ConfigureCertifyManager(services);
45+
}
3146

32-
var appDataPath = EnvironmentUtil.CreateAppDataPath("keys");
47+
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
48+
{
49+
var statusHubContext = app.ApplicationServices.GetRequiredService<IHubContext<Service.StatusHub>>()
50+
?? throw new Exception("Status Hub not registered");
51+
52+
var certifyManager = app.ApplicationServices.GetRequiredService<ICertifyManager>() as CertifyManager
53+
?? throw new Exception("Certify Manager not registered");
3354

34-
services
35-
.AddDataProtection(a =>
55+
if (env.IsDevelopment())
56+
{
57+
app.UseDeveloperExceptionPage();
58+
#if DEBUG
59+
ConfigureSwaggerUI(app);
60+
#endif
61+
}
62+
63+
certifyManager.SetStatusReporting(new Service.StatusHubReporting(statusHubContext));
64+
65+
if (bool.TryParse(Configuration["API:Service:UseHttps"], out var useHttps) && useHttps)
66+
{
67+
app.UseHttpsRedirection();
68+
}
69+
70+
app.UseRouting();
71+
app.UseAuthentication();
72+
app.UseAuthorization();
73+
app.UseCors();
74+
app.UseEndpoints(endpoints =>
75+
{
76+
endpoints.MapHub<Service.StatusHub>("/api/status");
77+
endpoints.MapControllers();
78+
#if DEBUG
79+
endpoints.MapGet("/debug/routes", (IEnumerable<EndpointDataSource> endpointSources) =>
3680
{
37-
a.ApplicationDiscriminator = "certify";
38-
})
39-
.PersistKeysToFileSystem(new DirectoryInfo(appDataPath));
81+
var sb = new System.Text.StringBuilder();
82+
var endpoints = endpointSources.SelectMany(es => es.Endpoints);
83+
foreach (var endpoint in endpoints)
84+
{
85+
if (endpoint is RouteEndpoint routeEndpoint)
86+
{
87+
sb.AppendLine($"{routeEndpoint.DisplayName} {routeEndpoint.RoutePattern.RawText}");
88+
}
89+
}
90+
91+
return sb.ToString();
92+
});
93+
#endif
94+
});
95+
}
4096

97+
private void ConfigureSignalR(IServiceCollection services)
98+
{
99+
services.AddSignalR().AddMessagePackProtocol();
100+
}
101+
102+
private void ConfigureDataProtection(IServiceCollection services)
103+
{
104+
var appDataPath = EnvironmentUtil.CreateAppDataPath("keys");
105+
services.AddDataProtection(a => a.ApplicationDiscriminator = "certify")
106+
.PersistKeysToFileSystem(new DirectoryInfo(appDataPath));
107+
}
108+
109+
private void ConfigureResponseCompression(IServiceCollection services)
110+
{
41111
services.AddResponseCompression(opts =>
42112
{
43113
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "application/octet-stream", "application/json" });
44114
});
115+
}
45116

117+
private void ConfigureCors(IServiceCollection services)
118+
{
46119
services.AddCors(options =>
47120
{
48-
options.AddDefaultPolicy(
49-
builder =>
50-
{
51-
52-
builder.AllowAnyOrigin();
53-
builder.AllowAnyMethod();
54-
});
121+
options.AddDefaultPolicy(builder =>
122+
{
123+
builder.AllowAnyOrigin().AllowAnyMethod();
124+
});
55125
});
126+
}
56127

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-
}
128+
private void ConfigureAuthentication(IServiceCollection services)
129+
{
130+
services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
131+
.AddScheme<ServiceAuthSchemeOptions, ServiceAuthSchemeHandler>(ServiceAuthScheme, opts => { })
132+
.AddNegotiate();
133+
}
77134

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
135+
private void ConfigureAuthorization(IServiceCollection services)
136+
{
137+
var windowsAuthRequired = DetermineWindowsAuthRequired();
82138

83139
services.AddAuthorization(options =>
84140
{
85-
// add policy to require admin role claim
86-
87-
if (windowsAuthRequired)
141+
options.AddPolicy(CertifyServiceAuthPolicy, policy =>
88142
{
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 =>
143+
if (windowsAuthRequired)
91144
{
92145
policy.AddAuthenticationSchemes(NegotiateDefaults.AuthenticationScheme);
93146
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 =>
147+
}
148+
else
101149
{
102-
policy.AddAuthenticationSchemes("ServiceAuthScheme");
103-
policy.RequireClaim(ClaimTypes.Role, new[] { "service_admin" });
104-
});
105-
}
150+
// apply custom auth scheme for service auth
151+
policy.AddAuthenticationSchemes(ServiceAuthScheme);
152+
}
153+
154+
policy.RequireClaim(ClaimTypes.Role, "service_admin");
155+
});
106156
});
157+
}
107158

108159
#if DEBUG
160+
private void ConfigureSwagger(IServiceCollection services)
161+
{
162+
109163
services.AddEndpointsApiExplorer();
110164

111-
// Register the Swagger generator, defining 1 or more Swagger documents
112-
// https://docs.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-3.1&tabs=visual-studio
113165
services.AddSwaggerGen(c =>
114166
{
115-
116-
c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
167+
c.SwaggerDoc(SwaggerDocVersion, new Microsoft.OpenApi.Models.OpenApiInfo
117168
{
118-
Title = "Certify Core Internal API",
119-
Version = "v1",
120-
Description = "Provides a private API for use by the Certify The Web UI and related components. This internal API changes between versions, you should use the public API when building integrations instead."
169+
Title = SwaggerDocTitle,
170+
Version = SwaggerDocVersion,
171+
Description = SwaggerDocDescription
121172
});
122173

123-
// declare authorization method
124174
c.AddSecurityDefinition("Bearer", new Microsoft.OpenApi.Models.OpenApiSecurityScheme
125175
{
126176
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
127177
Name = "Authorization",
128-
Scheme = "bearer",
178+
Scheme = "Bearer",
129179
BearerFormat = "JWT",
130180
In = Microsoft.OpenApi.Models.ParameterLocation.Header,
131181
Type = Microsoft.OpenApi.Models.SecuritySchemeType.Http
132182
});
133183

134-
// set security requirement
135184
c.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement
136185
{
137186
{
@@ -145,105 +194,56 @@ public void ConfigureServices(IServiceCollection services)
145194
}, new List<string>()
146195
}
147196
});
197+
});
198+
199+
}
148200

201+
private void ConfigureSwaggerUI(IApplicationBuilder app)
202+
{
203+
app.UseSwagger();
204+
205+
app.UseSwaggerUI(c =>
206+
{
207+
c.RoutePrefix = "docs";
208+
c.DocumentTitle = "Certify Core Server API";
209+
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Certify Core Server API");
149210
});
211+
}
150212
#endif
151213

152-
var useHttps = Configuration["API:Service:UseHttps"] != null ? bool.Parse(Configuration["API:Service:UseHttps"]) : false;
153-
154-
if (useHttps)
214+
private void ConfigureHttpsRedirection(IServiceCollection services)
215+
{
216+
if (bool.TryParse(Configuration["API:Service:UseHttps"], out var useHttps) && useHttps)
155217
{
156218
services.AddHttpsRedirection(options =>
157219
{
158220
options.RedirectStatusCode = Microsoft.AspNetCore.Http.StatusCodes.Status307TemporaryRedirect;
159221
options.HttpsPort = 443;
160222
});
161223
}
224+
}
162225

163-
// add claims transformation service, this is used to optionally check auth requirements
226+
private void ConfigureClaimsTransformation(IServiceCollection services)
227+
{
228+
var windowsAuthRequired = DetermineWindowsAuthRequired();
164229
services.AddSingleton<IClaimsTransformation, ClaimsTransformer>(c => new ClaimsTransformer(windowsAuthRequired));
230+
}
165231

166-
// inject instance of certify manager
232+
private void ConfigureCertifyManager(IServiceCollection services)
233+
{
167234
var certifyManager = new Management.CertifyManager();
168235
certifyManager.Init().Wait();
169-
170236
services.AddSingleton<Management.ICertifyManager>(certifyManager);
171237
}
172238

173-
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
174-
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
239+
private bool DetermineWindowsAuthRequired()
175240
{
176-
var statusHubContext = app.ApplicationServices.GetRequiredService<IHubContext<Service.StatusHub>>();
177-
if (statusHubContext == null)
178-
{
179-
throw new Exception("Status Hub not registered");
180-
}
181-
182-
var certifyManager = app.ApplicationServices.GetRequiredService(typeof(ICertifyManager)) as CertifyManager;
183-
184-
if (certifyManager == null)
185-
{
186-
throw new Exception("Certify Manager not registered");
187-
}
188-
189-
#if DEBUG
190-
if (env.IsDevelopment())
191-
{
192-
app.UseDeveloperExceptionPage();
193-
194-
// Enable middleware to serve generated Swagger as a JSON endpoint.
195-
app.UseSwagger();
196-
197-
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
198-
// specifying the Swagger JSON endpoint.
199-
app.UseSwaggerUI(c =>
200-
{
201-
c.RoutePrefix = "docs";
202-
c.DocumentTitle = "Certify Core Server API";
203-
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Certify Core Server API");
204-
});
205-
}
206-
#endif
207-
// set status report context provider
208-
certifyManager.SetStatusReporting(new Service.StatusHubReporting(statusHubContext));
209-
210-
var useHttps = Configuration["API:Service:UseHttps"] != null ? bool.Parse(Configuration["API:Service:UseHttps"]) : false;
211-
212-
if (useHttps)
241+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
213242
{
214-
app.UseHttpsRedirection();
243+
return Environment.GetEnvironmentVariable("CERTIFY_SERVICE_AUTH_MODE") != "none" &&
244+
Configuration["Service:AuthMode"] != "none";
215245
}
216-
217-
app.UseRouting();
218-
219-
// enable authentication middleware
220-
app.UseAuthentication();
221-
app.UseAuthorization();
222-
223-
app.UseCors();
224-
225-
app.UseEndpoints(endpoints =>
226-
{
227-
endpoints.MapHub<Service.StatusHub>("/api/status");
228-
endpoints.MapControllers();
229-
230-
#if DEBUG
231-
endpoints.MapGet("/debug/routes", (IEnumerable<EndpointDataSource> endpointSources) =>
232-
{
233-
var sb = new System.Text.StringBuilder();
234-
var endpoints = endpointSources.SelectMany(es => es.Endpoints);
235-
foreach (var endpoint in endpoints)
236-
{
237-
if (endpoint is RouteEndpoint routeEndpoint)
238-
{
239-
sb.AppendLine($"{routeEndpoint.DisplayName} {routeEndpoint.RoutePattern.RawText}");
240-
}
241-
}
242-
243-
return sb.ToString();
244-
});
245-
#endif
246-
});
246+
return false;
247247
}
248248
}
249249
}

0 commit comments

Comments
 (0)