-
Notifications
You must be signed in to change notification settings - Fork 494
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'users/ealsur/gwnullref' of https://github.com/Azure/azu…
…re-cosmos-dotnet-v3 into users/ealsur/gwnullref
- Loading branch information
Showing
37 changed files
with
1,404 additions
and
580 deletions.
There are no files selected for viewing
File renamed without changes.
73 changes: 73 additions & 0 deletions
73
Microsoft.Azure.Cosmos/src/Authorization/AuthorizationTokenProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
//------------------------------------------------------------ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
//------------------------------------------------------------ | ||
|
||
namespace Microsoft.Azure.Cosmos | ||
{ | ||
using System; | ||
using System.Globalization; | ||
using System.Net.Http; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Microsoft.Azure.Documents; | ||
using Microsoft.Azure.Documents.Collections; | ||
|
||
internal abstract class AuthorizationTokenProvider : ICosmosAuthorizationTokenProvider, IAuthorizationTokenProvider, IDisposable | ||
{ | ||
public async Task AddSystemAuthorizationHeaderAsync( | ||
DocumentServiceRequest request, | ||
string federationId, | ||
string verb, | ||
string resourceId) | ||
{ | ||
request.Headers[HttpConstants.HttpHeaders.XDate] = DateTime.UtcNow.ToString("r", CultureInfo.InvariantCulture); | ||
|
||
request.Headers[HttpConstants.HttpHeaders.Authorization] = (await this.GetUserAuthorizationAsync( | ||
resourceId ?? request.ResourceAddress, | ||
PathsHelper.GetResourcePath(request.ResourceType), | ||
verb, | ||
request.Headers, | ||
request.RequestAuthorizationTokenType)).token; | ||
} | ||
|
||
public abstract ValueTask AddAuthorizationHeaderAsync( | ||
INameValueCollection headersCollection, | ||
Uri requestAddress, | ||
string verb, | ||
AuthorizationTokenType tokenType); | ||
|
||
public abstract ValueTask<(string token, string payload)> GetUserAuthorizationAsync( | ||
string resourceAddress, | ||
string resourceType, | ||
string requestVerb, | ||
INameValueCollection headers, | ||
AuthorizationTokenType tokenType); | ||
|
||
public abstract ValueTask<string> GetUserAuthorizationTokenAsync( | ||
string resourceAddress, | ||
string resourceType, | ||
string requestVerb, | ||
INameValueCollection headers, | ||
AuthorizationTokenType tokenType, | ||
CosmosDiagnosticsContext diagnosticsContext); | ||
|
||
public abstract void TraceUnauthorized( | ||
DocumentClientException dce, | ||
string authorizationToken, | ||
string payload); | ||
|
||
public static AuthorizationTokenProvider CreateWithResourceTokenOrAuthKey(string authKeyOrResourceToken) | ||
{ | ||
if (AuthorizationHelper.IsResourceToken(authKeyOrResourceToken)) | ||
{ | ||
return new AuthorizationTokenProviderResourceToken(authKeyOrResourceToken); | ||
} | ||
else | ||
{ | ||
return new AuthorizationTokenProviderMasterKey(authKeyOrResourceToken); | ||
} | ||
} | ||
|
||
public abstract void Dispose(); | ||
} | ||
} |
202 changes: 202 additions & 0 deletions
202
Microsoft.Azure.Cosmos/src/Authorization/AuthorizationTokenProviderMasterKey.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
//------------------------------------------------------------ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
//------------------------------------------------------------ | ||
|
||
namespace Microsoft.Azure.Cosmos | ||
{ | ||
using System; | ||
using System.Globalization; | ||
using System.Net; | ||
using System.Net.Http; | ||
using System.Security; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Microsoft.Azure.Cosmos.Core.Trace; | ||
using Microsoft.Azure.Documents; | ||
using Microsoft.Azure.Documents.Collections; | ||
|
||
internal sealed class AuthorizationTokenProviderMasterKey : AuthorizationTokenProvider | ||
{ | ||
////The MAC signature found in the HTTP request is not the same as the computed signature.Server used following string to sign | ||
////The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign | ||
private const string MacSignatureString = "to sign"; | ||
private const string EnableAuthFailureTracesConfig = "enableAuthFailureTraces"; | ||
private readonly Lazy<bool> enableAuthFailureTraces; | ||
private readonly IComputeHash authKeyHashFunction; | ||
private bool isDisposed = false; | ||
|
||
public AuthorizationTokenProviderMasterKey(IComputeHash computeHash) | ||
{ | ||
this.authKeyHashFunction = computeHash ?? throw new ArgumentNullException(nameof(computeHash)); | ||
this.enableAuthFailureTraces = new Lazy<bool>(() => | ||
{ | ||
#if NETSTANDARD20 | ||
// GetEntryAssembly returns null when loaded from native netstandard2.0 | ||
if (System.Reflection.Assembly.GetEntryAssembly() == null) | ||
{ | ||
return false; | ||
} | ||
#endif | ||
string enableAuthFailureTracesString = System.Configuration.ConfigurationManager.AppSettings[EnableAuthFailureTracesConfig]; | ||
if (string.IsNullOrEmpty(enableAuthFailureTracesString) || | ||
!bool.TryParse(enableAuthFailureTracesString, out bool enableAuthFailureTracesFlag)) | ||
{ | ||
return false; | ||
} | ||
return enableAuthFailureTracesFlag; | ||
}); | ||
} | ||
|
||
public AuthorizationTokenProviderMasterKey(SecureString authKey) | ||
: this(new SecureStringHMACSHA256Helper(authKey)) | ||
{ | ||
} | ||
|
||
public AuthorizationTokenProviderMasterKey(string authKey) | ||
: this(new StringHMACSHA256Hash(authKey)) | ||
{ | ||
} | ||
|
||
public override ValueTask<(string token, string payload)> GetUserAuthorizationAsync( | ||
string resourceAddress, | ||
string resourceType, | ||
string requestVerb, | ||
INameValueCollection headers, | ||
AuthorizationTokenType tokenType) | ||
{ | ||
// this is masterkey authZ | ||
headers[HttpConstants.HttpHeaders.XDate] = DateTime.UtcNow.ToString("r", CultureInfo.InvariantCulture); | ||
|
||
string authorizationToken = AuthorizationHelper.GenerateKeyAuthorizationSignature( | ||
requestVerb, | ||
resourceAddress, | ||
resourceType, | ||
headers, | ||
this.authKeyHashFunction, | ||
out AuthorizationHelper.ArrayOwner arrayOwner); | ||
|
||
using (arrayOwner) | ||
{ | ||
string payload = null; | ||
if (arrayOwner.Buffer.Count > 0) | ||
{ | ||
payload = Encoding.UTF8.GetString(arrayOwner.Buffer.Array, arrayOwner.Buffer.Offset, (int)arrayOwner.Buffer.Count); | ||
} | ||
|
||
return new ValueTask<(string token, string payload)>((authorizationToken, payload)); | ||
} | ||
} | ||
|
||
public override ValueTask<string> GetUserAuthorizationTokenAsync( | ||
string resourceAddress, | ||
string resourceType, | ||
string requestVerb, | ||
INameValueCollection headers, | ||
AuthorizationTokenType tokenType, | ||
CosmosDiagnosticsContext diagnosticsContext) | ||
{ | ||
// this is masterkey authZ | ||
headers[HttpConstants.HttpHeaders.XDate] = DateTime.UtcNow.ToString("r", CultureInfo.InvariantCulture); | ||
|
||
string authorizationToken = AuthorizationHelper.GenerateKeyAuthorizationSignature( | ||
requestVerb, | ||
resourceAddress, | ||
resourceType, | ||
headers, | ||
this.authKeyHashFunction, | ||
out AuthorizationHelper.ArrayOwner arrayOwner); | ||
|
||
using (arrayOwner) | ||
{ | ||
return new ValueTask<string>(authorizationToken); | ||
} | ||
} | ||
|
||
public override ValueTask AddAuthorizationHeaderAsync( | ||
INameValueCollection headersCollection, | ||
Uri requestAddress, | ||
string verb, | ||
AuthorizationTokenType tokenType) | ||
{ | ||
string dateTime = DateTime.UtcNow.ToString("r", CultureInfo.InvariantCulture); | ||
headersCollection[HttpConstants.HttpHeaders.XDate] = dateTime; | ||
|
||
string token = AuthorizationHelper.GenerateKeyAuthorizationSignature( | ||
verb, | ||
requestAddress, | ||
headersCollection, | ||
this.authKeyHashFunction); | ||
|
||
headersCollection.Add(HttpConstants.HttpHeaders.Authorization, token); | ||
return default; | ||
} | ||
|
||
public override void TraceUnauthorized( | ||
DocumentClientException dce, | ||
string authorizationToken, | ||
string payload) | ||
{ | ||
if (payload != null | ||
&& dce.Message != null | ||
&& dce.StatusCode.HasValue | ||
&& dce.StatusCode.Value == HttpStatusCode.Unauthorized | ||
&& dce.Message.Contains(AuthorizationTokenProviderMasterKey.MacSignatureString)) | ||
{ | ||
// The following code is added such that we get trace data on unexpected 401/HMAC errors and it is | ||
// disabled by default. The trace will be trigger only when "enableAuthFailureTraces" named configuration | ||
// is set to true (currently true for CTL runs). | ||
// For production we will work directly with specific customers in order to enable this configuration. | ||
string normalizedPayload = AuthorizationTokenProviderMasterKey.NormalizeAuthorizationPayload(payload); | ||
if (this.enableAuthFailureTraces.Value) | ||
{ | ||
string tokenFirst5 = HttpUtility.UrlDecode(authorizationToken).Split('&')[2].Split('=')[1].Substring(0, 5); | ||
ulong authHash = 0; | ||
if (this.authKeyHashFunction?.Key != null) | ||
{ | ||
byte[] bytes = Encoding.UTF8.GetBytes(this.authKeyHashFunction?.Key?.ToString()); | ||
authHash = Documents.Routing.MurmurHash3.Hash64(bytes, bytes.Length); | ||
} | ||
DefaultTrace.TraceError("Un-expected authorization payload mis-match. Actual payload={0}, token={1}..., hash={2:X}..., error={3}", | ||
normalizedPayload, tokenFirst5, authHash, dce.Message); | ||
} | ||
else | ||
{ | ||
DefaultTrace.TraceError("Un-expected authorization payload mis-match. Actual {0} service expected {1}", normalizedPayload, dce.Message); | ||
} | ||
} | ||
} | ||
|
||
public override void Dispose() | ||
{ | ||
if (!this.isDisposed) | ||
{ | ||
this.authKeyHashFunction.Dispose(); | ||
this.isDisposed = true; | ||
} | ||
} | ||
|
||
private static string NormalizeAuthorizationPayload(string input) | ||
{ | ||
const int expansionBuffer = 12; | ||
StringBuilder builder = new StringBuilder(input.Length + expansionBuffer); | ||
for (int i = 0; i < input.Length; i++) | ||
{ | ||
switch (input[i]) | ||
{ | ||
case '\n': | ||
builder.Append("\\n"); | ||
break; | ||
case '/': | ||
builder.Append("\\/"); | ||
break; | ||
default: | ||
builder.Append(input[i]); | ||
break; | ||
} | ||
} | ||
|
||
return builder.ToString(); | ||
} | ||
} | ||
} |
75 changes: 75 additions & 0 deletions
75
Microsoft.Azure.Cosmos/src/Authorization/AuthorizationTokenProviderResourceToken.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
//------------------------------------------------------------ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
//------------------------------------------------------------ | ||
|
||
namespace Microsoft.Azure.Cosmos | ||
{ | ||
using System; | ||
using System.Net.Http; | ||
using System.Threading.Tasks; | ||
using Microsoft.Azure.Cosmos.Core.Trace; | ||
using Microsoft.Azure.Documents; | ||
using Microsoft.Azure.Documents.Collections; | ||
|
||
internal sealed class AuthorizationTokenProviderResourceToken : AuthorizationTokenProvider | ||
{ | ||
private readonly string urlEncodedAuthKeyResourceToken; | ||
private readonly ValueTask<string> urlEncodedAuthKeyResourceTokenValueTask; | ||
private readonly ValueTask<(string, string)> urlEncodedAuthKeyResourceTokenValueTaskWithPayload; | ||
private readonly ValueTask defaultValueTask; | ||
|
||
public AuthorizationTokenProviderResourceToken( | ||
string authKeyResourceToken) | ||
{ | ||
this.urlEncodedAuthKeyResourceToken = HttpUtility.UrlEncode(authKeyResourceToken); | ||
this.urlEncodedAuthKeyResourceTokenValueTask = new ValueTask<string>(this.urlEncodedAuthKeyResourceToken); | ||
this.urlEncodedAuthKeyResourceTokenValueTaskWithPayload = new ValueTask<(string, string)>((this.urlEncodedAuthKeyResourceToken, default)); | ||
this.defaultValueTask = new ValueTask(); | ||
} | ||
|
||
public override ValueTask<(string token, string payload)> GetUserAuthorizationAsync( | ||
string resourceAddress, | ||
string resourceType, | ||
string requestVerb, | ||
INameValueCollection headers, | ||
AuthorizationTokenType tokenType) | ||
{ | ||
// If the input auth token is a resource token, then use it as a bearer-token. | ||
return this.urlEncodedAuthKeyResourceTokenValueTaskWithPayload; | ||
} | ||
|
||
public override ValueTask<string> GetUserAuthorizationTokenAsync( | ||
string resourceAddress, | ||
string resourceType, | ||
string requestVerb, | ||
INameValueCollection headers, | ||
AuthorizationTokenType tokenType, | ||
CosmosDiagnosticsContext diagnosticsContext) | ||
{ | ||
// If the input auth token is a resource token, then use it as a bearer-token. | ||
return this.urlEncodedAuthKeyResourceTokenValueTask; | ||
} | ||
|
||
public override ValueTask AddAuthorizationHeaderAsync( | ||
INameValueCollection headersCollection, | ||
Uri requestAddress, | ||
string verb, | ||
AuthorizationTokenType tokenType) | ||
{ | ||
headersCollection.Add(HttpConstants.HttpHeaders.Authorization, this.urlEncodedAuthKeyResourceToken); | ||
return this.defaultValueTask; | ||
} | ||
|
||
public override void TraceUnauthorized( | ||
DocumentClientException dce, | ||
string authorizationToken, | ||
string payload) | ||
{ | ||
DefaultTrace.TraceError($"Un-expected authorization for resource token. {dce.Message}"); | ||
} | ||
|
||
public override void Dispose() | ||
{ | ||
} | ||
} | ||
} |
Oops, something went wrong.