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)));
            }
        }
Exemplo n.º 6
0
        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);
        }