1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 23:52:50 -05:00

Revert filescoped (#2227)

* Revert "Add git blame entry (#2226)"

This reverts commit 239286737d.

* Revert "Turn on file scoped namespaces (#2225)"

This reverts commit 34fb4cca2a.
This commit is contained in:
Justin Baur
2022-08-29 15:53:48 -04:00
committed by GitHub
parent 239286737d
commit bae03feffe
1208 changed files with 74317 additions and 73126 deletions

View File

@ -1,12 +1,13 @@
using Microsoft.AspNetCore.Mvc.ApplicationModels;
namespace Bit.Api.Utilities;
public class ApiExplorerGroupConvention : IControllerModelConvention
namespace Bit.Api.Utilities
{
public void Apply(ControllerModel controller)
public class ApiExplorerGroupConvention : IControllerModelConvention
{
var controllerNamespace = controller.ControllerType.Namespace;
controller.ApiExplorer.GroupName = controllerNamespace.Contains(".Public.") ? "public" : "internal";
public void Apply(ControllerModel controller)
{
var controllerNamespace = controller.ControllerType.Namespace;
controller.ApiExplorer.GroupName = controllerNamespace.Contains(".Public.") ? "public" : "internal";
}
}
}

View File

@ -4,69 +4,70 @@ using Azure.Messaging.EventGrid.SystemEvents;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Utilities;
public static class ApiHelpers
namespace Bit.Api.Utilities
{
public static string EventGridKey { get; set; }
public async static Task<T> ReadJsonFileFromBody<T>(HttpContext httpContext, IFormFile file, long maxSize = 51200)
public static class ApiHelpers
{
T obj = default(T);
if (file != null && httpContext.Request.ContentLength.HasValue && httpContext.Request.ContentLength.Value <= maxSize)
public static string EventGridKey { get; set; }
public async static Task<T> ReadJsonFileFromBody<T>(HttpContext httpContext, IFormFile file, long maxSize = 51200)
{
try
T obj = default(T);
if (file != null && httpContext.Request.ContentLength.HasValue && httpContext.Request.ContentLength.Value <= maxSize)
{
using var stream = file.OpenReadStream();
obj = await JsonSerializer.DeserializeAsync<T>(stream, JsonHelpers.IgnoreCase);
}
catch { }
}
return obj;
}
/// <summary>
/// Validates Azure event subscription and calls the appropriate event handler. Responds HttpOk.
/// </summary>
/// <param name="request">HttpRequest received from Azure</param>
/// <param name="eventTypeHandlers">Dictionary of eventType strings and their associated handlers.</param>
/// <returns>OkObjectResult</returns>
/// <remarks>Reference https://docs.microsoft.com/en-us/azure/event-grid/receive-events</remarks>
public async static Task<ObjectResult> HandleAzureEvents(HttpRequest request,
Dictionary<string, Func<EventGridEvent, Task>> eventTypeHandlers)
{
var queryKey = request.Query["key"];
if (!CoreHelpers.FixedTimeEquals(queryKey, EventGridKey))
{
return new UnauthorizedObjectResult("Authentication failed. Please use a valid key.");
}
var response = string.Empty;
var requestData = await BinaryData.FromStreamAsync(request.Body);
var eventGridEvents = EventGridEvent.ParseMany(requestData);
foreach (var eventGridEvent in eventGridEvents)
{
if (eventGridEvent.TryGetSystemEventData(out object systemEvent))
{
if (systemEvent is SubscriptionValidationEventData eventData)
try
{
// Might want to enable additional validation: subject, topic etc.
var responseData = new SubscriptionValidationResponse()
{
ValidationResponse = eventData.ValidationCode
};
using var stream = file.OpenReadStream();
obj = await JsonSerializer.DeserializeAsync<T>(stream, JsonHelpers.IgnoreCase);
}
catch { }
}
return new OkObjectResult(responseData);
return obj;
}
/// <summary>
/// Validates Azure event subscription and calls the appropriate event handler. Responds HttpOk.
/// </summary>
/// <param name="request">HttpRequest received from Azure</param>
/// <param name="eventTypeHandlers">Dictionary of eventType strings and their associated handlers.</param>
/// <returns>OkObjectResult</returns>
/// <remarks>Reference https://docs.microsoft.com/en-us/azure/event-grid/receive-events</remarks>
public async static Task<ObjectResult> HandleAzureEvents(HttpRequest request,
Dictionary<string, Func<EventGridEvent, Task>> eventTypeHandlers)
{
var queryKey = request.Query["key"];
if (!CoreHelpers.FixedTimeEquals(queryKey, EventGridKey))
{
return new UnauthorizedObjectResult("Authentication failed. Please use a valid key.");
}
var response = string.Empty;
var requestData = await BinaryData.FromStreamAsync(request.Body);
var eventGridEvents = EventGridEvent.ParseMany(requestData);
foreach (var eventGridEvent in eventGridEvents)
{
if (eventGridEvent.TryGetSystemEventData(out object systemEvent))
{
if (systemEvent is SubscriptionValidationEventData eventData)
{
// Might want to enable additional validation: subject, topic etc.
var responseData = new SubscriptionValidationResponse()
{
ValidationResponse = eventData.ValidationCode
};
return new OkObjectResult(responseData);
}
}
if (eventTypeHandlers.ContainsKey(eventGridEvent.EventType))
{
await eventTypeHandlers[eventGridEvent.EventType](eventGridEvent);
}
}
if (eventTypeHandlers.ContainsKey(eventGridEvent.EventType))
{
await eventTypeHandlers[eventGridEvent.EventType](eventGridEvent);
}
return new OkObjectResult(response);
}
return new OkObjectResult(response);
}
}

View File

@ -1,20 +1,21 @@
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Bit.Api.Utilities;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
namespace Bit.Api.Utilities
{
public void OnResourceExecuting(ResourceExecutingContext context)
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
{
var factories = context.ValueProviderFactories;
factories.RemoveType<FormValueProviderFactory>();
factories.RemoveType<FormFileValueProviderFactory>();
factories.RemoveType<JQueryFormValueProviderFactory>();
}
public void OnResourceExecuting(ResourceExecutingContext context)
{
var factories = context.ValueProviderFactories;
factories.RemoveType<FormValueProviderFactory>();
factories.RemoveType<FormFileValueProviderFactory>();
factories.RemoveType<JQueryFormValueProviderFactory>();
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
}
}

View File

@ -6,116 +6,117 @@ using Microsoft.IdentityModel.Tokens;
using Stripe;
using InternalApi = Bit.Core.Models.Api;
namespace Bit.Api.Utilities;
public class ExceptionHandlerFilterAttribute : ExceptionFilterAttribute
namespace Bit.Api.Utilities
{
private readonly bool _publicApi;
public ExceptionHandlerFilterAttribute(bool publicApi)
public class ExceptionHandlerFilterAttribute : ExceptionFilterAttribute
{
_publicApi = publicApi;
}
private readonly bool _publicApi;
public override void OnException(ExceptionContext context)
{
var errorMessage = "An error has occurred.";
var exception = context.Exception;
if (exception == null)
public ExceptionHandlerFilterAttribute(bool publicApi)
{
// Should never happen.
return;
_publicApi = publicApi;
}
ErrorResponseModel publicErrorModel = null;
InternalApi.ErrorResponseModel internalErrorModel = null;
if (exception is BadRequestException badRequestException)
public override void OnException(ExceptionContext context)
{
context.HttpContext.Response.StatusCode = 400;
if (badRequestException.ModelState != null)
var errorMessage = "An error has occurred.";
var exception = context.Exception;
if (exception == null)
{
if (_publicApi)
// Should never happen.
return;
}
ErrorResponseModel publicErrorModel = null;
InternalApi.ErrorResponseModel internalErrorModel = null;
if (exception is BadRequestException badRequestException)
{
context.HttpContext.Response.StatusCode = 400;
if (badRequestException.ModelState != null)
{
publicErrorModel = new ErrorResponseModel(badRequestException.ModelState);
if (_publicApi)
{
publicErrorModel = new ErrorResponseModel(badRequestException.ModelState);
}
else
{
internalErrorModel = new InternalApi.ErrorResponseModel(badRequestException.ModelState);
}
}
else
{
internalErrorModel = new InternalApi.ErrorResponseModel(badRequestException.ModelState);
errorMessage = badRequestException.Message;
}
}
else if (exception is StripeException stripeException && stripeException?.StripeError?.Type == "card_error")
{
context.HttpContext.Response.StatusCode = 400;
if (_publicApi)
{
publicErrorModel = new ErrorResponseModel(stripeException.StripeError.Param,
stripeException.Message);
}
else
{
internalErrorModel = new InternalApi.ErrorResponseModel(stripeException.StripeError.Param,
stripeException.Message);
}
}
else if (exception is GatewayException)
{
errorMessage = exception.Message;
context.HttpContext.Response.StatusCode = 400;
}
else if (exception is NotSupportedException && !string.IsNullOrWhiteSpace(exception.Message))
{
errorMessage = exception.Message;
context.HttpContext.Response.StatusCode = 400;
}
else if (exception is ApplicationException)
{
context.HttpContext.Response.StatusCode = 402;
}
else if (exception is NotFoundException)
{
errorMessage = "Resource not found.";
context.HttpContext.Response.StatusCode = 404;
}
else if (exception is SecurityTokenValidationException)
{
errorMessage = "Invalid token.";
context.HttpContext.Response.StatusCode = 403;
}
else if (exception is UnauthorizedAccessException)
{
errorMessage = "Unauthorized.";
context.HttpContext.Response.StatusCode = 401;
}
else
{
errorMessage = badRequestException.Message;
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<ExceptionHandlerFilterAttribute>>();
logger.LogError(0, exception, exception.Message);
errorMessage = "An unhandled server error has occurred.";
context.HttpContext.Response.StatusCode = 500;
}
}
else if (exception is StripeException stripeException && stripeException?.StripeError?.Type == "card_error")
{
context.HttpContext.Response.StatusCode = 400;
if (_publicApi)
{
publicErrorModel = new ErrorResponseModel(stripeException.StripeError.Param,
stripeException.Message);
var errorModel = publicErrorModel ?? new ErrorResponseModel(errorMessage);
context.Result = new ObjectResult(errorModel);
}
else
{
internalErrorModel = new InternalApi.ErrorResponseModel(stripeException.StripeError.Param,
stripeException.Message);
var errorModel = internalErrorModel ?? new InternalApi.ErrorResponseModel(errorMessage);
var env = context.HttpContext.RequestServices.GetRequiredService<IWebHostEnvironment>();
if (env.IsDevelopment())
{
errorModel.ExceptionMessage = exception.Message;
errorModel.ExceptionStackTrace = exception.StackTrace;
errorModel.InnerExceptionMessage = exception?.InnerException?.Message;
}
context.Result = new ObjectResult(errorModel);
}
}
else if (exception is GatewayException)
{
errorMessage = exception.Message;
context.HttpContext.Response.StatusCode = 400;
}
else if (exception is NotSupportedException && !string.IsNullOrWhiteSpace(exception.Message))
{
errorMessage = exception.Message;
context.HttpContext.Response.StatusCode = 400;
}
else if (exception is ApplicationException)
{
context.HttpContext.Response.StatusCode = 402;
}
else if (exception is NotFoundException)
{
errorMessage = "Resource not found.";
context.HttpContext.Response.StatusCode = 404;
}
else if (exception is SecurityTokenValidationException)
{
errorMessage = "Invalid token.";
context.HttpContext.Response.StatusCode = 403;
}
else if (exception is UnauthorizedAccessException)
{
errorMessage = "Unauthorized.";
context.HttpContext.Response.StatusCode = 401;
}
else
{
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<ExceptionHandlerFilterAttribute>>();
logger.LogError(0, exception, exception.Message);
errorMessage = "An unhandled server error has occurred.";
context.HttpContext.Response.StatusCode = 500;
}
if (_publicApi)
{
var errorModel = publicErrorModel ?? new ErrorResponseModel(errorMessage);
context.Result = new ObjectResult(errorModel);
}
else
{
var errorModel = internalErrorModel ?? new InternalApi.ErrorResponseModel(errorMessage);
var env = context.HttpContext.RequestServices.GetRequiredService<IWebHostEnvironment>();
if (env.IsDevelopment())
{
errorModel.ExceptionMessage = exception.Message;
errorModel.ExceptionStackTrace = exception.StackTrace;
errorModel.InnerExceptionMessage = exception?.InnerException?.Message;
}
context.Result = new ObjectResult(errorModel);
}
}
}

View File

@ -3,26 +3,27 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using InternalApi = Bit.Core.Models.Api;
namespace Bit.Api.Utilities;
public class ModelStateValidationFilterAttribute : SharedWeb.Utilities.ModelStateValidationFilterAttribute
namespace Bit.Api.Utilities
{
private readonly bool _publicApi;
public ModelStateValidationFilterAttribute(bool publicApi)
public class ModelStateValidationFilterAttribute : SharedWeb.Utilities.ModelStateValidationFilterAttribute
{
_publicApi = publicApi;
}
private readonly bool _publicApi;
protected override void OnModelStateInvalid(ActionExecutingContext context)
{
if (_publicApi)
public ModelStateValidationFilterAttribute(bool publicApi)
{
context.Result = new BadRequestObjectResult(new ErrorResponseModel(context.ModelState));
_publicApi = publicApi;
}
else
protected override void OnModelStateInvalid(ActionExecutingContext context)
{
context.Result = new BadRequestObjectResult(new InternalApi.ErrorResponseModel(context.ModelState));
if (_publicApi)
{
context.Result = new BadRequestObjectResult(new ErrorResponseModel(context.ModelState));
}
else
{
context.Result = new BadRequestObjectResult(new InternalApi.ErrorResponseModel(context.ModelState));
}
}
}
}

View File

@ -5,41 +5,75 @@ using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Bit.Api.Utilities;
public static class MultipartFormDataHelper
namespace Bit.Api.Utilities
{
private static readonly FormOptions _defaultFormOptions = new FormOptions();
public static async Task GetFileAsync(this HttpRequest request, Func<Stream, string, string, Task> callback)
public static class MultipartFormDataHelper
{
var boundary = GetBoundary(MediaTypeHeaderValue.Parse(request.ContentType),
_defaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, request.Body);
private static readonly FormOptions _defaultFormOptions = new FormOptions();
var firstSection = await reader.ReadNextSectionAsync();
if (firstSection != null)
public static async Task GetFileAsync(this HttpRequest request, Func<Stream, string, string, Task> callback)
{
if (ContentDispositionHeaderValue.TryParse(firstSection.ContentDisposition, out var firstContent))
var boundary = GetBoundary(MediaTypeHeaderValue.Parse(request.ContentType),
_defaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, request.Body);
var firstSection = await reader.ReadNextSectionAsync();
if (firstSection != null)
{
if (HasFileContentDisposition(firstContent))
if (ContentDispositionHeaderValue.TryParse(firstSection.ContentDisposition, out var firstContent))
{
// Old style with just data
var fileName = HeaderUtilities.RemoveQuotes(firstContent.FileName).ToString();
using (firstSection.Body)
if (HasFileContentDisposition(firstContent))
{
await callback(firstSection.Body, fileName, null);
// Old style with just data
var fileName = HeaderUtilities.RemoveQuotes(firstContent.FileName).ToString();
using (firstSection.Body)
{
await callback(firstSection.Body, fileName, null);
}
}
else if (HasDispositionName(firstContent, "key"))
{
// New style with key, then data
string key = null;
using (var sr = new StreamReader(firstSection.Body))
{
key = await sr.ReadToEndAsync();
}
var secondSection = await reader.ReadNextSectionAsync();
if (secondSection != null)
{
if (ContentDispositionHeaderValue.TryParse(secondSection.ContentDisposition,
out var secondContent) && HasFileContentDisposition(secondContent))
{
var fileName = HeaderUtilities.RemoveQuotes(secondContent.FileName).ToString();
using (secondSection.Body)
{
await callback(secondSection.Body, fileName, key);
}
}
secondSection = null;
}
}
}
else if (HasDispositionName(firstContent, "key"))
{
// New style with key, then data
string key = null;
using (var sr = new StreamReader(firstSection.Body))
{
key = await sr.ReadToEndAsync();
}
firstSection = null;
}
}
public static async Task GetSendFileAsync(this HttpRequest request, Func<Stream, string,
SendRequestModel, Task> callback)
{
var boundary = GetBoundary(MediaTypeHeaderValue.Parse(request.ContentType),
_defaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, request.Body);
var firstSection = await reader.ReadNextSectionAsync();
if (firstSection != null)
{
if (ContentDispositionHeaderValue.TryParse(firstSection.ContentDisposition, out _))
{
var secondSection = await reader.ReadNextSectionAsync();
if (secondSection != null)
{
@ -49,102 +83,69 @@ public static class MultipartFormDataHelper
var fileName = HeaderUtilities.RemoveQuotes(secondContent.FileName).ToString();
using (secondSection.Body)
{
await callback(secondSection.Body, fileName, key);
var model = await JsonSerializer.DeserializeAsync<SendRequestModel>(firstSection.Body);
await callback(secondSection.Body, fileName, model);
}
}
secondSection = null;
}
}
firstSection = null;
}
firstSection = null;
}
}
public static async Task GetSendFileAsync(this HttpRequest request, Func<Stream, string,
SendRequestModel, Task> callback)
{
var boundary = GetBoundary(MediaTypeHeaderValue.Parse(request.ContentType),
_defaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, request.Body);
var firstSection = await reader.ReadNextSectionAsync();
if (firstSection != null)
public static async Task GetFileAsync(this HttpRequest request, Func<Stream, Task> callback)
{
if (ContentDispositionHeaderValue.TryParse(firstSection.ContentDisposition, out _))
var boundary = GetBoundary(MediaTypeHeaderValue.Parse(request.ContentType),
_defaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, request.Body);
var dataSection = await reader.ReadNextSectionAsync();
if (dataSection != null)
{
var secondSection = await reader.ReadNextSectionAsync();
if (secondSection != null)
if (ContentDispositionHeaderValue.TryParse(dataSection.ContentDisposition, out var dataContent)
&& HasFileContentDisposition(dataContent))
{
if (ContentDispositionHeaderValue.TryParse(secondSection.ContentDisposition,
out var secondContent) && HasFileContentDisposition(secondContent))
using (dataSection.Body)
{
var fileName = HeaderUtilities.RemoveQuotes(secondContent.FileName).ToString();
using (secondSection.Body)
{
var model = await JsonSerializer.DeserializeAsync<SendRequestModel>(firstSection.Body);
await callback(secondSection.Body, fileName, model);
}
await callback(dataSection.Body);
}
secondSection = null;
}
dataSection = null;
}
firstSection = null;
}
}
public static async Task GetFileAsync(this HttpRequest request, Func<Stream, Task> callback)
{
var boundary = GetBoundary(MediaTypeHeaderValue.Parse(request.ContentType),
_defaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, request.Body);
var dataSection = await reader.ReadNextSectionAsync();
if (dataSection != null)
private static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)
{
if (ContentDispositionHeaderValue.TryParse(dataSection.ContentDisposition, out var dataContent)
&& HasFileContentDisposition(dataContent))
var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary);
if (StringSegment.IsNullOrEmpty(boundary))
{
using (dataSection.Body)
{
await callback(dataSection.Body);
}
throw new InvalidDataException("Missing content-type boundary.");
}
dataSection = null;
if (boundary.Length > lengthLimit)
{
throw new InvalidDataException($"Multipart boundary length limit {lengthLimit} exceeded.");
}
return boundary.ToString();
}
}
private static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)
{
var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary);
if (StringSegment.IsNullOrEmpty(boundary))
private static bool HasFileContentDisposition(ContentDispositionHeaderValue content)
{
throw new InvalidDataException("Missing content-type boundary.");
// Content-Disposition: form-data; name="data"; filename="Misc 002.jpg"
return content != null && content.DispositionType.Equals("form-data") &&
(!StringSegment.IsNullOrEmpty(content.FileName) || !StringSegment.IsNullOrEmpty(content.FileNameStar));
}
if (boundary.Length > lengthLimit)
private static bool HasDispositionName(ContentDispositionHeaderValue content, string name)
{
throw new InvalidDataException($"Multipart boundary length limit {lengthLimit} exceeded.");
// Content-Disposition: form-data; name="key";
return content != null && content.DispositionType.Equals("form-data") && content.Name == name;
}
return boundary.ToString();
}
private static bool HasFileContentDisposition(ContentDispositionHeaderValue content)
{
// Content-Disposition: form-data; name="data"; filename="Misc 002.jpg"
return content != null && content.DispositionType.Equals("form-data") &&
(!StringSegment.IsNullOrEmpty(content.FileName) || !StringSegment.IsNullOrEmpty(content.FileNameStar));
}
private static bool HasDispositionName(ContentDispositionHeaderValue content, string name)
{
// Content-Disposition: form-data; name="key";
return content != null && content.DispositionType.Equals("form-data") && content.Name == name;
}
}

View File

@ -1,14 +1,15 @@
using Microsoft.AspNetCore.Mvc.ApplicationModels;
namespace Bit.Api.Utilities;
public class PublicApiControllersModelConvention : IControllerModelConvention
namespace Bit.Api.Utilities
{
public void Apply(ControllerModel controller)
public class PublicApiControllersModelConvention : IControllerModelConvention
{
var controllerNamespace = controller.ControllerType.Namespace;
var publicApi = controllerNamespace.Contains(".Public.");
controller.Filters.Add(new ExceptionHandlerFilterAttribute(publicApi));
controller.Filters.Add(new ModelStateValidationFilterAttribute(publicApi));
public void Apply(ControllerModel controller)
{
var controllerNamespace = controller.ControllerType.Namespace;
var publicApi = controllerNamespace.Contains(".Public.");
controller.Filters.Add(new ExceptionHandlerFilterAttribute(publicApi));
controller.Filters.Add(new ModelStateValidationFilterAttribute(publicApi));
}
}
}

View File

@ -1,20 +1,22 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Bit.Api.Utilities;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class SecretsManagerAttribute : Attribute, IResourceFilter
namespace Bit.Api.Utilities
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
var env = context.HttpContext.RequestServices.GetService<IHostEnvironment>();
if (!env.IsDevelopment())
{
context.Result = new NotFoundResult();
}
}
public void OnResourceExecuted(ResourceExecutedContext context) { }
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class SecretsManagerAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
var env = context.HttpContext.RequestServices.GetService<IHostEnvironment>();
if (!env.IsDevelopment())
{
context.Result = new NotFoundResult();
}
}
public void OnResourceExecuted(ResourceExecutedContext context) { }
}
}

View File

@ -1,71 +1,72 @@
using Bit.Core.Settings;
using Microsoft.OpenApi.Models;
namespace Bit.Api.Utilities;
public static class ServiceCollectionExtensions
namespace Bit.Api.Utilities
{
public static void AddSwagger(this IServiceCollection services, GlobalSettings globalSettings)
public static class ServiceCollectionExtensions
{
services.AddSwaggerGen(config =>
public static void AddSwagger(this IServiceCollection services, GlobalSettings globalSettings)
{
config.SwaggerDoc("public", new OpenApiInfo
services.AddSwaggerGen(config =>
{
Title = "Bitwarden Public API",
Version = "latest",
Contact = new OpenApiContact
config.SwaggerDoc("public", new OpenApiInfo
{
Name = "Bitwarden Support",
Url = new Uri("https://bitwarden.com"),
Email = "support@bitwarden.com"
},
Description = "The Bitwarden public APIs.",
License = new OpenApiLicense
{
Name = "GNU Affero General Public License v3.0",
Url = new Uri("https://github.com/bitwarden/server/blob/master/LICENSE.txt")
}
});
config.SwaggerDoc("internal", new OpenApiInfo { Title = "Bitwarden Internal API", Version = "latest" });
config.AddSecurityDefinition("OAuth2 Client Credentials", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
ClientCredentials = new OpenApiOAuthFlow
Title = "Bitwarden Public API",
Version = "latest",
Contact = new OpenApiContact
{
TokenUrl = new Uri($"{globalSettings.BaseServiceUri.Identity}/connect/token"),
Scopes = new Dictionary<string, string>
{
{ "api.organization", "Organization APIs" },
},
}
},
});
config.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "OAuth2 Client Credentials"
},
Name = "Bitwarden Support",
Url = new Uri("https://bitwarden.com"),
Email = "support@bitwarden.com"
},
new[] { "api.organization" }
}
Description = "The Bitwarden public APIs.",
License = new OpenApiLicense
{
Name = "GNU Affero General Public License v3.0",
Url = new Uri("https://github.com/bitwarden/server/blob/master/LICENSE.txt")
}
});
config.SwaggerDoc("internal", new OpenApiInfo { Title = "Bitwarden Internal API", Version = "latest" });
config.AddSecurityDefinition("OAuth2 Client Credentials", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
ClientCredentials = new OpenApiOAuthFlow
{
TokenUrl = new Uri($"{globalSettings.BaseServiceUri.Identity}/connect/token"),
Scopes = new Dictionary<string, string>
{
{ "api.organization", "Organization APIs" },
},
}
},
});
config.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "OAuth2 Client Credentials"
},
},
new[] { "api.organization" }
}
});
config.DescribeAllParametersInCamelCase();
// config.UseReferencedDefinitionsForEnums();
var apiFilePath = Path.Combine(AppContext.BaseDirectory, "Api.xml");
config.IncludeXmlComments(apiFilePath, true);
var coreFilePath = Path.Combine(AppContext.BaseDirectory, "Core.xml");
config.IncludeXmlComments(coreFilePath);
});
config.DescribeAllParametersInCamelCase();
// config.UseReferencedDefinitionsForEnums();
var apiFilePath = Path.Combine(AppContext.BaseDirectory, "Api.xml");
config.IncludeXmlComments(apiFilePath, true);
var coreFilePath = Path.Combine(AppContext.BaseDirectory, "Core.xml");
config.IncludeXmlComments(coreFilePath);
});
}
}
}