1 using Microsoft.Extensions.DependencyInjection;
2 using Microsoft.Net.Http.Headers;
3 using Microsoft.OpenApi.Any;
4 using Microsoft.OpenApi.Models;
5 using Swashbuckle.AspNetCore.SwaggerGen;
7 using System.Collections.Generic;
25 const string PasswordSecuritySchemeId =
"Password_Login_Scheme";
30 const string TokenSecuritySchemeId =
"Token_Authorization_Scheme";
34 var errorMessageContent =
new Dictionary<string, OpenApiMediaType>
40 Schema =
new OpenApiSchema
42 Reference =
new OpenApiReference
45 Type = ReferenceType.Schema
52 void AddDefaultResponse(HttpStatusCode code, OpenApiResponse concrete)
54 string responseKey = $
"{(int)code}";
56 document.Components.Responses.Add(responseKey, concrete);
58 var referenceResponse =
new OpenApiResponse
60 Reference =
new OpenApiReference
62 Type = ReferenceType.Response,
67 foreach (var operation
in document.Paths.SelectMany(path => path.Value.Operations))
68 operation.Value.Responses.TryAdd(responseKey, referenceResponse);
71 AddDefaultResponse(HttpStatusCode.BadRequest,
new OpenApiResponse
73 Description =
"A badly formatted request was made. See error message for details.",
74 Content = errorMessageContent,
77 AddDefaultResponse(HttpStatusCode.Unauthorized,
new OpenApiResponse
79 Description =
"No/invalid token provided." 82 AddDefaultResponse(HttpStatusCode.Forbidden,
new OpenApiResponse
84 Description =
"User lacks sufficient permissions for the operation." 87 AddDefaultResponse(HttpStatusCode.Conflict,
new OpenApiResponse
89 Description =
"A data integrity check failed while performing the operation. See error message for details.",
90 Content = errorMessageContent
93 AddDefaultResponse(HttpStatusCode.InternalServerError,
new OpenApiResponse
95 Description =
"The server encountered an unhandled error. See error message for details.",
96 Content = errorMessageContent
99 AddDefaultResponse(HttpStatusCode.ServiceUnavailable,
new OpenApiResponse
101 Description =
"The server may be starting up or shutting down." 104 AddDefaultResponse(HttpStatusCode.NotImplemented,
new OpenApiResponse
106 Description =
"This operation requires POSIX system identites to be implemented. See https://github.com/tgstation/tgstation-server/issues/709",
107 Content = errorMessageContent
117 public static void Configure(SwaggerGenOptions swaggerGenOptions,
string assemblyDocumentationPath,
string apiDocumentationPath)
119 swaggerGenOptions.SwaggerDoc(
129 swaggerGenOptions.IncludeXmlComments(assemblyDocumentationPath);
130 swaggerGenOptions.IncludeXmlComments(apiDocumentationPath);
136 swaggerGenOptions.CustomSchemaIds(type =>
138 if (type == typeof(Api.Models.Internal.User))
139 return "ShallowUser";
144 swaggerGenOptions.AddSecurityDefinition(PasswordSecuritySchemeId,
new OpenApiSecurityScheme
146 In = ParameterLocation.Header,
147 Type = SecuritySchemeType.Http,
148 Name = HeaderNames.Authorization,
152 swaggerGenOptions.AddSecurityDefinition(TokenSecuritySchemeId,
new OpenApiSecurityScheme
154 BearerFormat =
"JWT",
155 In = ParameterLocation.Header,
156 Type = SecuritySchemeType.Http,
157 Name = HeaderNames.Authorization,
163 public void Apply(OpenApiOperation operation, OperationFilterContext context)
165 if (operation == null)
166 throw new ArgumentNullException(nameof(operation));
168 throw new ArgumentNullException(nameof(context));
170 operation.OperationId = $
"{context.MethodInfo.DeclaringType.Name}.{context.MethodInfo.Name}";
172 var authAttributes = context
175 .GetCustomAttributes(
true)
179 .GetCustomAttributes(
true))
182 if (authAttributes.Any())
184 var tokenScheme =
new OpenApiSecurityScheme
186 Reference =
new OpenApiReference
188 Type = ReferenceType.SecurityScheme,
189 Id = TokenSecuritySchemeId
193 operation.Security =
new List<OpenApiSecurityRequirement>
195 new OpenApiSecurityRequirement
205 operation.Parameters.Add(
new OpenApiParameter
207 Reference =
new OpenApiReference
209 Type = ReferenceType.Parameter,
217 var passwordScheme =
new OpenApiSecurityScheme
219 Reference =
new OpenApiReference
221 Type = ReferenceType.SecurityScheme,
222 Id = PasswordSecuritySchemeId
226 operation.Security =
new List<OpenApiSecurityRequirement>
228 new OpenApiSecurityRequirement
240 public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
242 if (swaggerDoc == null)
243 throw new ArgumentNullException(nameof(swaggerDoc));
245 throw new ArgumentNullException(nameof(context));
249 In = ParameterLocation.Header,
250 Name = ApiHeaders.InstanceIdHeader,
251 Description =
"The instance ID being accessed",
253 Style = ParameterStyle.Simple,
254 Schema = new OpenApiSchema
260 var productHeaderSchema =
new OpenApiSchema
263 Format =
"productheader" 268 In = ParameterLocation.Header,
269 Name = ApiHeaders.ApiVersionHeader,
270 Description =
"The API version being used in the form \"Tgstation.Server.Api/[API version]\"",
272 Style = ParameterStyle.Simple,
273 Example = new OpenApiString($
"Tgstation.Server.Api/{ApiHeaders.Version}"),
274 Schema = productHeaderSchema
277 swaggerDoc.Components.Parameters.Add(HeaderNames.UserAgent,
new OpenApiParameter
279 In = ParameterLocation.Header,
280 Name = HeaderNames.UserAgent,
281 Description =
"The user agent of the calling client.",
283 Style = ParameterStyle.Simple,
284 Example = new OpenApiString(
"Your-user-agent/1.0.0.0"),
285 Schema = productHeaderSchema
288 string bridgeOperationPath = null;
289 foreach (var path
in swaggerDoc.Paths)
290 foreach (var operation
in path.Value.Operations.Select(x => x.Value))
292 if (operation.OperationId.Equals(
"BridgeController.Process", StringComparison.Ordinal))
294 bridgeOperationPath = path.Key;
298 operation.Parameters.Add(
new OpenApiParameter
300 Reference =
new OpenApiReference
302 Type = ReferenceType.Parameter,
307 operation.Parameters.Add(
new OpenApiParameter
309 Reference =
new OpenApiReference
311 Type = ReferenceType.Parameter,
312 Id = HeaderNames.UserAgent
317 swaggerDoc.Paths.Remove(bridgeOperationPath);
319 AddDefaultResponses(swaggerDoc);
323 public void Apply(OpenApiSchema schema, SchemaFilterContext context)
326 throw new ArgumentNullException(nameof(schema));
328 throw new ArgumentNullException(nameof(context));
331 schema.Required.Clear();
333 if (!schema.Enum?.Any() ??
false)
337 Type enumType = context.Type.IsConstructedGenericType
338 ? context.Type.GenericTypeArguments.First()
static bool IsInstanceRight(RightsType rightsType)
Check if a given rightsType is meant for an Models.Instance
void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
Use server authentication
void Apply(OpenApiSchema schema, SchemaFilterContext context)
Helper for using the AuthorizeAttribute with the Api.Rights system
void Apply(OpenApiOperation operation, OperationFilterContext context)
Implements various filters for Swashbuckle.
static void Configure(SwaggerGenOptions swaggerGenOptions, string assemblyDocumentationPath, string apiDocumentationPath)
Configure the swagger settings.
static void Apply(OpenApiSchema openApiSchema, Type enumType)
Applies the extension to a give openApiSchema .
static void AddDefaultResponses(OpenApiDocument document)
Implements the "x-enum-varnames" OpenAPI 3.0 extension.
Represents an error message returned by the server