Microsoft.OpenAPI examples or documentation? - c#

Just tried to use Swashbuckle 5 rc2 + Microsoft OpenAPI implementation but struggling to make sense of how to inject the security requirements using the OpenApiSecurityRequirement via an OperationFilter
I'm converting the OperationFilter from Swashbuckle 4 to Swashbuckle 5 rc2 which makes use of Microsoft's OpenApi. WIthin the Swashbuckle 4 implementation I had OperationFilter (and this allowed me to use both oauth2 implicit flow scopes as well as api_key where I would explicitly set a bearer JTW token in the SwaggerUI:
// Swashbuckle 4 code
operation.Security = new List<Dictionary<String, IEnumerable<String>>>
{
new Dictionary<string, IEnumerable<string>>
{
{ "Bearer", new string[] { } }
},
new Dictionary<string, IEnumerable<string>>
{
{ "oauth2", requiredScopes }
}
};
Not very sure how to use OpenAPI for describing the same security requirements, but in the converted OperationFilter implementation I've basically sought out the endpoints which have the Authorize attribute and read the policy to retrieve the scopes:
if (requiredScopes.Any())
{
operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" });
OpenApiSecurityRequirement bearerSecurityRequirement = new OpenApiSecurityRequirement();
bearerSecurityRequirement[new OpenApiSecurityScheme()
{
Type = SecuritySchemeType.Http,
Scheme = "Bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Name = "api_key",
}] = new List<String>();
OpenApiSecurityRequirement oauth2SecurityRequirement = new OpenApiSecurityRequirement();
oauth2SecurityRequirement[new OpenApiSecurityScheme()
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows()
{
Implicit = new OpenApiOAuthFlow()
{
AuthorizationUrl = new Uri("<authorization url here>"),
TokenUrl = new Uri("<token url here>"),
Scopes = requiredScopes.ToDictionary(x => x) // TODO: Fix descriptions
}
},
In = ParameterLocation.Header,
Name = "oauth2"
}] = new List<String>(requiredScopes);
operation.Security = new List<OpenApiSecurityRequirement>
{
bearerSecurityRequirement,
oauth2SecurityRequirement
};
}
In the json output of the generated swagger doc/openapi doc for the operation I'm just seeing:
"security" : [
{"api_key": []},
{
"oauth2": [
"",
""
]
}
]
I think I am aiming to generate the following json in terms OpenAPI standard, where api_key and oauth2 are just the names of my security schemes.
"security" : [
{"api_key": []},
{
"oauth2": [
"<scope1>",
"<scope2>"
]
}
]
Are there any docs coming or perhaps some fuller examples that actually demonstrate how to declare protected endpoints for oauth2 and api key approaches?

You need to set the Reference of the OpenApiSecurityScheme.
I ended up with a result just like you, just an empty object.
I tried to add HTTP basic authentication like in the example:
openapi: 3.0.0
...
components:
securitySchemes:
basicAuth: # <-- arbitrary name for the security scheme
type: http
scheme: basic
security:
- basicAuth: [] # <-- use the same name here
And to do that with Microsoft.OpenApi:
var document = new OpenApiDocument
{
Components = new OpenApiComponents
{
SecuritySchemes =
{
{
"basicAuth", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.Http,
Scheme = "basic"
}
}
}
},
SecurityRequirements =
{
new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference()
{
Type = ReferenceType.SecurityScheme,
Id = "basicAuth"
}
},
new List<string>()
}
}
}
};
By looking at the source code of OpenApiSecurityRequirement one can see that it's checking OpenApiSecurityScheme.Reference and not Name:
foreach (var securitySchemeAndScopesValuePair in this)
{
var securityScheme = securitySchemeAndScopesValuePair.Key;
var scopes = securitySchemeAndScopesValuePair.Value;
if (securityScheme.Reference == null)
{
// Reaching this point means the reference to a specific OpenApiSecurityScheme fails.
// We are not able to serialize this SecurityScheme/Scopes key value pair since we do not know what
// string to output.
continue;
}
securityScheme.SerializeAsV3(writer);
writer.WriteStartArray();
foreach (var scope in scopes)
{
writer.WriteValue(scope);
}
writer.WriteEndArray();
}

Related

.NET Core Web Api Azure AD and Swagger not authenticating

I am trying to set up a .NET Core Web API with Azure AD authentication and Swagger but it gives an error when trying to authenticate.
I am following this guide: http://www.sharepointconfig.com/2018/08/configure-swagger-to-authenticate-against-azure-ad/
ConfigureServices contains this code:
services.AddSwaggerGen(c =>
{
c.AddSecurityDefinition("oauth2", //Name the security scheme
new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows() { Implicit = new OpenApiOAuthFlow() },
Scheme = "oauth2",
OpenIdConnectUrl = new Uri($"https://login.microsoftonline.com/" + _configuration["AzureAD:TenantId"] + "/oauth2/authorize"),
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement{
{
new OpenApiSecurityScheme{
Reference = new OpenApiReference{
Id = "oauth2",
Type = ReferenceType.SecurityScheme
}
},new List<string>()
}
});
});
Configure contains this code:
app.UseSwaggerUI(c =>
{
c.OAuthClientId(_configuration["Swagger:ClientId"]);
c.OAuthClientSecret(_configuration["Swagger:ClientSecret"]);
c.OAuthRealm(_configuration["AzureAD:ClientId"]);
c.OAuthAppName("WerfRegistratie V1");
c.OAuthScopeSeparator(" ");
c.OAuthAdditionalQueryStringParams(new Dictionary<string, string> { { "resource", _configuration["AzureAD:ClientId"] } });
});
The problem I keep running in to is this one:
Swagger authentication error
It looks like when I try to click the Authorize button a variable in Swagger is not filled in and I have been trying different settings in the SwaggerUI but this keeps happening.
We have configured it like this:
var authority = "https://login.microsoftonline.com/your-aad-tenant-id";
o.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
Implicit = new OpenApiOAuthFlow
{
Scopes = new Dictionary<string, string>
{
["Stuff.Read"] = "Read stuff" // TODO: Replace with your scopes
},
AuthorizationUrl = new Uri(authority + "/oauth2/authorize")
}
}
});

How to create k8s deployment using kubernetes-client in c#?

I'm getting Microsoft.Rest.HttpOperationException: 'Operation returned an invalid status code 'BadRequest'' on this line.
var result = client.CreateNamespacedDeployment(deployment, namespace);
Kubernetes-client has a small number of good resources and most of them is written in other language such as java and python. So i'm referring to these documentations.
this is my implementation so far.
V1Deployment deployment = new V1Deployment()
{
ApiVersion = "extensions/v1beta1",
Kind = "Deployment",
Metadata = new V1ObjectMeta()
{
Name = "...",
NamespaceProperty = env,
Labels = new Dictionary<string, string>()
{
{ "app", "..." }
}
},
Spec = new V1DeploymentSpec
{
Replicas = 1,
Selector = new V1LabelSelector()
{
MatchLabels = new Dictionary<string, string>
{
{ "app", "..." }
}
},
Template = new V1PodTemplateSpec()
{
Metadata = new V1ObjectMeta()
{
CreationTimestamp = null,
Labels = new Dictionary<string, string>
{
{ "app", "..." }
}
},
Spec = new V1PodSpec
{
Containers = new List<V1Container>()
{
new V1Container()
{
Name = "...",
Image = "...",
ImagePullPolicy = "Always",
Ports = new List<V1ContainerPort> { new V1ContainerPort(80) }
}
}
}
}
},
Status = new V1DeploymentStatus()
{
Replicas = 1
}
};
var result = client.CreateNamespacedDeployment(deployment, namespace);
I want to know the proper way on how to create kubernetes deployment using kubernetes-client, and also i want to know the cause of this issue.
For the full clarity and future visitors, it's worth to mention, what is exactly behind this bad request error (code: 400) returned from API server, when using your code sample:
"the API version in the data (extensions/v1beta1) does not match the expected API version (apps/v1)"
Solution:
ApiVersion = "extensions/v1beta1" -> ApiVersion = "apps/v1"
Full code sample:
private static void Main(string[] args)
{
var k8SClientConfig = new KubernetesClientConfiguration { Host = "http://127.0.0.1:8080" };
IKubernetes client = new Kubernetes(k8SClientConfig);
ListDeployments(client);
V1Deployment deployment = new V1Deployment()
{
ApiVersion = "apps/v1",
Kind = "Deployment",
Metadata = new V1ObjectMeta()
{
Name = "nepomucen",
NamespaceProperty = null,
Labels = new Dictionary<string, string>()
{
{ "app", "nepomucen" }
}
},
Spec = new V1DeploymentSpec
{
Replicas = 1,
Selector = new V1LabelSelector()
{
MatchLabels = new Dictionary<string, string>
{
{ "app", "nepomucen" }
}
},
Template = new V1PodTemplateSpec()
{
Metadata = new V1ObjectMeta()
{
CreationTimestamp = null,
Labels = new Dictionary<string, string>
{
{ "app", "nepomucen" }
}
},
Spec = new V1PodSpec
{
Containers = new List<V1Container>()
{
new V1Container()
{
Name = "nginx",
Image = "nginx:1.7.9",
ImagePullPolicy = "Always",
Ports = new List<V1ContainerPort> { new V1ContainerPort(80) }
}
}
}
}
},
Status = new V1DeploymentStatus()
{
Replicas = 1
}
};
Closing this issue (Resolved)
Reference: https://github.com/Azure/autorest/issues/931
Cause of issue: incorrect version of Kubernetes ApiVersion.
Solution: get and replace ApiVersion from kubernetes api.
Can also handle the exception using:
try
{
var result = client.CreateNamespacedDeployment(deployment, namespace);
}
catch (Microsoft.Rest.HttpOperationException httpOperationException)
{
var phase = httpOperationException.Response.ReasonPhrase;
var content = httpOperationException.Response.Content;
}

Create Initial Push into Newly Created Repository using VSTS Git API

How to create initial push into newly created Repository using VSTS Git API?
I have created a new repository.
using Microsoft.TeamFoundation.SourceControl.WebApi;
using Microsoft.VisualStudio.Services.WebApi;
using Microsoft.TeamFoundation.Core.WebApi
var accountUri = new Uri("https://mysite.visualstudio.com");
var personalAccessToken = "myaccesstoken";
var connection = new VssConnection(accountUri, new VssBasicCredential(string.Empty, personalAccessToken));
// Get a GitHttpClient to talk to the Git endpoints
var gitClient = connection.GetClient<GitHttpClient>();
var teamProject = projectClient.GetProject("MyProject", true, true).Result;
var repo = gitClient.CreateRepositoryAsync(new GitRepository
{
DefaultBranch = "refs/heads/master",
Name = "TestRepo",
ProjectReference = new TeamProjectReference
{
Id = teamProject.Id
}
}).Result;
The repo is successfully created. But why is the repo.DefaultBranch value is null?
Next step, I'd like to push my initial commit.
var newBranch = new GitRefUpdate
{
RepositoryId = repo.Id,
Name = $"refs/heads/master"
};
string newFileName = "README.md";
GitCommitRef newCommit = new GitCommitRef
{
Comment = "Initial commit",
Changes = new GitChange[]
{
new GitChange
{
ChangeType = VersionControlChangeType.Add,
Item = new GitItem { Path = $"/master/{newFileName}" },
NewContent = new ItemContent
{
Content = "# Thank you for using VSTS!",
ContentType = ItemContentType.RawText,
},
}
}
};
GitPush push = gitClient.CreatePushAsync(new GitPush
{
RefUpdates = new GitRefUpdate[] { newBranch },
Commits = new GitCommitRef[] { newCommit },
}, repo.Id).Result;
I got an error when calling CreatePushAsync:
VssServiceException: The combination of parameters is either not valid
or not complete. Parameter name: baseCommitId
Please help how to create initial commit properly.
You could use the rest api to achieve what you want. The rest api of creating an initial commit (create a new branch) is as below:
POST https://fabrikam.visualstudio.com/_apis/git/repositories/{repositoryId}/pushes?api-version=4.1
{
"refUpdates": [
{
"name": "refs/heads/master",
"oldObjectId": "0000000000000000000000000000000000000000"
}
],
"commits": [
{
"comment": "Initial commit.",
"changes": [
{
"changeType": "add",
"item": {
"path": "/readme.md"
},
"newContent": {
"content": "My first file!",
"contentType": "rawtext"
}
}
]
}
]
}

Using Elasticsearch Nest library throws error when creating a token Filter on index

I need to create a Synonym token filter in Elasticsearch using the Nest library, but it keeps throwing an error.
SynonymTokenFilter synonymFilter = new SynonymTokenFilter {
Format = SynonymFormat.Solr,
SynonymsPath= "synonym.txt",
Tokenizer="whitespace",
};
isettings.Analysis.TokenFilters.Add("mysynonym",synonymFilter);
isettings.NumberOfReplicas = 1;
isettings.NumberOfShards = 2;
IndexState indexConfig = new IndexState
{
Settings = isettings,
};
everything else works if i comment out this line isettings.Analysis.TokenFilters.Add("mysynonym",synonymFilter);
it throws a "object not set to an instance of an object" error.
I really need help with this, this is my first time use the Nest Library
I'm using ElasticSearch 6 and the latest version of the Nest Library.
It looks like either Analysis on isettings, or TokenFilters on isettings.Analysis is null. Here's an example that works
var isettings = new IndexSettings
{
Analysis = new Analysis
{
TokenFilters = new TokenFilters()
}
};
SynonymTokenFilter synonymFilter = new SynonymTokenFilter
{
Format = SynonymFormat.Solr,
SynonymsPath = "synonym.txt",
Tokenizer = "whitespace",
};
isettings.Analysis.TokenFilters.Add("mysynonym", synonymFilter);
isettings.NumberOfReplicas = 1;
isettings.NumberOfShards = 2;
IndexState indexConfig = new IndexState
{
Settings = isettings,
};
var client - new ElasticClient();
client.CreateIndex(new CreateIndexRequest("index", indexConfig));
which creates the following JSON request
PUT http://localhost:9200/index
{
"settings": {
"index.number_of_replicas": 1,
"analysis": {
"filter": {
"mysynonym": {
"synonyms_path": "synonym.txt",
"format": "solr",
"tokenizer": "whitespace",
"type": "synonym"
}
}
},
"index.number_of_shards": 2
}
}

Programmatically setting up a static website using Amazon S3 and Route 53 APIs

Assume I already have purchased a domain example.com with IP address 203.0.113.2. Using C# and the The Amazon Web Services SDK for .NET 2.0.2.2, I'd like to create a static website using a custom domain using Amazon S3 and Route 53. The manual process is described in the Amazon documentation.
When trying to create an alias, I get an exception with the message:
Invalid XML ; cvc-complex-type.2.4.a: Invalid content was found starting with element 'AliasTarget'.
One of '{"https://route53.amazonaws.com/doc/2012-12-12/":ResourceRecords}' is expected.
First, I created or updated a bucket (e.g. "example.com") in Amazon S3. If it already existed, content is deleted.
using (var client = AWSClientFactory.CreateAmazonS3Client(RegionEndpoint.USWest1))
{
if (!S3BucketExists(name, client))
{
client.PutBucket(new PutBucketRequest
{
BucketName = name,
BucketRegion = S3Region.USW1,
CannedACL = S3CannedACL.PublicRead
});
}
else
{
var request = new ListObjectsRequest
{
BucketName = name
};
var objects = client.ListObjects(request).S3Objects;
foreach (var o in objects)
{
client.DeleteObject(new DeleteObjectRequest
{
BucketName = name,
Key = o.Key
});
}
client.PutACL(new PutACLRequest
{
CannedACL = S3CannedACL.PublicRead,
BucketName = name
});
}
client.PutBucketWebsite(new PutBucketWebsiteRequest
{
BucketName = name,
WebsiteConfiguration = new WebsiteConfiguration
{
ErrorDocument = "404.html",
IndexDocumentSuffix = "index.html"
}
});
CreateObject(name, client, "index.html", "text/html", "<p>The site is under maintenance</p>");
CreateObject(name, client, "404.html", "text/html", "<p>Not Found</p>");
}
S3BucketExists returns whether a bucket exist or not, and CreateObject creates a simple page and uploads it to the bucket. Its omitted for brevity sake. I'm able to connect to the S3 hosted site without any problems.
Then I use the Route 53 API to update an existing hosted zone or create one for "example.com". All resources, except for the SOA and NS entries are deleted.
using (var client = AWSClientFactory.CreateAmazonRoute53Client())
{
var hostedZone = FindHostedZoneByName(client, domainName);
if (hostedZone != null)
{
var resourceRecordSets = client.ListResourceRecordSets(new ListResourceRecordSetsRequest
{
HostedZoneId = hostedZone.Id,
});
bool hasElements = false;
var request1 = new ChangeResourceRecordSetsRequest
{
HostedZoneId = hostedZone.Id,
ChangeBatch = new ChangeBatch
{
Changes = new List<Change>()
}
};
foreach (var resourceRecordSet in resourceRecordSets.ResourceRecordSets)
{
switch (resourceRecordSet.Type)
{
case "SOA":
case "NS":
continue;
}
var change = new Change
{
Action = "DELETE",
ResourceRecordSet = resourceRecordSet
};
request1.ChangeBatch.Changes.Add(change);
hasElements = true;
}
if (hasElements)
{
var response = client.ChangeResourceRecordSets(request1);
}
}
else
{
hostedZone = CreateHostedZone(client, domainName);
}
var hostedZoneId = hostedZone.Id;
var request = new ChangeResourceRecordSetsRequest
{
HostedZoneId = hostedZoneId,
ChangeBatch = new ChangeBatch
{
Changes = new List<Change>
{
new Change
{
Action = ChangeAction.CREATE,
ResourceRecordSet = new ResourceRecordSet
{
Name = GetQualifiedName(domainName),
Type = RRType.A,
TTL = 300,
AliasTarget = new AliasTarget()
{
HostedZoneId = "Z2F56UZL2M1ACD",
DNSName = "s3-website-us-west-1.amazonaws.com.",
},
},
},
}
}
};
client.ChangeResourceRecordSets(request);
}
The hosted zone id ("Z2F56UZL2M1ACD") and DNS names ("s3-website-us-west-1.amazonaws.com.") are public knowledge and documented on Amazon's website.
The call to ChangeResourceRecordSets throws the exception. I created an empty ResourceRecords list, with a A record of "203.0.113.2", but have not had any luck creating an alias.
That said, I can manually create the alias to the Amazon S3 site afterwards using the "Route 53 Management Console". I'm sure it's something small I'm missing.
After re-reading the documentation, it turns out that one cannot specify the TTL when specifying an alias. The following change works. Replace the code that creates an instance of ChangeResourceRecordSetsRequest to the following:
var request = new ChangeResourceRecordSetsRequest
{
HostedZoneId = hostedZoneId,
ChangeBatch = new ChangeBatch
{
Changes = new List<Change>
{
new Change
{
Action = ChangeAction.CREATE,
ResourceRecordSet = new ResourceRecordSet
{
Name = GetQualifiedName(domainName),
Type = RRType.A,
AliasTarget = new AliasTarget
{
HostedZoneId = "Z2F56UZL2M1ACD",
DNSName = "s3-website-us-west-1.amazonaws.com.",
EvaluateTargetHealth = false,
},
},
},
}
}
};
The difference was evident when the output produced by System.Net tracing was compared to the request specified in the Amazon example.

Categories

Resources