コード例 #1
0
        /// <summary>
        /// Parse request to system/user type ServiceRequest.
        /// </summary>
        /// <param name="request">Upstream HttpRequest.</param>
        /// <param name="options"></param>
        /// <param name="cancellationToken"></param>
        /// <returns>Deserialize <see cref="WebPubSubEventRequest"/></returns>
        internal static async Task <WebPubSubEventRequest> ReadWebPubSubEventAsync(this HttpRequest request, WebPubSubValidationOptions options = null, CancellationToken cancellationToken = default)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            // validation request.
            if (request.IsPreflightRequest(out var requestHosts))
            {
                if (options == null || !options.ContainsHost())
                {
                    return(new PreflightRequest(true));
                }
                else
                {
                    foreach (var item in requestHosts)
                    {
                        if (options.ContainsHost(item))
                        {
                            return(new PreflightRequest(true));
                        }
                    }
                }
                return(new PreflightRequest(false));
            }

            if (!request.TryParseCloudEvents(out var context))
            {
                throw new ArgumentException("Invalid Web PubSub upstream request missing required fields in header.");
            }

            if (!context.IsValidSignature(options))
            {
                throw new UnauthorizedAccessException("Signature validation failed.");
            }

            var requestType = context.GetRequestType();

            switch (requestType)
            {
            case RequestType.Connect:
            {
                var content      = await new StreamReader(request.Body).ReadToEndAsync().ConfigureAwait(false);
                var eventRequest = JsonSerializer.Deserialize <ConnectEventRequest>(content);
                return(new ConnectEventRequest(context, eventRequest.Claims, eventRequest.Query, eventRequest.Subprotocols, eventRequest.ClientCertificates));
            }

            case RequestType.User:
            {
                const int bufferSize = 4096;
                using var ms = new MemoryStream();
                await request.Body.CopyToAsync(ms, bufferSize, cancellationToken).ConfigureAwait(false);

                var message = BinaryData.FromBytes(ms.ToArray());
                if (!MediaTypeHeaderValue.Parse(request.ContentType).MediaType.IsValidMediaType(out var dataType))
                {
                    throw new ArgumentException($"ContentType is not supported: {request.ContentType}");
                }
                return(new UserEventRequest(context, message, dataType));
            }

            case RequestType.Connected:
            {
                return(new ConnectedEventRequest(context));
            }

            case RequestType.Disconnected:
            {
                var content      = await new StreamReader(request.Body).ReadToEndAsync().ConfigureAwait(false);
                var eventRequest = JsonSerializer.Deserialize <DisconnectedEventRequest>(content);
                return(new DisconnectedEventRequest(context, eventRequest.Reason));
            }

            default:
                return(null);
            }
        }
コード例 #2
0
        internal static bool IsValidSignature(this WebPubSubConnectionContext connectionContext, WebPubSubValidationOptions options)
        {
            // no options skip validation.
            if (options == null || !options.ContainsHost())
            {
                return(true);
            }

            // TODO: considering add cache to improve.
            if (options.TryGetKey(connectionContext.Origin, out var accessKey))
            {
                // server side disable signature checks.
                if (string.IsNullOrEmpty(accessKey))
                {
                    return(true);
                }

                var signatures = connectionContext.Signature.ToHeaderList();
                if (signatures == null)
                {
                    return(false);
                }
                using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(accessKey));
                var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(connectionContext.ConnectionId));
                var hash      = "sha256=" + BitConverter.ToString(hashBytes).Replace("-", "");
                if (signatures.Contains(hash, StringComparer.OrdinalIgnoreCase))
                {
                    return(true);
                }
            }
            return(false);
        }