From 1bba144092737ef7633c5fbde0d55373e777a071 Mon Sep 17 00:00:00 2001 From: Devin Garner Date: Tue, 17 Apr 2018 10:34:10 -0600 Subject: [PATCH 1/9] Correct Twitter OAuth signing code & refactor copy/pasted code. #1695 (#1720) --- .../TwitterHandler.cs | 182 ++++++------------ 1 file changed, 63 insertions(+), 119 deletions(-) diff --git a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs index 670e76f7e..d0d54bf74 100644 --- a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs @@ -148,53 +148,92 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop await Events.RedirectToAuthorizationEndpoint(redirectContext); } - private async Task ObtainRequestTokenAsync(string callBackUri, AuthenticationProperties properties) + private async Task ExecuteRequestAsync(string url, HttpMethod httpMethod, RequestToken accessToken = null, Dictionary extraOAuthPairs = null, Dictionary queryParameters = null, Dictionary formData = null) { - Logger.ObtainRequestToken(); - - var nonce = Guid.NewGuid().ToString("N"); - - var authorizationParts = new SortedDictionary + var authorizationParts = new SortedDictionary(extraOAuthPairs ?? new Dictionary()) { - { "oauth_callback", callBackUri }, { "oauth_consumer_key", Options.ConsumerKey }, - { "oauth_nonce", nonce }, + { "oauth_nonce", Guid.NewGuid().ToString("N") }, { "oauth_signature_method", "HMAC-SHA1" }, { "oauth_timestamp", GenerateTimeStamp() }, { "oauth_version", "1.0" } }; + if (accessToken != null) + { + authorizationParts.Add("oauth_token", accessToken.Token); + } + + var signatureParts = new SortedDictionary(authorizationParts); + if (queryParameters != null) + { + foreach (var queryParameter in queryParameters) + { + signatureParts.Add(queryParameter.Key, queryParameter.Value); + } + } + if (formData != null) + { + foreach (var formItem in formData) + { + signatureParts.Add(formItem.Key, formItem.Value); + } + } + var parameterBuilder = new StringBuilder(); - foreach (var authorizationKey in authorizationParts) + foreach (var signaturePart in signatureParts) { - parameterBuilder.AppendFormat("{0}={1}&", UrlEncoder.Encode(authorizationKey.Key), UrlEncoder.Encode(authorizationKey.Value)); + parameterBuilder.AppendFormat("{0}={1}&", Uri.EscapeDataString(signaturePart.Key), Uri.EscapeDataString(signaturePart.Value)); } parameterBuilder.Length--; var parameterString = parameterBuilder.ToString(); var canonicalizedRequestBuilder = new StringBuilder(); - canonicalizedRequestBuilder.Append(HttpMethod.Post.Method); + canonicalizedRequestBuilder.Append(httpMethod.Method); canonicalizedRequestBuilder.Append("&"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(RequestTokenEndpoint)); + canonicalizedRequestBuilder.Append(Uri.EscapeDataString(url)); canonicalizedRequestBuilder.Append("&"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(parameterString)); + canonicalizedRequestBuilder.Append(Uri.EscapeDataString(parameterString)); - var signature = ComputeSignature(Options.ConsumerSecret, null, canonicalizedRequestBuilder.ToString()); + var signature = ComputeSignature(Options.ConsumerSecret, accessToken?.TokenSecret, canonicalizedRequestBuilder.ToString()); authorizationParts.Add("oauth_signature", signature); + var queryString = ""; + if (queryParameters != null) + { + var queryStringBuilder = new StringBuilder("?"); + foreach (var queryParam in queryParameters) + { + queryStringBuilder.AppendFormat("{0}={1}&", queryParam.Key, queryParam.Value); + } + queryStringBuilder.Length--; + queryString = queryStringBuilder.ToString(); + } + var authorizationHeaderBuilder = new StringBuilder(); authorizationHeaderBuilder.Append("OAuth "); foreach (var authorizationPart in authorizationParts) { - authorizationHeaderBuilder.AppendFormat( - "{0}=\"{1}\", ", authorizationPart.Key, UrlEncoder.Encode(authorizationPart.Value)); + authorizationHeaderBuilder.AppendFormat("{0}=\"{1}\",", authorizationPart.Key, Uri.EscapeDataString(authorizationPart.Value)); } - authorizationHeaderBuilder.Length = authorizationHeaderBuilder.Length - 2; + authorizationHeaderBuilder.Length--; - var request = new HttpRequestMessage(HttpMethod.Post, RequestTokenEndpoint); + var request = new HttpRequestMessage(httpMethod, url + queryString); request.Headers.Add("Authorization", authorizationHeaderBuilder.ToString()); - var response = await Backchannel.SendAsync(request, Context.RequestAborted); + if (formData != null) + { + request.Content = new FormUrlEncodedContent(formData); + } + + return await Backchannel.SendAsync(request, Context.RequestAborted); + } + + private async Task ObtainRequestTokenAsync(string callBackUri, AuthenticationProperties properties) + { + Logger.ObtainRequestToken(); + + var response = await ExecuteRequestAsync(RequestTokenEndpoint, HttpMethod.Post, extraOAuthPairs: new Dictionary() { { "oauth_callback", callBackUri } }); response.EnsureSuccessStatusCode(); var responseText = await response.Content.ReadAsStringAsync(); @@ -213,58 +252,8 @@ private async Task ObtainAccessTokenAsync(RequestToken token, strin Logger.ObtainAccessToken(); - var nonce = Guid.NewGuid().ToString("N"); - - var authorizationParts = new SortedDictionary - { - { "oauth_consumer_key", Options.ConsumerKey }, - { "oauth_nonce", nonce }, - { "oauth_signature_method", "HMAC-SHA1" }, - { "oauth_token", token.Token }, - { "oauth_timestamp", GenerateTimeStamp() }, - { "oauth_verifier", verifier }, - { "oauth_version", "1.0" }, - }; - - var parameterBuilder = new StringBuilder(); - foreach (var authorizationKey in authorizationParts) - { - parameterBuilder.AppendFormat("{0}={1}&", UrlEncoder.Encode(authorizationKey.Key), UrlEncoder.Encode(authorizationKey.Value)); - } - parameterBuilder.Length--; - var parameterString = parameterBuilder.ToString(); - - var canonicalizedRequestBuilder = new StringBuilder(); - canonicalizedRequestBuilder.Append(HttpMethod.Post.Method); - canonicalizedRequestBuilder.Append("&"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(AccessTokenEndpoint)); - canonicalizedRequestBuilder.Append("&"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(parameterString)); - - var signature = ComputeSignature(Options.ConsumerSecret, token.TokenSecret, canonicalizedRequestBuilder.ToString()); - authorizationParts.Add("oauth_signature", signature); - authorizationParts.Remove("oauth_verifier"); - - var authorizationHeaderBuilder = new StringBuilder(); - authorizationHeaderBuilder.Append("OAuth "); - foreach (var authorizationPart in authorizationParts) - { - authorizationHeaderBuilder.AppendFormat( - "{0}=\"{1}\", ", authorizationPart.Key, UrlEncoder.Encode(authorizationPart.Value)); - } - authorizationHeaderBuilder.Length = authorizationHeaderBuilder.Length - 2; - - var request = new HttpRequestMessage(HttpMethod.Post, AccessTokenEndpoint); - request.Headers.Add("Authorization", authorizationHeaderBuilder.ToString()); - - var formPairs = new Dictionary() - { - { "oauth_verifier", verifier }, - }; - - request.Content = new FormUrlEncodedContent(formPairs); - - var response = await Backchannel.SendAsync(request, Context.RequestAborted); + var formPost = new Dictionary { { "oauth_verifier", verifier } }; + var response = await ExecuteRequestAsync(AccessTokenEndpoint, HttpMethod.Post, token, formData: formPost); if (!response.IsSuccessStatusCode) { @@ -289,53 +278,8 @@ private async Task RetrieveUserDetailsAsync(AccessToken accessToken, Cl { Logger.RetrieveUserDetails(); - var nonce = Guid.NewGuid().ToString("N"); - - var authorizationParts = new SortedDictionary - { - { "oauth_consumer_key", Options.ConsumerKey }, - { "oauth_nonce", nonce }, - { "oauth_signature_method", "HMAC-SHA1" }, - { "oauth_timestamp", GenerateTimeStamp() }, - { "oauth_token", accessToken.Token }, - { "oauth_version", "1.0" } - }; - - var parameterBuilder = new StringBuilder(); - foreach (var authorizationKey in authorizationParts) - { - parameterBuilder.AppendFormat("{0}={1}&", UrlEncoder.Encode(authorizationKey.Key), UrlEncoder.Encode(authorizationKey.Value)); - } - parameterBuilder.Length--; - var parameterString = parameterBuilder.ToString(); - - var resource_url = "https://api.twitter.com/1.1/account/verify_credentials.json"; - var resource_query = "include_email=true"; - var canonicalizedRequestBuilder = new StringBuilder(); - canonicalizedRequestBuilder.Append(HttpMethod.Get.Method); - canonicalizedRequestBuilder.Append("&"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(resource_url)); - canonicalizedRequestBuilder.Append("&"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(resource_query)); - canonicalizedRequestBuilder.Append("%26"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(parameterString)); - - var signature = ComputeSignature(Options.ConsumerSecret, accessToken.TokenSecret, canonicalizedRequestBuilder.ToString()); - authorizationParts.Add("oauth_signature", signature); - - var authorizationHeaderBuilder = new StringBuilder(); - authorizationHeaderBuilder.Append("OAuth "); - foreach (var authorizationPart in authorizationParts) - { - authorizationHeaderBuilder.AppendFormat( - "{0}=\"{1}\", ", authorizationPart.Key, UrlEncoder.Encode(authorizationPart.Value)); - } - authorizationHeaderBuilder.Length = authorizationHeaderBuilder.Length - 2; - - var request = new HttpRequestMessage(HttpMethod.Get, resource_url + "?include_email=true"); - request.Headers.Add("Authorization", authorizationHeaderBuilder.ToString()); + var response = await ExecuteRequestAsync("https://api.twitter.com/1.1/account/verify_credentials.json", HttpMethod.Get, accessToken, queryParameters: new Dictionary() { { "include_email", "true" } }); - var response = await Backchannel.SendAsync(request, Context.RequestAborted); if (!response.IsSuccessStatusCode) { Logger.LogError("Email request failed with a status code of " + response.StatusCode); @@ -361,8 +305,8 @@ private string ComputeSignature(string consumerSecret, string tokenSecret, strin algorithm.Key = Encoding.ASCII.GetBytes( string.Format(CultureInfo.InvariantCulture, "{0}&{1}", - UrlEncoder.Encode(consumerSecret), - string.IsNullOrEmpty(tokenSecret) ? string.Empty : UrlEncoder.Encode(tokenSecret))); + Uri.EscapeDataString(consumerSecret), + string.IsNullOrEmpty(tokenSecret) ? string.Empty : Uri.EscapeDataString(tokenSecret))); var hash = algorithm.ComputeHash(Encoding.ASCII.GetBytes(signatureData)); return Convert.ToBase64String(hash); } From ad4ab722a864fdbf8814094c1b37c9612b48ff56 Mon Sep 17 00:00:00 2001 From: Alexey Malinin Date: Tue, 1 May 2018 23:22:27 +0700 Subject: [PATCH 2/9] Code improvements of JwtBearer module (#1742) * Use pattern matching to check exception type in JwtBearerHandler * Use expression body to configure events in JwtBearerHandler --- .../JwtBearerHandler.cs | 62 +++++++++---------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs b/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs index 6d5c7f5f5..452d9639f 100644 --- a/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs @@ -32,8 +32,8 @@ public JwtBearerHandler(IOptionsMonitor options, ILoggerFactor /// protected new JwtBearerEvents Events { - get { return (JwtBearerEvents)base.Events; } - set { base.Events = value; } + get => (JwtBearerEvents)base.Events; + set => base.Events = value; } protected override Task CreateEventsAsync() => Task.FromResult(new JwtBearerEvents()); @@ -267,9 +267,8 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop private static string CreateErrorDescription(Exception authFailure) { IEnumerable exceptions; - if (authFailure is AggregateException) + if (authFailure is AggregateException agEx) { - var agEx = authFailure as AggregateException; exceptions = agEx.InnerExceptions; } else @@ -283,37 +282,32 @@ private static string CreateErrorDescription(Exception authFailure) { // Order sensitive, some of these exceptions derive from others // and we want to display the most specific message possible. - if (ex is SecurityTokenInvalidAudienceException) + switch (ex) { - messages.Add("The audience is invalid"); - } - else if (ex is SecurityTokenInvalidIssuerException) - { - messages.Add("The issuer is invalid"); - } - else if (ex is SecurityTokenNoExpirationException) - { - messages.Add("The token has no expiration"); - } - else if (ex is SecurityTokenInvalidLifetimeException) - { - messages.Add("The token lifetime is invalid"); - } - else if (ex is SecurityTokenNotYetValidException) - { - messages.Add("The token is not valid yet"); - } - else if (ex is SecurityTokenExpiredException) - { - messages.Add("The token is expired"); - } - else if (ex is SecurityTokenSignatureKeyNotFoundException) - { - messages.Add("The signature key was not found"); - } - else if (ex is SecurityTokenInvalidSignatureException) - { - messages.Add("The signature is invalid"); + case SecurityTokenInvalidAudienceException _: + messages.Add("The audience is invalid"); + break; + case SecurityTokenInvalidIssuerException _: + messages.Add("The issuer is invalid"); + break; + case SecurityTokenNoExpirationException _: + messages.Add("The token has no expiration"); + break; + case SecurityTokenInvalidLifetimeException _: + messages.Add("The token lifetime is invalid"); + break; + case SecurityTokenNotYetValidException _: + messages.Add("The token is not valid yet"); + break; + case SecurityTokenExpiredException _: + messages.Add("The token is expired"); + break; + case SecurityTokenSignatureKeyNotFoundException _: + messages.Add("The signature key was not found"); + break; + case SecurityTokenInvalidSignatureException _: + messages.Add("The signature is invalid"); + break; } } From 8d4f51cf40ed2df1d35b7e2fe8414dd7c4f961bc Mon Sep 17 00:00:00 2001 From: "Chris Ross (ASP.NET)" Date: Wed, 16 May 2018 15:41:01 -0700 Subject: [PATCH 3/9] Rewrite JwtBearer token test #640 --- .../JwtBearerTests.cs | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/test/Microsoft.AspNetCore.Authentication.Test/JwtBearerTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/JwtBearerTests.cs index b472a4162..20d625d31 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/JwtBearerTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/JwtBearerTests.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Net; using System.Net.Http; @@ -432,19 +433,37 @@ public async Task VerifySchemeDefaults() Assert.Null(scheme.DisplayName); } - [ConditionalFact(Skip = "Need to remove dependency on AAD since the generated tokens will expire")] - [FrameworkSkipCondition(RuntimeFrameworks.Mono)] - // https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/179 + [Fact] public async Task BearerTokenValidation() { + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(new string('a', 128))); + var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + + var claims = new[] + { + new Claim(ClaimTypes.NameIdentifier, "Bob") + }; + + var token = new JwtSecurityToken( + issuer: "issuer.contoso.com", + audience: "audience.contoso.com", + claims: claims, + expires: DateTime.Now.AddMinutes(30), + signingCredentials: creds); + + var tokenText = new JwtSecurityTokenHandler().WriteToken(token); + var server = CreateServer(o => { - o.Authority = "https://login.windows.net/tushartest.onmicrosoft.com"; - o.Audience = "https://TusharTest.onmicrosoft.com/TodoListService-ManualJwt"; - o.TokenValidationParameters.ValidateLifetime = false; + o.TokenValidationParameters = new TokenValidationParameters() + { + ValidIssuer = "issuer.contoso.com", + ValidAudience = "audience.contoso.com", + IssuerSigningKey = key, + }; }); - var newBearerToken = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6ImtyaU1QZG1Cdng2OHNrVDgtbVBBQjNCc2VlQSJ9.eyJhdWQiOiJodHRwczovL1R1c2hhclRlc3Qub25taWNyb3NvZnQuY29tL1RvZG9MaXN0U2VydmljZS1NYW51YWxKd3QiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9hZmJlY2UwMy1hZWFhLTRmM2YtODVlNy1jZTA4ZGQyMGNlNTAvIiwiaWF0IjoxNDE4MzMwNjE0LCJuYmYiOjE0MTgzMzA2MTQsImV4cCI6MTQxODMzNDUxNCwidmVyIjoiMS4wIiwidGlkIjoiYWZiZWNlMDMtYWVhYS00ZjNmLTg1ZTctY2UwOGRkMjBjZTUwIiwiYW1yIjpbInB3ZCJdLCJvaWQiOiI1Mzk3OTdjMi00MDE5LTQ2NTktOWRiNS03MmM0Yzc3NzhhMzMiLCJ1cG4iOiJWaWN0b3JAVHVzaGFyVGVzdC5vbm1pY3Jvc29mdC5jb20iLCJ1bmlxdWVfbmFtZSI6IlZpY3RvckBUdXNoYXJUZXN0Lm9ubWljcm9zb2Z0LmNvbSIsInN1YiI6IkQyMm9aMW9VTzEzTUFiQXZrdnFyd2REVE80WXZJdjlzMV9GNWlVOVUwYnciLCJmYW1pbHlfbmFtZSI6Ikd1cHRhIiwiZ2l2ZW5fbmFtZSI6IlZpY3RvciIsImFwcGlkIjoiNjEzYjVhZjgtZjJjMy00MWI2LWExZGMtNDE2Yzk3ODAzMGI3IiwiYXBwaWRhY3IiOiIwIiwic2NwIjoidXNlcl9pbXBlcnNvbmF0aW9uIiwiYWNyIjoiMSJ9.N_Kw1EhoVGrHbE6hOcm7ERdZ7paBQiNdObvp2c6T6n5CE8p0fZqmUd-ya_EqwElcD6SiKSiP7gj0gpNUnOJcBl_H2X8GseaeeMxBrZdsnDL8qecc6_ygHruwlPltnLTdka67s1Ow4fDSHaqhVTEk6lzGmNEcbNAyb0CxQxU6o7Fh0yHRiWoLsT8yqYk8nKzsHXfZBNby4aRo3_hXaa4i0SZLYfDGGYPdttG4vT_u54QGGd4Wzbonv2gjDlllOVGOwoJS6kfl1h8mk0qxdiIaT_ChbDWgkWvTB7bTvBE-EgHgV0XmAo0WtJeSxgjsG3KhhEPsONmqrSjhIUV4IVnF2w"; + var newBearerToken = "Bearer " + tokenText; var response = await SendAsync(server, "http://example.com/oauth", newBearerToken); Assert.Equal(HttpStatusCode.OK, response.Response.StatusCode); } From 402116feb2c4f71c859cf98fa0e7191e38d56e46 Mon Sep 17 00:00:00 2001 From: Rick Anderson Date: Fri, 18 May 2018 14:56:14 -1000 Subject: [PATCH 4/9] Cookie Policy sample essential cookie (#1762) --- samples/CookiePolicySample/Startup.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/samples/CookiePolicySample/Startup.cs b/samples/CookiePolicySample/Startup.cs index 7ce9c2d2d..4dcc6d4dc 100644 --- a/samples/CookiePolicySample/Startup.cs +++ b/samples/CookiePolicySample/Startup.cs @@ -58,6 +58,13 @@ private void NestedApp(IApplicationBuilder app) case "/RemoveTempCookie": context.Response.Cookies.Delete("Temp"); break; + case "/CreateEssentialCookie": + context.Response.Cookies.Append("EssentialCookie", "2", + new CookieOptions() { IsEssential = true }); + break; + case "/RemoveEssentialCookie": + context.Response.Cookies.Delete("EssentialCookie"); + break; case "/GrantConsent": context.Features.Get().GrantConsent(); break; @@ -84,6 +91,8 @@ private async Task HomePage(HttpContext context) await response.WriteAsync($"Logout
\r\n"); await response.WriteAsync($"Create Temp Cookie
\r\n"); await response.WriteAsync($"Remove Temp Cookie
\r\n"); + await response.WriteAsync($"Create Essential Cookie
\r\n"); + await response.WriteAsync($"Remove Essential Cookie
\r\n"); await response.WriteAsync($"Grant Consent
\r\n"); await response.WriteAsync($"Withdraw Consent
\r\n"); await response.WriteAsync("
\r\n"); From eed86864b475baa8fbcc073e08aacd7777c7a406 Mon Sep 17 00:00:00 2001 From: Tim Hess Date: Wed, 23 May 2018 10:25:19 -0500 Subject: [PATCH 5/9] Include AuthenticationTicket.Properties in AuthenticationTicket success result handling #1765 (#1767) --- src/Microsoft.AspNetCore.Authentication/HandleRequestResult.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.AspNetCore.Authentication/HandleRequestResult.cs b/src/Microsoft.AspNetCore.Authentication/HandleRequestResult.cs index 3f6c2d917..da9b6ea01 100644 --- a/src/Microsoft.AspNetCore.Authentication/HandleRequestResult.cs +++ b/src/Microsoft.AspNetCore.Authentication/HandleRequestResult.cs @@ -33,7 +33,7 @@ public class HandleRequestResult : AuthenticateResult { throw new ArgumentNullException(nameof(ticket)); } - return new HandleRequestResult() { Ticket = ticket }; + return new HandleRequestResult() { Ticket = ticket, Properties = ticket.Properties }; } /// From ce316be4c20fe6a4de8e9a8dce31525193ff034a Mon Sep 17 00:00:00 2001 From: "Nate McMaster (automated)" Date: Fri, 25 May 2018 16:17:12 -0700 Subject: [PATCH 6/9] Update bootstrapper scripts (automated commit) [ci skip] --- run.ps1 | 25 +++++++++++++++++++------ run.sh | 33 +++++++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/run.ps1 b/run.ps1 index 27dcf848f..3b2738246 100644 --- a/run.ps1 +++ b/run.ps1 @@ -26,12 +26,18 @@ The base url where build tools can be downloaded. Overrides the value from the c .PARAMETER Update Updates KoreBuild to the latest version even if a lock file is present. +.PARAMETER Reinstall +Re-installs KoreBuild + .PARAMETER ConfigFile The path to the configuration file that stores values. Defaults to korebuild.json. .PARAMETER ToolsSourceSuffix The Suffix to append to the end of the ToolsSource. Useful for query strings in blob stores. +.PARAMETER CI +Sets up CI specific settings and variables. + .PARAMETER Arguments Arguments to be passed to the command @@ -65,8 +71,10 @@ param( [string]$ToolsSource, [Alias('u')] [switch]$Update, - [string]$ConfigFile, + [switch]$Reinstall, [string]$ToolsSourceSuffix, + [string]$ConfigFile = $null, + [switch]$CI, [Parameter(ValueFromRemainingArguments = $true)] [string[]]$Arguments ) @@ -93,6 +101,10 @@ function Get-KoreBuild { $version = $version.TrimStart('version:').Trim() $korebuildPath = Join-Paths $DotNetHome ('buildtools', 'korebuild', $version) + if ($Reinstall -and (Test-Path $korebuildPath)) { + Remove-Item -Force -Recurse $korebuildPath + } + if (!(Test-Path $korebuildPath)) { Write-Host -ForegroundColor Magenta "Downloading KoreBuild $version" New-Item -ItemType Directory -Path $korebuildPath | Out-Null @@ -101,9 +113,9 @@ function Get-KoreBuild { try { $tmpfile = Join-Path ([IO.Path]::GetTempPath()) "KoreBuild-$([guid]::NewGuid()).zip" Get-RemoteFile $remotePath $tmpfile $ToolsSourceSuffix - if (Get-Command -Name 'Expand-Archive' -ErrorAction Ignore) { + if (Get-Command -Name 'Microsoft.PowerShell.Archive\Expand-Archive' -ErrorAction Ignore) { # Use built-in commands where possible as they are cross-plat compatible - Expand-Archive -Path $tmpfile -DestinationPath $korebuildPath + Microsoft.PowerShell.Archive\Expand-Archive -Path $tmpfile -DestinationPath $korebuildPath } else { # Fallback to old approach for old installations of PowerShell @@ -167,8 +179,9 @@ if (Test-Path $ConfigFile) { } } catch { - Write-Warning "$ConfigFile could not be read. Its settings will be ignored." - Write-Warning $Error[0] + Write-Host -ForegroundColor Red $Error[0] + Write-Error "$ConfigFile contains invalid JSON." + exit 1 } } @@ -188,7 +201,7 @@ $korebuildPath = Get-KoreBuild Import-Module -Force -Scope Local (Join-Path $korebuildPath 'KoreBuild.psd1') try { - Set-KoreBuildSettings -ToolsSource $ToolsSource -DotNetHome $DotNetHome -RepoPath $Path -ConfigFile $ConfigFile + Set-KoreBuildSettings -ToolsSource $ToolsSource -DotNetHome $DotNetHome -RepoPath $Path -ConfigFile $ConfigFile -CI:$CI Invoke-KoreBuildCommand $Command @Arguments } finally { diff --git a/run.sh b/run.sh index 834961fc3..02aac1587 100755 --- a/run.sh +++ b/run.sh @@ -14,10 +14,12 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" [ -z "${DOTNET_HOME:-}" ] && DOTNET_HOME="$HOME/.dotnet" verbose=false update=false +reinstall=false repo_path="$DIR" channel='' tools_source='' tools_source_suffix='' +ci=false # # Functions @@ -38,6 +40,8 @@ __usage() { echo " -s|--tools-source|-ToolsSource The base url where build tools can be downloaded. Overrides the value from the config file." echo " --tools-source-suffix|-ToolsSourceSuffix The suffix to append to tools-source. Useful for query strings." echo " -u|--update Update to the latest KoreBuild even if the lock file is present." + echo " --reinstall Reinstall KoreBuild." + echo " --ci Apply CI specific settings and environment variables." echo "" echo "Description:" echo " This function will create a file \$DIR/korebuild-lock.txt. This lock file can be committed to source, but does not have to be." @@ -62,6 +66,10 @@ get_korebuild() { version="$(echo "${version#version:}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" local korebuild_path="$DOTNET_HOME/buildtools/korebuild/$version" + if [ "$reinstall" = true ] && [ -d "$korebuild_path" ]; then + rm -rf "$korebuild_path" + fi + { if [ ! -d "$korebuild_path" ]; then mkdir -p "$korebuild_path" @@ -175,6 +183,12 @@ while [[ $# -gt 0 ]]; do -u|--update|-Update) update=true ;; + --reinstall|-[Rr]einstall) + reinstall=true + ;; + --ci|-[Cc][Ii]) + ci=true + ;; --verbose|-Verbose) verbose=true ;; @@ -206,17 +220,28 @@ if [ -f "$config_file" ]; then config_channel="$(jq -r 'select(.channel!=null) | .channel' "$config_file")" config_tools_source="$(jq -r 'select(.toolsSource!=null) | .toolsSource' "$config_file")" else - __warn "$config_file is invalid JSON. Its settings will be ignored." + _error "$config_file contains invalid JSON." + exit 1 fi elif __machine_has python ; then if python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'))" >/dev/null ; then config_channel="$(python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['channel'] if 'channel' in obj else '')")" config_tools_source="$(python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['toolsSource'] if 'toolsSource' in obj else '')")" else - __warn "$config_file is invalid JSON. Its settings will be ignored." + _error "$config_file contains invalid JSON." + exit 1 + fi + elif __machine_has python3 ; then + if python3 -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'))" >/dev/null ; then + config_channel="$(python3 -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['channel'] if 'channel' in obj else '')")" + config_tools_source="$(python3 -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['toolsSource'] if 'toolsSource' in obj else '')")" + else + _error "$config_file contains invalid JSON." + exit 1 fi else - __warn 'Missing required command: jq or pyton. Could not parse the JSON file. Its settings will be ignored.' + _error 'Missing required command: jq or python. Could not parse the JSON file.' + exit 1 fi [ ! -z "${config_channel:-}" ] && channel="$config_channel" @@ -227,5 +252,5 @@ fi [ -z "$tools_source" ] && tools_source='https://aspnetcore.blob.core.windows.net/buildtools' get_korebuild -set_korebuildsettings "$tools_source" "$DOTNET_HOME" "$repo_path" "$config_file" +set_korebuildsettings "$tools_source" "$DOTNET_HOME" "$repo_path" "$config_file" "$ci" invoke_korebuild_command "$command" "$@" From efa700367e3a5ad0b5bd4c3621510c529862afb0 Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Thu, 7 Jun 2018 15:48:06 -0700 Subject: [PATCH 7/9] Adding VSTS file --- .vsts-pipelines/builds/ci-internal.yml | 13 +++++++++++++ .vsts-pipelines/builds/ci-public.yml | 15 +++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 .vsts-pipelines/builds/ci-internal.yml create mode 100644 .vsts-pipelines/builds/ci-public.yml diff --git a/.vsts-pipelines/builds/ci-internal.yml b/.vsts-pipelines/builds/ci-internal.yml new file mode 100644 index 000000000..d7ceb7637 --- /dev/null +++ b/.vsts-pipelines/builds/ci-internal.yml @@ -0,0 +1,13 @@ +trigger: +- dev +- release/* + +resources: + repositories: + - repository: buildtools + type: git + name: aspnet-BuildTools + ref: refs/heads/dev + +phases: +- template: .vsts-pipelines/templates/project-ci.yml@buildtools diff --git a/.vsts-pipelines/builds/ci-public.yml b/.vsts-pipelines/builds/ci-public.yml new file mode 100644 index 000000000..b7f25723f --- /dev/null +++ b/.vsts-pipelines/builds/ci-public.yml @@ -0,0 +1,15 @@ +trigger: +- dev +- release/* + +# See https://github.com/aspnet/BuildTools +resources: + repositories: + - repository: buildtools + type: github + endpoint: DotNet-Bot GitHub Connection + name: aspnet/BuildTools + ref: refs/heads/dev + +phases: +- template: .vsts-pipelines/templates/project-ci.yml@buildtools From 6b7846c4336d7f3885df9be14d37b56ddeefdedd Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Thu, 28 Jun 2018 15:56:06 -0700 Subject: [PATCH 8/9] Fix regression in Cookies --- .../CookieAuthenticationHandler.cs | 11 +-- .../CookieTests.cs | 70 +++++++++++++++++++ 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs index 343cf1b3a..b77a51ef4 100644 --- a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs @@ -85,7 +85,7 @@ private void CheckForRefresh(AuthenticationTicket ticket) } } - private void RequestRefresh(AuthenticationTicket ticket) + private void RequestRefresh(AuthenticationTicket ticket, ClaimsPrincipal replacedPrincipal = null) { var issuedUtc = ticket.Properties.IssuedUtc; var expiresUtc = ticket.Properties.ExpiresUtc; @@ -97,14 +97,15 @@ private void RequestRefresh(AuthenticationTicket ticket) _refreshIssuedUtc = currentUtc; var timeSpan = expiresUtc.Value.Subtract(issuedUtc.Value); _refreshExpiresUtc = currentUtc.Add(timeSpan); - _refreshTicket = CloneTicket(ticket); + _refreshTicket = CloneTicket(ticket, replacedPrincipal); } } - private AuthenticationTicket CloneTicket(AuthenticationTicket ticket) + private AuthenticationTicket CloneTicket(AuthenticationTicket ticket, ClaimsPrincipal replacedPrincipal) { + var principal = replacedPrincipal ?? ticket.Principal; var newPrincipal = new ClaimsPrincipal(); - foreach (var identity in ticket.Principal.Identities) + foreach (var identity in principal.Identities) { newPrincipal.AddIdentity(identity.Clone()); } @@ -183,7 +184,7 @@ protected override async Task HandleAuthenticateAsync() if (context.ShouldRenew) { - RequestRefresh(result.Ticket); + RequestRefresh(result.Ticket, context.Principal); } return AuthenticateResult.Success(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name)); diff --git a/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs index 945ec82ee..81032e054 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs @@ -900,6 +900,76 @@ public async Task CookieCanBeRenewedByValidator() Assert.Null(FindClaimValue(transaction5, ClaimTypes.Name)); } + [Fact] + public async Task CookieCanBeReplacedByValidator() + { + var server = CreateServer(o => + { + o.Events = new CookieAuthenticationEvents + { + OnValidatePrincipal = ctx => + { + ctx.ShouldRenew = true; + ctx.ReplacePrincipal(new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice2", "Cookies2")))); + return Task.FromResult(0); + } + }; + }, + context => + context.SignInAsync("Cookies", + new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice", "Cookies"))))); + + var transaction1 = await SendAsync(server, "http://example.com/testpath"); + + var transaction2 = await SendAsync(server, "http://example.com/me/Cookies", transaction1.CookieNameValue); + Assert.NotNull(transaction2.SetCookie); + Assert.Equal("Alice2", FindClaimValue(transaction2, ClaimTypes.Name)); + } + + [Fact] + public async Task CookieCanBeUpdatedByValidatorDuringRefresh() + { + var replace = false; + var server = CreateServer(o => + { + o.ExpireTimeSpan = TimeSpan.FromMinutes(10); + o.Events = new CookieAuthenticationEvents + { + OnValidatePrincipal = ctx => + { + if (replace) + { + ctx.ShouldRenew = true; + ctx.ReplacePrincipal(new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice2", "Cookies2")))); + } + return Task.FromResult(0); + } + }; + }, + context => + context.SignInAsync("Cookies", + new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice", "Cookies"))))); + + var transaction1 = await SendAsync(server, "http://example.com/testpath"); + + var transaction2 = await SendAsync(server, "http://example.com/me/Cookies", transaction1.CookieNameValue); + Assert.Equal("Alice", FindClaimValue(transaction2, ClaimTypes.Name)); + + var transaction3 = await SendAsync(server, "http://example.com/me/Cookies", transaction1.CookieNameValue); + Assert.Equal("Alice", FindClaimValue(transaction2, ClaimTypes.Name)); + + replace = true; + + var transaction4 = await SendAsync(server, "http://example.com/me/Cookies", transaction1.CookieNameValue); + Assert.NotNull(transaction4.SetCookie); + Assert.Equal("Alice2", FindClaimValue(transaction4, ClaimTypes.Name)); + + replace = false; + + var transaction5 = await SendAsync(server, "http://example.com/me/Cookies", transaction4.CookieNameValue); + Assert.Equal("Alice2", FindClaimValue(transaction5, ClaimTypes.Name)); + } + [Fact] public async Task CookieCanBeRenewedByValidatorWithSlidingExpiry() { From 64e7409e7134a912204748d60f29c6af60ac1da1 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Thu, 5 Jul 2018 13:57:35 -0700 Subject: [PATCH 9/9] Also verify properties are renewed properly --- .../CookieTests.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs index 81032e054..766d1e2e5 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs @@ -941,6 +941,7 @@ public async Task CookieCanBeUpdatedByValidatorDuringRefresh() { ctx.ShouldRenew = true; ctx.ReplacePrincipal(new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice2", "Cookies2")))); + ctx.Properties.Items["updated"] = "yes"; } return Task.FromResult(0); } @@ -957,17 +958,20 @@ public async Task CookieCanBeUpdatedByValidatorDuringRefresh() var transaction3 = await SendAsync(server, "http://example.com/me/Cookies", transaction1.CookieNameValue); Assert.Equal("Alice", FindClaimValue(transaction2, ClaimTypes.Name)); + Assert.Null(FindPropertiesValue(transaction3, "updated")); replace = true; var transaction4 = await SendAsync(server, "http://example.com/me/Cookies", transaction1.CookieNameValue); Assert.NotNull(transaction4.SetCookie); Assert.Equal("Alice2", FindClaimValue(transaction4, ClaimTypes.Name)); + Assert.Equal("yes", FindPropertiesValue(transaction4, "updated")); replace = false; var transaction5 = await SendAsync(server, "http://example.com/me/Cookies", transaction4.CookieNameValue); Assert.Equal("Alice2", FindClaimValue(transaction5, ClaimTypes.Name)); + Assert.Equal("yes", FindPropertiesValue(transaction4, "updated")); } [Fact] @@ -1800,6 +1804,16 @@ private static string FindClaimValue(Transaction transaction, string claimType) return claim.Attribute("value").Value; } + private static string FindPropertiesValue(Transaction transaction, string key) + { + var property = transaction.ResponseElement.Elements("extra").SingleOrDefault(elt => elt.Attribute("type").Value == key); + if (property == null) + { + return null; + } + return property.Attribute("value").Value; + } + private static async Task GetAuthData(TestServer server, string url, string cookie) { var request = new HttpRequestMessage(HttpMethod.Get, url);