public async Task <ActionResult> Listen()
        {
            // Validate the new subscription by sending the token back to Microsoft Graph.
            // This response is required for each subscription.
            var query = QueryHelpers.ParseQuery(Request.QueryString.ToString());

            if (query.ContainsKey("validationToken"))
            {
                return(Content(query["validationToken"], "plain/text"));
            }

            // Parse the received notifications.
            else
            {
                try
                {
                    Dictionary <string, Notification> notifications = new Dictionary <string, Notification>();
                    using (var inputStream = new System.IO.StreamReader(Request.Body))
                    {
                        JObject jsonObject = JObject.Parse(inputStream.ReadToEnd());
                        if (jsonObject != null)
                        {
                            // Notifications are sent in a 'value' array. The array might contain multiple notifications for events that are
                            // registered for the same notification endpoint, and that occur within a short timespan.
                            JArray value = JArray.Parse(jsonObject["value"].ToString());
                            foreach (var notification in value)
                            {
                                Notification      current      = JsonConvert.DeserializeObject <Notification>(notification.ToString());
                                SubscriptionStore subscription = subscriptionStore.GetSubscriptionInfo(current.SubscriptionId);

                                // Verify the current client state matches the one that was sent.
                                if (current.ClientState == subscription.ClientState)
                                {
                                    // Just keep the latest notification for each resource. No point pulling data more than once.
                                    notifications[current.Resource] = current;
                                }
                            }

                            if (notifications.Count > 0)
                            {
                                // Query for the changed messages.
                                await GetChangedMessagesAsync(notifications.Values);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    logger.LogError($"ParsingNotification: { ex.Message }");

                    // TODO: Handle the exception.
                    // Still return a 202 so the service doesn't resend the notification.
                }
                return(new StatusCodeResult(202));
            }
        }
Exemple #2
0
        public async Task <IActionResult> Listen([FromQuery] string validationToken = null)
        {
            if (string.IsNullOrEmpty(validationToken))
            {
                try
                {
                    // Parse the received notifications.
                    var plainNotifications = new Dictionary <string, ChangeNotification>();
                    var options            = new JsonSerializerOptions
                    {
                        PropertyNameCaseInsensitive = true
                    };
                    options.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
                    var collection = await JsonSerializer.DeserializeAsync <ChangeNotificationCollection>(Request.Body, options);

                    foreach (var notification in collection.Value.Where(x => x.EncryptedContent == null))
                    {
                        SubscriptionStore subscription = subscriptionStore.GetSubscriptionInfo(notification.SubscriptionId.Value);

                        // Verify the current client state matches the one that was sent.
                        if (notification.ClientState == subscription.ClientState)
                        {
                            // Just keep the latest notification for each resource. No point pulling data more than once.
                            plainNotifications[notification.Resource] = notification;
                        }
                    }

                    if (plainNotifications.Count > 0)
                    {
                        // Query for the changed messages.
                        await GetChangedMessagesAsync(plainNotifications.Values);
                    }

                    if (collection.ValidationTokens != null && collection.ValidationTokens.Any())
                    { // we're getting notifications with resource data and we should validate tokens and decrypt data
                        TokenValidator tokenValidator           = new TokenValidator(identityOptions.Value.TenantId, new[] { identityOptions.Value.ClientId });
                        bool           areValidationTokensValid = (await Task.WhenAll(
                                                                       collection.ValidationTokens.Select(x => tokenValidator.ValidateToken(x))).ConfigureAwait(false))
                                                                  .Aggregate((x, y) => x && y);
                        if (areValidationTokensValid)
                        {
                            List <NotificationViewModel> notificationsToDisplay = new List <NotificationViewModel>();
                            foreach (var notificationItem in collection.Value.Where(x => x.EncryptedContent != null))
                            {
                                string decryptedpublisherNotification =
                                    Decryptor.Decrypt(
                                        notificationItem.EncryptedContent.Data,
                                        notificationItem.EncryptedContent.DataKey,
                                        notificationItem.EncryptedContent.DataSignature,
                                        await keyVaultManager.GetDecryptionCertificate().ConfigureAwait(false));

                                notificationsToDisplay.Add(new NotificationViewModel(decryptedpublisherNotification));
                            }

                            await notificationService.SendNotificationToClient(notificationHub, notificationsToDisplay);

                            return(Accepted());
                        }
                        else
                        {
                            return(Unauthorized("Token Validation failed"));
                        }
                    }
                }
                catch (Exception ex)
                {
                    logger.LogError($"ParsingNotification: { ex.Message }");

                    // Still return a 202 so the service doesn't resend the notification.
                }
                return(Accepted());
            }
            else
            {
                // Validate the new subscription by sending the token back to Microsoft Graph.
                // This response is required for each subscription.
                return(Content(validationToken));
            }
        }