@@ -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