Swashbuckle: Customize endpoint path (AspNetCore) - swashbuckle

I have this setup where I want to organize multiple APIs served by the same application
.
For each api I get the prefix from the subdomain and redirect it to the application server, example:
user.api.example.com/v1/profile --> api.example.com/user/v1/profile
admin.api.example.com/v1/companies --> api.example.com/admin/v1/companies
With this setup, I need to remove the path prefix ("/user", "/admin") when generating the swagger json file.
Is it possible to configure a function that will manipulate the path for each endpoint before generating the json file?
I only want to alter what goes into the swagger json file, not the actual endpoint path!

Document Filters were the answers to that particular need:
public class PathPrefixDocumentFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
var editedPaths = new OpenApiPaths();
foreach(var kv in swaggerDoc.Paths)
{
var newKey = string.Join("/", kv.Key.Split('/').Skip(2));
editedPaths.Add("/"+newKey, kv.Value);
}
swaggerDoc.Paths = editedPaths;
var a = swaggerDoc.Paths;
}
}

Related

Virtual Hosting on Next.js app with Apollo GraphQL

I have a webapp made with Next.js and Apollo as show in example with-apollo. I want to serve multiple domains with my webapp (name-based virtual hosting). Unfortunately HttpLink of ApolloClient requires absolute server URL with domain but this makes backend app unable to recognize domain which user really visited. Is there a way to configure HttpLink with a dynamic URL based on real request or use relative URL or anything else?
Either use an Apollo Link to intercept the query and set uri property on the context
const authMiddleware = setContext((operation, { uri }) => {
return refreshToken().then(res => ({
uri: this.getURI()
})
}))
Or intercept the request with Angular's HttpClient interceptor and change the endpoint.
https://github.com/apollographql/apollo-angular/tree/master/packages/apollo-angular-link-http#options
Source: Updating uri of apollo client instance
The NextPageContext object passed to getInitialProps includes the req object when called on the server-side. So you can do something like:
WithApollo.getInitialProps = async ctx => {
const { AppTree, req } = ctx
const linkBaseUrl = req ? req.protocol + '://' + req.get('host') : ''
...
}
You can then pass this base url down to createApolloClient along with the initial state and prepend your HttpLink's url with it. On the client side, this will prepend an empty string (you only need the full URL on the server).

How do I remove this Artifact

I am using swashbuckle to document my API. When I open the swagger page, I get this item highlighted in the image. How do I get rid of it or replace it? Using v5 of the swashbuckle tooling
Here are the section.
Startup.cs - Services
// Register the Swagger generator, defining 1 or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v0", new OpenApiInfo
{
Version = "v0",
Title = "LTK Manager",
Description = "Sample Description",
TermsOfService = new Uri("https://example.com/terms"),
});
// Set the comments path for the Swagger JSON and UI.
var xmlFile = $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
c.IgnoreObsoleteActions();
c.IgnoreObsoleteProperties();
});
Startup.cs - Configuration
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("../swagger/v0/swagger.json", "LTK Manager V0 (alpha)");
c.DisplayOperationId();
});
Route Decoration
/// <summary>
/// Retrieves a parameter store value from an account/region
/// </summary>
/// <remarks>Awesomeness!</remarks>
/// <response code="200">Returns an Object Containing the response from the API.</response>
[HttpPost]
[Route("getParamStoreValue")]
[ProducesResponseType(200)]
public IActionResult getKey([FromBody] ParamStoreRequest req)

AWS S3 Presigned URL with other query parameters

I create a pre-signed URL and get back something like
https://s3.amazonaws.com/MyBucket/MyItem/
?X-Amz-Security-Token=TOKEN
&X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Date=20171206T014837Z
&X-Amz-SignedHeaders=host
&X-Amz-Expires=3600
&X-Amz-Credential=CREDENTIAL
&X-Amz-Signature=SIGNATURE
I can now curl this no problem. However, if I now add another query parameter, I will get back a 403, i.e.
https://s3.amazonaws.com/MyBucket/MyItem/
?X-Amz-Security-Token=TOKEN
&X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Date=20171206T014837Z
&X-Amz-SignedHeaders=host
&X-Amz-Expires=3600
&X-Amz-Credential=CREDENTIAL
&X-Amz-Signature=SIGNATURE
&Foo=123
How come? Is it possible to generate a pre-signed url that supports custom queries?
It seems to be technically feasible to insert custom query parameters into a v4 pre-signed URL, before it is signed, but not all of the AWS SDKs expose a way to do this.
Here's an example of a roundabout way to do this with the AWS JavaScript SDK:
const AWS = require('aws-sdk');
var s3 = new AWS.S3({region: 'us-east-1', signatureVersion: 'v4'});
var req = s3.getObject({Bucket: 'mybucket', Key: 'mykey'});
req.on('build', () => { req.httpRequest.path += '?session=ABC123'; });
console.log(req.presign());
I've tried this with custom query parameters that begin with X- and without it. Both appeared to work fine. I've tried with multiple query parameters (?a=1&b=2) and that worked too.
The customized pre-signed URLs work correctly (I can use them to get S3 objects) and the query parameters make it into CloudWatch Logs so can be used for correlation purposes.
Note that if you want to supply a custom expiration time, then do it as follows:
const Expires = 120;
const url = req.presign(Expires);
I'm not aware of other (non-JavaScript) SDKs that allow you to insert query parameters into the URL construction process like this so it may be a challenge to do this in other languages. I'd recommend using a small JavaScript Lambda function (or API Gateway plus Lambda function) that would simply create and return the customized pre-signed URL.
The custom query parameters are also tamper-proof. They are included in the signing of the URL so, if you tamper with them, the URL becomes invalid, yielding 403 Forbidden.
I used this code to generate your pre-signed URL. The result was:
https://s3.amazonaws.com/MyBucket/MyItem
?Foo=123
&X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Credential=AKIA...27%2Fus-east-1%2Fs3%2Faws4_request
&X-Amz-Date=20180427T0012345Z
&X-Amz-Expires=3600
&X-Amz-Signature=e3...7b
&X-Amz-SignedHeaders=host
None of this is a guarantee that this technique will continue to work, of course, if AWS changes things under the covers but for right now it seems to work and is certainly useful.
Attribution: the source of this discovery was aws-sdk-js/issues/502.
If you change one of the headers or add / subtract, then you have to resign the URL.
This is part of the AWS signing design and this process is designed for higher levels of security. One of the AWS reasons for changing to signing version 4 from signing version 2.
The signing design does not know which headers are important and which are not. That would create a nightmare trying to track all of the AWS services.
I created this solution for Ruby SDK. It is sort of a hack, but it works as expected:
require 'aws-sdk-s3'
require 'active_support/core_ext/object/to_query.rb'
# Modified S3 pre signer class that can inject query params to the URL
#
# Usage example:
#
# bucket_name = "bucket_name"
# key = "path/to/file.json"
# filename = "download_file_name.json"
# duration = 3600
#
# params = {
# bucket: bucket_name,
# key: key,
# response_content_disposition: "attachment; filename=#{filename}",
# expires_in: duration
# }
#
# signer = S3PreSignerWithQueryParams.new({'x-your-custom-field': "banana", 'x-some-other-field': 1234})
# url = signer.presigned_url(:get_object, params)
#
# puts "url = #{url}"
#
class S3PreSignerWithQueryParams < Aws::S3::Presigner
def initialize(query_params = {}, options = {})
#query_params = query_params
super(options)
end
def build_signer(cfg)
signer = super(cfg)
my_params = #query_params.to_h.to_query()
signer.define_singleton_method(:presign_url,
lambda do |options|
options[:url].query += "&" + my_params
super(options)
end)
signer
end
end
While not documented, you can add parameters as arguments to the call to presigned_url.
obj.presigned_url(:get,
expires_in: expires_in_sec,
response_content_disposition: "attachment"
)
https://bucket.s3.us-east-2.amazonaws.com/file.txt?response-content-disposition=attachment&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=PUBLICKEY%2F20220309%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20220309T031958Z&X-Amz-Expires=43200&X-Amz-SignedHeaders=host&X-Amz-Signature=SIGNATUREVALUE
If you are looking on for JavaScript SDK V3:
import { HttpRequest } from "#aws-sdk/protocol-http";
import { S3RequestPresigner } from "#aws-sdk/s3-request-presigner";
import { parseUrl } from "#aws-sdk/url-parser";
import { Sha256 } from "#aws-crypto/sha256-browser";
import { Hash } from "#aws-sdk/hash-node";
import { formatUrl } from "#aws-sdk/util-format-url";
// Make custom query in Record<string, string | Array<string> | null> format
const customQuery = {
hello: "world",
};
const s3ObjectUrl = parseUrl(
`https://${bucketName}.s3.${region}.amazonaws.com/${key}`
);
s3ObjectUrl.query = customQuery; //Insert custom query here
const presigner = new S3RequestPresigner({
credentials,
region,
sha256: Hash.bind(null, "sha256"), // In Node.js
//sha256: Sha256 // In browsers
});
// Create a GET request from S3 url.
const url = await presigner.presign(new HttpRequest(s3ObjectUrl));
console.log("PRESIGNED URL: ", formatUrl(url));
Code template taken from: https://aws.amazon.com/blogs/developer/generate-presigned-url-modular-aws-sdk-javascript/

How to calculate AWS signature V4 in Swagger before request

For our AWS API Endpoints we use AWS_IAM authorization and want to make a call from Swagger UI.
To make a successful call there must be 2 headers 'Authorization' and 'x-amz-date'. To form 'Authorization' we use following steps from aws doc.
We must to change 'x-amz-date' with every call to go through authorization.
The question is: How to write script in Swagger to sign request, which run every time before request send to aws?
(We know how to specify both headers one time before loading Swagger page, but this process should be re-run before every call).
Thanks in advance.
There is built-in support in swagger-js to add requestInterceptors to do just this. The swagger-ui project uses swagger-js under the hood.
Simply create a request interceptor like such:
requestInterceptor: {
apply: function (request) {
// modify the request object here
return request;
}
}
and apply it to your swagger instance on creation:
window.swaggerUi = new SwaggerUi({
url: url,
dom_id: "swagger-ui-container",
requestInterceptor: requestInterceptor,
Here you can set headers in the request object (note, this is not the standard javascript http request object, inspect it for details). But you do have access to all headers here, so you can calculate and inject them as needed.
You can pretty easily monkeypatch signing from the AWS SDK into SwaggerJS(and thus SwaggerUI). See here
I have a slightly modified SwaggerUI here. Given some AWS credentials and an API ID, it will pull down the Swagger definition, display it in SwaggerUI, and then you can call the API using sigv4.
The Authorizer implementation looks like this:
var AWSSigv4RequestSigner = function(credentialProvider, aws) {
this.name = "sigv4";
this.aws = aws;
this.credentialProvider = credentialProvider;
};
AWSSigv4RequestSigner.prototype.apply = function(options, authorizations) {
var serviceName = "execute-api";
//If we are loading the definition itself, then we need to sign for apigateway.
if (options && options.url.indexOf("apigateway") >= 0) {
serviceName = "apigateway";
}
if(serviceName == "apigateway" || (options.operation && options.operation.authorizations && options.operation.authorizations[0].sigv4))
{
/**
* All of the below is an adapter to get this thing into the right form for the AWS JS SDK Signer
*/
var parts = options.url.split('?');
var host = parts[0].substr(8, parts[0].indexOf("/", 8) - 8);
var path = parts[0].substr(parts[0].indexOf("/", 8));
var querystring = parts[1];
var now = new Date();
if (!options.headers)
{
options.headers = [];
}
options.headers.host = host;
if(serviceName == "apigateway")
{
//For the swagger endpoint, apigateway is strict about content-type
options.headers.accept = "application/json";
}
options.pathname = function () {
return path;
};
options.methodIndex = options.method;
options.search = function () {
return querystring ? querystring : "";
};
options.region = this.aws.config.region || 'us-east-1';
//AWS uses CAPS for method names, but swagger does not.
options.method = options.methodIndex.toUpperCase();
var signer = new this.aws.Signers.V4(options, serviceName);
//Actually add the Authorization header here
signer.addAuthorization(this.credentialProvider, now);
//SwaggerJS/yourbrowser complains if these are still around
delete options.search;
delete options.pathname;
delete options.headers.host;
return true;
}
return false;
};

simple azure web service, http404 error on publish when adding /dowork to URL

I am trying my first web app service using Azure services. I've created it in VS, and it works locally. All it does it return a string that says "hello user" is JSON.
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Service1
{
// To use HTTP GET, add [WebGet] attribute. (Default ResponseFormat is WebMessageFormat.Json)
// To create an operation that returns XML,
// add [WebGet(ResponseFormat=WebMessageFormat.Xml)],
// and include the following line in the operation body:
// WebOperationContext.Current.OutgoingResponse.ContentType = "text/xml";
[OperationContract]
[WebGet(UriTemplate = "/DoWork")]
public string DoWork()
{
// Add your operation implementation here
return "hello user";
}
// Add more operations here and mark them with [OperationContract]
}
}
Problem is when I publish it, says successful. I can see it running on portal.
When I goto published site I get the standard THIS WEB APP HAS BEEN SUCCESSFULLY CREATED, but... when I add the /DoWork to the URL I get HTTP error 404.
I know I must be missing something simple...
any ideas?
you're missing the name of the service. In your case would be something like:
http://engineappservicev001.azurewebsites.net/something.svc/dowork
More info in here:
http://www.codeproject.com/Articles/571813/A-Beginners-Tutorial-on-Creating-WCF-REST-Services