Category: .NET Core

Retarder- idea and POC–part 1

I was thinking about a new project in .NET Core  – Retarder ( Delayer  seems to be better , but I digress ). What about a middleware in .NET Core, that delays execution of any request with 1 second ( or something between 1 millisecond and 1 second )? That way , if someone wants to improve the project , just remove the Retarder !

Other ( better ) uses are :

  1. Delay execution of some endpoints, based on how frequent are their uses
  2. Delay execution based on headers / query string /  routes
  3. Delay execution based on client IP
  4. Delay execution based on the response

 

But for the moment just stick with the static delay of all requests.

Seems to be a good idea to play with middleware. I started a new .NET Core 3.1 project and started coding .

You can find the first version at tag : https://github.com/ignatandrei/NetCoreRetarder/commits/version_1_just_data

The code for Retarder middleware is

using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace NetCoreRetarder
{
    public class RetarderMiddleware : IMiddleware
    {

        public RetarderMiddleware()
        {
        }

        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            var random = new Random();
            //var awaitMilliseconds =random.Next(10000, 100000);
            var awaitMilliseconds = random.Next(1, 1000);
            Console.WriteLine($"awaiting {awaitMilliseconds}");
            await Task.Delay(awaitMilliseconds);
            Console.WriteLine($"***awaited {awaitMilliseconds}");

            await next(context);
        }
    }
}


 

The code for using Retarder is:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace NetCoreRetarder
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            //what if we comment this ?
            //services.AddSingleton<RetarderMiddleware>();
            services.AddTransient<RetarderMiddleware>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();

            app.UseAuthorization();
            //app.UseMiddleware(typeof(RetarderMiddleware));
            app.UseMiddleware<RetarderMiddleware>();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

Some questions :

1.  What is the difference if we have services.AddSingleton  vs services.AddTransient for the middleware ?

2. What if we comment both ?

3. What if we do call later ( or before ) app.UseMiddleware ?

Documentation–part 6

WebAPI2CLI

This is a part of the series where about how I made the WebAPI2CLI - Execute ASP.NET Core WebAPI from Command Line
Source code on https://github.com/ignatandrei/webAPI2CLI/
1WebAPI2CLI - Description
2WebAPI2CLI- Organization
3WebAPI2CLI - implementing
4WebAPI2CLI - tests
5WebAPI2CLI - Devops and CI/CD
6WebAPI2CLI - documentation
7WebAPI2CLI - Conclusions
8WebAPI2CLI - Zip application

I need 2 types of documentation:

1. For let people know how to use

2. For the programmers – to understand what the classes are doing

 

For the first point, documentation to use

a. Github has already the readme.md file that shows details for the repository . What I need is something more- i.e. detailed instruction, FAQ, Author.

b. Github has made easy to make an online site  -by having the docs folder public with his own address. For example, from  https://github.com/ignatandrei/webAPI2CLI/ I can have https://ignatandrei.github.io/WebAPI2CLI/ . Now , the problem is how to concatenate those and put back to site

c. GitHub has also the concept of Actions –i.e.  DevOps integrated.

 

So –what I have done is to use pandoc , compile all the .md files into html ( and pdf ) and push modifications back to the site.

See https://github.com/ignatandrei/WebAPI2CLI/blob/master/.github/workflows/main.yml

– uses: actions/checkout@v2

– run: |

echo generate help file

mkdir output

git pull

– name: generate html

uses: docker://pandoc/latex:2.9

with: # needs a README in your repo root!

#args: “–standalone –output=output/README.html README.md”

args: “README.md docs/Demo.md docs/FAQ.md docs/Author.md –standalone -f gfm -t html  –toc -o output/output.html –metadata title=WebAPI2CLI”

– name: generate pdf

uses: docker://pandoc/latex:2.9

with: # needs a README in your repo root!

#args: “–standalone –output=output/README.html README.md”

args: “README.md docs/Demo.md docs/FAQ.md docs/Author.md –standalone -f gfm -t pdf  –toc -o output/output.pdf –metadata title=WebAPI2CLI”

– run: |

cp ./output/output.html ./docs/index.html

cp ./output/output.pdf ./docs/Web2CLI.pdf

rm -rf ./output

 

You can see the results at https://ignatandrei.github.io/WebAPI2CLI/ and the sources at https://github.com/ignatandrei/WebAPI2CLI/tree/master/docs

For the second point, generating documentation from XML Comments in C#

I need some free .NET tool to do this. I did not found. I have hesitated also between https://github.com/EWSoftware/SHFB ( an old friend, but cumbersome ) and https://github.com/dotnet/docfx/ ( a new friend, but still cumbersome)

What I have done is to download docfx, add to the site, follow instructions , modify some of the templates , resist temptation to put everything into docfx ( he kinda want to be all inclusive)

docs_csharp:

# The type of runner that the job will run on

runs-on: windows-latest

steps:

# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it

– uses: actions/checkout@v2

– name: run documentation

run: |

DocumentationSources.bat

rem git config –local user.email “action@github.com”

rem git config –local user.name “GitHub Action”

rem git commit -m “generate documentation sources” -a –allow-empty

shell: cmd

– name: Push changes

uses: ad-m/github-push-action@master

with:

github_token: ${{ secrets.GITHUB_TOKEN }}

You can see the results at https://ignatandrei.github.io/WebAPI2CLI/sitedocs/api/index.html  and sources at https://github.com/ignatandrei/WebAPI2CLI/tree/master/docs/sitedocs

Devops + CI/CD-part 5

WebAPI2CLI

This is a part of the series where about how I made the WebAPI2CLI - Execute ASP.NET Core WebAPI from Command Line
Source code on https://github.com/ignatandrei/webAPI2CLI/
1WebAPI2CLI - Description
2WebAPI2CLI- Organization
3WebAPI2CLI - implementing
4WebAPI2CLI - tests
5WebAPI2CLI - Devops and CI/CD
6WebAPI2CLI - documentation
7WebAPI2CLI - Conclusions
8WebAPI2CLI - Zip application

What I need for the devops:

1. Building the solution

2. Running tests

3. Deploying packages to Nuget

Being part of the Microsoft stack, it is normal that I have choose for CI / CD the Azure Devops.

You can see the AzureDevops at https://dev.azure.com/ignatandrei0674/WebAPI2CLI/_build?definitionId=7&_a=summary and how it is done by reading https://github.com/ignatandrei/WebAPI2CLI/blob/master/azure-pipelines.yml

Seeing code coverage in AzureDevops

I need not only to know that tests have runned with success, but also to see the code coverage

For this I use the .NET local tools, coverlet and report generator

dotnet coverlet bin\$(buildConfiguration)\netcoreapp3.1\CLITests.dll –target “dotnet” –targetargs “test –no-build –configuration $(buildConfiguration)” –exclude ‘[*Test*]*’ –format opencover  –output $(Build.ArtifactStagingDirectory)\testResults\coverlet.xml

dotnet reportgenerator “-reports:$(Build.ArtifactStagingDirectory)\testResults\coverlet.xml” “-targetdir:$(Build.ArtifactStagingDirectory)\testResults” “-reporttypes:Cobertura;HtmlSummary;Badges;HtmlInline_AzurePipelines”

– task: PublishTestResults@2

inputs:

testResultsFormat: ‘VSTest’

testResultsFiles: ‘**/*.trx’

searchFolder: ‘$(Build.ArtifactStagingDirectory)\trx’

displayName: publish tests

– task: PublishCodeCoverageResults@1

displayName: ‘Publish code coverage’

inputs:

codeCoverageTool: Cobertura

summaryFileLocation: ‘$(Build.ArtifactStagingDirectory)\testResults\Cobertura.xml’

reportDirectory: ‘$(Build.ArtifactStagingDirectory)\testResults’

In this way , you can see the

Modify version 

For a good CD I need also a way to modify the version automatically.

 

The .Net local Tools provides a way to install some tools on the local system to be run within dotnet.

So I install pwsh  -and run a setversion.ps1

 

$TimeNow = Get-Date

$d = $TimeNow.ToUniversalTime()

$year = $TimeNow.Year

$startOfYear = Get-Date -Year $year -Month 1 -Day 1 -Hour 0 -Minute 0 -Second 0 -Millisecond 0

$diff = NEW-TIMESPAN -Start $startOfYear -End $TimeNow

#$diff.TotalSeconds -as [int]

$assemblyVersion=$d.ToString(“1.yyyy.1MMdd.1HHmm”)

dotnet-property “**/*.csproj” AssemblyVersion:”$assemblyVersion”

dotnet dotnet-property “**/*.csproj” AssemblyVersion:”$assemblyVersion”

$version=$d.ToString(“1.0.yyyy.”) + ($diff.TotalSeconds -as [int]).ToString()

dotnet-property “**/*.csproj” Version:”$version”

dotnet dotnet-property “**/*.csproj” Version:”$version”

$releaseNotes = “BuildNumber $env:BUILD_BUILDNUMBER”

$releaseNotes += “;author $env:BUILD_SOURCEVERSIONAUTHOR”

$releaseNotes += “;message $env:BUILD_SOURCEVERSIONMESSAGE”

$releaseNotes +=”;source for this release github.com/ignatandrei/webAPI2CLI/commit/$env:BUILD_SOURCEVERSION”

$releaseNotes

dotnet-property “**/*.csproj” PackageReleaseNotes:”$releaseNotes”

dotnet dotnet-property “**/*.csproj” PackageReleaseNotes:”$releaseNotes”

I think that it is self explanatory.

Be a good nuget citizen –do not deploy every time a modification is made

For this I use devops variable :  deployNuget: ‘0’

– task: NuGetCommand@2

condition: and(succeeded(), eq(variables[‘deployNuget’], ‘1’))

inputs:

command: push

nuGetFeedType: external

packagesToPush: ‘$(Build.ArtifactStagingDirectory)/**/*.symbols.nupkg’

publishFeedCredentials: ‘nugetAndrei’

displayName: ‘dotnet nuget push’

Be a good AzureDevops citizen – do not run automation every time

I have some documentation on .md files – no need to rebuild everything when just the documentation is done

This is done in AzureDevOps by the trigger:

trigger:

branches:

include:

– master

paths:

exclude:

– docs/*

– README.md

Test your application

I also can test in AzureDevops what I have done by runnning

TestWebAPISite.exe  –CLI_ENABLED=1 –CLI_Commands=”GetMathId_Http,MathPOST”

It is just nice – maybe I can find a way to do something practical …

Create and running tests- part 4

WebAPI2CLI

This is a part of the series where about how I made the WebAPI2CLI - Execute ASP.NET Core WebAPI from Command Line
Source code on https://github.com/ignatandrei/webAPI2CLI/
1WebAPI2CLI - Description
2WebAPI2CLI- Organization
3WebAPI2CLI - implementing
4WebAPI2CLI - tests
5WebAPI2CLI - Devops and CI/CD
6WebAPI2CLI - documentation
7WebAPI2CLI - Conclusions
8WebAPI2CLI - Zip application

I have made a TestWebSite , in .NET Core 3.1, I was also thinking that WebApplicationFactory exists and tutorial like https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.1   it will be easy to test the site.

However, I have found the hard way that is not a real server : it does not have the adresses / ports open. Read https://github.com/dotnet/aspnetcore/issues/4892 .

So I have made a hack – see public class LocalServerFactory in the source code.

Also, WithWebHostBuilder helps create a new WebApplicationFactory  -used to test with / or without /  CLI_ENABLED command line

 

Second point was testing methods like

configuration.GetValue<int?>(“CLI_HELP”, null);

Because it is an extension function, I cannot mock. So I refactored the code to

public bool ShouldShowHelp()
{
//deleted for easy of automating testing
//var showHelp = configuration.GetValue<int?>(“CLI_HELP”, null);
var cliHelp = configuration[“CLI_HELP”];
var showHelp = int.TryParse(cliHelp, out var val) && val == 1;
return showHelp;
}

Also, I have resorted to XBehave, to have tests written in easy form. And to Moq to mock various properties.

Implementing–part 3

WebAPI2CLI

This is a part of the series where about how I made the WebAPI2CLI - Execute ASP.NET Core WebAPI from Command Line
Source code on https://github.com/ignatandrei/webAPI2CLI/
1WebAPI2CLI - Description
2WebAPI2CLI- Organization
3WebAPI2CLI - implementing
4WebAPI2CLI - tests
5WebAPI2CLI - Devops and CI/CD
6WebAPI2CLI - documentation
7WebAPI2CLI - Conclusions
8WebAPI2CLI - Zip application

First implementation was just a bunch a code thrown in an API project. All self contained, all in one WebAPI project.

Some problems that I have faced:

 

1. The class that executes( the hosted service )  should have access to the address of the server . However, the class is injected at the moment of creating services – and at this time the IServerAddressesFeature do not exists yet.

Solution: Inject at useCLi the IApplicationBuilder to get the services and verify if the adresses are ok:

// in private async void DoWork(object state)

serverAddresses = app.ServerFeatures.Get<IServerAddressesFeature>();

if (serverAddresses == null)

 

2. But how to get the instance of the Hosted service  ? Simple,with Singletons:

// in public static IServiceCollection AddCLI(this IServiceCollection serviceCollection)

serviceCollection.AddSingleton<CLIAPIHostedService>();

serviceCollection.AddHostedService<CLIAPIHostedService>(p => p.GetService<CLIAPIHostedService>());

 

and later on

//in public static IApplicationBuilder UseCLI(this IApplicationBuilder app)

var service = app.ApplicationServices.GetService<CLIAPIHostedService>();
service.app = app;

 

3.  How to serialize the headers, url, parameters, body and others in a file  – as to pass easy json ?

The idea is that I wrote Json( for sending body data)  in a json file it will become faster complicated. So I was thinking : What other serialized formats I know ?

So I was attracted by YAMLhttps://en.wikipedia.org/wiki/YAML . And there is an implementation https://github.com/aaubry/YamlDotNet . You can find a sample file at https://github.com/ignatandrei/WebAPI2CLI/blob/master/src/TestWebAPISite/cli.txt

4. What to do with errors ?

For example, if some HTTP returns an error message , what can I do  ? The obvious idea is to return on the console the error too…

So I implemented this class to emulate answer

public class ReturnValue_v1
{
public ReturnValue_v1(HttpStatusCode statusCode):this(statusCode,null)
{

}
public ReturnValue_v1(HttpStatusCode statusCode,string result)
{
StatusCode = statusCode;
Result = result;
}
public ICLICommand Command { get; private set; }

public HttpStatusCode StatusCode { get; private set; }
public string Result { get; private set; }

public ReturnValue_v1 FromCommand(ICLICommand cmd)
{
this.Command = cmd;
return this;
}

}

to can return to the console the JSON of this object.

5. How to exit the application ?

This is simple –  Environment.Exit(0);

However , maybe could be a feature to exit if we encounter an error in calling the API ? And if you want to help, https://github.com/ignatandrei/WebAPI2CLI/issues/10

6. How to figure the WebAPI endpoints ?

Simple – with IApiDescriptionGroupCollectionProvider

7. How to help the user to not change the port of adresses every time the .NET Core server change the adress ?

There are so many ways to configure the address of ASP .NET Core  – see https://josephwoodward.co.uk/2017/02/many-different-ways-specifying-host-port-asp-net-core . The CLI.txt file should work the same , no matter the port . So I decided to put just the beginning of the http and then find the right address ( and handle also the case with full URL)

In this file put just –
Host: http://

or

Host: https://

( Assumption : just one http and/or just 1 https when asp.net core will start )

WebAPI2CLI will find the adress and the port comparing

Alternatively, you can put the full URI ( without RelativeRequestUrl ! )

Host: http://localhost:5000/

8. How to make easier for the developer to integrate ?

Create extension – AddCLI and UseCLI .

Thinking of the project–part 2

WebAPI2CLI

This is a part of the series where about how I made the WebAPI2CLI - Execute ASP.NET Core WebAPI from Command Line
Source code on https://github.com/ignatandrei/webAPI2CLI/
1WebAPI2CLI - Description
2WebAPI2CLI- Organization
3WebAPI2CLI - implementing
4WebAPI2CLI - tests
5WebAPI2CLI - Devops and CI/CD
6WebAPI2CLI - documentation
7WebAPI2CLI - Conclusions
8WebAPI2CLI - Zip application

The questions that I have to solve is :

1.  How to run something in an ASP.NET Core project, when we have just WEBAPI controllers ?

The answer was pretty simple: Hosted Services: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio . It runs unattended and can be started by the project

2. Which version of .NET Core should I use ?

Another simple answer, looking at https://dotnet.microsoft.com/platform/support/policy/dotnet-core – the only one that has LTS now is .NET Core 3.1 . Although, if someone requires , I can do a .NET Core 2.1

3. How can I serialize the HTTP arguments to send to the  commands?

There are 2 means of a program to read arguments: by Command Line arguments and by file on disk.

Now, let’s see how many options we have to configure a HTTP call : Verb, Arguments, ContentType, URL, Headers…. hard to fit into a command line. Not mentions that I want to have the option to run many HTTP calls / commands . So , as usual , it will be a mix: comand line for activating the file with the commands.

4. What do I do with the result of HTTP calls ?

The output of the commands will be written to the console. It is the easy way  – and you can redirect this output to a file to persist.

5. Where do I store the sources ?

Easy – GitHub – https://github.com/ignatandrei/WebAPI2CLI/

6. How do I find the the project works ?

Azure DevOps to the rescue – it can build the project and run tests. And it is free for Github

7. Where and how I deploy it ?

Where : easy – it will be hosted on NuGet https://www.nuget.org/packages/ExtensionNetCore3

How –easy  -Azure DevOps again.  https://dev.azure.com/ignatandrei0674/WebAPI2CLI/_build?definitionId=7&_a=summary

8. Where and how I can make the help for this project ?

Again –easy – GitHub has docs – https://ignatandrei.github.io/WebAPI2CLI/

 

Now let’s do the implementation!

 

New side project- WebAPI To CLI–part 1

WebAPI2CLI

This is a part of the series where about how I made the WebAPI2CLI - Execute ASP.NET Core WebAPI from Command Line
Source code on https://github.com/ignatandrei/webAPI2CLI/
1WebAPI2CLI - Description
2WebAPI2CLI- Organization
3WebAPI2CLI - implementing
4WebAPI2CLI - tests
5WebAPI2CLI - Devops and CI/CD
6WebAPI2CLI - documentation
7WebAPI2CLI - Conclusions
8WebAPI2CLI - Zip application

I have had an idea; What if the WebAPI application can be called from the console application itself  ?

This will permit just having the DI running  – and all the stuff that WebAPI can work with when calling a controller.

Will be good in a CI / CD workflow – testing the full API instead of relying to test just the dll ( Yes, I know about skinny controllers )

And will be good in Jenkins / any other tool / in order to execute directly the WebAPI instead of recurring to curl / powershell

 

So – seems to have a good piece of software to start – how we do this ?

First, how all the commands should be passed ? If we pass via command line, there is pretty much possible for someone to miss things.

Second , if the user does not pass via command line, the WebAPI should work as usual.

So , 5 days and 88 commits later , this is the result: https://github.com/ignatandrei/webAPI2CLI/ .

It has the documentation about how to install and use it. Enjoy !

 

.NET Core Multiple Authentication–Windows AD, Azure AD, Database

I was having a project about how to do in .NET Core multiple authentication: Windows AD, Azure AD, Database – and integrate with Roles.

It was an interesting project – and I decide to not make everything – but to construct on an existing project.

The most promising sounds IdentityServer – with the Federation Gateway. : http://docs.identityserver.io/en/latest/topics/federation_gateway.html

So I decided to give a twist , and after many readings and searching , I have found https://github.com/damienbod/AspNetCoreWindowsAuth . It was pretty amazing  – however, some problems occurred.

 

External Integration – with local Active Directory

See in Startup.cs the following

    services.Configure<IISOptions>(iis =>
        {
            iis.AuthenticationDisplayName = "Windows";
            iis.AutomaticAuthentication = true;
        });

When the user clicks the Windows authenticatio the following code is called

public class AccountController : Controller
{
    //ommitted code
    [HttpGet]
    public async Task<IActionResult> ExternalLogin(string provider, string returnUrl)
    {
        if (AccountOptions.WindowsAuthenticationSchemeName == provider)
        {
            // windows authentication needs special handling
            return await ProcessWindowsLoginAsync(returnUrl);
        }
        //ommitted code
    }
    //ommitted code
    private async Task<IActionResult> ProcessWindowsLoginAsync(string returnUrl)
        {
            // see if windows auth has already been requested and succeeded
            var result = await HttpContext.AuthenticateAsync(AccountOptions.WindowsAuthenticationSchemeName);
            if (result?.Principal is WindowsPrincipal wp)
            {
                //ommitted code
            }
            else
            {
                // trigger windows auth
                // since windows auth don't support the redirect uri,
                // this URL is re-triggered when we call challenge
                return Challenge(AccountOptions.WindowsAuthenticationSchemeName);
            }
        }
}

External Integration – with Azure Active Directory

This was by far the most complicated
As a pre-requisites , we need to configure the Azure Active Directory and grab the client Id for the application.

The code in the startup.cs os

        services
        .AddAuthentication(IISDefaults.AuthenticationScheme)
        .AddOpenIdConnect("aad", "Sign-in with Azure AD", options =>
        {
            //options.Authority = $"https://login.microsoftonline.com/common/v2.0/";
            //options.Authority = $"https://ignatandreiyahoo.onmicrosoft.com";
            //options.Authority = $"https://login.windows.net/{tenantId}";
            options.Authority = "https://login.microsoftonline.com/common/v2.0/";
            options.ClientId = $"{clientId}";
            //options.RequireHttpsMetadata = true;
            options.RemoteAuthenticationTimeout = new System.TimeSpan(0,1,58);

            options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
            options.SignOutScheme = IdentityServerConstants.SignoutScheme;

            options.ResponseType = OpenIdConnectResponseType.IdToken; //"id_token";
            options.CallbackPath = "/signin-aad";
            options.SignedOutCallbackPath = "/signout-callback-aad";
            options.RemoteSignOutPath = "/signout-aad";

            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = false,
                //ValidAudience = "f59d5739-1ec9-46fc-961d-b01ef6fb3c51",

                NameClaimType = "name",
                RoleClaimType = "role"
            };
            options.Events.OnRemoteFailure = (context) =>
            {
                string s = context.ToString();
                return Task.CompletedTask;
            };
        })

        ;
        services.AddOidcStateDataFormatterCache("aad");
   

And the code that retrieves the user is:

public class AccountController : Controller
{

    [HttpGet]
    public async Task<IActionResult> ExternalLoginCallback()
    {
        //ommitted code
        // read external identity from the temporary cookie
        var result = await HttpContext.AuthenticateAsync(IdentityConstants.ExternalScheme);
        if (result?.Succeeded != true)
        {
            result = await HttpContext.AuthenticateAsync("aad");
            if (result?.Succeeded != true)
            throw new Exception("External authentication error");
        }

        // lookup our user and external provider info
        var (user, provider, providerUserId, claims) = await FindUserFromExternalProviderAsync(result);
        //ommitted code
    }
    //ommitted code
    private async Task<(ApplicationUser user, string provider, string providerUserId, IEnumerable<Claim> claims)> 
        FindUserFromExternalProviderAsync(AuthenticateResult result)
    {
        var externalUser = result.Principal;

        // try to determine the unique id of the external user (issued by the provider)
        // the most common claim type for that are the sub claim and the NameIdentifier
        // depending on the external provider, some other claim type might be used
        var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ??
                          externalUser.FindFirst(ClaimTypes.NameIdentifier) ??
                          throw new Exception("Unknown userid");

        // remove the user id claim so we don't include it as an extra claim if/when we provision the user
        var claims = externalUser.Claims.ToList();
        claims.Remove(userIdClaim);

        var provider = result.Properties.Items["scheme"];
        var providerUserId = userIdClaim.Value;

        // find external user
        var user = await _userManager.FindByLoginAsync(provider, providerUserId);

        return (user, provider, providerUserId, claims);
    }
    

Configure Azure Active Directory

This implies to go to portal.azure.com.

First you create a new application in the Azure Active Directory

Please retain the applicationId in order to put to the code

    options.ClientId = $"{clientId}";
    

Do not forget about checking the token on authentication

because of this code

    options.ResponseType = OpenIdConnectResponseType.IdToken; //"id_token";
//ommitted code
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = false,
        //ValidAudience = "f59d5739-1ec9-46fc-961d-b01ef6fb3c51",

        NameClaimType = "name",
        RoleClaimType = "role"
    };

Roles for Windows


In Computer Management, I define the user : testUser that belongs to MyGroup

In startup.cs I define the Policy for this:

services.AddAuthorization(options =>
            {
                options.AddPolicy("MyGroupPolicy", policy => policy.RequireClaim("role",@"MyGroup"));
            });
    

I also define an Controller that require this policy

public class TestController : Controller
{
	[Authorize(Policy="MyGroupPolicy")]
	public IActionResult Index()
	{
		return Content(" you can see this because you are authorized to see MyGroupPolicy");
	}
}
    

So now if you logon on Windows with testuser, it will require the policy to be satisfied

Now, to understand the code

Read

https://docs.microsoft.com/en-us/aspnet/core/security/authorization/claims?view=aspnetcore-3.1#multiple-policy-evaluation

and

https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-3.1


Now some more code:

Transforming Groups to role claims

string pcName = Environment.MachineName;
// add the groups as claims -- be careful if the number of groups is too large
if (AccountOptions.IncludeWindowsGroups)
{
	var wi = wp.Identity as WindowsIdentity;
	var groups = wi.Groups.Translate(typeof(NTAccount));
	
	var roles = groups
		.Select(it=>it.Value)
		.Select(it=> it.StartsWith(pcName +"\\",StringComparison.InvariantCultureIgnoreCase)?
					it.Substring(pcName.Length+1): it)                        
		.Select(x => new Claim(JwtClaimTypes.Role, x));
	id.AddClaims(roles);
}

And putting back to user

[HttpGet]
public async Task<IActionResult> ExternalLoginCallback()
{
//omitted code
	// lookup our user and external provider info
	var (user, provider, providerUserId, claims) = await FindUserFromExternalProviderAsync(result);
//omitted code
    var principal = await _signInManager.CreateUserPrincipalAsync(user);

	foreach (var claim in claims)
	{
		additionalLocalClaims.AddRange(claims);
	}
//ommitted code
	await HttpContext.SignInAsync(user.Id, name, provider, localSignInProps, additionalLocalClaims.ToArray());


You can see the code at https://github.com/ignatandrei/identintegra and more documentation at https://ignatandrei.github.io/IdentIntegra/

Caching data backend–.NET Core–part 49

We have 2 options to cache data: One in webAPI, one when returns from database.

For WebAPI, I have read https://docs.microsoft.com/en-us/aspnet/core/performance/caching/response?view=aspnetcore-3.1

and

https://docs.microsoft.com/en-us/aspnet/core/performance/caching/middleware?view=aspnetcore-3.1

It is also important to read the conditions for caching : https://docs.microsoft.com/en-us/aspnet/core/performance/caching/middleware?view=aspnetcore-3.1#conditions-for-caching ( e.g. only GET or HEAD is cached)

It is just very easy – see github commit https://github.com/ignatandrei/InfoValutar/commit/b8d38658b5135e4d3c93da78f77ffb9f01376829

Also, can be tested by setting a breakpoint in VisualStudio  – see that is activated just 1 time.

Now, the interesting part – I need the cache for the remaining of the day, in case the exchange rate is in the same day .

Because I need this functionality for both ECB and BNR banks( and may for others), I either create a C# extension, either a base class (instead of an interface ) , either I use a feature of C# 8,  interface that have implementations.

The first try is here: https://github.com/ignatandrei/InfoValutar/commit/3aeeab7a5c519493764a365885b812f3da6ccb8e

Where I have do the code wrong: the key to cache and the items to cache are different. See

https://github.com/ignatandrei/InfoValutar/commit/a68da1f16a27ae06c7978d3acd9b2f1ba1bebc0f

I am using Memory Cache

namespace InfoValutarShared
{
#pragma warning disable IDE1006 // Naming Styles
public interface BankGetExchange
#pragma warning restore IDE1006 // Naming Styles
{
public string Bank { get; }
Task<IEnumerable<ExchangeRates>> GetActualRates();
DateTime Key()
{
var now = DateTime.UtcNow;
if (now.DayOfWeek == DayOfWeek.Sunday)
now = now.AddDays(-1);
if (now.DayOfWeek == DayOfWeek.Saturday)
now = now.AddDays(-1);
return now;

}
IEnumerable<ExchangeRates> TodayFromCache
{
get

{
var keyDate = Key();
string key = $”{this.Bank}_{keyDate.ToString(“yyyyMMdd”)}”;
var mc = MemoryCache.Default;
if (mc.Contains(key))
return mc[key] as ExchangeRates[];

return null;

}
set
{
var keyDate = Key();
string key = $”{this.Bank}_{keyDate.ToString(“yyyyMMdd”)}”;
var mc = MemoryCache.Default;
value = value
.Where(it => Math.Abs(it.Date.Subtract(keyDate).TotalDays) < 1)
.ToArray();
if (value.Any())
{
mc.Set(key, value, DateTime.UtcNow.AddDays(7));

}
}
}

}
}

and using this like this

public async Task<IEnumerable<ExchangeRates>> GetActualRates()
{
var b = this as BankGetExchange;
if (b.TodayFromCache != null)
return b.TodayFromCache;

//code omitted

}
b.TodayFromCache = ret;

return ret;

 

And this is it…

Routing angular with .net core and RxJs switchmap- part 46

Now the problem is that the route

 

works if I access first the index.html file, but it does not route when entered directly

Trying to get from SPATemplate

https://github.com/ignatandrei/InfoValutar/commit/a364dc57653ac2ba04ab0596f1540a8f9fdb73f6

However, this will redirect ALL routes to index.html – including swagger and API.  Trying to learn more – looking at the code.

I have observed that I have put

app.UseOpenApi();
app.UseSwaggerUi3();

after

this.route.params.pipe(

tap(rp => {

this.idBank = rp.id;

})

, switchMap((it) => this.bs.GetRates(it.id))

, tap(v => this.rates = v)

).subscribe();

What If I put first?

app.UseOpenApi();
app.UseSwaggerUi3();

app.UseSpa(spa =>

 

And it works! https://github.com/ignatandrei/InfoValutar/commit/726ebd07095ea62387d4eeebb017b4f6ae4ab8e6

What I need more, is to retrieve the exchange rates for today for the selected bank.

The code to retrieve the bank id was:

this.route.params.subscribe(rp => {

this.idBank = rp.id;

}

);

I can put into subscribe the next call, but it will be not so nice ( subscribe into subscribe into subscribe)

this.route.params.subscribe(rp => {

this.idBank = rp.id;

this.bs.GetRates(it.id).subscribe( ….)

}

)

 

Instead of this, I use switchMap , pipe and tap

this.route.params.pipe(

tap(rp => {

this.idBank = rp.id;

})

, switchMap((it) => this.bs.GetRates(it.id))

, tap(v => this.rates = v)

).subscribe();

To fast show data, I use the Json pipe from Angular

<ul>

<li *ngFor=”let rate of rates”>

{{rate | json}}

</li>

</ul>

Modifications at https://github.com/ignatandrei/InfoValutar/commit/a27c7c7beb4ea10b9f8a522b01d558c6003d5386

Infovalutar

And one hour passes...
(This is the result of 1 hour per day auto-challenge as a full cycle developer for an exchange rates application)
( You can see the sources at https://github.com/ignatandrei/InfoValutar/ )
NrPost 
1Start
2Reading NBR from internet
3Source control and build
4Badge and test
5CI and action
6Artifacts and dotnet try
7Docker with .NET Try
8ECB
9Intermezzo - Various implementations for programmers
10Intermezzo - similar code - options
11Plugin implementation
12GUI for console
13WebAPI
14Plugin in .NET Core 3
15Build and Versioning
16Add swagger
17Docker - first part
18Docker - second part
19Docker - build Azure
20Pipeline send to Docker Hub
21Play with Docker - online
22Run VSCode and Docker
23Deploy Azure
24VSCode see tests and powershell
25Code Coverage
26Database in Azure
27Sql In Memory or Azure
28Azure ConString, RSS
29Middleware for backward compatibility
30Identical Tables in EFCore
31Multiple Data in EFCore
32Dot net try again
33Start Azure Function
34Azure function - deploy
35Solving my problems
36IAsyncEnumerable transformed to IEnumerable and making Azure Functions works
37Azure functions - final
38Review of 37 hours
39Last Commit in AzureDevOps
40Create Angular WebSite
41Add static Angular to WebAPI .NET Core
42Docker for Angular
43Angular and CORS
44SSL , VSCode, Docker
45Routing in Angular
46RxJS for Routing
47RxJs Unsubscribe

Andrei Ignat weekly software news(mostly .NET)

* indicates required

Please select all the ways you would like to hear from me:

You can unsubscribe at any time by clicking the link in the footer of our emails. For information about our privacy practices, please visit our website.

We use Mailchimp as our marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp's privacy practices here.