public static string GenerateMessagePayload(string verb, string resourceId, string resourceType, INameValueCollection headers, bool bUseUtcNowForMissingXDate = false) { string xDate = AuthorizationHelper.GetHeaderValue(headers, HttpConstants.HttpHeaders.XDate); string date = AuthorizationHelper.GetHeaderValue(headers, HttpConstants.HttpHeaders.HttpDate); // At-least one of date header should present // https://docs.microsoft.com/en-us/rest/api/documentdb/access-control-on-documentdb-resources if (string.IsNullOrEmpty(xDate) && string.IsNullOrWhiteSpace(date)) { if (!bUseUtcNowForMissingXDate) { throw new UnauthorizedException(RMResources.InvalidDateHeader); } headers[HttpConstants.HttpHeaders.XDate] = DateTime.UtcNow.ToString("r", CultureInfo.InvariantCulture); xDate = AuthorizationHelper.GetHeaderValue(headers, HttpConstants.HttpHeaders.XDate); } // for name based, it is case sensitive, we won't use the lower case if (!PathsHelper.IsNameBased(resourceId)) { resourceId = resourceId.ToLowerInvariant(); } string payLoad = string.Format(CultureInfo.InvariantCulture, "{0}\n{1}\n{2}\n{3}\n{4}\n", verb.ToLowerInvariant(), resourceType.ToLowerInvariant(), resourceId, xDate.ToLowerInvariant(), xDate.Equals(string.Empty, StringComparison.OrdinalIgnoreCase) ? date.ToLowerInvariant() : string.Empty); return(payLoad); }
// This API is a helper method to create auth header based on client request. // Uri is split into resourceType/resourceId - // For feed/post/put requests, resourceId = parentId, // For point get requests, resourceId = last segment in URI public static string GenerateKeyAuthorizationSignature(string verb, Uri uri, INameValueCollection headers, IComputeHash stringHMACSHA256Helper, string clientVersion = "") { if (string.IsNullOrEmpty(verb)) { throw new ArgumentException(RMResources.StringArgumentNullOrEmpty, "verb"); } if (uri == null) { throw new ArgumentNullException("uri"); } if (stringHMACSHA256Helper == null) { throw new ArgumentNullException("stringHMACSHA256Helper"); } if (headers == null) { throw new ArgumentNullException("headers"); } string resourceType = string.Empty; string resourceIdValue = string.Empty; bool isNameBased = false; AuthorizationHelper.GetResourceTypeAndIdOrFullName(uri, out isNameBased, out resourceType, out resourceIdValue, clientVersion); return(AuthorizationHelper.GenerateKeyAuthorizationSignature(verb, resourceIdValue, resourceType, headers, stringHMACSHA256Helper)); }
private static void ValidateInputRequestTime( string dateToCompare, int masterTokenExpiryInSeconds, int allowedClockSkewInSeconds) { if (string.IsNullOrEmpty(dateToCompare)) { throw new UnauthorizedException(RMResources.MissingDateForAuthorization); } DateTime utcStartTime; if (!DateTime.TryParse(dateToCompare, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal | DateTimeStyles.AllowWhiteSpaces, out utcStartTime)) { throw new UnauthorizedException(RMResources.InvalidDateHeader); } // Check if time range is beyond DateTime.MaxValue bool outOfRange = utcStartTime >= DateTime.MaxValue.AddSeconds(-masterTokenExpiryInSeconds); if (outOfRange) { string message = string.Format(CultureInfo.InvariantCulture, RMResources.InvalidTokenTimeRange, utcStartTime.ToString("r", CultureInfo.InvariantCulture), DateTime.MaxValue.ToString("r", CultureInfo.InvariantCulture), DateTime.UtcNow.ToString("r", CultureInfo.InvariantCulture)); DefaultTrace.TraceError(message); throw new ForbiddenException(message); } DateTime utcEndTime = utcStartTime + TimeSpan.FromSeconds(masterTokenExpiryInSeconds); AuthorizationHelper.CheckTimeRangeIsCurrent(allowedClockSkewInSeconds, utcStartTime, utcEndTime); }
public static AuthorizationTokenType GetSystemOperationType(bool readOnlyRequest, string resourceType) { if (!AuthorizationHelper.IsUserRequest(resourceType)) { if (readOnlyRequest) { return(AuthorizationTokenType.SystemReadOnly); } else { return(AuthorizationTokenType.SystemAll); } } // operations on user resources if (readOnlyRequest) { return(AuthorizationTokenType.SystemReadOnly); } else { return(AuthorizationTokenType.SystemReadWrite); } }
public static string GenerateKeyAuthorizationSignature(string verb, string resourceId, string resourceType, INameValueCollection headers, IComputeHash stringHMACSHA256Helper, out string payload) { string authorizationToken = AuthorizationHelper.GenerateAuthorizationTokenWithHashCore( verb, resourceId, resourceType, headers, stringHMACSHA256Helper, out ArrayOwner payloadStream); using (payloadStream) { payload = AuthorizationHelper.AuthorizationEncoding.GetString(payloadStream.Buffer.Array, payloadStream.Buffer.Offset, (int)payloadStream.Buffer.Count); return(HttpUtility.UrlEncode(string.Format(CultureInfo.InvariantCulture, Constants.Properties.AuthorizationFormat, Constants.Properties.MasterToken, Constants.Properties.TokenVersion, authorizationToken))); } }
public override ValueTask <string> GetUserAuthorizationTokenAsync( string resourceAddress, string resourceType, string requestVerb, INameValueCollection headers, AuthorizationTokenType tokenType, ITrace trace) { // 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)); } }
private static string GenerateKeyAuthorizationCore( string verb, string resourceId, string resourceType, INameValueCollection headers, string key, out ArraySegment <byte> payload, bool bUseUtcNowForMissingXDate = false) { string authorizationToken; // resourceId can be null for feed-read of /dbs if (string.IsNullOrEmpty(verb)) { throw new ArgumentException(RMResources.StringArgumentNullOrEmpty, nameof(verb)); } if (resourceType == null) { throw new ArgumentNullException(nameof(resourceType)); // can be empty } if (string.IsNullOrEmpty(key)) { throw new ArgumentException(RMResources.StringArgumentNullOrEmpty, nameof(key)); } if (headers == null) { throw new ArgumentNullException(nameof(headers)); } byte[] keyBytes = Convert.FromBase64String(key); using (HMACSHA256 hmacSha256 = new HMACSHA256(keyBytes)) { // Order of the values included in the message payload is a protocol that clients/BE need to follow exactly. // More headers can be added in the future. // If any of the value is optional, it should still have the placeholder value of "" // OperationType -> ResourceType -> ResourceId/OwnerId -> XDate -> Date string verbInput = verb ?? string.Empty; string resourceIdInput = resourceId ?? string.Empty; string resourceTypeInput = resourceType ?? string.Empty; string authResourceId = AuthorizationHelper.GetAuthorizationResourceIdOrFullName(resourceTypeInput, resourceIdInput); int memoryStreamCapacity = AuthorizationHelper.ComputeMemoryCapacity(verbInput, authResourceId, resourceTypeInput); byte[] buffer = ArrayPool <byte> .Shared.Rent(memoryStreamCapacity); using ArrayOwner owner = new ArrayOwner(ArrayPool <byte> .Shared, new ArraySegment <byte>(buffer, 0, buffer.Length)); Span <byte> payloadBytes = buffer; int length = AuthorizationHelper.SerializeMessagePayload( payloadBytes, verbInput, authResourceId, resourceTypeInput, headers); byte[] hashPayLoad = hmacSha256.ComputeHash(buffer, 0, length); authorizationToken = Convert.ToBase64String(hashPayLoad); } return(authorizationToken); }
private static string GenerateAuthorizationTokenWithHashCore( string verb, string resourceId, string resourceType, INameValueCollection headers, IComputeHash stringHMACSHA256Helper, out ArrayOwner payload) { // resourceId can be null for feed-read of /dbs if (string.IsNullOrEmpty(verb)) { throw new ArgumentException(RMResources.StringArgumentNullOrEmpty, nameof(verb)); } if (resourceType == null) { throw new ArgumentNullException(nameof(resourceType)); // can be empty } if (stringHMACSHA256Helper == null) { throw new ArgumentNullException(nameof(stringHMACSHA256Helper)); } if (headers == null) { throw new ArgumentNullException(nameof(headers)); } // Order of the values included in the message payload is a protocol that clients/BE need to follow exactly. // More headers can be added in the future. // If any of the value is optional, it should still have the placeholder value of "" // OperationType -> ResourceType -> ResourceId/OwnerId -> XDate -> Date string verbInput = verb ?? string.Empty; string resourceIdInput = resourceId ?? string.Empty; string resourceTypeInput = resourceType ?? string.Empty; string authResourceId = AuthorizationHelper.GetAuthorizationResourceIdOrFullName(resourceTypeInput, resourceIdInput); int capacity = AuthorizationHelper.ComputeMemoryCapacity(verbInput, authResourceId, resourceTypeInput); byte[] buffer = ArrayPool <byte> .Shared.Rent(capacity); try { Span <byte> payloadBytes = buffer; int length = AuthorizationHelper.SerializeMessagePayload( payloadBytes, verbInput, authResourceId, resourceTypeInput, headers); payload = new ArrayOwner(ArrayPool <byte> .Shared, new ArraySegment <byte>(buffer, 0, length)); byte[] hashPayLoad = stringHMACSHA256Helper.ComputeHash(payload.Buffer); string authorizationToken = Convert.ToBase64String(hashPayLoad); return(authorizationToken); } catch { ArrayPool <byte> .Shared.Return(buffer); throw; } }
public static int SerializeMessagePayload( Span <byte> stream, string verb, string resourceId, string resourceType, INameValueCollection headers, bool bUseUtcNowForMissingXDate = false) { string xDate = AuthorizationHelper.GetHeaderValue(headers, HttpConstants.HttpHeaders.XDate); string date = AuthorizationHelper.GetHeaderValue(headers, HttpConstants.HttpHeaders.HttpDate); // At-least one of date header should present // https://docs.microsoft.com/en-us/rest/api/documentdb/access-control-on-documentdb-resources if (string.IsNullOrEmpty(xDate) && string.IsNullOrWhiteSpace(date)) { if (!bUseUtcNowForMissingXDate) { throw new UnauthorizedException(RMResources.InvalidDateHeader); } headers[HttpConstants.HttpHeaders.XDate] = DateTime.UtcNow.ToString("r", CultureInfo.InvariantCulture); xDate = AuthorizationHelper.GetHeaderValue(headers, HttpConstants.HttpHeaders.XDate); } // for name based, it is case sensitive, we won't use the lower case if (!PathsHelper.IsNameBased(resourceId)) { resourceId = resourceId.ToLowerInvariant(); } int totalLength = 0; int length = stream.Write(verb.ToLowerInvariant()); totalLength += length; stream = stream.Slice(length); length = stream.Write("\n"); totalLength += length; stream = stream.Slice(length); length = stream.Write(resourceType.ToLowerInvariant()); totalLength += length; stream = stream.Slice(length); length = stream.Write("\n"); totalLength += length; stream = stream.Slice(length); length = stream.Write(resourceId); totalLength += length; stream = stream.Slice(length); length = stream.Write("\n"); totalLength += length; stream = stream.Slice(length); length = stream.Write(xDate.ToLowerInvariant()); totalLength += length; stream = stream.Slice(length); length = stream.Write("\n"); totalLength += length; stream = stream.Slice(length); length = stream.Write(xDate.Equals(string.Empty, StringComparison.OrdinalIgnoreCase) ? date.ToLowerInvariant() : string.Empty); totalLength += length; stream = stream.Slice(length); length = stream.Write("\n"); totalLength += length; return(totalLength); }