public async Task <IActionResult> ContentHit([FromHeader] string authorization, [FromBody] JObject request)
        {
            var tokenModel     = TokenConvert.DeserializeEncryptedToken <WebhookTokenModel>(authorization, _config.GetValue <string>("EncodingKey") ?? "whoops");
            var recordProvider = new QuickBaseRecordProvider(new HttpClient(), tokenModel.QuickBaseRealm, tokenModel.QuickBaseToken);
            var requestModel   = new UpdateRecordRequest
            {
                To   = tokenModel.QuickBaseRequestTableId,
                Data = new List <Dictionary <string, RecordValue> >
                {
                    new Dictionary <string, RecordValue>
                    {
                        { tokenModel.QuickBaseRequestTableMatrix["applicationId"], new RecordValue(request["data"]["originalRequest"]["applicationId"].Value <string>()) },
                        { tokenModel.QuickBaseRequestTableMatrix["requestDate"], new RecordValue(request["data"]["eventDate"].Value <string>()) },
                        { tokenModel.QuickBaseRequestTableMatrix["platform"], new RecordValue(request["data"]["originalRequest"]["assistant"].Value <string>()) },
                        { tokenModel.QuickBaseRequestTableMatrix["requestId"], new RecordValue(request["data"]["originalRequest"]["requestId"]?.Value <string>() ?? Guid.NewGuid().ToString()) },
                        { tokenModel.QuickBaseRequestTableMatrix["userId"], new RecordValue(request["data"]["originalRequest"]["userId"].Value <string>()) },
                        { tokenModel.QuickBaseRequestTableMatrix["sessionId"], new RecordValue(request["data"]["originalRequest"]["sessionId"].Value <string>()) },
                        { tokenModel.QuickBaseRequestTableMatrix["slots"], new RecordValue(request["data"]["originalRequest"]["slots"].ToString()) },
                        { tokenModel.QuickBaseRequestTableMatrix["contentItemId"], new RecordValue(request["data"]["content"]["id"].Value <string>()) },
                        { tokenModel.QuickBaseRequestTableMatrix["featureTypeId"], new RecordValue(request["data"]["featureTypeId"].Value <string>()) }
                    }
                }
            };

            var result = await recordProvider.AddOrUpdateRecord(requestModel);

            if (result?.ResultType == ServiceResult.ResultType.Ok)
            {
                return(Ok(result.Data));
            }

            return(BadRequest(result.Errors));
        }
Example #2
0
        public async Task <CapabilitiesResponse> GetAsync(CapabilitiesRequestArgs args, CancellationToken token)
        {
            try
            {
                var requestArgs = new TokenSendRequestArgs(args.RequestUri, accessToken: args.AccessToken);
                var response    = await _client.SendRequestAsync(requestArgs, token);

                var assertionModel = _jwtTokenParser.Parse(response);
                var validationArgs = CreateValidationArgs(args, assertionModel);
                if (!await _tokenResponseValidator.IsValidAsync(validationArgs, args.SchemeOwnerAccessToken, token))
                {
                    throw new UnsuccessfulResponseException($"Token which was retrieved from {args.RequestUri} is corrupted.");
                }

                return(TokenConvert.DeserializeClaim <CapabilitiesResponse>(
                           assertionModel.JwtSecurityToken,
                           "capabilities_info"));
            }
            catch (UnsuccessfulResponseException)
            {
                throw;
            }
            catch (Exception e)
            {
                throw new UnsuccessfulResponseException("Capabilities request was unsuccessful.", e);
            }
        }
        public async Task <IReadOnlyCollection <CertificateAuthority> > GetAsync(string accessToken, CancellationToken token)
        {
            try
            {
                var requestArgs = new TokenSendRequestArgs(_requestUri, accessToken: accessToken);
                var response    = await _client.SendRequestAsync(requestArgs, token);

                var clientAssertion = _jwtTokenParser.Parse(response);
                if (!_tokenResponseValidator.IsValid(clientAssertion))
                {
                    throw new UnsuccessfulResponseException("Token which was retrieved from SO is corrupted.");
                }

                return(TokenConvert.DeserializeClaim <List <CertificateAuthority> >(
                           clientAssertion.JwtSecurityToken,
                           "trusted_list"));
            }
            catch (UnsuccessfulResponseException)
            {
                throw;
            }
            catch (Exception e)
            {
                throw new UnsuccessfulResponseException("Trusted list request was unsuccessful.", e);
            }
        }
Example #4
0
        public void DeserializeClaim_JwtSecurityTokenDoesNotContainProvidedClaim_Throws()
        {
            var jwtToken = CreateCapabilitiesToken();

            Action act = () => TokenConvert.DeserializeClaim <dynamic>(jwtToken, "invalid");

            act.Should().Throw <InvalidOperationException>();
        }
Example #5
0
        public void DeserializeClaim_NullJwtSecurityToken_Throws()
        {
            JwtSecurityToken jwtToken = null;

            Action act = () => TokenConvert.DeserializeClaim <dynamic>(jwtToken, "not_null");

            act.Should().Throw <ArgumentNullException>();
        }
Example #6
0
        public void DeserializeClaim_InvalidClaimName_Throws(string claimName)
        {
            var jwtToken = new JwtSecurityToken();

            Action act = () => TokenConvert.DeserializeClaim <dynamic>(jwtToken, claimName);

            act.Should().Throw <ArgumentNullException>();
        }
Example #7
0
        public void DeserializeClaim_ValidClaimButInvalidType_ReturnsObjectWithNulls()
        {
            var jwtToken = CreateCapabilitiesToken();

            var result = TokenConvert.DeserializeClaim <CertificateAuthority>(jwtToken, "capabilities_info");

            result.Should().NotBeNull();
            result.Subject.Should().BeNull();
            result.CertificateFingerprint.Should().BeNull();
            result.Validity.Should().BeNull();
            result.Status.Should().BeNull();
        }
Example #8
0
        public void DeserializeClaim_ClaimWhichContainsArrayPayload_ReturnsValidObject()
        {
            var jwtToken = CreateTrustedListToken();

            var result = TokenConvert.DeserializeClaim <CertificateAuthority[]>(jwtToken, "trusted_list");

            result.Should().HaveCount(7);
            var ca = result.First();

            ca.Subject.Should().Be("C=NL, O=Staat der Nederlanden, CN=TEST Staat der Nederlanden Organisatie Services CA - G3");
            ca.CertificateFingerprint.Should().Be("DC13FC94FF0149DE1B07F7965F655AED54C6A6BDA7ADF71A732FFCFABC454C7A");
            ca.Validity.Should().Be("valid");
            ca.Status.Should().Be("granted");
        }
        public void SuccessfullyEncryptsAndDecrypts()
        {
            var testObj = new Credentials
            {
                Username = "******",
                Password = "******"
            };
            var key = Guid.NewGuid().ToString();

            var token = TokenConvert.SerializeEncryptedToken(testObj, key, "123123salt 1");

            var newObj = TokenConvert.DeserializeEncryptedToken <Credentials>(token, key, "123123salt 1");

            newObj.Username.Should().Be("test1");
            newObj.Password.Should().Be("test2");
        }
Example #10
0
        public void DeserializeClaim_ValidClaimAndValidType_ReturnsValidObject()
        {
            var jwtToken = CreateCapabilitiesToken();

            var result = TokenConvert.DeserializeClaim <CapabilitiesResponse>(jwtToken, "capabilities_info");

            result.Should().NotBeNull();
            result.PartyId.Should().Be("EU.EORI.NL000000003");
            result.Roles.Should().HaveCount(1);
            result.SupportedVersions.Should().NotBeNull();
            result.SupportedVersions.Should().HaveCount(1);
            result.SupportedVersions.First().Version.Should().Be("1.7");
            result.SupportedVersions.First().SupportedFeatures.First().Public.Should().HaveCount(4);
            var firstPublicFeature = result.SupportedVersions.First().SupportedFeatures.First().Public.First();

            firstPublicFeature.Id.Should().Be("A51D413F-B3CC-477D-96C4-E37A9003BFE3");
            firstPublicFeature.Feature.Should().Be("capabilities");
            firstPublicFeature.Description.Should().Be("Retrieves iSHARE capabilities");
            firstPublicFeature.Url.Should().Be("https://w13.isharetest.net/capabilities");
            firstPublicFeature.TokenEndpoint.Should().Be("https://w13.isharetest.net/connect/token");
            result.SupportedVersions.First().SupportedFeatures.First().Restricted.Should().HaveCount(0);
        }
        public void FailsWithDifferentSalts()
        {
            var testObj = new Credentials
            {
                Username = "******",
                Password = "******"
            };
            var key = Guid.NewGuid().ToString();

            var token   = TokenConvert.SerializeEncryptedToken(testObj, key, "123123salt 1");
            var success = true;

            try
            {
                var newObj = TokenConvert.DeserializeEncryptedToken <Credentials>(token, key, "123123salt 2");
            }
            catch
            {
                success = false;
            }

            success.Should().BeFalse();
        }
Example #12
0
        public async Task <PartiesResponse> GetAsync(PartiesRequestArgs args, CancellationToken token)
        {
            try
            {
                var requestArgs = MapIntoTokenSendRequestArgs(args);
                var response    = await _client.SendRequestAsync(requestArgs, token);

                var clientAssertion = _jwtTokenParser.Parse(response);
                if (!_tokenResponseValidator.IsValid(clientAssertion))
                {
                    throw new UnsuccessfulResponseException("Token which was retrieved from SO is corrupted.");
                }

                return(TokenConvert.DeserializeClaim <PartiesResponse>(clientAssertion.JwtSecurityToken, "parties_info"));
            }
            catch (UnsuccessfulResponseException)
            {
                throw;
            }
            catch (Exception e)
            {
                throw new UnsuccessfulResponseException("Parties request was unsuccessful.", e);
            }
        }
        public async Task <IActionResult> Config([FromBody] IntegrationSetupRequest request)
        {
            request.AdditionalProperties.TryGetValue("voicify-app", out var voicifyAppId);
            request.AdditionalProperties.TryGetValue("quick-base-token", out var quickbaseToken);
            request.AdditionalProperties.TryGetValue("quick-base-realm", out var quickbaseRealm);

            // validate inputs
            if (string.IsNullOrEmpty(voicifyAppId))
            {
                return(BadRequest(new[] { "You must select a Voicify app." }));
            }
            if (string.IsNullOrEmpty(quickbaseToken))
            {
                return(BadRequest(new[] { "You must provide a user token for Quick Base." }));
            }
            if (string.IsNullOrEmpty(quickbaseRealm))
            {
                return(BadRequest(new[] { "You must provide a Quick Base realm." }));
            }
            var voicifyConfig = new Configuration
            {
                BasePath      = "https://cms.voicify.com",
                DefaultHeader = new Dictionary <string, string>
                {
                    { "Authorization", $"Bearer {request.AccessToken}" }
                }
            };

            using var client = new HttpClient();
            var appProvider   = new QuickBaseAppProvider(client, quickbaseRealm, quickbaseToken);
            var tableProvider = new QuickBaseTableProvider(client, quickbaseRealm, quickbaseToken);
            var fieldProvider = new QuickBaseFieldProvider(client, quickbaseRealm, quickbaseToken);
            var appApi        = new ApplicationApi(voicifyConfig);
            var userApi       = new UserApi(voicifyConfig);
            var webhookApi    = new WebhookApi(voicifyConfig);
            var orgApi        = new OrganizationApi(voicifyConfig);


            // get voicify org
            var orgs = await orgApi.GetForUserAsync();

            var org = orgs.FirstOrDefault(o => o.Id == request.OrganizationId);

            // get voicify app
            var app = await appApi.FindApplicationAsync(voicifyAppId);

            // create quick base app
            var appResult = await appProvider.CreateApp(new NewQuickBaseAppRequest
            {
                AssignToken = true,
                Description = app.Description,
                Name        = app.Name
            });

            if (appResult.ResultType != ResultType.Ok)
            {
                return(BadRequest(appResult.Errors));
            }

            // create tables
            var faqTableResult = await tableProvider.CreateTable(appResult.Data.Id, new NewTableRequest
            {
                Name             = "Questions and Answers",
                Description      = "Free form question and answer options",
                PluralRecordName = "Questions and Answers",
                SingleRecordName = "Question and Answer"
            });

            if (faqTableResult.ResultType != ResultType.Ok)
            {
                return(BadRequest(faqTableResult.Errors));
            }
            var requestTableResult = await tableProvider.CreateTable(appResult.Data.Id, new NewTableRequest
            {
                Name             = "Conversational Request Data",
                Description      = "Data from the requests made by users to the voice/bot application",
                PluralRecordName = "Requests",
                SingleRecordName = "Request"
            });

            if (requestTableResult.ResultType != ResultType.Ok)
            {
                return(BadRequest(requestTableResult.Errors));
            }

            // create fields for tables
            var results = await Task.WhenAll(new List <Task <Result <(QuickBaseField Field, string TableId)> > >
            {
                fieldProvider.CreateField(faqTableResult.Data.Id, new NewFieldRequest
                {
                    Label = "question"
                }),
                fieldProvider.CreateField(faqTableResult.Data.Id, new NewFieldRequest
                {
                    Label = "answer"
                }),
                fieldProvider.CreateField(faqTableResult.Data.Id, new NewFieldRequest
                {
                    Label = "foregroundImageUrl"
                }),
                fieldProvider.CreateField(faqTableResult.Data.Id, new NewFieldRequest
                {
                    Label = "followUpPrompt"
                }),
                fieldProvider.CreateField(faqTableResult.Data.Id, new NewFieldRequest
                {
                    Label = "nextItemRecordIds"
                }),


                fieldProvider.CreateField(requestTableResult.Data.Id, new NewFieldRequest
                {
                    Label = "applicationId"
                }),
                fieldProvider.CreateField(requestTableResult.Data.Id, new NewFieldRequest
                {
                    Label = "requestDate"
                }),
                fieldProvider.CreateField(requestTableResult.Data.Id, new NewFieldRequest
                {
                    Label = "platform"
                }),
                fieldProvider.CreateField(requestTableResult.Data.Id, new NewFieldRequest
                {
                    Label = "requestId"
                }),
                fieldProvider.CreateField(requestTableResult.Data.Id, new NewFieldRequest
                {
                    Label = "userId"
                }),
                fieldProvider.CreateField(requestTableResult.Data.Id, new NewFieldRequest
                {
                    Label = "sessionId"
                }),
                fieldProvider.CreateField(requestTableResult.Data.Id, new NewFieldRequest
                {
                    Label = "slots"
                }),
                fieldProvider.CreateField(requestTableResult.Data.Id, new NewFieldRequest
                {
                    Label = "contentItemId"
                }),
                fieldProvider.CreateField(requestTableResult.Data.Id, new NewFieldRequest
                {
                    Label = "featureTypeId"
                }),
            });


            // create API user in voicify
            var apiUser = await userApi.CreateApiUserAsync(new NewApiUserRequest(request.OrganizationId));

            // create token with api user credentials
            var token = TokenConvert.SerializeEncryptedToken(new WebhookTokenModel
            {
                VoicifyOrganizationId       = org.Id,
                VoicifyOrganizationSecret   = org.Secret,
                VoicifyApiUserName          = apiUser.Username,
                VoicifyApiUserSecret        = apiUser.Password,
                QuickBaseToken              = quickbaseToken,
                QuickBaseRealm              = quickbaseRealm,
                VoicifyApplicationId        = voicifyAppId,
                QuickBaseAppId              = appResult.Data.Id,
                QuickBaseFaqTableId         = faqTableResult.Data.Id,
                QuickBaseRequestTableId     = requestTableResult.Data.Id,
                QuickBaseRequestTableMatrix = results.Where(r => r.Data.TableId == requestTableResult.Data.Id)
                                              .ToDictionary(
                    d => d.Data.Field.Label,
                    d => d.Data.Field.Id.ToString()),
                QuickBaseFaqTableMatrix = results.Where(r => r.Data.TableId == faqTableResult.Data.Id)
                                          .ToDictionary(
                    d => d.Data.Field.Label,
                    d => d.Data.Field.Id.ToString())
            }, _config.GetValue <string>("EncodingKey") ?? "whoops");

            // create webhook in voicify
            var webhook = await webhookApi.CreateWebhookAsync(request.OrganizationId,
                                                              new NewWebhookRequest(
                                                                  title : $"{app?.Name ?? "App"} - Quick Base Request Event",
                                                                  description : "Creates event records in Quick Base when a request is received through Voicify. Use this token for Quick Base pipelines as well",
                                                                  url : "https://quick-base-voicify-sync.azurewebsites.net/api/voicify/contentHit",
                                                                  webhookTypeId : "53b40ef2-769c-46e6-bc99-c709e7600c03", // Content Hit Event webhook type
                                                                  accessToken : token
                                                                  ));

            // add webhook to app
            var appWebhook = await appApi.AddWebhookAsync(voicifyAppId, webhook.Id, new WebhookParametersRequest(values : new Dictionary <string, string>(), userDefinedParameters : new Dictionary <string, string>()));


            return(Ok());
        }
Example #14
0
        public async Task <IActionResult> AddItem([FromHeader] string authorization, [FromBody] QuickBaseQuestionAnswerItem item)
        {
            try
            {
                var tokenModel = TokenConvert.DeserializeEncryptedToken <WebhookTokenModel>(authorization, _config.GetValue <string>("EncodingKey") ?? "whoops");

                var authApi = new AuthenticationApi("https://cms.voicify.com");

                // sign in with api user creds to get token
                var tokenResponse = await authApi.AuthenticateAsync(tokenModel.VoicifyOrganizationId, tokenModel.VoicifyOrganizationSecret, "password", tokenModel.VoicifyApiUserName, tokenModel.VoicifyApiUserSecret);

                var voicifyConfig = new Configuration
                {
                    BasePath      = "https://cms.voicify.com",
                    DefaultHeader = new Dictionary <string, string>
                    {
                        { "Authorization", $"Bearer {tokenResponse.AccessToken}" }
                    }
                };
                var            featureApi        = new FeatureApi(voicifyConfig);
                var            questionAnswerApi = new QuestionAnswerApi(voicifyConfig);
                var            mediaItemApi      = new MediaItemApi(voicifyConfig);
                MediaItemModel mediaItem         = null;
                if (!string.IsNullOrEmpty(item?.ForegroundImageUrl) && Uri.TryCreate(item.ForegroundImageUrl, UriKind.Absolute, out var imageUri))
                {
                    // TODO: once voicify allows external urls through this api, send it up
                    //mediaItem = await mediaItemApi.CreateMediaItem_0Async(tokenModel.VoicifyApplicationId, new NewMediaItemRequest(item.ForegroundImageUrl, item.ForegroundImageUrl, item.ForegroundImageUrl, ""));
                }

                // get root feature of app
                var features = await featureApi.GetFeaturesForApplicationAsync(tokenModel.VoicifyApplicationId);

                var rootFeature = features.FirstOrDefault(af => string.IsNullOrEmpty(af.ParentId));

                // add question answer to root feature
                var faq = await questionAnswerApi.CreateFullContentItemAsync(new QuestionAnswerModel(
                                                                                 applicationFeatureId : rootFeature.Id,
                                                                                 applicationId : rootFeature.ApplicationId,
                                                                                 isLive : true,
                                                                                 title : item.Question,
                                                                                 questionSet : new List <QuestionModel>
                {
                    new QuestionModel(content : item.Question)
                },
                                                                                 responses : new List <AnswerModel>
                {
                    new AnswerModel(
                        content : item.Answer,
                        largeImage : mediaItem,
                        followUp : string.IsNullOrEmpty(item.FollowUpPrompt)
                                ? null
                        : new FollowUpModel(applicationId : tokenModel.VoicifyApplicationId, content : item.FollowUpPrompt))
                }));
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }


            return(Ok());
        }