Nuget package update and refactored the code to support the latest version of the bot builder
This commit is contained in:
Родитель
daf9b4320e
Коммит
3ff892ac83
|
@ -0,0 +1,13 @@
|
|||
## Partner Center Bot Changelog
|
||||
|
||||
<a name="x.y.z"></a>
|
||||
# x.y.z (yyyy-mm-dd)
|
||||
|
||||
*Features*
|
||||
* ...
|
||||
|
||||
*Bug Fixes*
|
||||
* ...
|
||||
|
||||
*Breaking Changes*
|
||||
* ...
|
|
@ -0,0 +1 @@
|
|||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
|
@ -1,32 +1,76 @@
|
|||
# How to Contribute
|
||||
One of the easiest ways to contribute is to participate in discussions and discuss issues. You can also contribute by submitting pull requests with code changes.
|
||||
# Contributing to Partner Center Bot
|
||||
|
||||
## General feedback, discussions, bugs, feature requests?
|
||||
Please start a discussion or log a new issue on the [Home repo issue tracker](https://github.com/Microsoft/Partner-Center-Bot/issues).
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
||||
the rights to use your contribution. For details, visit https://cla.microsoft.com.
|
||||
|
||||
## Filing issues
|
||||
When filing issues, please consider using this sample [bug filing template](https://github.com/aspnet/Home/wiki/Functional-bug-template) from the ASP.Net team.
|
||||
The best way to get your bug fixed is to be as detailed as you can be about the problem.
|
||||
Providing steps to reproduce the problem is ideal.
|
||||
Here are questions you can answer before you file a bug to make sure you're not missing any important information.
|
||||
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
|
||||
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
|
||||
provided by the bot. You will only need to do this once across all repos using our CLA.
|
||||
|
||||
1. Are you using a TIP (integration sandbox) tenant?
|
||||
2. Did you include the snippet of broken code in the issue?
|
||||
3. What are the *EXACT* steps to reproduce this problem?
|
||||
4. What version of the Bot Framework are you using?
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
GitHub supports [markdown](http://github.github.com/github-flavored-markdown/), so when filing bugs make sure you check the formatting before clicking submit.
|
||||
- [Code of Conduct](#coc)
|
||||
- [Issues and Bugs](#issue)
|
||||
- [Feature Requests](#feature)
|
||||
- [Submission Guidelines](#submit)
|
||||
|
||||
## Contributing code and content
|
||||
If you don't know what a pull request is read this article: https://help.github.com/articles/using-pull-requests.
|
||||
You might also read these two blogs posts on contributing code: [Open Source Contribution Etiquette](http://tirania.org/blog/archive/2010/Dec-31.html) by Miguel de Icaza and [Don't "Push" Your Pull Requests](http://www.igvita.com/2011/12/19/dont-push-your-pull-requests/) by Ilya Grigorik.
|
||||
## <a name="coc"></a> Code of Conduct
|
||||
Help us keep this project open and inclusive. Please read and follow our [Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
|
||||
**Commit/Pull Request Format**
|
||||
## <a name="issue"></a> Found an Issue?
|
||||
If you find a bug in the source code or a mistake in the documentation, you can help us by
|
||||
[submitting an issue](#submit-issue) to the GitHub Repository. Even better, you can
|
||||
[submit a Pull Request](#submit-pr) with a fix.
|
||||
|
||||
## <a name="feature"></a> Want a Feature?
|
||||
You can *request* a new feature by [submitting an issue](#submit-issue) to the GitHub
|
||||
Repository. If you would like to *implement* a new feature, please submit an issue with
|
||||
a proposal for your work first, to be sure that we can use it.
|
||||
|
||||
* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
|
||||
|
||||
## <a name="submit"></a> Submission Guidelines
|
||||
|
||||
### <a name="submit-issue"></a> Submitting an Issue
|
||||
Before you submit an issue, search the archive, maybe your question was already answered.
|
||||
|
||||
If your issue appears to be a bug, and hasn't been reported, open a new issue.
|
||||
Help us to maximize the effort we can spend fixing issues and adding new
|
||||
features, by not reporting duplicate issues. Providing the following information will increase the
|
||||
chances of your issue being dealt with quickly:
|
||||
|
||||
* **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
|
||||
* **Version** - what version is affected (e.g. 0.1.2)
|
||||
* **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you
|
||||
* **Browsers and Operating System** - is this a problem with all browsers?
|
||||
* **Reproduce the Error** - provide a live example or a unambiguous set of steps
|
||||
* **Related Issues** - has a similar issue been reported before?
|
||||
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
|
||||
causing the problem (line of code or commit)
|
||||
|
||||
You can file new issues by providing the above information at the corresponding repository's issues link: https://github.com/Microsoft/Partner-Center-Bot/issues/new].
|
||||
|
||||
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
|
||||
Before you submit your Pull Request (PR) consider the following guidelines:
|
||||
|
||||
* Search the repository (https://github.com/Microsoft/Partner-Center-Bot/pulls) for an open or closed PR
|
||||
that relates to your submission. You don't want to duplicate effort.
|
||||
|
||||
* Make your changes in a new git fork:
|
||||
|
||||
* Commit your changes using a descriptive commit message
|
||||
* Push your fork to GitHub:
|
||||
* In GitHub, create a pull request
|
||||
* If we suggest changes then:
|
||||
* Make the required updates.
|
||||
* Rebase your fork and force push to your GitHub repository (this will update your Pull Request):
|
||||
|
||||
```shell
|
||||
git rebase master -i
|
||||
git push -f
|
||||
```
|
||||
Summary of the changes (Less than 80 chars)
|
||||
- Detail 1
|
||||
- Detail 2
|
||||
|
||||
#bugnumber (in this specific format)
|
||||
```
|
||||
That's it! Thank you for your contribution!
|
|
@ -0,0 +1,5 @@
|
|||
### Expected behavior
|
||||
|
||||
### Actual behavior
|
||||
|
||||
### Steps to reproduce the problem
|
|
@ -7,8 +7,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
azuredeploy.json = azuredeploy.json
|
||||
azuredeploy.parameters.json = azuredeploy.parameters.json
|
||||
CHANGELOG.md = CHANGELOG.md
|
||||
CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md
|
||||
CONTRIBUTING.md = CONTRIBUTING.md
|
||||
ISSUE_TEMPLATE.md = ISSUE_TEMPLATE.md
|
||||
LICENSE = LICENSE
|
||||
Partner-Center-Bot.json = Partner-Center-Bot.json
|
||||
README.md = README.md
|
||||
|
@ -59,4 +61,7 @@ Global
|
|||
{A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4} = {07FC2994-6ECD-42BC-9CF3-77CE505216AA}
|
||||
{B55A743C-BA04-491F-9C64-E7339C30684F} = {BAD9B155-1FCF-4735-A8EC-D8ECCDAB9136}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {D966732D-8A63-4571-81B5-ED6624F0B40E}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
13
README.md
13
README.md
|
@ -1,20 +1,17 @@
|
|||
# Partner Center Bot
|
||||
This is a sample project that demonstrates how to leverage the Microsoft Bot framework to construct a bot that interacts with
|
||||
Partner Center. With this initial release limitedfeatures are supported. Additional abilities will be added over time. The
|
||||
intent of this project is not to provide a fully functional bot, but rather to demonstrate how this technology can be utilized
|
||||
to support customers.
|
||||
Partner Center. With this initial release, limited features are supported. Additional abilities will be added over time. The intent of this project is not to provide a fully functional bot, but rather to demonstrate how this technology can be utilized to support customers.
|
||||
|
||||
This project is being provided with community support only. If you need help please
|
||||
log an issue using the [issue tracker](https://github.com/Microsoft/Partner-Center-Bot/issues).
|
||||
This project is being provided with community support only. If you need help, please log an issue using the [issue tracker](https://github.com/Microsoft/Partner-Center-Bot/issues).
|
||||
|
||||
__Current Build Status:__ ![Build Status](https://ustechsales.visualstudio.com/_apis/public/build/definitions/08b6a9c4-c5bc-47c3-b945-aa13e7567100/18/badge)
|
||||
__Current Build Status:__ ![Build Status](https://isaiahwilliams.visualstudio.com/_apis/public/build/definitions/5a07f243-b522-4dc6-9ad8-9addc8633db6/3/badge)
|
||||
|
||||
## Features
|
||||
Currently this project has the ability to list customers, select a specific customer, and list the subscriptions for that particular customer.
|
||||
Currently, this project has the ability to list customers, select a specific customer, and list the subscriptions for that particular customer.
|
||||
|
||||
![Bot Interaction](docs/Images/bot01.png)
|
||||
|
||||
In addition to integrating with Partner Center the bot integrates with the [QnA Maker](docs/QnAMaker.md) cognitive service.
|
||||
In addition to integrating with Partner Center, the bot integrates with the [QnA Maker](docs/QnAMaker.md) cognitive service.
|
||||
This makes it where the user can get answers to commonly asked questions.
|
||||
|
||||
![Bot Interaction](docs/Images/bot02.png)
|
||||
|
|
133
azuredeploy.json
133
azuredeploy.json
|
@ -27,10 +27,6 @@
|
|||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"branch": {
|
||||
"type": "string",
|
||||
"defaultValue": "master"
|
||||
},
|
||||
"cacheSKUName": {
|
||||
"type": "string",
|
||||
"allowedValues": [
|
||||
|
@ -72,24 +68,6 @@
|
|||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"keyVaultAppCertThumbprint": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Thumbprint for the certificate used to access Key Vault"
|
||||
},
|
||||
"minLength": 1
|
||||
},
|
||||
"keyVaultAppId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Identifier of the application used to access Key Vault"
|
||||
},
|
||||
"minLength": 1
|
||||
},
|
||||
"keyVaultName": {
|
||||
"type": "string",
|
||||
"minLength": 3
|
||||
},
|
||||
"keyVaultSkuName": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard",
|
||||
|
@ -101,10 +79,6 @@
|
|||
"description": "SKU for the vault"
|
||||
}
|
||||
},
|
||||
"keyVaultTenantId": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"luisAccountName": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
|
@ -128,6 +102,10 @@
|
|||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"partnerCenterAccountId": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"partnerCenterApplicationId": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
|
@ -136,10 +114,6 @@
|
|||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"partnerCenterApplicationTenantId": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"qnAKnowledgebaseId": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
|
@ -152,6 +126,10 @@
|
|||
"type": "string",
|
||||
"defaultValue": "https://github.com/Microsoft/Partner-Center-Bot"
|
||||
},
|
||||
"branch": {
|
||||
"type": "string",
|
||||
"defaultValue": "master"
|
||||
},
|
||||
"siteName": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
|
@ -182,24 +160,12 @@
|
|||
"metadata": {
|
||||
"description": "Describes the plan's instance count"
|
||||
}
|
||||
},
|
||||
"storageAccountType": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard_LRS",
|
||||
"allowedValues": [
|
||||
"Standard_LRS",
|
||||
"Standard_GRS",
|
||||
"Standard_ZRS",
|
||||
"Premium_LRS"
|
||||
],
|
||||
"metadata": {
|
||||
"description": "Storage Account Type"
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"cacheName": "[concat('cache', uniqueString(resourceGroup().id))]",
|
||||
"storageAccountName": "[concat('storage', uniqueString(resourceGroup().id))]"
|
||||
"identityResourceId": "[concat(resourceId('Microsoft.Web/sites', parameters('siteName')),'/providers/Microsoft.ManagedIdentity/Identities/default')]",
|
||||
"keyVaultName": "[concat('vault', uniqueString(resourceGroup().id))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
|
@ -216,89 +182,91 @@
|
|||
},
|
||||
{
|
||||
"type": "Microsoft.KeyVault/vaults",
|
||||
"name": "[parameters('keyVaultName')]",
|
||||
"name": "[variables('keyVaultName')]",
|
||||
"apiVersion": "2015-06-01",
|
||||
"location": "[resourceGroup().location]",
|
||||
"tags": {},
|
||||
"properties": {
|
||||
"tenantId": "[parameters('keyVaultTenantId')]",
|
||||
"accessPolicies": [],
|
||||
"sku": {
|
||||
"name": "[parameters('keyVaultSkuName')]",
|
||||
"family": "A"
|
||||
"family": "A",
|
||||
"name": "premium"
|
||||
},
|
||||
"tenantId": "[reference(variables('identityResourceId'), '2015-08-31-PREVIEW').tenantId]",
|
||||
"accessPolicies": [
|
||||
{
|
||||
"tenantId": "[reference(variables('identityResourceId'), '2015-08-31-PREVIEW').tenantId]",
|
||||
"objectId": "[reference(variables('identityResourceId'), '2015-08-31-PREVIEW').principalId]",
|
||||
"permissions": {
|
||||
"secrets": [
|
||||
"get"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"enabledForDeployment": false
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Web/sites/', parameters('siteName'))]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.KeyVault/vaults/secrets",
|
||||
"name": "[concat(parameters('keyVaultName'), '/', 'ApplicationSecret')]",
|
||||
"name": "[concat(variables('keyVaultName'), '/', 'ApplicationSecret')]",
|
||||
"apiVersion": "2015-06-01",
|
||||
"properties": {
|
||||
"contentType": "text/plain",
|
||||
"value": "[parameters('applicationSecret')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.KeyVault/vaults/', parameters('keyVaultName'))]"
|
||||
"[concat('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.KeyVault/vaults/secrets",
|
||||
"name": "[concat(parameters('keyVaultName'), '/', 'LuisApiKey')]",
|
||||
"name": "[concat(variables('keyVaultName'), '/', 'LuisApiKey')]",
|
||||
"apiVersion": "2015-06-01",
|
||||
"properties": {
|
||||
"contentType": "text/plain",
|
||||
"value": "[parameters('luisApiKey')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.KeyVault/vaults/', parameters('keyVaultName'))]"
|
||||
"[concat('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.KeyVault/vaults/secrets",
|
||||
"name": "[concat(parameters('keyVaultName'), '/', 'MicrosoftAppPassword')]",
|
||||
"name": "[concat(variables('keyVaultName'), '/', 'MicrosoftAppPassword')]",
|
||||
"apiVersion": "2015-06-01",
|
||||
"properties": {
|
||||
"contentType": "text/plain",
|
||||
"value": "[parameters('microsoftAppPassword')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.KeyVault/vaults/', parameters('keyVaultName'))]"
|
||||
"[concat('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.KeyVault/vaults/secrets",
|
||||
"name": "[concat(parameters('keyVaultName'), '/', 'PartnerCenterApplicationSecret')]",
|
||||
"name": "[concat(variables('keyVaultName'), '/', 'PartnerCenterApplicationSecret')]",
|
||||
"apiVersion": "2015-06-01",
|
||||
"properties": {
|
||||
"contentType": "text/plain",
|
||||
"value": "[parameters('partnerCenterApplicationSecret')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.KeyVault/vaults/', parameters('keyVaultName'))]"
|
||||
"[concat('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.KeyVault/vaults/secrets",
|
||||
"name": "[concat(parameters('keyVaultName'), '/', 'QnASubscriptionKey')]",
|
||||
"name": "[concat(variables('keyVaultName'), '/', 'QnASubscriptionKey')]",
|
||||
"apiVersion": "2015-06-01",
|
||||
"properties": {
|
||||
"contentType": "text/plain",
|
||||
"value": "[parameters('qnASubscriptionKey')]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.KeyVault/vaults/', parameters('keyVaultName'))]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.KeyVault/vaults/secrets",
|
||||
"name": "[concat(parameters('keyVaultName'), '/', 'StorageConnectionString')]",
|
||||
"apiVersion": "2015-06-01",
|
||||
"properties": {
|
||||
"contentType": "text/plain",
|
||||
"value": "[Concat('DefaultEndpointsProtocol=https;AccountName=',variables('StorageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName')), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).keys[0].value)]"
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.KeyVault/vaults/', parameters('keyVaultName'))]",
|
||||
"[concat('Microsoft.Storage/storageAccounts/', variables('StorageAccountName'))]"
|
||||
"[concat('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -318,18 +286,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Storage/storageAccounts",
|
||||
"name": "[variables('storageAccountName')]",
|
||||
"apiVersion": "2016-01-01",
|
||||
"location": "[resourceGroup().location]",
|
||||
"sku": {
|
||||
"name": "[parameters('storageAccountType')]"
|
||||
},
|
||||
"kind": "Storage",
|
||||
"properties": {
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2015-08-01",
|
||||
"name": "[parameters('hostingPlanName')]",
|
||||
|
@ -397,12 +353,9 @@
|
|||
"ApplicationId": "[parameters('applicationId')]",
|
||||
"ApplicationTenantId": "[parameters('applicationTenantId')]",
|
||||
"InstrumentationKey": "[reference(concat('Microsoft.Insights/components/', parameters('siteName'))).InstrumentationKey]",
|
||||
"PartnerCenterApplicationId": "[parameters('partnerCenterApplicationId')]",
|
||||
"PartnerCenterApplicationTenantId": "[parameters('partnerCenterApplicationTenantId')]",
|
||||
"VaultApplicationCertThumbprint": "[parameters('keyVaultAppCertThumbprint')]",
|
||||
"VaultApplicationId": "[parameters('keyVaultAppId')]",
|
||||
"VaultApplicationTenantId": "[parameters('keyVaultTenantId')]",
|
||||
"VaultBaseAddress": "[concat('https://', parameters('keyVaultName'), '.vault.azure.net')]"
|
||||
"PartnerCenter.AccountId": "[parameters('partnerCenterAccountId')]",
|
||||
"PartnerCenter.ApplicationId": "[parameters('partnerCenterApplicationId')]",
|
||||
"KeyVaultEndpoint": "[concat('https://', variables('keyVaultName'), '.vault.azure.net')]"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Deployment
|
||||
Deploying and configure the Partner Center Bot requires numerous configurations. This document will guide you through each of the
|
||||
Deploying and configuring the Partner Center Bot requires numerous configurations. This document will guide you through each of the
|
||||
configurations. If you need help please log an issue using the [issue tracker](https://github.com/Microsoft/Partner-Center-Bot/issues).
|
||||
|
||||
## Prerequisites
|
||||
|
@ -12,58 +12,10 @@ The following are _required_ prerequisites in order to perform the steps outline
|
|||
|
||||
If you do not have the privileges then you will not able to perform the remaining operations in this guide.
|
||||
|
||||
## Azure Key Vault
|
||||
Azure Key Vault is used to protect application secrets and connection strings. An instance of Key Vault will be deployed,
|
||||
the following sections will walk you through the configurations that are not automated yet.
|
||||
|
||||
### Create Certificate
|
||||
A certificate is utilized to obtain the required access token in order to interact with the vault. Perform
|
||||
the following to create the certificate
|
||||
|
||||
Modify the following PowerShell and then invoke it
|
||||
|
||||
```powershell
|
||||
$cert = New-SelfSignedCertificate -Subject "PartnerCenterBot" -CertStoreLocation Cert:CurrentUser -KeyExportPolicy Exportable -Type DocumentEncryptionCert -KeyUsage KeyEncipherment -KeySpec KeyExchange -KeyLength 2048
|
||||
|
||||
Export-Certificate -Cert $cert -FilePath C:\cert\bot.cer
|
||||
```
|
||||
|
||||
### Create Azure AD Application
|
||||
An Azure AD application is utilized to access the instance of Key Vault. This application is confiugred to
|
||||
utilize the certificate that was created in the above step. Perform the following to create the and configure
|
||||
the application
|
||||
|
||||
1. Open PowerShell and install the [Azure PowerShell cmdlets](https://docs.microsoft.com/en-us/powershell/azureps-cmdlets-docs/)
|
||||
if you necessary
|
||||
2. Update the following cmdlets and then invoke them
|
||||
|
||||
```powershell
|
||||
Login-AzureRmAccount
|
||||
|
||||
## Update these variable before invoking the rest of the cmdlets
|
||||
$certFile = "C:\cert\bot.cer"
|
||||
$identifierUri = "https://{0}/{1}" -f "tenant.onmicrosoft.com", [System.Guid]::NewGuid()
|
||||
$resourceGroupName = "ResourceGroupName"
|
||||
|
||||
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
|
||||
$cert.Import($certFile)
|
||||
$value = [System.Convert]::ToBase64String($cert.GetRawCertData())
|
||||
|
||||
$app = New-AzureRmADApplication -DisplayName "Partern Center Bot Vault App" -HomePage "https://localhost" -IdentifierUris $identifierUri -CertValue $value -EndDate $cert.NotAfter -StartDate $cert.NotBefore
|
||||
$spn = New-AzureRmADServicePrincipal -ApplicationId $app.ApplicationId
|
||||
|
||||
# Get the certificate thumbprint value for the VaultApplicationCertThumbprint parameter
|
||||
$cert.Thumbprint
|
||||
|
||||
# Get the application identifier that will be used to access the instance of Key Vault
|
||||
$app.ApplicationId
|
||||
```
|
||||
|
||||
## Partner Center Azure AD Application
|
||||
The Partner Center API is utilized to verify the authenticated user belongs to a customer that
|
||||
has a relationship with the configured partner. Perform the following to create the application
|
||||
The Partner Center API is utilized to verify the authenticated user belongs to a customer that has a relationship with the configured partner. Perform the following to create the application
|
||||
|
||||
1. Login into the [Partner Center](https://partnercenter.microsoft.com) portal using credentials that have _AdminAgents_ and _Global Admin_ privileges
|
||||
1. Login to the [Partner Center](https://partnercenter.microsoft.com) portal using credentials that have _AdminAgents_ and _Global Admin_ privileges
|
||||
2. Click _Dashboard_ -> _Account Settings_ -> _App Management_
|
||||
3. Click on _Register existing_ app if you want to use an existing Azure AD application, or click _Add new web app_ to create a new one
|
||||
|
||||
|
@ -76,8 +28,8 @@ has a relationship with the configured partner. Perform the following to create
|
|||
## Creating the Bot Azure AD Application
|
||||
The bot requires an Azure AD application that grants privileges to Azure AD and the Microsoft Graph. Perform the following tasks to create and configure the application
|
||||
|
||||
1. Login into the [Azure Management portal](https://portal.azure.com) using credentials that have _Global Admin_ privileges
|
||||
2. Open the _Azure Active Directory_ user experince and then click _App registration_
|
||||
1. Login to the [Azure Management portal](https://portal.azure.com) using credentials that have _Global Admin_ privileges
|
||||
2. Open the _Azure Active Directory_ user experience and then click _App registration_
|
||||
|
||||
![Azure AD application creation](Images/aad01.png)
|
||||
|
||||
|
@ -105,7 +57,7 @@ Perform the following to create and configure the Language Understanding Intelli
|
|||
Please checkout [Publish your app](https://docs.microsoft.com/en-us/azure/cognitive-services/LUIS/publishapp) for more information.
|
||||
|
||||
## Create a New Instance of the QnA Service
|
||||
Perform the following in order to create a new instnace of the QnA service
|
||||
Perform the following in order to create a new instance of the QnA service
|
||||
|
||||
1. Browse to https://qnamaker.ai/ and login using an appropriate account
|
||||
2. Click the _Create new service_ link found at the top of the page
|
||||
|
|
|
@ -23,7 +23,7 @@ $AppId = 'INSERT-APPLICATION-ID-HERE'
|
|||
$DisplayName = 'INSERT-APPLICATION-DISPLAY-NAME-HERE'
|
||||
|
||||
$g = Get-AzureADGroup | ? {$_.DisplayName -eq 'AdminAgents'}
|
||||
$s = Get-AzureADServicePrincipal | ? {$_.AppId -eq $AppId}
|
||||
$s = Get-AzureADServicePrincipal -All $true | ? {$_.AppId -eq $AppId}
|
||||
|
||||
if ($s -eq $null) { $s = New-AzureADServicePrincipal -AppId $AppId -DisplayName $DisplayName }
|
||||
Add-AzureADGroupMember -ObjectId $g.ObjectId -RefObjectId $s.ObjectId
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
<ExcludeComponentCorrelationHttpHeadersOnDomains>
|
||||
<!--
|
||||
Requests to the following hostnames will not be modified by adding correlation headers.
|
||||
This is only applicable if Profiler is installed via either StatusMonitor or Azure Extension.
|
||||
Add entries here to exclude additional hostnames.
|
||||
NOTE: this configuration will be lost upon NuGet upgrade.
|
||||
-->
|
||||
|
@ -32,7 +31,13 @@
|
|||
<Add>core.chinacloudapi.cn</Add>
|
||||
<Add>core.cloudapi.de</Add>
|
||||
<Add>core.usgovcloudapi.net</Add>
|
||||
<Add>localhost</Add>
|
||||
<Add>127.0.0.1</Add>
|
||||
</ExcludeComponentCorrelationHttpHeadersOnDomains>
|
||||
<IncludeDiagnosticSourceActivities>
|
||||
<Add>Microsoft.Azure.EventHubs</Add>
|
||||
<Add>Microsoft.Azure.ServiceBus</Add>
|
||||
</IncludeDiagnosticSourceActivities>
|
||||
</Add>
|
||||
<Add Type="Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector.PerformanceCollectorModule, Microsoft.AI.PerfCounterCollector">
|
||||
<!--
|
||||
|
@ -56,7 +61,10 @@
|
|||
<Add Type="Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector.QuickPulse.QuickPulseTelemetryModule, Microsoft.AI.PerfCounterCollector"/>
|
||||
<Add Type="Microsoft.ApplicationInsights.WindowsServer.DeveloperModeWithDebuggerAttachedTelemetryModule, Microsoft.AI.WindowsServer"/>
|
||||
<Add Type="Microsoft.ApplicationInsights.WindowsServer.UnhandledExceptionTelemetryModule, Microsoft.AI.WindowsServer"/>
|
||||
<Add Type="Microsoft.ApplicationInsights.WindowsServer.UnobservedExceptionTelemetryModule, Microsoft.AI.WindowsServer"/>
|
||||
<Add Type="Microsoft.ApplicationInsights.WindowsServer.UnobservedExceptionTelemetryModule, Microsoft.AI.WindowsServer">
|
||||
<!--</Add>
|
||||
<Add Type="Microsoft.ApplicationInsights.WindowsServer.FirstChanceExceptionStatisticsTelemetryModule, Microsoft.AI.WindowsServer">-->
|
||||
</Add>
|
||||
<Add Type="Microsoft.ApplicationInsights.Web.RequestTrackingTelemetryModule, Microsoft.AI.Web">
|
||||
<Handlers>
|
||||
<!--
|
||||
|
@ -64,7 +72,6 @@
|
|||
|
||||
NOTE: handler configuration will be lost upon NuGet upgrade.
|
||||
-->
|
||||
<Add>System.Web.Handlers.TransferRequestHandler</Add>
|
||||
<Add>Microsoft.VisualStudio.Web.PageInspector.Runtime.Tracing.RequestDataHttpHandler</Add>
|
||||
<Add>System.Web.StaticFileHandler</Add>
|
||||
<Add>System.Web.Handlers.AssemblyResourceLoader</Add>
|
||||
|
@ -76,11 +83,37 @@
|
|||
</Handlers>
|
||||
</Add>
|
||||
<Add Type="Microsoft.ApplicationInsights.Web.ExceptionTrackingTelemetryModule, Microsoft.AI.Web"/>
|
||||
<Add Type="Microsoft.ApplicationInsights.Web.AspNetDiagnosticTelemetryModule, Microsoft.AI.Web"/>
|
||||
</TelemetryModules>
|
||||
<TelemetryProcessors>
|
||||
<Add Type="Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector.QuickPulse.QuickPulseTelemetryProcessor, Microsoft.AI.PerfCounterCollector"/>
|
||||
<Add Type="Microsoft.ApplicationInsights.Extensibility.AutocollectedMetricsExtractor, Microsoft.ApplicationInsights"/>
|
||||
<Add Type="Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel.AdaptiveSamplingTelemetryProcessor, Microsoft.AI.ServerTelemetryChannel">
|
||||
<MaxTelemetryItemsPerSecond>5</MaxTelemetryItemsPerSecond>
|
||||
<ExcludedTypes>Event</ExcludedTypes>
|
||||
</Add>
|
||||
<Add Type="Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel.AdaptiveSamplingTelemetryProcessor, Microsoft.AI.ServerTelemetryChannel">
|
||||
<MaxTelemetryItemsPerSecond>5</MaxTelemetryItemsPerSecond>
|
||||
<IncludedTypes>Event</IncludedTypes>
|
||||
</Add>
|
||||
<Add Type="Microsoft.ApplicationInsights.SnapshotCollector.SnapshotCollectorTelemetryProcessor, Microsoft.ApplicationInsights.SnapshotCollector">
|
||||
<!-- The default is true, but you can disable Snapshot Debugging by setting it to false -->
|
||||
<IsEnabled>true</IsEnabled>
|
||||
<!-- Snapshot Debugging is usually disabled in developer mode, but you can enable it by setting this to true. -->
|
||||
<!-- DeveloperMode is a property on the active TelemetryChannel. -->
|
||||
<IsEnabledInDeveloperMode>false</IsEnabledInDeveloperMode>
|
||||
<!-- How many times we need to see an exception before we ask for snapshots. -->
|
||||
<ThresholdForSnapshotting>1</ThresholdForSnapshotting>
|
||||
<!-- The maximum number of snapshots we collect for a single problem. -->
|
||||
<MaximumSnapshotsRequired>3</MaximumSnapshotsRequired>
|
||||
<!-- The maximum number of problems that we can be tracking at any time. -->
|
||||
<MaximumCollectionPlanSize>50</MaximumCollectionPlanSize>
|
||||
<!-- How often to reset problem counters. -->
|
||||
<ProblemCounterResetInterval>24:00:00</ProblemCounterResetInterval>
|
||||
<!-- The maximum number of snapshots allowed per day. -->
|
||||
<SnapshotsPerDayLimit>30</SnapshotsPerDayLimit>
|
||||
<!--Whether or not to collect snapshot in low IO priority thread.-->
|
||||
<SnapshotInLowPriorityThread>true</SnapshotInLowPriorityThread>
|
||||
</Add>
|
||||
</TelemetryProcessors>
|
||||
<TelemetryChannel Type="Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel.ServerTelemetryChannel, Microsoft.AI.ServerTelemetryChannel"/>
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
<TargetFrameworkProfile />
|
||||
<Use64BitIISExpress />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
@ -42,107 +43,154 @@
|
|||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Autofac, Version=3.5.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Autofac.3.5.2\lib\net40\Autofac.dll</HintPath>
|
||||
<Reference Include="Autofac, Version=4.6.2.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Autofac.4.6.2\lib\net45\Autofac.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Autofac.Integration.WebApi, Version=4.0.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Autofac.WebApi2.4.0.1\lib\net45\Autofac.Integration.WebApi.dll</HintPath>
|
||||
<Reference Include="Autofac.Integration.WebApi, Version=4.1.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Autofac.WebApi2.4.1.0\lib\net45\Autofac.Integration.WebApi.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Chronic, Version=0.3.2.0, Culture=neutral, PublicKeyToken=3bd1f1ef638b0d3c, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Chronic.Signed.0.3.2\lib\net40\Chronic.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AI.Agent.Intercept, Version=2.0.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.ApplicationInsights.Agent.Intercept.2.0.7\lib\net45\Microsoft.AI.Agent.Intercept.dll</HintPath>
|
||||
<Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AI.DependencyCollector, Version=2.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.ApplicationInsights.DependencyCollector.2.3.0\lib\net45\Microsoft.AI.DependencyCollector.dll</HintPath>
|
||||
<Reference Include="EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AI.PerfCounterCollector, Version=2.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.ApplicationInsights.PerfCounterCollector.2.3.0\lib\net45\Microsoft.AI.PerfCounterCollector.dll</HintPath>
|
||||
<Reference Include="Microsoft.AI.Agent.Intercept, Version=2.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.ApplicationInsights.Agent.Intercept.2.4.0\lib\net45\Microsoft.AI.Agent.Intercept.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AI.ServerTelemetryChannel, Version=2.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel.2.3.0\lib\net45\Microsoft.AI.ServerTelemetryChannel.dll</HintPath>
|
||||
<Reference Include="Microsoft.AI.DependencyCollector, Version=2.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.ApplicationInsights.DependencyCollector.2.5.1\lib\net45\Microsoft.AI.DependencyCollector.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AI.Web, Version=2.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.ApplicationInsights.Web.2.3.0\lib\net45\Microsoft.AI.Web.dll</HintPath>
|
||||
<Reference Include="Microsoft.AI.PerfCounterCollector, Version=2.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.ApplicationInsights.PerfCounterCollector.2.5.1\lib\net45\Microsoft.AI.PerfCounterCollector.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AI.WindowsServer, Version=2.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.ApplicationInsights.WindowsServer.2.3.0\lib\net45\Microsoft.AI.WindowsServer.dll</HintPath>
|
||||
<Reference Include="Microsoft.AI.ServerTelemetryChannel, Version=2.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel.2.5.1\lib\net45\Microsoft.AI.ServerTelemetryChannel.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.ApplicationInsights, Version=2.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.ApplicationInsights.2.3.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
|
||||
<Reference Include="Microsoft.AI.Web, Version=2.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.ApplicationInsights.Web.2.5.1\lib\net45\Microsoft.AI.Web.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AI.WindowsServer, Version=2.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.ApplicationInsights.WindowsServer.2.5.1\lib\net45\Microsoft.AI.WindowsServer.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.ApplicationInsights, Version=2.5.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.ApplicationInsights.2.5.1\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.ApplicationInsights.SnapshotCollector, Version=1.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.ApplicationInsights.SnapshotCollector.1.2.1\lib\net45\Microsoft.ApplicationInsights.SnapshotCollector.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AspNet.TelemetryCorrelation, Version=1.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.AspNet.TelemetryCorrelation.1.0.1\lib\net45\Microsoft.AspNet.TelemetryCorrelation.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Azure.Documents.Client, Version=1.21.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Azure.DocumentDB.1.21.1\lib\net45\Microsoft.Azure.Documents.Client.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Azure.KeyVault, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Azure.KeyVault.2.0.6\lib\net45\Microsoft.Azure.KeyVault.dll</HintPath>
|
||||
<HintPath>..\..\packages\Microsoft.Azure.KeyVault.2.3.2\lib\net452\Microsoft.Azure.KeyVault.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Azure.KeyVault.Core, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Azure.KeyVault.Core.2.0.4\lib\net45\Microsoft.Azure.KeyVault.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Azure.KeyVault.WebKey, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Azure.KeyVault.WebKey.2.0.5\lib\net452\Microsoft.Azure.KeyVault.WebKey.dll</HintPath>
|
||||
<HintPath>..\..\packages\Microsoft.Azure.KeyVault.WebKey.2.0.7\lib\net452\Microsoft.Azure.KeyVault.WebKey.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Bot.Builder, Version=3.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Bot.Builder.3.8.0\lib\net46\Microsoft.Bot.Builder.dll</HintPath>
|
||||
<Reference Include="Microsoft.Azure.Services.AppAuthentication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Azure.Services.AppAuthentication.1.1.0-preview\lib\net452\Microsoft.Azure.Services.AppAuthentication.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Bot.Builder.Autofac, Version=3.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Bot.Builder.3.8.0\lib\net46\Microsoft.Bot.Builder.Autofac.dll</HintPath>
|
||||
<Reference Include="Microsoft.Bot.Builder, Version=3.14.1.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Bot.Builder.3.14.1.1\lib\net46\Microsoft.Bot.Builder.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Bot.Builder.CognitiveServices.QnAMaker, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Bot.Builder.CognitiveServices.1.1.0\lib\net46\Microsoft.Bot.Builder.CognitiveServices.QnAMaker.dll</HintPath>
|
||||
<Reference Include="Microsoft.Bot.Builder.Autofac, Version=3.14.1.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Bot.Builder.3.14.1.1\lib\net46\Microsoft.Bot.Builder.Autofac.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Bot.Connector, Version=3.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Bot.Builder.3.8.0\lib\net46\Microsoft.Bot.Connector.dll</HintPath>
|
||||
<Reference Include="Microsoft.Bot.Builder.Azure, Version=3.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Bot.Builder.Azure.3.2.5\lib\net46\Microsoft.Bot.Builder.Azure.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Bot.Builder.CognitiveServices.QnAMaker, Version=1.1.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Bot.Builder.CognitiveServices.1.1.2\lib\net46\Microsoft.Bot.Builder.CognitiveServices.QnAMaker.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Bot.Builder.History, Version=3.14.1.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Bot.Builder.History.3.14.1.1\lib\net46\Microsoft.Bot.Builder.History.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Bot.Connector, Version=3.14.1.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Bot.Connector.3.14.1.1\lib\net46\Microsoft.Bot.Connector.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="Microsoft.Graph, Version=1.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Graph.1.3.0\lib\net45\Microsoft.Graph.dll</HintPath>
|
||||
<Reference Include="Microsoft.Graph, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Graph.1.8.1\lib\net45\Microsoft.Graph.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Graph.Core, Version=1.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Graph.Core.1.4.0\lib\net45\Microsoft.Graph.Core.dll</HintPath>
|
||||
<Reference Include="Microsoft.Graph.Core, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Graph.Core.1.8.1\lib\net45\Microsoft.Graph.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.IdentityModel.Clients.ActiveDirectory, Version=3.13.9.1126, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.13.9\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll</HintPath>
|
||||
<Reference Include="Microsoft.IdentityModel.Clients.ActiveDirectory, Version=3.19.2.6005, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.19.2\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.IdentityModel.Clients.ActiveDirectory.Platform, Version=3.13.9.1126, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.13.9\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll</HintPath>
|
||||
<Reference Include="Microsoft.IdentityModel.Clients.ActiveDirectory.Platform, Version=3.19.2.6005, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.19.2\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.IdentityModel.Logging, Version=5.2.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.IdentityModel.Logging.5.2.1\lib\net451\Microsoft.IdentityModel.Logging.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.IdentityModel.Protocol.Extensions, Version=1.0.40306.1554, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.IdentityModel.Protocol.Extensions.1.0.4.403061554\lib\net45\Microsoft.IdentityModel.Protocol.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.IdentityModel.Protocols, Version=5.2.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.IdentityModel.Protocols.5.2.1\lib\net451\Microsoft.IdentityModel.Protocols.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect, Version=5.2.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.IdentityModel.Protocols.OpenIdConnect.5.2.1\lib\net451\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.IdentityModel.Tokens, Version=5.2.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.IdentityModel.Tokens.5.2.1\lib\net451\Microsoft.IdentityModel.Tokens.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Rest.ClientRuntime, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Rest.ClientRuntime.2.3.7\lib\net452\Microsoft.Rest.ClientRuntime.dll</HintPath>
|
||||
<HintPath>..\..\packages\Microsoft.Rest.ClientRuntime.2.3.11\lib\net452\Microsoft.Rest.ClientRuntime.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Rest.ClientRuntime.Azure, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Rest.ClientRuntime.Azure.3.3.6\lib\net452\Microsoft.Rest.ClientRuntime.Azure.dll</HintPath>
|
||||
<HintPath>..\..\packages\Microsoft.Rest.ClientRuntime.Azure.3.3.12\lib\net452\Microsoft.Rest.ClientRuntime.Azure.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Store.PartnerCenter, Version=1.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Store.PartnerCenter.1.4.0\lib\net45\Microsoft.Store.PartnerCenter.dll</HintPath>
|
||||
<Reference Include="Microsoft.Store.PartnerCenter, Version=1.7.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Store.PartnerCenter.1.7.1\lib\Net45\Microsoft.Store.PartnerCenter.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Store.PartnerCenter.Extensions, Version=1.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Store.PartnerCenter.1.4.0\lib\net45\Microsoft.Store.PartnerCenter.Extensions.dll</HintPath>
|
||||
<Reference Include="Microsoft.Store.PartnerCenter.Extensions, Version=1.7.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Store.PartnerCenter.1.7.1\lib\Net45\Microsoft.Store.PartnerCenter.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Store.PartnerCenter.Models, Version=1.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Store.PartnerCenter.1.4.0\lib\net45\Microsoft.Store.PartnerCenter.Models.dll</HintPath>
|
||||
<Reference Include="Microsoft.Store.PartnerCenter.Models, Version=1.7.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Store.PartnerCenter.1.7.1\lib\Net45\Microsoft.Store.PartnerCenter.Models.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Reference Include="Microsoft.WindowsAzure.Storage, Version=9.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\WindowsAzure.Storage.9.1.0\lib\net45\Microsoft.WindowsAzure.Storage.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="StackExchange.Redis.StrongName, Version=1.2.3.0, Culture=neutral, PublicKeyToken=c219ff1ca8c2ce46, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\StackExchange.Redis.StrongName.1.2.3\lib\net46\StackExchange.Redis.StrongName.dll</HintPath>
|
||||
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="StackExchange.Redis.StrongName, Version=1.2.6.0, Culture=neutral, PublicKeyToken=c219ff1ca8c2ce46, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\StackExchange.Redis.StrongName.1.2.6\lib\net46\StackExchange.Redis.StrongName.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.IdentityModel.Tokens.Jwt, Version=4.0.40306.1554, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.IdentityModel.Tokens.Jwt.4.0.4.403061554\lib\net45\System.IdentityModel.Tokens.Jwt.dll</HintPath>
|
||||
<Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.2.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Diagnostics.DiagnosticSource.4.4.1\lib\net46\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.IdentityModel.Tokens.Jwt, Version=5.2.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.IdentityModel.Tokens.Jwt.5.2.1\lib\net451\System.IdentityModel.Tokens.Jwt.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Reference Include="System.Net" />
|
||||
<Reference Include="System.Net.Http, Version=4.1.1.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Net.Http.4.3.2\lib\net46\System.Net.Http.dll</HintPath>
|
||||
<Reference Include="System.Net.Http, Version=4.1.1.2, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Net.Http.4.3.3\lib\net46\System.Net.Http.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http.Formatting, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll</HintPath>
|
||||
<Reference Include="System.Net.Http.Formatting, Version=5.2.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.4\lib\net45\System.Net.Http.Formatting.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http.WebRequest" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Security.Cryptography.Algorithms, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net461\System.Security.Cryptography.Algorithms.dll</HintPath>
|
||||
<HintPath>..\..\packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net461\System.Security.Cryptography.Algorithms.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Security.Cryptography.Encoding, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll</HintPath>
|
||||
|
@ -150,8 +198,8 @@
|
|||
<Reference Include="System.Security.Cryptography.Primitives, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Security.Cryptography.X509Certificates, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll</HintPath>
|
||||
<Reference Include="System.Security.Cryptography.X509Certificates, Version=4.1.1.2, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net461\System.Security.Cryptography.X509Certificates.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.DynamicData" />
|
||||
<Reference Include="System.Web.Entity" />
|
||||
|
@ -162,11 +210,11 @@
|
|||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Web.Extensions" />
|
||||
<Reference Include="System.Web.Http, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll</HintPath>
|
||||
<Reference Include="System.Web.Http, Version=5.2.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.AspNet.WebApi.Core.5.2.4\lib\net45\System.Web.Http.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.Http.WebHost, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll</HintPath>
|
||||
<Reference Include="System.Web.Http.WebHost, Version=5.2.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.4\lib\net45\System.Web.Http.WebHost.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Configuration" />
|
||||
|
@ -189,11 +237,14 @@
|
|||
<Compile Include="App_Start\WebApiConfig.cs" />
|
||||
<Compile Include="Cache\CacheDatabaseType.cs" />
|
||||
<Compile Include="Cache\CacheException.cs" />
|
||||
<Compile Include="Cache\CacheService.cs" />
|
||||
<Compile Include="Cache\DistributedTokenCache.cs" />
|
||||
<Compile Include="Cache\ICacheService.cs" />
|
||||
<Compile Include="Configuration\Configuration.cs" />
|
||||
<Compile Include="Configuration\IConfiguration.cs" />
|
||||
<Compile Include="Extensions\BotContextExtensions.cs" />
|
||||
<Compile Include="Extensions\CustomerPrincipalExtensions.cs" />
|
||||
<Compile Include="Extensions\EnumExtensions.cs" />
|
||||
<Compile Include="Extensions\HealthEventExtensions.cs" />
|
||||
<Compile Include="Extensions\SubscriptionExtensions.cs" />
|
||||
<Compile Include="Providers\ConfigurationProvider.cs" />
|
||||
<Compile Include="Providers\IConfigurationProvider.cs" />
|
||||
<Compile Include="Controllers\BaseApiController.cs" />
|
||||
<Compile Include="Controllers\ImageController.cs" />
|
||||
<Compile Include="Controllers\MessagesController.cs" />
|
||||
|
@ -201,6 +252,8 @@
|
|||
<Compile Include="Dialogs\ActionDialog.cs" />
|
||||
<Compile Include="Dialogs\AuthDialog.cs" />
|
||||
<Compile Include="Dialogs\QuestionDialog.cs" />
|
||||
<Compile Include="Extensions\StringExtensions.cs" />
|
||||
<Compile Include="Extensions\ValidationExtensions.cs" />
|
||||
<Compile Include="Global.asax.cs">
|
||||
<DependentUpon>Global.asax</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -223,26 +276,26 @@
|
|||
<Compile Include="Logic\Office\ServiceCommunications.cs" />
|
||||
<Compile Include="Logic\OperationContext.cs" />
|
||||
<Compile Include="Logic\PartnerOperations.cs" />
|
||||
<Compile Include="Models\ApplicationCredential.cs" />
|
||||
<Compile Include="Providers\AccessTokenProvider.cs" />
|
||||
<Compile Include="Providers\BotProvider.cs" />
|
||||
<Compile Include="Providers\IAccessTokenProvider.cs" />
|
||||
<Compile Include="Providers\IBotProvider.cs" />
|
||||
<Compile Include="Providers\ICacheProvider.cs" />
|
||||
<Compile Include="Providers\IVaultProvider.cs" />
|
||||
<Compile Include="Providers\KeyVaultProvider.cs" />
|
||||
<Compile Include="Providers\RedisCacheprovider.cs" />
|
||||
<Compile Include="Security\AuthenticationProvider.cs" />
|
||||
<Compile Include="Security\CustomBotAuthentication.cs" />
|
||||
<Compile Include="Security\CustomerPrincipal.cs" />
|
||||
<Compile Include="Security\ITokenManagement.cs" />
|
||||
<Compile Include="Security\IVaultService.cs" />
|
||||
<Compile Include="Security\Permissions.cs" />
|
||||
<Compile Include="Security\UserRoles.cs" />
|
||||
<Compile Include="Security\TokenManagement.cs" />
|
||||
<Compile Include="Logic\BotConstants.cs" />
|
||||
<Compile Include="Logic\BotService.cs" />
|
||||
<Compile Include="Security\DataProtectorException.cs" />
|
||||
<Compile Include="Logic\Extensions.cs" />
|
||||
<Compile Include="BotConstants.cs" />
|
||||
<Compile Include="Logic\GraphClient.cs" />
|
||||
<Compile Include="Logic\IBotService.cs" />
|
||||
<Compile Include="Security\IDataProtector.cs" />
|
||||
<Compile Include="Logic\IGraphClient.cs" />
|
||||
<Compile Include="Logic\ILocalizationService.cs" />
|
||||
<Compile Include="Logic\LocalizationService.cs" />
|
||||
<Compile Include="Security\MachineKeyDataProtector.cs" />
|
||||
<Compile Include="Models\PartnerCenterTokenModel.cs" />
|
||||
<Compile Include="Providers\ILocalizationProvider.cs" />
|
||||
<Compile Include="Providers\LocalizationProvider.cs" />
|
||||
<Compile Include="Models\PartnerCenterToken.cs" />
|
||||
<Compile Include="Models\RoleModel.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Resources.Designer.cs">
|
||||
|
@ -250,7 +303,6 @@
|
|||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Security\VaultService.cs" />
|
||||
<Compile Include="Telemetry\ApplicationInsightsTelemetryProvider.cs" />
|
||||
<Compile Include="Telemetry\EmptyTelemetryProvider.cs" />
|
||||
<Compile Include="Telemetry\ITelemetryProvider.cs" />
|
||||
|
@ -262,9 +314,8 @@
|
|||
<EmbeddedResource Include="Resources.de.resx">
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<Content Include="packages.config">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\PublishProfiles\partnercenterbot - Web Deploy.pubxml" />
|
||||
<None Include="Web.Debug.config">
|
||||
<DependentUpon>Web.config</DependentUpon>
|
||||
</None>
|
||||
|
@ -299,7 +350,7 @@
|
|||
<VisualStudio>
|
||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
||||
<WebProjectProperties>
|
||||
<UseIIS>True</UseIIS>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>3979</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
|
@ -313,6 +364,17 @@
|
|||
</FlavorProperties>
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
<Import Project="..\..\packages\Microsoft.Azure.Services.AppAuthentication.1.1.0-preview\build\Microsoft.Azure.Services.AppAuthentication.targets" Condition="Exists('..\..\packages\Microsoft.Azure.Services.AppAuthentication.1.1.0-preview\build\Microsoft.Azure.Services.AppAuthentication.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\packages\Microsoft.Azure.Services.AppAuthentication.1.1.0-preview\build\Microsoft.Azure.Services.AppAuthentication.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Azure.Services.AppAuthentication.1.1.0-preview\build\Microsoft.Azure.Services.AppAuthentication.targets'))" />
|
||||
<Error Condition="!Exists('..\..\packages\Microsoft.Azure.DocumentDB.1.21.1\build\Microsoft.Azure.DocumentDB.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Azure.DocumentDB.1.21.1\build\Microsoft.Azure.DocumentDB.targets'))" />
|
||||
<Error Condition="!Exists('..\..\packages\Microsoft.ApplicationInsights.SnapshotCollector.1.2.1\build\Microsoft.ApplicationInsights.SnapshotCollector.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.ApplicationInsights.SnapshotCollector.1.2.1\build\Microsoft.ApplicationInsights.SnapshotCollector.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\..\packages\Microsoft.Azure.DocumentDB.1.21.1\build\Microsoft.Azure.DocumentDB.targets" Condition="Exists('..\..\packages\Microsoft.Azure.DocumentDB.1.21.1\build\Microsoft.Azure.DocumentDB.targets')" />
|
||||
<Import Project="..\..\packages\Microsoft.ApplicationInsights.SnapshotCollector.1.2.1\build\Microsoft.ApplicationInsights.SnapshotCollector.targets" Condition="Exists('..\..\packages\Microsoft.ApplicationInsights.SnapshotCollector.1.2.1\build\Microsoft.ApplicationInsights.SnapshotCollector.targets')" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
|
|
@ -4,18 +4,13 @@
|
|||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
||||
namespace Microsoft.Store.PartnerCenter.Bot
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines various constant value utilized by the bot.
|
||||
/// </summary>
|
||||
internal static class BotConstants
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the application used for identification purposes with the Partner Center API.
|
||||
/// </summary>
|
||||
public const string ApplicationName = "US HMSP - Partner Center Bot v0.5";
|
||||
|
||||
/// <summary>
|
||||
/// Key utilized to access the authentication token stored in the user data.
|
||||
/// </summary>
|
|
@ -7,8 +7,11 @@
|
|||
namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using IdentityModel.Clients.ActiveDirectory;
|
||||
using Logic;
|
||||
using Extensions;
|
||||
using Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Custom implementation of the <see cref="TokenCache"/> class.
|
||||
|
@ -17,41 +20,54 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
public class DistributedTokenCache : TokenCache
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to core application services.
|
||||
/// Claim type for the object identifier claim.
|
||||
/// </summary>
|
||||
private readonly IBotService service;
|
||||
private const string ObjectIdClaimType = "http://schemas.microsoft.com/identity/claims/objectidentifier";
|
||||
|
||||
/// <summary>
|
||||
/// The unique identifier for the cache entry.
|
||||
/// </summary>
|
||||
private readonly string keyValue;
|
||||
|
||||
/// <summary>
|
||||
/// Resource that is being accessed.
|
||||
/// </summary>
|
||||
private readonly string resource;
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to the core bot providers.
|
||||
/// </summary>
|
||||
private readonly IBotProvider provider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DistributedTokenCache"/> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core application services.</param>
|
||||
/// <param name="provider">Provides access to the core explorer providers.</param>
|
||||
/// <param name="resource">The resource being accessed.</param>
|
||||
/// <param name="key">The unique identifier for the cache entry.</param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="resource"/> is empty or null.
|
||||
/// or
|
||||
/// <paramref name="key"/> is empty or null.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="service"/> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public DistributedTokenCache(IBotService service, string resource, string key)
|
||||
public DistributedTokenCache(IBotProvider provider, string resource, string key = null)
|
||||
{
|
||||
service.AssertNotNull(nameof(service));
|
||||
key.AssertNotEmpty(nameof(key));
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
resource.AssertNotEmpty(nameof(resource));
|
||||
|
||||
this.service = service;
|
||||
this.Key = key;
|
||||
this.provider = provider;
|
||||
keyValue = key;
|
||||
this.resource = resource;
|
||||
|
||||
this.AfterAccess = this.AfterAccessNotification;
|
||||
this.BeforeAccess = this.BeforeAccessNotification;
|
||||
AfterAccess = AfterAccessNotification;
|
||||
BeforeAccess = BeforeAccessNotification;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique identifier for the cache entry.
|
||||
/// </summary>
|
||||
private string Key { get; }
|
||||
private string Key => string.IsNullOrEmpty(keyValue) ? $"Resource::{resource}::Identifier::{ClaimsPrincipal.Current.Identities.First().FindFirst(ObjectIdClaimType).Value}" : keyValue;
|
||||
|
||||
/// <summary>
|
||||
/// Notification method called after any library method accesses the cache.
|
||||
|
@ -59,22 +75,22 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
/// <param name="args">Contains parameters used by the ADAL call accessing the cache.</param>
|
||||
public void AfterAccessNotification(TokenCacheNotificationArgs args)
|
||||
{
|
||||
if (!this.HasStateChanged)
|
||||
if (!HasStateChanged)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.Count > 0)
|
||||
if (Count > 0)
|
||||
{
|
||||
this.service.Cache.Store(
|
||||
CacheDatabaseType.Authentication, this.Key, Convert.ToBase64String(this.Serialize()));
|
||||
provider.Cache.Store(
|
||||
CacheDatabaseType.Authentication, Key, Convert.ToBase64String(Serialize()));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.service.Cache.Delete(CacheDatabaseType.Authentication, this.Key);
|
||||
provider.Cache.Delete(CacheDatabaseType.Authentication, Key);
|
||||
}
|
||||
|
||||
this.HasStateChanged = false;
|
||||
HasStateChanged = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -83,14 +99,14 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
/// <param name="args">Contains parameters used by the ADAL call accessing the cache.</param>
|
||||
public void BeforeAccessNotification(TokenCacheNotificationArgs args)
|
||||
{
|
||||
string value = this.service.Cache.Fetch<string>(CacheDatabaseType.Authentication, this.Key);
|
||||
string value = provider.Cache.Fetch<string>(CacheDatabaseType.Authentication, Key);
|
||||
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.Deserialize(Convert.FromBase64String(value));
|
||||
Deserialize(Convert.FromBase64String(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -100,7 +116,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
public override void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
this.service.Cache.Clear(CacheDatabaseType.Authentication);
|
||||
provider.Cache.Clear(CacheDatabaseType.Authentication);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -110,7 +126,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
public override void DeleteItem(TokenCacheItem item)
|
||||
{
|
||||
base.DeleteItem(item);
|
||||
this.service.Cache.Delete(CacheDatabaseType.Authentication, this.Key);
|
||||
provider.Cache.Delete(CacheDatabaseType.Authentication, Key);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,199 +0,0 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="Configuration.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Configuration
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using Logic;
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulates configurations utilized by the bot.
|
||||
/// </summary>
|
||||
public class Configuration : IConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to core services.
|
||||
/// </summary>
|
||||
private readonly IBotService service;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Configuration" /> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core services.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="service"/> is null.
|
||||
/// </exception>
|
||||
public Configuration(IBotService service)
|
||||
{
|
||||
service.AssertNotNull(nameof(service));
|
||||
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Azure Active Directory endpoint.
|
||||
/// </summary>
|
||||
public string ActiveDirectoryEndpoint => ConfigurationManager.AppSettings["ActiveDirectoryEndpoint"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Azure AD application identifier.
|
||||
/// </summary>
|
||||
public string ApplicationId => ConfigurationManager.AppSettings["ApplicationId"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Azure AD application secret.
|
||||
/// </summary>
|
||||
public string ApplicationSecret => this.GetConfigurationValue("ApplicationSecret");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Azure AD application tenant identifier.
|
||||
/// </summary>
|
||||
public string ApplicationTenantId => ConfigurationManager.AppSettings["ApplicationTenantId"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Microsoft Graph endpoint.
|
||||
/// </summary>
|
||||
public string GraphEndpoint => ConfigurationManager.AppSettings["MicrosoftGraphEndpoint"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Application Insights instrumentation key.
|
||||
/// </summary>
|
||||
public string InstrumentationKey => ConfigurationManager.AppSettings["InstrumentationKey"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the LUIS application identifier.
|
||||
/// </summary>
|
||||
public string LuisAppId => ConfigurationManager.AppSettings["LuisAppId"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the LUIS API key.
|
||||
/// </summary>
|
||||
public string LuisApiKey => this.GetConfigurationValue("LuisApiKey");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Microsoft application identifier.
|
||||
/// </summary>
|
||||
public string MicrosoftAppId => ConfigurationManager.AppSettings["MicrosoftAppId"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Microsoft application password.
|
||||
/// </summary>
|
||||
public string MicrosoftAppPassword => this.GetConfigurationValue("MicrosoftAppPassword");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Office 365 management endpoint address.
|
||||
/// </summary>
|
||||
public string OfficeManagementEndpoint => ConfigurationManager.AppSettings["OfficeManagementEndpoint"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Partner Center API endpoint.
|
||||
/// </summary>
|
||||
public string PartnerCenterEndpoint => ConfigurationManager.AppSettings["PartnerCenterEndpoint"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Partner Center application identifier value.
|
||||
/// </summary>
|
||||
public string PartnerCenterApplicationId => ConfigurationManager.AppSettings["PartnerCenterApplicationId"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Partner Center application secret value.
|
||||
/// </summary>
|
||||
public string PartnerCenterApplicationSecret => this.GetConfigurationValue("PartnerCenterApplicationSecret");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Partner Center application tenant identifier.
|
||||
/// </summary>
|
||||
public string PartnerCenterApplicationTenantId => ConfigurationManager.AppSettings["PartnerCenterApplicationTenantId"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the question and answer knowledgebase identifier.
|
||||
/// </summary>
|
||||
public string QnAKnowledgebaseId => ConfigurationManager.AppSettings["QnAKnowledgebaseId"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets question and answer subscription subscription key.
|
||||
/// </summary>
|
||||
public string QnASubscriptionKey => this.GetConfigurationValue("QnASubscriptionKey");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Redis Cache connection string.
|
||||
/// </summary>
|
||||
public string RedisCacheConnectionString => this.GetConfigurationValue("RedisCacheConnectionString");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vault application certificate thumbprint.
|
||||
/// </summary>
|
||||
public string VaultApplicationCertThumbprint => ConfigurationManager.AppSettings["VaultApplicationCertThumbprint"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vault application tenant identifier.
|
||||
/// </summary>
|
||||
public string VaultApplicationId => ConfigurationManager.AppSettings["VaultApplicationId"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vault application tenant identifier.
|
||||
/// </summary>
|
||||
public string VaultApplicationTenantId => ConfigurationManager.AppSettings["VaultApplicationTenantId"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the base address for the vault.
|
||||
/// </summary>
|
||||
public string VaultBaseAddress => ConfigurationManager.AppSettings["VaultBaseAddress"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configuration value.
|
||||
/// </summary>
|
||||
/// <param name="identifier">Identifier of the resource being requested.</param>
|
||||
/// <returns>A string represented the value of the configuration.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="identifier"/> is empty or null.
|
||||
/// </exception>
|
||||
private string GetConfigurationValue(string identifier)
|
||||
{
|
||||
DateTime startTime;
|
||||
Dictionary<string, double> eventMetrics;
|
||||
Dictionary<string, string> eventProperties;
|
||||
string value;
|
||||
|
||||
identifier.AssertNotNull(nameof(identifier));
|
||||
|
||||
try
|
||||
{
|
||||
startTime = DateTime.Now;
|
||||
|
||||
value = this.service.Vault.Get(identifier);
|
||||
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
value = ConfigurationManager.AppSettings[identifier];
|
||||
}
|
||||
|
||||
// Track the event measurements for analysis.
|
||||
eventMetrics = new Dictionary<string, double>
|
||||
{
|
||||
{ "ElapsedMilliseconds", DateTime.Now.Subtract(startTime).TotalMilliseconds }
|
||||
};
|
||||
|
||||
// Capture the request for the customer summary for analysis.
|
||||
eventProperties = new Dictionary<string, string>
|
||||
{
|
||||
{ "Identifier", identifier }
|
||||
};
|
||||
|
||||
this.service.Telemetry.TrackEvent("Configuration/GetConfigurationValue", eventProperties, eventMetrics);
|
||||
|
||||
return value;
|
||||
}
|
||||
finally
|
||||
{
|
||||
eventMetrics = null;
|
||||
eventProperties = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,7 +7,8 @@
|
|||
namespace Microsoft.Store.PartnerCenter.Bot.Controllers
|
||||
{
|
||||
using System.Web.Http;
|
||||
using Logic;
|
||||
using Extensions;
|
||||
using Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all API controllers.
|
||||
|
@ -18,20 +19,19 @@ namespace Microsoft.Store.PartnerCenter.Bot.Controllers
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseApiController"/> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core application services.</param>
|
||||
/// <param name="provider">Provides access to core application services.</param>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// <paramref name="service"/> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
protected BaseApiController(IBotService service)
|
||||
protected BaseApiController(IBotProvider provider)
|
||||
{
|
||||
service.AssertNotNull(nameof(service));
|
||||
|
||||
this.Service = service;
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
Provider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the bot service.
|
||||
/// </summary>
|
||||
protected IBotService Service { get; private set; }
|
||||
protected IBotProvider Provider { get; private set; }
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Controllers
|
|||
using System.Net.Http.Headers;
|
||||
using System.Web.Http;
|
||||
using Logic;
|
||||
using Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the ability to dynamically generate images.
|
||||
|
@ -25,8 +26,8 @@ namespace Microsoft.Store.PartnerCenter.Bot.Controllers
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImageController"/> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core services.</param>
|
||||
public ImageController(IBotService service) : base(service)
|
||||
/// <param name="provider">Provides access to core services.</param>
|
||||
public ImageController(IBotProvider provider) : base(provider)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -14,9 +14,10 @@ namespace Microsoft.Store.PartnerCenter.Bot.Controllers
|
|||
using System.Threading.Tasks;
|
||||
using System.Web.Http;
|
||||
using Dialogs;
|
||||
using Logic;
|
||||
using Extensions;
|
||||
using Microsoft.Bot.Builder.Dialogs;
|
||||
using Microsoft.Bot.Connector;
|
||||
using Providers;
|
||||
using Security;
|
||||
|
||||
/// <summary>
|
||||
|
@ -30,11 +31,11 @@ namespace Microsoft.Store.PartnerCenter.Bot.Controllers
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MessagesController"/> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core services.</param>
|
||||
/// <param name="provider">Provides access to core services.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="service"/> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public MessagesController(IBotService service) : base(service)
|
||||
public MessagesController(IBotProvider provider) : base(provider)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -63,8 +64,8 @@ namespace Microsoft.Store.PartnerCenter.Bot.Controllers
|
|||
client = new ConnectorClient(
|
||||
new Uri(activity.ServiceUrl),
|
||||
new MicrosoftAppCredentials(
|
||||
Service.Configuration.MicrosoftAppId,
|
||||
Service.Configuration.MicrosoftAppPassword));
|
||||
Provider.Configuration.MicrosoftAppId,
|
||||
Provider.Configuration.MicrosoftAppPassword.ToUnsecureString()));
|
||||
|
||||
await client.Conversations.ReplyToActivityAsync(activity.CreateReply(Resources.Welcome));
|
||||
}
|
||||
|
@ -72,7 +73,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Controllers
|
|||
|
||||
if (activity.Type == ActivityTypes.Message)
|
||||
{
|
||||
await Conversation.SendAsync(activity, () => new ActionDialog(Service));
|
||||
await Conversation.SendAsync(activity, () => new ActionDialog(Provider));
|
||||
}
|
||||
|
||||
// Capture the request for the customer summary for analysis.
|
||||
|
@ -89,10 +90,15 @@ namespace Microsoft.Store.PartnerCenter.Bot.Controllers
|
|||
{ "ElapsedMilliseconds", DateTime.Now.Subtract(startTime).TotalMilliseconds }
|
||||
};
|
||||
|
||||
Service.Telemetry.TrackEvent("api/messages", eventProperties, eventMeasurements);
|
||||
Provider.Telemetry.TrackEvent("api/messages", eventProperties, eventMeasurements);
|
||||
|
||||
return new HttpResponseMessage(HttpStatusCode.Accepted);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Provider.Telemetry.TrackException(ex);
|
||||
return new HttpResponseMessage(HttpStatusCode.BadRequest);
|
||||
}
|
||||
finally
|
||||
{
|
||||
client = null;
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Controllers
|
|||
using System.Web.Http;
|
||||
using Autofac;
|
||||
using Exceptions;
|
||||
using Extensions;
|
||||
using IdentityModel.Clients.ActiveDirectory;
|
||||
using Logic;
|
||||
using Microsoft.Bot.Builder.ConnectorEx;
|
||||
|
@ -24,6 +25,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Controllers
|
|||
using Microsoft.Bot.Builder.Dialogs.Internals;
|
||||
using Microsoft.Bot.Connector;
|
||||
using Models;
|
||||
using Providers;
|
||||
using Security;
|
||||
|
||||
/// <summary>
|
||||
|
@ -35,11 +37,11 @@ namespace Microsoft.Store.PartnerCenter.Bot.Controllers
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OAuthCallbackController"/> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core services.</param>
|
||||
/// <param name="provider">Provides access to core services.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="service"/> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public OAuthCallbackController(IBotService service) : base(service)
|
||||
public OAuthCallbackController(IBotProvider provider) : base(provider)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -134,7 +136,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Controllers
|
|||
{ "NumberOfRoles", principal.Roles.Count }
|
||||
};
|
||||
|
||||
Service.Telemetry.TrackEvent("api/OAuthCallback", eventProperties, eventMeasurements);
|
||||
Provider.Telemetry.TrackEvent("api/OAuthCallback", eventProperties, eventMeasurements);
|
||||
|
||||
response = Request.CreateResponse(HttpStatusCode.OK);
|
||||
response.Content = new StringContent(Resources.SuccessfulAuthentication);
|
||||
|
@ -142,7 +144,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Controllers
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Service.Telemetry.TrackException(ex);
|
||||
Provider.Telemetry.TrackException(ex);
|
||||
response = Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
|
||||
}
|
||||
finally
|
||||
|
@ -182,20 +184,26 @@ namespace Microsoft.Store.PartnerCenter.Bot.Controllers
|
|||
redirectUri =
|
||||
new Uri($"{HttpContext.Current.Request.Url.Scheme}://{HttpContext.Current.Request.Url.Host}:{HttpContext.Current.Request.Url.Port}/{BotConstants.CallbackPath}");
|
||||
|
||||
authResult = await Service.TokenManagement.GetTokenByAuthorizationCodeAsync(
|
||||
$"{Service.Configuration.ActiveDirectoryEndpoint}/{BotConstants.AuthorityEndpoint}",
|
||||
authResult = await Provider.AccessToken.AcquireTokenByAuthorizationCodeAsync(
|
||||
$"{Provider.Configuration.ActiveDirectoryEndpoint}/{BotConstants.AuthorityEndpoint}",
|
||||
code,
|
||||
Service.Configuration.GraphEndpoint,
|
||||
redirectUri);
|
||||
new ApplicationCredential
|
||||
{
|
||||
ApplicationId = Provider.Configuration.ApplicationId,
|
||||
ApplicationSecret = Provider.Configuration.ApplicationSecret,
|
||||
UseCache = true
|
||||
},
|
||||
redirectUri,
|
||||
Provider.Configuration.GraphEndpoint);
|
||||
|
||||
client = new GraphClient(Service, authResult.TenantId);
|
||||
client = new GraphClient(Provider, authResult.TenantId);
|
||||
|
||||
roles = await client.GetDirectoryRolesAsync(authResult.UserInfo.UniqueId);
|
||||
|
||||
principal = new CustomerPrincipal
|
||||
{
|
||||
AccessToken = authResult.AccessToken,
|
||||
AvailableIntents = (from intent in Service.Intent.Intents
|
||||
AvailableIntents = (from intent in Provider.Intent.Intents
|
||||
let roleList = Permissions.GetRoles(intent.Value.Permissions)
|
||||
from r in roleList
|
||||
where roles.SingleOrDefault(x => x.DisplayName.Equals(r)) != null
|
||||
|
@ -207,11 +215,11 @@ namespace Microsoft.Store.PartnerCenter.Bot.Controllers
|
|||
Roles = roles
|
||||
};
|
||||
|
||||
if (!Service.Configuration.ApplicationTenantId.Equals(
|
||||
if (!Provider.Configuration.ApplicationTenantId.Equals(
|
||||
authResult.TenantId,
|
||||
StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
await Service.PartnerOperations.GetCustomerAsync(principal, authResult.TenantId);
|
||||
await Provider.PartnerOperations.GetCustomerAsync(principal, authResult.TenantId);
|
||||
}
|
||||
|
||||
return principal;
|
||||
|
|
|
@ -11,12 +11,13 @@ namespace Microsoft.Store.PartnerCenter.Bot.Dialogs
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Logic;
|
||||
using Extensions;
|
||||
using Microsoft.Bot.Builder.Dialogs;
|
||||
using Microsoft.Bot.Builder.Luis;
|
||||
using Microsoft.Bot.Builder.Luis.Models;
|
||||
using Microsoft.Bot.Connector;
|
||||
using Security;
|
||||
using Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Dialog that handles communication with the user.
|
||||
|
@ -30,21 +31,20 @@ namespace Microsoft.Store.PartnerCenter.Bot.Dialogs
|
|||
/// <summary>
|
||||
/// Provides access to core application services.
|
||||
/// </summary>
|
||||
private readonly IBotService service;
|
||||
private readonly IBotProvider provider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActionDialog"/> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core application services.</param>
|
||||
/// <param name="provider">Provides access to core application services.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="service"/> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public ActionDialog(IBotService service) :
|
||||
base(new LuisService(new LuisModelAttribute(service.Configuration.LuisAppId, service.Configuration.LuisApiKey)))
|
||||
public ActionDialog(IBotProvider provider) :
|
||||
base(new LuisService(new LuisModelAttribute(provider.Configuration.LuisAppId, provider.Configuration.LuisApiKey.ToUnsecureString())))
|
||||
{
|
||||
service.AssertNotNull(nameof(service));
|
||||
|
||||
this.service = service;
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -64,7 +64,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Dialogs
|
|||
{
|
||||
message = context.MakeMessage();
|
||||
|
||||
principal = await context.GetCustomerPrincipalAsync(this.service);
|
||||
principal = await context.GetCustomerPrincipalAsync(provider);
|
||||
|
||||
if (principal == null)
|
||||
{
|
||||
|
@ -113,7 +113,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Dialogs
|
|||
{
|
||||
key = result.TopScoringIntent.Intent.ToCamelCase();
|
||||
|
||||
principal = await context.GetCustomerPrincipalAsync(service);
|
||||
principal = await context.GetCustomerPrincipalAsync(provider);
|
||||
|
||||
if (principal == null)
|
||||
{
|
||||
|
@ -124,7 +124,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Dialogs
|
|||
if (principal.AvailableIntents.ContainsKey(key))
|
||||
{
|
||||
await principal.AvailableIntents[key]
|
||||
.ExecuteAsync(context, message, result, service);
|
||||
.ExecuteAsync(context, message, result, provider);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -154,7 +154,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Dialogs
|
|||
if (message.Text.Equals(Resources.Login, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
await context.Forward(
|
||||
new AuthDialog(service, message),
|
||||
new AuthDialog(provider, message),
|
||||
ResumeAfterAuth,
|
||||
message,
|
||||
CancellationToken.None);
|
||||
|
|
|
@ -10,11 +10,12 @@ namespace Microsoft.Store.PartnerCenter.Bot.Dialogs
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Logic;
|
||||
using Extensions;
|
||||
using Microsoft.Bot.Builder.ConnectorEx;
|
||||
using Microsoft.Bot.Builder.Dialogs;
|
||||
using Microsoft.Bot.Connector;
|
||||
using Security;
|
||||
using Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Dialog that handles authentication requests.
|
||||
|
@ -25,7 +26,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Dialogs
|
|||
/// <summary>
|
||||
/// Provides access to core application services.
|
||||
/// </summary>
|
||||
private readonly IBotService service;
|
||||
private readonly IBotProvider provider;
|
||||
|
||||
/// <summary>
|
||||
/// The object that relates to a particular point in the conversation.
|
||||
|
@ -36,19 +37,19 @@ namespace Microsoft.Store.PartnerCenter.Bot.Dialogs
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AuthDialog"/> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core application services.</param>
|
||||
/// <param name="provider">Provides access to core application services.</param>
|
||||
/// <param name="message">Message received by the bot from the end user.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="service"/> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// or
|
||||
/// <paramref name="message"/> is null.
|
||||
/// </exception>
|
||||
public AuthDialog(IBotService service, IMessageActivity message)
|
||||
public AuthDialog(IBotProvider provider, IMessageActivity message)
|
||||
{
|
||||
service.AssertNotNull(nameof(service));
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
message.AssertNotNull(nameof(message));
|
||||
|
||||
this.service = service;
|
||||
this.provider = provider;
|
||||
conversationReference = message.ToConversationReference();
|
||||
}
|
||||
|
||||
|
@ -117,10 +118,11 @@ namespace Microsoft.Store.PartnerCenter.Bot.Dialogs
|
|||
|
||||
state = $"&state={GenerateState(context)}";
|
||||
|
||||
authUrl = await service.TokenManagement.GetAuthorizationRequestUrlAsync(
|
||||
$"{service.Configuration.ActiveDirectoryEndpoint}/{BotConstants.AuthorityEndpoint}",
|
||||
authUrl = await provider.AccessToken.GetAuthorizationRequestUrlAsync(
|
||||
$"{provider.Configuration.ActiveDirectoryEndpoint}/{BotConstants.AuthorityEndpoint}",
|
||||
provider.Configuration.GraphEndpoint,
|
||||
provider.Configuration.ApplicationId,
|
||||
redirectUri,
|
||||
service.Configuration.GraphEndpoint,
|
||||
state);
|
||||
|
||||
message = context.MakeMessage();
|
||||
|
|
|
@ -9,10 +9,11 @@ namespace Microsoft.Store.PartnerCenter.Bot.Dialogs
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Logic;
|
||||
using Extensions;
|
||||
using Microsoft.Bot.Builder.CognitiveServices.QnAMaker;
|
||||
using Microsoft.Bot.Builder.Dialogs;
|
||||
using Microsoft.Bot.Connector;
|
||||
using Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Dialog that processes questions from the authenticated user.
|
||||
|
@ -28,14 +29,14 @@ namespace Microsoft.Store.PartnerCenter.Bot.Dialogs
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QuestionDialog"/> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core services.</param>
|
||||
public QuestionDialog(IBotService service)
|
||||
/// <param name="provider">Provides access to core services.</param>
|
||||
public QuestionDialog(IBotProvider provider)
|
||||
{
|
||||
service.AssertNotNull(nameof(service));
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
|
||||
QnAService = new QnAMakerService(new QnAMakerAttribute(
|
||||
service.Configuration.QnASubscriptionKey,
|
||||
service.Configuration.QnAKnowledgebaseId,
|
||||
provider.Configuration.QnASubscriptionKey.ToUnsecureString(),
|
||||
provider.Configuration.QnAKnowledgebaseId,
|
||||
"default message",
|
||||
0.6));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="BotContextExtensions.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Extensions
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using IdentityModel.Clients.ActiveDirectory;
|
||||
using Microsoft.Bot.Builder.Dialogs;
|
||||
using Logic;
|
||||
using Providers;
|
||||
using Security;
|
||||
|
||||
internal static class BotContextExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the customer principal from the private bot data associated with the user.
|
||||
/// </summary>
|
||||
/// <param name="context">The context for the bot.</param>
|
||||
/// <param name="provider">Provides access to core application services.</param>
|
||||
/// <returns>An instance of <see cref="CustomerPrincipal"/> that represents the authenticated user.</returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="context"/> is null.
|
||||
/// or
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public static async Task<CustomerPrincipal> GetCustomerPrincipalAsync(this IBotContext context, IBotProvider provider)
|
||||
{
|
||||
CustomerPrincipal principal = null;
|
||||
AuthenticationResult authResult;
|
||||
|
||||
context.AssertNotNull(nameof(context));
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
|
||||
try
|
||||
{
|
||||
if (context.PrivateConversationData.TryGetValue(BotConstants.CustomerPrincipalKey, out principal))
|
||||
{
|
||||
if (principal.ExpiresOn < DateTime.UtcNow)
|
||||
{
|
||||
authResult = await provider.AccessToken.AcquireTokenSilentAsync(
|
||||
$"{provider.Configuration.ActiveDirectoryEndpoint}/{principal.CustomerId}",
|
||||
provider.Configuration.GraphEndpoint,
|
||||
provider.Configuration.ApplicationId,
|
||||
new UserIdentifier(principal.ObjectId, UserIdentifierType.UniqueId)).ConfigureAwait(false);
|
||||
|
||||
principal.AccessToken = authResult.AccessToken;
|
||||
principal.ExpiresOn = authResult.ExpiresOn;
|
||||
|
||||
context.StoreCustomerPrincipal(principal);
|
||||
}
|
||||
|
||||
return principal;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
authResult = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores an instance of <see cref="CustomerPrincipal"/> in the private bot data associated with the user.
|
||||
/// </summary>
|
||||
/// <param name="context">The context for the bot.</param>
|
||||
/// <param name="principal">An instance of <see cref="CustomerPrincipal"/> associated with the authenticated user.</param>
|
||||
public static void StoreCustomerPrincipal(this IBotContext context, CustomerPrincipal principal)
|
||||
{
|
||||
context.AssertNotNull(nameof(context));
|
||||
principal.AssertNotNull(nameof(principal));
|
||||
|
||||
context.PrivateConversationData.SetValue(BotConstants.CustomerPrincipalKey, principal);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="CustomerPrincipalExtensions.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Extensions
|
||||
{
|
||||
using System;
|
||||
using Security;
|
||||
|
||||
public static class CustomerPrincipalExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Ensures the given <see cref="CustomerPrincipal"/> has a valid customer context.
|
||||
/// </summary>
|
||||
/// <param name="principalToValidate">An instance of <see cref="CustomerPrincipal"/> to validate.</param>
|
||||
/// <param name="message">The message to report in the exception.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="principalToValidate"/> is null.
|
||||
/// </exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// <paramref name="principalToValidate"/> does not contain a valid customer identifier.
|
||||
/// </exception>
|
||||
public static void AssertValidCustomerContext(this CustomerPrincipal principalToValidate, string message)
|
||||
{
|
||||
principalToValidate.AssertNotNull(nameof(principalToValidate));
|
||||
|
||||
if (string.IsNullOrEmpty(principalToValidate.Operation.CustomerId))
|
||||
{
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="EnumExtensions.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Extensions
|
||||
{
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
|
||||
public static class EnumExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the text from the description attribute.
|
||||
/// </summary>
|
||||
/// <param name="value">The enumeration value associated with the attribute.</param>
|
||||
/// <returns>A <see cref="string"/> containing the text from the description attribute.</returns>
|
||||
public static string GetDescription(this Enum value)
|
||||
{
|
||||
DescriptionAttribute attribute;
|
||||
|
||||
try
|
||||
{
|
||||
attribute = value.GetType()
|
||||
.GetField(value.ToString())
|
||||
.GetCustomAttributes(typeof(DescriptionAttribute), false)
|
||||
.SingleOrDefault() as DescriptionAttribute;
|
||||
|
||||
return attribute == null ? value.ToString() : attribute.Description;
|
||||
}
|
||||
finally
|
||||
{
|
||||
attribute = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="HealthEventExtensions.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Extensions
|
||||
{
|
||||
using Logic.Office;
|
||||
using Microsoft.Bot.Connector;
|
||||
|
||||
public static class HealthEventExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Transforms an instance of <see cref="HealthEvent"/> into an instance of <see cref="Attachment"/>
|
||||
/// </summary>
|
||||
/// <param name="healthEvent">An instance of <see cref="HealthEvent"/> to be transformed.</param>
|
||||
/// <returns>An instance of <see cref="Attachment"/> that represents the health event.</returns>
|
||||
public static Attachment ToAttachment(this HealthEvent healthEvent)
|
||||
{
|
||||
HeroCard card = new HeroCard
|
||||
{
|
||||
Subtitle = healthEvent.Status,
|
||||
Title = healthEvent.WorkloadDisplayName
|
||||
};
|
||||
|
||||
return card.ToAttachment();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="StringExtensions.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Extensions
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
public static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the value to camel case.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to be converted.</param>
|
||||
/// <returns>A string in camel case notation.</returns>
|
||||
public static string ToCamelCase(this string value)
|
||||
{
|
||||
return value.Substring(0, 1).ToLower().Insert(1, value.Substring(1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the string value to an instance of <see cref="SecureString"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The string value to be converted.</param>
|
||||
/// <returns>An instance of <see cref="SecureString"/> that represents the string.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="value"/> is empty or null.
|
||||
/// </exception>
|
||||
public static SecureString ToSecureString(this string value)
|
||||
{
|
||||
SecureString secureValue = new SecureString();
|
||||
|
||||
value.AssertNotEmpty(nameof(value));
|
||||
|
||||
foreach (char c in value)
|
||||
{
|
||||
secureValue.AppendChar(c);
|
||||
}
|
||||
|
||||
secureValue.MakeReadOnly();
|
||||
|
||||
return secureValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an instance of <see cref="SecureString"/> to a <see cref="string"/>.
|
||||
/// </summary>
|
||||
/// <param name="secureString">Secure string to be converted.</param>
|
||||
/// <returns>An instance of <see cref="string"/> that represents the <see cref="SecureString"/> value.</returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="secureString"/> is null.
|
||||
/// </exception>
|
||||
public static string ToUnsecureString(this SecureString secureString)
|
||||
{
|
||||
IntPtr unmanagedString = IntPtr.Zero;
|
||||
|
||||
secureString.AssertNotNull(nameof(secureString));
|
||||
|
||||
try
|
||||
{
|
||||
unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(secureString);
|
||||
return Marshal.PtrToStringUni(unmanagedString);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="ListSubscriptionsIntent.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Extensions
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Web;
|
||||
using Microsoft.Bot.Connector;
|
||||
using PartnerCenter.Models.Subscriptions;
|
||||
|
||||
public static class SubscriptionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Transforms an instance of <see cref="Subscription"/> into an instance of <see cref="Attachment"/>
|
||||
/// </summary>
|
||||
/// <param name="subscription">An instance of <see cref="Subscription"/> to be transformed.</param>
|
||||
/// <returns>An instance <see cref="Attachment"/> that represents the subscription.</returns>
|
||||
public static Attachment ToAttachment(this Subscription subscription)
|
||||
{
|
||||
string imageUrl;
|
||||
string offerName = subscription.OfferName.ToLower();
|
||||
|
||||
if (offerName.Contains("azure") || offerName.Contains("active directory"))
|
||||
{
|
||||
imageUrl = $"{HttpContext.Current.Request.Url.Scheme}://{HttpContext.Current.Request.Url.Host}:{HttpContext.Current.Request.Url.Port}/content/images/azure-logo.png";
|
||||
}
|
||||
else if (offerName.Contains("office") || offerName.Contains("365"))
|
||||
{
|
||||
imageUrl = $"{HttpContext.Current.Request.Url.Scheme}://{HttpContext.Current.Request.Url.Host}:{HttpContext.Current.Request.Url.Port}/content/images/office-logo.png";
|
||||
}
|
||||
else
|
||||
{
|
||||
imageUrl = $"{HttpContext.Current.Request.Url.Scheme}://{HttpContext.Current.Request.Url.Host}:{HttpContext.Current.Request.Url.Port}/content/images/microsoft-logo.png";
|
||||
}
|
||||
|
||||
HeroCard card = new HeroCard
|
||||
{
|
||||
Buttons = new List<CardAction>
|
||||
{
|
||||
new CardAction
|
||||
{
|
||||
Title = Resources.SelectCaptial,
|
||||
Type = ActionTypes.PostBack,
|
||||
Value = $"select subscription {subscription.Id}"
|
||||
}
|
||||
},
|
||||
Images = new List<CardImage>
|
||||
{
|
||||
new CardImage
|
||||
{
|
||||
Url = imageUrl
|
||||
}
|
||||
},
|
||||
Subtitle = subscription.Id,
|
||||
Title = subscription.FriendlyName
|
||||
};
|
||||
|
||||
return card.ToAttachment();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="ValidationExtensions.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Extensions
|
||||
{
|
||||
using System;
|
||||
|
||||
public static class ValidationExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Ensures that a string is not empty.
|
||||
/// </summary>
|
||||
/// <param name="nonEmptyString">The string to validate.</param>
|
||||
/// <param name="caption">The name to report in the exception.</param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="nonEmptyString"/> is empty or null.
|
||||
/// </exception>
|
||||
public static void AssertNotEmpty(this string nonEmptyString, string caption)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(nonEmptyString))
|
||||
{
|
||||
throw new ArgumentException($"{caption ?? "string"} is not set");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that a given object is not null. Throws an exception otherwise.
|
||||
/// </summary>
|
||||
/// <param name="objectToValidate">The object we are validating.</param>
|
||||
/// <param name="caption">The name to report in the exception.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="objectToValidate"/> is null.
|
||||
/// </exception>
|
||||
public static void AssertNotNull(this object objectToValidate, string caption)
|
||||
{
|
||||
if (objectToValidate == null)
|
||||
{
|
||||
throw new ArgumentNullException(caption);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,11 +6,18 @@
|
|||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot
|
||||
{
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Web.Http;
|
||||
using Autofac;
|
||||
using Autofac.Integration.WebApi;
|
||||
using Logic;
|
||||
using Providers;
|
||||
using Microsoft.Bot.Builder.Dialogs;
|
||||
using Microsoft.Bot.Connector;
|
||||
using Microsoft.Bot.Builder.Azure;
|
||||
using Microsoft.Bot.Builder.Dialogs.Internals;
|
||||
using System.Threading.Tasks;
|
||||
using Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the methods and properties that are common to application objects.
|
||||
|
@ -27,23 +34,80 @@ namespace Microsoft.Store.PartnerCenter.Bot
|
|||
/// </summary>
|
||||
protected void Application_Start()
|
||||
{
|
||||
ContainerBuilder builder = new ContainerBuilder();
|
||||
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
|
||||
builder.RegisterType<BotService>().As<IBotService>().AsImplementedInterfaces();
|
||||
|
||||
Container = builder.Build();
|
||||
RegisterContainer();
|
||||
GlobalConfiguration.Configure(WebApiConfig.Register);
|
||||
|
||||
using (ILifetimeScope scope = Container.BeginLifetimeScope())
|
||||
{
|
||||
IBotService service = scope.Resolve<IBotService>();
|
||||
IBotProvider provider = scope.Resolve<IBotProvider>();
|
||||
|
||||
Task.Run(() => provider.InitializeAsync()).Wait();
|
||||
|
||||
ApplicationInsights.Extensibility.TelemetryConfiguration.Active.InstrumentationKey =
|
||||
service.Configuration.InstrumentationKey;
|
||||
provider.Configuration.InstrumentationKey;
|
||||
|
||||
service.InitializeAsync().Wait();
|
||||
Conversation.UpdateContainer(
|
||||
builder =>
|
||||
{
|
||||
builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly()));
|
||||
|
||||
DocumentDbBotDataStore store = new DocumentDbBotDataStore(
|
||||
new Uri(provider.Configuration.CosmosDbEndpoint),
|
||||
provider.Configuration.CosmosDbAccessKey.ToUnsecureString());
|
||||
|
||||
builder.Register(c =>
|
||||
{
|
||||
return new MicrosoftAppCredentials(
|
||||
provider.Configuration.MicrosoftAppId,
|
||||
provider.Configuration.MicrosoftAppPassword.ToUnsecureString());
|
||||
}).SingleInstance();
|
||||
|
||||
builder.Register(c => store)
|
||||
.Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
|
||||
.AsSelf()
|
||||
.SingleInstance();
|
||||
|
||||
builder.Register(c => new CachingBotDataStore(store, CachingBotDataStoreConsistencyPolicy.ETagBasedConsistency))
|
||||
.As<IBotDataStore<BotData>>()
|
||||
.AsSelf()
|
||||
.InstancePerLifetimeScope();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
GlobalConfiguration.Configure(WebApiConfig.Register);
|
||||
protected void Application_Error(object sender, EventArgs e)
|
||||
{
|
||||
Exception exception;
|
||||
IBotProvider provider;
|
||||
|
||||
try
|
||||
{
|
||||
using (ILifetimeScope scope = Container.BeginLifetimeScope())
|
||||
{
|
||||
exception = Server.GetLastError();
|
||||
provider = scope.Resolve<IBotProvider>();
|
||||
|
||||
provider.Telemetry.TrackException(exception);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
exception = null;
|
||||
provider = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the container for the web application.
|
||||
/// </summary>
|
||||
private void RegisterContainer()
|
||||
{
|
||||
ContainerBuilder builder = new ContainerBuilder();
|
||||
|
||||
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
|
||||
builder.RegisterType<BotProvider>().As<IBotProvider>().AsImplementedInterfaces();
|
||||
|
||||
Container = builder.Build();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
using Microsoft.Bot.Builder.Luis.Models;
|
||||
using Microsoft.Bot.Connector;
|
||||
using Security;
|
||||
using Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an intent discovered in the conversation.
|
||||
|
@ -39,17 +40,8 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
/// <param name="context">The context of the conversational process.</param>
|
||||
/// <param name="message">The message from the authenticated user.</param>
|
||||
/// <param name="result">The result from Language Understanding cognitive service.</param>
|
||||
/// <param name="service">Provides access to core services.</param>
|
||||
/// <param name="provider">Provides access to core services.</param>
|
||||
/// <returns>An instance of <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// <paramref name="context"/> is null.
|
||||
/// or
|
||||
/// <paramref name="message"/> is null.
|
||||
/// or
|
||||
/// <paramref name="result"/> is null.
|
||||
/// or
|
||||
/// <paramref name="service"/> is null.
|
||||
/// </exception>
|
||||
Task ExecuteAsync(IDialogContext context, IAwaitable<IMessageActivity> message, LuisResult result, IBotService service);
|
||||
Task ExecuteAsync(IDialogContext context, IAwaitable<IMessageActivity> message, LuisResult result, IBotProvider provider);
|
||||
}
|
||||
}
|
|
@ -10,7 +10,8 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Logic;
|
||||
using Extensions;
|
||||
using Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to the supported intents.
|
||||
|
@ -21,7 +22,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
/// <summary>
|
||||
/// Provides access to core core services.
|
||||
/// </summary>
|
||||
private readonly IBotService service;
|
||||
private readonly IBotProvider provider;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a collection of supported intents.
|
||||
|
@ -31,14 +32,14 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IntentService"/> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core services.</param>
|
||||
/// <param name="provider">Provides access to core services.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="service"/> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public IntentService(IBotService service)
|
||||
public IntentService(IBotProvider provider)
|
||||
{
|
||||
service.AssertNotNull(nameof(service));
|
||||
this.service = service;
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -69,7 +70,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
}
|
||||
|
||||
intents.Add(intent.Name, intent);
|
||||
service.Telemetry.TrackTrace($"Initialized {intent.Name} intent.");
|
||||
provider.Telemetry.TrackTrace($"Initialized {intent.Name} intent.");
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
|
|
@ -11,12 +11,13 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Logic;
|
||||
using Extensions;
|
||||
using Microsoft.Bot.Builder.Dialogs;
|
||||
using Microsoft.Bot.Builder.Luis.Models;
|
||||
using Microsoft.Bot.Connector;
|
||||
using PartnerCenter.Models.Customers;
|
||||
using Security;
|
||||
using Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Processes the request to list customers.
|
||||
|
@ -46,7 +47,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
/// <param name="context">The context of the conversational process.</param>
|
||||
/// <param name="message">The message from the authenticated user.</param>
|
||||
/// <param name="result">The result from Language Understanding cognitive service.</param>
|
||||
/// <param name="service">Provides access to core services.</param>
|
||||
/// <param name="provider">Provides access to core services.</param>
|
||||
/// <returns>An instance of <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="context"/> is null.
|
||||
|
@ -55,9 +56,9 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
/// or
|
||||
/// <paramref name="result"/> is null.
|
||||
/// or
|
||||
/// <paramref name="service"/> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public async Task ExecuteAsync(IDialogContext context, IAwaitable<IMessageActivity> message, LuisResult result, IBotService service)
|
||||
public async Task ExecuteAsync(IDialogContext context, IAwaitable<IMessageActivity> message, LuisResult result, IBotProvider provider)
|
||||
{
|
||||
CustomerPrincipal principal;
|
||||
DateTime startTime;
|
||||
|
@ -69,15 +70,15 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
context.AssertNotNull(nameof(context));
|
||||
message.AssertNotNull(nameof(message));
|
||||
result.AssertNotNull(nameof(result));
|
||||
service.AssertNotNull(nameof(service));
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
|
||||
try
|
||||
{
|
||||
startTime = DateTime.Now;
|
||||
|
||||
principal = await context.GetCustomerPrincipalAsync(service);
|
||||
principal = await context.GetCustomerPrincipalAsync(provider);
|
||||
|
||||
customers = await service.PartnerOperations.GetCustomersAsync(principal);
|
||||
customers = await provider.PartnerOperations.GetCustomersAsync(principal);
|
||||
|
||||
response = context.MakeMessage();
|
||||
response.AttachmentLayout = AttachmentLayoutTypes.Carousel;
|
||||
|
@ -123,7 +124,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
{ "NumberOfCustomers", response.Attachments.Count }
|
||||
};
|
||||
|
||||
service.Telemetry.TrackEvent("ListCustomers/Execute", eventProperties, eventMeasurements);
|
||||
provider.Telemetry.TrackEvent("ListCustomers/Execute", eventProperties, eventMeasurements);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
@ -10,13 +10,14 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Logic;
|
||||
using Extensions;
|
||||
using Microsoft.Bot.Builder.Dialogs;
|
||||
using Microsoft.Bot.Builder.Luis.Models;
|
||||
using Microsoft.Bot.Connector;
|
||||
using PartnerCenter.Models.Customers;
|
||||
using PartnerCenter.Models.Subscriptions;
|
||||
using Security;
|
||||
using Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Processes the request to list subscriptions.
|
||||
|
@ -45,7 +46,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
/// <param name="context">The context of the conversational process.</param>
|
||||
/// <param name="message">The message from the authenticated user.</param>
|
||||
/// <param name="result">The result from Language Understanding cognitive service.</param>
|
||||
/// <param name="service">Provides access to core services;.</param>
|
||||
/// <param name="provider">Provides access to core services;.</param>
|
||||
/// <returns>An instance of <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="context"/> is null.
|
||||
|
@ -54,9 +55,9 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
/// or
|
||||
/// <paramref name="result"/> is null.
|
||||
/// or
|
||||
/// <paramref name="service"/> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public async Task ExecuteAsync(IDialogContext context, IAwaitable<IMessageActivity> message, LuisResult result, IBotService service)
|
||||
public async Task ExecuteAsync(IDialogContext context, IAwaitable<IMessageActivity> message, LuisResult result, IBotProvider provider)
|
||||
{
|
||||
Customer customer = null;
|
||||
CustomerPrincipal principal;
|
||||
|
@ -69,24 +70,24 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
context.AssertNotNull(nameof(context));
|
||||
message.AssertNotNull(nameof(message));
|
||||
result.AssertNotNull(nameof(result));
|
||||
service.AssertNotNull(nameof(principal));
|
||||
provider.AssertNotNull(nameof(principal));
|
||||
|
||||
try
|
||||
{
|
||||
startTime = DateTime.Now;
|
||||
|
||||
principal = await context.GetCustomerPrincipalAsync(service);
|
||||
principal = await context.GetCustomerPrincipalAsync(provider);
|
||||
|
||||
if (principal.CustomerId.Equals(service.Configuration.PartnerCenterApplicationTenantId))
|
||||
if (principal.CustomerId.Equals(provider.Configuration.PartnerCenterAccountId))
|
||||
{
|
||||
customer = await service.PartnerOperations.GetCustomerAsync(principal);
|
||||
customer = await provider.PartnerOperations.GetCustomerAsync(principal);
|
||||
|
||||
response = context.MakeMessage();
|
||||
response.Text = string.Format(Resources.SubscriptionRequestMessage, customer.CompanyProfile.CompanyName);
|
||||
await context.PostAsync(response);
|
||||
}
|
||||
|
||||
subscriptions = await service.PartnerOperations.GetSubscriptionsAsync(principal);
|
||||
subscriptions = await provider.PartnerOperations.GetSubscriptionsAsync(principal);
|
||||
|
||||
response = context.MakeMessage();
|
||||
response.AttachmentLayout = AttachmentLayoutTypes.Carousel;
|
||||
|
@ -110,7 +111,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
{ "UserId", principal.ObjectId }
|
||||
};
|
||||
|
||||
service.Telemetry.TrackEvent("ListCustomers/Execute", eventProperties, eventMetrics);
|
||||
provider.Telemetry.TrackEvent("ListCustomers/Execute", eventProperties, eventMetrics);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
@ -10,11 +10,14 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Logic;
|
||||
using Extensions;
|
||||
using IdentityModel.Clients.ActiveDirectory;
|
||||
using Logic.Office;
|
||||
using Microsoft.Bot.Builder.Dialogs;
|
||||
using Microsoft.Bot.Builder.Luis.Models;
|
||||
using Microsoft.Bot.Connector;
|
||||
using Models;
|
||||
using Providers;
|
||||
using Security;
|
||||
|
||||
/// <summary>
|
||||
|
@ -44,7 +47,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
/// <param name="context">The context of the conversational process.</param>
|
||||
/// <param name="message">The message from the authenticated user.</param>
|
||||
/// <param name="result">The result from Language Understanding cognitive service.</param>
|
||||
/// <param name="service">Provides access to core services;.</param>
|
||||
/// <param name="provider">Provides access to core services;.</param>
|
||||
/// <returns>An instance of <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="context"/> is null.
|
||||
|
@ -53,48 +56,58 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
/// or
|
||||
/// <paramref name="result"/> is null.
|
||||
/// or
|
||||
/// <paramref name="service"/> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public async Task ExecuteAsync(IDialogContext context, IAwaitable<IMessageActivity> message, LuisResult result, IBotService service)
|
||||
public async Task ExecuteAsync(IDialogContext context, IAwaitable<IMessageActivity> message, LuisResult result, IBotProvider provider)
|
||||
{
|
||||
AuthenticationToken token;
|
||||
AuthenticationResult authResult;
|
||||
CustomerPrincipal principal;
|
||||
DateTime startTime;
|
||||
Dictionary<string, double> eventMetrics;
|
||||
Dictionary<string, string> eventProperties;
|
||||
IMessageActivity response;
|
||||
List<HealthEvent> healthEvents;
|
||||
string authority;
|
||||
string customerId;
|
||||
|
||||
context.AssertNotNull(nameof(context));
|
||||
message.AssertNotNull(nameof(message));
|
||||
result.AssertNotNull(nameof(result));
|
||||
service.AssertNotNull(nameof(service));
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
|
||||
try
|
||||
{
|
||||
startTime = DateTime.Now;
|
||||
|
||||
principal = await context.GetCustomerPrincipalAsync(service);
|
||||
principal = await context.GetCustomerPrincipalAsync(provider);
|
||||
|
||||
if (principal.CustomerId.Equals(service.Configuration.PartnerCenterApplicationTenantId, StringComparison.CurrentCultureIgnoreCase))
|
||||
if (principal.CustomerId.Equals(provider.Configuration.PartnerCenterAccountId, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
principal.AssertValidCustomerContext(Resources.SelectCustomerFirst);
|
||||
|
||||
token = await service.TokenManagement.GetAppOnlyTokenAsync(
|
||||
$"{service.Configuration.ActiveDirectoryEndpoint}/{principal.Operation.CustomerId}",
|
||||
service.Configuration.OfficeManagementEndpoint);
|
||||
|
||||
healthEvents = await service.ServiceCommunications.GetCurrentStatusAsync(principal.Operation.CustomerId, token.Token);
|
||||
authority = $"{provider.Configuration.ActiveDirectoryEndpoint}/{principal.Operation.CustomerId}";
|
||||
customerId = principal.Operation.CustomerId;
|
||||
}
|
||||
else
|
||||
{
|
||||
token = await service.TokenManagement.GetAppOnlyTokenAsync(
|
||||
$"{service.Configuration.ActiveDirectoryEndpoint}/{principal.CustomerId}",
|
||||
service.Configuration.OfficeManagementEndpoint);
|
||||
|
||||
healthEvents = await service.ServiceCommunications.GetCurrentStatusAsync(principal.CustomerId, token.Token);
|
||||
authority = $"{provider.Configuration.ActiveDirectoryEndpoint}/{principal.CustomerId}";
|
||||
customerId = principal.CustomerId;
|
||||
}
|
||||
|
||||
authResult = await provider.AccessToken.GetAccessTokenAsync(
|
||||
$"{provider.Configuration.ActiveDirectoryEndpoint}/{principal.Operation.CustomerId}",
|
||||
provider.Configuration.OfficeManagementEndpoint,
|
||||
new ApplicationCredential
|
||||
{
|
||||
ApplicationId = provider.Configuration.ApplicationId,
|
||||
ApplicationSecret = provider.Configuration.ApplicationSecret,
|
||||
UseCache = true
|
||||
});
|
||||
|
||||
healthEvents = await provider.ServiceCommunications.GetCurrentStatusAsync(
|
||||
customerId,
|
||||
authResult.AccessToken);
|
||||
|
||||
response = context.MakeMessage();
|
||||
response.AttachmentLayout = AttachmentLayoutTypes.Carousel;
|
||||
response.Attachments = healthEvents.Select(e => e.ToAttachment()).ToList();
|
||||
|
@ -117,14 +130,14 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
{ "UserId", principal.ObjectId }
|
||||
};
|
||||
|
||||
service.Telemetry.TrackEvent("OfficeIssues/Execute", eventProperties, eventMetrics);
|
||||
provider.Telemetry.TrackEvent("OfficeIssues/Execute", eventProperties, eventMetrics);
|
||||
}
|
||||
catch (CommunicationException ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
response = context.MakeMessage();
|
||||
response.Text = Resources.ErrorMessage;
|
||||
|
||||
service.Telemetry.TrackException(ex);
|
||||
provider.Telemetry.TrackException(ex);
|
||||
|
||||
await context.PostAsync(response);
|
||||
}
|
||||
|
|
|
@ -9,11 +9,12 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dialogs;
|
||||
using Logic;
|
||||
using Extensions;
|
||||
using Microsoft.Bot.Builder.Dialogs;
|
||||
using Microsoft.Bot.Builder.Luis.Models;
|
||||
using Microsoft.Bot.Connector;
|
||||
using Security;
|
||||
using Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Processes the question intent.
|
||||
|
@ -42,7 +43,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
/// <param name="context">The context of the conversational process.</param>
|
||||
/// <param name="message">The message from the authenticated user.</param>
|
||||
/// <param name="result">The result from Language Understanding cognitive service.</param>
|
||||
/// <param name="service">Provides access to core services;.</param>
|
||||
/// <param name="provider">Provides access to core services;.</param>
|
||||
/// <returns>An instance of <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// <paramref name="context"/> is null.
|
||||
|
@ -51,23 +52,23 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
/// or
|
||||
/// <paramref name="result"/> is null.
|
||||
/// or
|
||||
/// <paramref name="service"/> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public async Task ExecuteAsync(IDialogContext context, IAwaitable<IMessageActivity> message, LuisResult result, IBotService service)
|
||||
public async Task ExecuteAsync(IDialogContext context, IAwaitable<IMessageActivity> message, LuisResult result, IBotProvider provider)
|
||||
{
|
||||
IMessageActivity messageActivity;
|
||||
|
||||
context.AssertNotNull(nameof(context));
|
||||
message.AssertNotNull(nameof(message));
|
||||
result.AssertNotNull(nameof(result));
|
||||
service.AssertNotNull(nameof(service));
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
|
||||
try
|
||||
{
|
||||
messageActivity = await message;
|
||||
|
||||
await context.Forward(
|
||||
new QuestionDialog(service),
|
||||
new QuestionDialog(provider),
|
||||
ResumeAfterQnA,
|
||||
messageActivity,
|
||||
CancellationToken.None);
|
||||
|
|
|
@ -9,13 +9,14 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Logic;
|
||||
using Extensions;
|
||||
using Microsoft.Bot.Builder.Dialogs;
|
||||
using Microsoft.Bot.Builder.Luis;
|
||||
using Microsoft.Bot.Builder.Luis.Models;
|
||||
using Microsoft.Bot.Connector;
|
||||
using PartnerCenter.Models.Customers;
|
||||
using Security;
|
||||
using Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Processes the request to select a specific customer.
|
||||
|
@ -44,7 +45,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
/// <param name="context">The context of the conversational process.</param>
|
||||
/// <param name="message">The message from the authenticated user.</param>
|
||||
/// <param name="result">The result from Language Understanding cognitive service.</param>
|
||||
/// <param name="service">Provides access to core services.</param>
|
||||
/// <param name="provider">Provides access to core services.</param>
|
||||
/// <returns>An instance of <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// <paramref name="context"/> is null.
|
||||
|
@ -53,9 +54,9 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
/// or
|
||||
/// <paramref name="result"/> is null.
|
||||
/// or
|
||||
/// <paramref name="service"/> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public async Task ExecuteAsync(IDialogContext context, IAwaitable<IMessageActivity> message, LuisResult result, IBotService service)
|
||||
public async Task ExecuteAsync(IDialogContext context, IAwaitable<IMessageActivity> message, LuisResult result, IBotProvider provider)
|
||||
{
|
||||
CustomerPrincipal principal;
|
||||
Customer customer;
|
||||
|
@ -69,14 +70,14 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
context.AssertNotNull(nameof(context));
|
||||
message.AssertNotNull(nameof(message));
|
||||
result.AssertNotNull(nameof(result));
|
||||
service.AssertNotNull(nameof(service));
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
|
||||
try
|
||||
{
|
||||
startTime = DateTime.Now;
|
||||
response = context.MakeMessage();
|
||||
|
||||
principal = await context.GetCustomerPrincipalAsync(service);
|
||||
principal = await context.GetCustomerPrincipalAsync(provider);
|
||||
|
||||
if (result.TryFindEntity("identifier", out indentifierEntity))
|
||||
{
|
||||
|
@ -92,7 +93,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
}
|
||||
else
|
||||
{
|
||||
customer = await service.PartnerOperations.GetCustomerAsync(principal, customerId);
|
||||
customer = await provider.PartnerOperations.GetCustomerAsync(principal, customerId);
|
||||
response.Text = $"{Resources.CustomerContext} {customer.CompanyProfile.CompanyName}";
|
||||
}
|
||||
|
||||
|
@ -114,7 +115,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
{ "ElapsedMilliseconds", DateTime.Now.Subtract(startTime).TotalMilliseconds }
|
||||
};
|
||||
|
||||
service.Telemetry.TrackEvent("SelectCustomer/ExecuteAsync", eventProperties, eventMeasurements);
|
||||
provider.Telemetry.TrackEvent("SelectCustomer/ExecuteAsync", eventProperties, eventMeasurements);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
@ -9,13 +9,14 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Logic;
|
||||
using Extensions;
|
||||
using Microsoft.Bot.Builder.Dialogs;
|
||||
using Microsoft.Bot.Builder.Luis;
|
||||
using Microsoft.Bot.Builder.Luis.Models;
|
||||
using Microsoft.Bot.Connector;
|
||||
using PartnerCenter.Models.Subscriptions;
|
||||
using Security;
|
||||
using Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Processes the request to select a specific subscription.
|
||||
|
@ -44,7 +45,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
/// <param name="context">The context of the conversational process.</param>
|
||||
/// <param name="message">The message from the authenticated user.</param>
|
||||
/// <param name="result">The result from Language Understanding cognitive service.</param>
|
||||
/// <param name="service">Provides access to core services.</param>
|
||||
/// <param name="provider">Provides access to core services.</param>
|
||||
/// <returns>An instance of <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="context"/> is null.
|
||||
|
@ -53,9 +54,9 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
/// or
|
||||
/// <paramref name="result"/> is null.
|
||||
/// or
|
||||
/// <paramref name="service"/> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public async Task ExecuteAsync(IDialogContext context, IAwaitable<IMessageActivity> message, LuisResult result, IBotService service)
|
||||
public async Task ExecuteAsync(IDialogContext context, IAwaitable<IMessageActivity> message, LuisResult result, IBotProvider provider)
|
||||
{
|
||||
CustomerPrincipal principal;
|
||||
DateTime startTime;
|
||||
|
@ -69,21 +70,21 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
context.AssertNotNull(nameof(context));
|
||||
message.AssertNotNull(nameof(message));
|
||||
result.AssertNotNull(nameof(result));
|
||||
service.AssertNotNull(nameof(service));
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
|
||||
try
|
||||
{
|
||||
startTime = DateTime.Now;
|
||||
response = context.MakeMessage();
|
||||
|
||||
principal = await context.GetCustomerPrincipalAsync(service);
|
||||
principal = await context.GetCustomerPrincipalAsync(provider);
|
||||
|
||||
if (result.TryFindEntity("identifier", out indentifierEntity))
|
||||
{
|
||||
subscriptionId = indentifierEntity.Entity.Replace(" ", string.Empty);
|
||||
principal.Operation.SubscriptionId = subscriptionId;
|
||||
|
||||
subscription = await service.PartnerOperations.GetSubscriptionAsync(principal);
|
||||
subscription = await provider.PartnerOperations.GetSubscriptionAsync(principal);
|
||||
|
||||
if (subscription == null)
|
||||
{
|
||||
|
@ -122,7 +123,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Intents
|
|||
{ "UserId", principal.ObjectId }
|
||||
};
|
||||
|
||||
service.Telemetry.TrackEvent("SelectSubscription/ExecuteAsync", eventProperties, eventMetrics);
|
||||
provider.Telemetry.TrackEvent("SelectSubscription/ExecuteAsync", eventProperties, eventMetrics);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
@ -8,6 +8,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
{
|
||||
using System.Web.Http.ExceptionHandling;
|
||||
using Autofac;
|
||||
using Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an unhandled exception logger.
|
||||
|
@ -23,11 +24,11 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
{
|
||||
using (ILifetimeScope scope = WebApiApplication.Container.BeginLifetimeScope())
|
||||
{
|
||||
IBotService service = scope.Resolve<IBotService>();
|
||||
IBotProvider provider = scope.Resolve<IBotProvider>();
|
||||
|
||||
if (context?.Exception != null)
|
||||
{
|
||||
service.Telemetry.TrackException(context.Exception);
|
||||
provider.Telemetry.TrackException(context.Exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,259 +0,0 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="Extensions.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using IdentityModel.Clients.ActiveDirectory;
|
||||
using Microsoft.Bot.Builder.Dialogs;
|
||||
using Microsoft.Bot.Connector;
|
||||
using Office;
|
||||
using PartnerCenter.Models.Subscriptions;
|
||||
using Security;
|
||||
|
||||
/// <summary>
|
||||
/// Provides useful methods used for retrieving, transforming, validating objects.
|
||||
/// </summary>
|
||||
internal static class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the customer principal from the private bot data associated with the user.
|
||||
/// </summary>
|
||||
/// <param name="context">The context for the bot.</param>
|
||||
/// <param name="service">Provides access to core application services.</param>
|
||||
/// <returns>An instance of <see cref="CustomerPrincipal"/> that represents the authenticated user.</returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="context"/> is null.
|
||||
/// or
|
||||
/// <paramref name="service"/> is null.
|
||||
/// </exception>
|
||||
public static async Task<CustomerPrincipal> GetCustomerPrincipalAsync(this IBotContext context, IBotService service)
|
||||
{
|
||||
CustomerPrincipal principal = null;
|
||||
AuthenticationResult authResult;
|
||||
|
||||
context.AssertNotNull(nameof(context));
|
||||
service.AssertNotNull(nameof(service));
|
||||
|
||||
try
|
||||
{
|
||||
if (context.PrivateConversationData.TryGetValue(BotConstants.CustomerPrincipalKey, out principal))
|
||||
{
|
||||
if (principal.ExpiresOn < DateTime.UtcNow)
|
||||
{
|
||||
authResult = await service.TokenManagement.AcquireTokenSilentAsync(
|
||||
$"{service.Configuration.ActiveDirectoryEndpoint}/{principal.CustomerId}",
|
||||
service.Configuration.GraphEndpoint,
|
||||
new UserIdentifier(principal.ObjectId, UserIdentifierType.UniqueId));
|
||||
|
||||
principal.AccessToken = authResult.AccessToken;
|
||||
principal.ExpiresOn = authResult.ExpiresOn;
|
||||
|
||||
context.StoreCustomerPrincipal(principal);
|
||||
}
|
||||
|
||||
return principal;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
authResult = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text from the description attribute.
|
||||
/// </summary>
|
||||
/// <param name="value">The enumeration value associated with the attribute.</param>
|
||||
/// <returns>A <see cref="string"/> containing the text from the description attribute.</returns>
|
||||
public static string GetDescription(this Enum value)
|
||||
{
|
||||
DescriptionAttribute attribute;
|
||||
|
||||
try
|
||||
{
|
||||
attribute = value.GetType()
|
||||
.GetField(value.ToString())
|
||||
.GetCustomAttributes(typeof(DescriptionAttribute), false)
|
||||
.SingleOrDefault() as DescriptionAttribute;
|
||||
|
||||
return attribute == null ? value.ToString() : attribute.Description;
|
||||
}
|
||||
finally
|
||||
{
|
||||
attribute = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms an instance of <see cref="HealthEvent"/> into an instance of <see cref="Attachment"/>
|
||||
/// </summary>
|
||||
/// <param name="healthEvent">An instance of <see cref="HealthEvent"/> to be transformed.</param>
|
||||
/// <returns>An instance of <see cref="Attachment"/> that represents the health event.</returns>
|
||||
public static Attachment ToAttachment(this HealthEvent healthEvent)
|
||||
{
|
||||
HeroCard card = new HeroCard
|
||||
{
|
||||
Subtitle = healthEvent.Status,
|
||||
Title = healthEvent.WorkloadDisplayName
|
||||
};
|
||||
|
||||
return card.ToAttachment();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms an instance of <see cref="Subscription"/> into an instance of <see cref="Attachment"/>
|
||||
/// </summary>
|
||||
/// <param name="subscription">An instance of <see cref="Subscription"/> to be transformed.</param>
|
||||
/// <returns>An instance <see cref="Attachment"/> that represents the subscription.</returns>
|
||||
public static Attachment ToAttachment(this Subscription subscription)
|
||||
{
|
||||
string imageUrl;
|
||||
string offerName = subscription.OfferName.ToLower();
|
||||
|
||||
if (offerName.Contains("azure") || offerName.Contains("active directory"))
|
||||
{
|
||||
imageUrl = $"{HttpContext.Current.Request.Url.Scheme}://{HttpContext.Current.Request.Url.Host}:{HttpContext.Current.Request.Url.Port}/content/images/azure-logo.png";
|
||||
}
|
||||
else if (offerName.Contains("office") || offerName.Contains("365"))
|
||||
{
|
||||
imageUrl = $"{HttpContext.Current.Request.Url.Scheme}://{HttpContext.Current.Request.Url.Host}:{HttpContext.Current.Request.Url.Port}/content/images/office-logo.png";
|
||||
}
|
||||
else
|
||||
{
|
||||
imageUrl = $"{HttpContext.Current.Request.Url.Scheme}://{HttpContext.Current.Request.Url.Host}:{HttpContext.Current.Request.Url.Port}/content/images/microsoft-logo.png";
|
||||
}
|
||||
|
||||
HeroCard card = new HeroCard
|
||||
{
|
||||
Buttons = new List<CardAction>
|
||||
{
|
||||
new CardAction
|
||||
{
|
||||
Title = Resources.SelectCaptial,
|
||||
Type = ActionTypes.PostBack,
|
||||
Value = $"select subscription {subscription.Id}"
|
||||
}
|
||||
},
|
||||
Images = new List<CardImage>
|
||||
{
|
||||
new CardImage
|
||||
{
|
||||
Url = imageUrl
|
||||
}
|
||||
},
|
||||
Subtitle = subscription.Id,
|
||||
Title = subscription.FriendlyName
|
||||
};
|
||||
|
||||
return card.ToAttachment();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the value to camel case.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to be converted.</param>
|
||||
/// <returns>A string in camel case notation.</returns>
|
||||
public static string ToCamelCase(this string value)
|
||||
{
|
||||
return value.Substring(0, 1).ToLower().Insert(1, value.Substring(1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that a string is not empty.
|
||||
/// </summary>
|
||||
/// <param name="nonEmptyString">The string to validate.</param>
|
||||
/// <param name="caption">The name to report in the exception.</param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="nonEmptyString"/> is empty or null.
|
||||
/// </exception>
|
||||
public static void AssertNotEmpty(this string nonEmptyString, string caption)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(nonEmptyString))
|
||||
{
|
||||
throw new ArgumentException($"{caption ?? "string"} is not set");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that a given object is not null. Throws an exception otherwise.
|
||||
/// </summary>
|
||||
/// <param name="objectToValidate">The object we are validating.</param>
|
||||
/// <param name="caption">The name to report in the exception.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="objectToValidate"/> is null.
|
||||
/// </exception>
|
||||
public static void AssertNotNull(this object objectToValidate, string caption)
|
||||
{
|
||||
if (objectToValidate == null)
|
||||
{
|
||||
throw new ArgumentNullException(caption);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the given <see cref="CustomerPrincipal"/> has a valid customer context.
|
||||
/// </summary>
|
||||
/// <param name="principalToValidate">An instance of <see cref="CustomerPrincipal"/> to validate.</param>
|
||||
/// <param name="message">The message to report in the exception.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="principalToValidate"/> is null.
|
||||
/// </exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// <paramref name="principalToValidate"/> does not contain a valid customer identifier.
|
||||
/// </exception>
|
||||
public static void AssertValidCustomerContext(this CustomerPrincipal principalToValidate, string message)
|
||||
{
|
||||
principalToValidate.AssertNotNull(nameof(principalToValidate));
|
||||
|
||||
if (string.IsNullOrEmpty(principalToValidate.Operation.CustomerId))
|
||||
{
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the given <see cref="CustomerPrincipal"/> has a valid subscription context.
|
||||
/// </summary>
|
||||
/// <param name="principalToValidate">An instance of <see cref="CustomerPrincipal"/> to validate.</param>
|
||||
/// <param name="message">The message to report in the exception.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="principalToValidate"/> is null.
|
||||
/// </exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// <paramref name="principalToValidate"/> does not contain a valid subscription identifier.
|
||||
/// </exception>
|
||||
public static void AssertValidSubscriptionContext(this CustomerPrincipal principalToValidate, string message)
|
||||
{
|
||||
principalToValidate.AssertNotNull(nameof(principalToValidate));
|
||||
|
||||
if (string.IsNullOrEmpty(principalToValidate.Operation.SubscriptionId))
|
||||
{
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores an instance of <see cref="CustomerPrincipal"/> in the private bot data associated with the user.
|
||||
/// </summary>
|
||||
/// <param name="context">The context for the bot.</param>
|
||||
/// <param name="principal">An instance of <see cref="CustomerPrincipal"/> associated with the authenticated user.</param>
|
||||
public static void StoreCustomerPrincipal(this IBotContext context, CustomerPrincipal principal)
|
||||
{
|
||||
context.AssertNotNull(nameof(context));
|
||||
principal.AssertNotNull(nameof(principal));
|
||||
|
||||
context.PrivateConversationData.SetValue(BotConstants.CustomerPrincipalKey, principal);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,8 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
using Graph;
|
||||
using Models;
|
||||
using Security;
|
||||
using Providers;
|
||||
using Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the ability to interact with the Microsoft Graph.
|
||||
|
@ -23,7 +25,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
/// <summary>
|
||||
/// Provides access to core application services.
|
||||
/// </summary>
|
||||
private readonly IBotService service;
|
||||
private readonly IBotProvider provider;
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to the Microsoft Graph API.
|
||||
|
@ -38,40 +40,40 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GraphClient"/> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core application services.</param>
|
||||
/// <param name="provider">Provides access to core application services.</param>
|
||||
/// <param name="customerId">Identifier for customer whose resources are being accessed.</param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="customerId"/> is empty or null.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="service"/> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public GraphClient(IBotService service, string customerId)
|
||||
public GraphClient(IBotProvider provider, string customerId)
|
||||
{
|
||||
service.AssertNotNull(nameof(service));
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
customerId.AssertNotEmpty(nameof(customerId));
|
||||
|
||||
this.customerId = customerId;
|
||||
this.service = service;
|
||||
client = new GraphServiceClient(new AuthenticationProvider(this.service, customerId));
|
||||
this.provider = provider;
|
||||
client = new GraphServiceClient(new AuthenticationProvider(this.provider, customerId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GraphClient"/> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core application services.</param>
|
||||
/// <param name="provider">Provides access to core application services.</param>
|
||||
/// <param name="client">Provides the ability to interact with the Microsoft Graph.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="service"/> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// or
|
||||
/// <paramref name="client"/> is null.
|
||||
/// </exception>
|
||||
public GraphClient(IBotService service, IGraphServiceClient client)
|
||||
public GraphClient(IBotProvider provider, IGraphServiceClient client)
|
||||
{
|
||||
service.AssertNotNull(nameof(service));
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
client.AssertNotNull(nameof(client));
|
||||
|
||||
this.service = service;
|
||||
this.provider = provider;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
|
@ -116,7 +118,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
}));
|
||||
}
|
||||
|
||||
if (customerId.Equals(service.Configuration.PartnerCenterApplicationTenantId))
|
||||
if (customerId.Equals(provider.Configuration.PartnerCenterAccountId))
|
||||
{
|
||||
groups = directoryGroups.CurrentPage.OfType<Group>().Where(
|
||||
g => g.DisplayName.Equals("AdminAgents") || g.DisplayName.Equals("HelpdeskAgents") || g.DisplayName.Equals("SalesAgent")).ToList();
|
||||
|
@ -154,7 +156,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
{ "NumberOfRoles", roles.Count }
|
||||
};
|
||||
|
||||
service.Telemetry.TrackEvent("GetDirectoryRolesAsync", eventProperties, eventMeasurements);
|
||||
provider.Telemetry.TrackEvent("GetDirectoryRolesAsync", eventProperties, eventMeasurements);
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,9 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic.Office
|
|||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Threading.Tasks;
|
||||
using Extensions;
|
||||
using Newtonsoft.Json;
|
||||
using Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the ability to interact with Office 365 Service Communications API.
|
||||
|
@ -21,19 +23,19 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic.Office
|
|||
/// <summary>
|
||||
/// Provides access to core services.
|
||||
/// </summary>
|
||||
private readonly IBotService service;
|
||||
private readonly IBotProvider provider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ServiceCommunications" /> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core services.</param>
|
||||
/// <param name="provider">Provides access to core services.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="service"/> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public ServiceCommunications(IBotService service)
|
||||
public ServiceCommunications(IBotProvider provider)
|
||||
{
|
||||
service.AssertNotNull(nameof(service));
|
||||
this.service = service;
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -60,7 +62,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic.Office
|
|||
{
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||
|
||||
requestUri = $"{service.Configuration.OfficeManagementEndpoint}/api/v1.0/{customerId}/ServiceComms/CurrentStatus";
|
||||
requestUri = $"{provider.Configuration.OfficeManagementEndpoint}/api/v1.0/{customerId}/ServiceComms/CurrentStatus";
|
||||
|
||||
response = await client.GetAsync(requestUri);
|
||||
content = await response.Content.ReadAsStringAsync();
|
||||
|
@ -86,7 +88,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic.Office
|
|||
{ "RequestUri", requestUri }
|
||||
};
|
||||
|
||||
service.Telemetry.TrackEvent("GetCurrentStatusAsync", eventProperties, eventMetrics);
|
||||
provider.Telemetry.TrackEvent("GetCurrentStatusAsync", eventProperties, eventMetrics);
|
||||
|
||||
return odataResponse.Value;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Cache;
|
||||
using Microsoft.Store.PartnerCenter.Extensions;
|
||||
using PartnerCenter.Enumerators;
|
||||
using PartnerCenter.Exceptions;
|
||||
using PartnerCenter.Models;
|
||||
|
@ -16,7 +18,9 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
using PartnerCenter.Models.Customers;
|
||||
using PartnerCenter.Models.Partners;
|
||||
using PartnerCenter.Models.Subscriptions;
|
||||
using Providers;
|
||||
using RequestContext;
|
||||
using Bot.Extensions;
|
||||
using Security;
|
||||
|
||||
/// <summary>
|
||||
|
@ -24,6 +28,16 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
/// </summary>
|
||||
public class PartnerOperations : IPartnerOperations
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the application calling the Partner Center Managed API.
|
||||
/// </summary>
|
||||
private const string ApplicationName = "Partner Center Bot v0.2";
|
||||
|
||||
/// <summary>
|
||||
/// Key utilized to retrieve and store Partner Center access tokens.
|
||||
/// </summary>
|
||||
private const string PartnerCenterCacheKey = "Resource::PartnerCenter::AppOnly";
|
||||
|
||||
/// <summary>
|
||||
/// Provides the ability to perform partner operation using app only authentication.
|
||||
/// </summary>
|
||||
|
@ -32,7 +46,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
/// <summary>
|
||||
/// Provides access to core services.
|
||||
/// </summary>
|
||||
private IBotService service;
|
||||
private IBotProvider provider;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a way to ensure that <see cref="appOperations"/> is only being modified
|
||||
|
@ -43,14 +57,14 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PartnerOperations"/> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core services.</param>
|
||||
/// <param name="provider">Provides access to core services.</param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="service"/> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public PartnerOperations(IBotService service)
|
||||
public PartnerOperations(IBotProvider provider)
|
||||
{
|
||||
service.AssertNotNull(nameof(service));
|
||||
this.service = service;
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -80,7 +94,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
correlationId = Guid.NewGuid();
|
||||
operations = await GetAppOperationsAsync(correlationId);
|
||||
|
||||
rules = await operations.CountryValidationRules.ByCountry(countryCode).GetAsync();
|
||||
rules = await operations.CountryValidationRules.ByCountry(countryCode).GetAsync().ConfigureAwait(false);
|
||||
|
||||
// Track the event measurements for analysis.
|
||||
eventMetrics = new Dictionary<string, double>
|
||||
|
@ -95,7 +109,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
{ "ParternCenterCorrelationId", correlationId.ToString() }
|
||||
};
|
||||
|
||||
service.Telemetry.TrackEvent("GetCountryValidationRulesAsync", eventProperties, eventMetrics);
|
||||
provider.Telemetry.TrackEvent(nameof(GetCountryValidationRulesAsync), eventProperties, eventMetrics);
|
||||
|
||||
return rules;
|
||||
}
|
||||
|
@ -114,7 +128,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
/// <returns>An instance of <see cref="Customer"/> that represents the specified customer.</returns>
|
||||
public async Task<Customer> GetCustomerAsync(CustomerPrincipal principal)
|
||||
{
|
||||
return await GetCustomerAsync(principal, principal.Operation.CustomerId);
|
||||
return await GetCustomerAsync(principal, principal.Operation.CustomerId).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -147,13 +161,13 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
correlationId = Guid.NewGuid();
|
||||
operations = await GetAppOperationsAsync(correlationId);
|
||||
|
||||
if (principal.CustomerId.Equals(service.Configuration.PartnerCenterApplicationTenantId))
|
||||
if (principal.CustomerId.Equals(provider.Configuration.PartnerCenterAccountId))
|
||||
{
|
||||
customer = await operations.Customers.ById(customerId).GetAsync();
|
||||
customer = await operations.Customers.ById(customerId).GetAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
customer = await operations.Customers.ById(principal.CustomerId).GetAsync();
|
||||
customer = await operations.Customers.ById(principal.CustomerId).GetAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// Track the event measurements for analysis.
|
||||
|
@ -170,7 +184,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
{ "ParternCenterCorrelationId", correlationId.ToString() }
|
||||
};
|
||||
|
||||
service.Telemetry.TrackEvent("GetCustomerAsync", eventProperties, eventMetrics);
|
||||
provider.Telemetry.TrackEvent(nameof(GetCustomerAsync), eventProperties, eventMetrics);
|
||||
|
||||
return customer;
|
||||
}
|
||||
|
@ -213,20 +227,20 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
|
||||
customers = new List<Customer>();
|
||||
|
||||
if (principal.CustomerId.Equals(service.Configuration.PartnerCenterApplicationTenantId))
|
||||
if (principal.CustomerId.Equals(provider.Configuration.PartnerCenterAccountId))
|
||||
{
|
||||
seekCustomers = await operations.Customers.GetAsync();
|
||||
seekCustomers = await operations.Customers.GetAsync().ConfigureAwait(false);
|
||||
customersEnumerator = operations.Enumerators.Customers.Create(seekCustomers);
|
||||
|
||||
while (customersEnumerator.HasValue)
|
||||
{
|
||||
customers.AddRange(customersEnumerator.Current.Items);
|
||||
await customersEnumerator.NextAsync();
|
||||
await customersEnumerator.NextAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
customer = await operations.Customers.ById(principal.CustomerId).GetAsync();
|
||||
customer = await operations.Customers.ById(principal.CustomerId).GetAsync().ConfigureAwait(false);
|
||||
customers.Add(customer);
|
||||
}
|
||||
|
||||
|
@ -245,7 +259,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
{ "ParternCenterCorrelationId", correlationId.ToString() }
|
||||
};
|
||||
|
||||
service.Telemetry.TrackEvent("GetCustomersAsync", eventProperties, eventMetrics);
|
||||
provider.Telemetry.TrackEvent(nameof(GetCustomersAsync), eventProperties, eventMetrics);
|
||||
|
||||
return customers;
|
||||
}
|
||||
|
@ -278,7 +292,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
|
||||
try
|
||||
{
|
||||
if (service.Initialized)
|
||||
if (provider.Initialized)
|
||||
{
|
||||
throw new InvalidOperationException(Resources.ServiceInitializedException);
|
||||
}
|
||||
|
@ -287,7 +301,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
correlationId = Guid.NewGuid();
|
||||
operations = await GetAppOperationsAsync(correlationId);
|
||||
|
||||
profile = await operations.Profiles.LegalBusinessProfile.GetAsync();
|
||||
profile = await operations.Profiles.LegalBusinessProfile.GetAsync().ConfigureAwait(false);
|
||||
|
||||
// Track the event measurements for analysis.
|
||||
eventMetrics = new Dictionary<string, double>
|
||||
|
@ -301,7 +315,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
{ "ParternCenterCorrelationId", correlationId.ToString() }
|
||||
};
|
||||
|
||||
service.Telemetry.TrackEvent("GetLegalBusinessProfileAsync", eventProperties, eventMetrics);
|
||||
provider.Telemetry.TrackEvent(nameof(GetLegalBusinessProfileAsync), eventProperties, eventMetrics);
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
@ -348,7 +362,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
try
|
||||
{
|
||||
subscription = await operations.Customers.ById(principal.Operation.CustomerId)
|
||||
.Subscriptions.ById(principal.Operation.SubscriptionId).GetAsync();
|
||||
.Subscriptions.ById(principal.Operation.SubscriptionId).GetAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (PartnerException ex)
|
||||
{
|
||||
|
@ -372,7 +386,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
{ "ParternCenterCorrelationId", correlationId.ToString() }
|
||||
};
|
||||
|
||||
service.Telemetry.TrackEvent("GetSubscriptionAsync", eventProperties, eventMetrics);
|
||||
provider.Telemetry.TrackEvent("GetSubscriptionAsync", eventProperties, eventMetrics);
|
||||
|
||||
return subscription;
|
||||
}
|
||||
|
@ -409,7 +423,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
correlationId = Guid.NewGuid();
|
||||
operations = await GetAppOperationsAsync(correlationId);
|
||||
|
||||
if (principal.CustomerId.Equals(service.Configuration.PartnerCenterApplicationTenantId))
|
||||
if (principal.CustomerId.Equals(provider.Configuration.PartnerCenterAccountId))
|
||||
{
|
||||
principal.AssertValidCustomerContext(Resources.InvalidCustomerContextException);
|
||||
subscriptions = await operations.Customers.ById(principal.Operation.CustomerId).Subscriptions.GetAsync();
|
||||
|
@ -434,7 +448,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
{ "ParternCenterCorrelationId", correlationId.ToString() }
|
||||
};
|
||||
|
||||
service.Telemetry.TrackEvent("GetCustomersAsync", eventProperties, eventMetrics);
|
||||
provider.Telemetry.TrackEvent("GetCustomersAsync", eventProperties, eventMetrics);
|
||||
|
||||
return new List<Subscription>(subscriptions.Items);
|
||||
}
|
||||
|
@ -457,19 +471,52 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
{
|
||||
if (appOperations == null || appOperations.Credentials.ExpiresAt > DateTime.UtcNow)
|
||||
{
|
||||
IPartnerCredentials credentials = await service.TokenManagement
|
||||
.GetPartnerCenterAppOnlyCredentialsAsync(
|
||||
$"{service.Configuration.ActiveDirectoryEndpoint}/{service.Configuration.PartnerCenterApplicationTenantId}");
|
||||
|
||||
PartnerService.Instance.ApplicationName = BotConstants.ApplicationName;
|
||||
IPartnerCredentials credentials = await GetPartnerCenterCredentialsAsync().ConfigureAwait(false);
|
||||
|
||||
lock (appLock)
|
||||
{
|
||||
appOperations = PartnerService.Instance.CreatePartnerOperations(credentials);
|
||||
}
|
||||
|
||||
PartnerService.Instance.ApplicationName = ApplicationName;
|
||||
}
|
||||
|
||||
// TODO -- Add localization
|
||||
// return appOperations.With(RequestContextFactory.Instance.Create(correlationId, service.Localization.Locale));
|
||||
return appOperations.With(RequestContextFactory.Instance.Create(correlationId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance of <see cref="IPartnerCredentials"/> used to access the Partner Center Managed API.
|
||||
/// </summary>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <returns>
|
||||
/// An instance of <see cref="IPartnerCredentials" /> that represents the access token.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="authority"/> is empty or null.
|
||||
/// </exception>
|
||||
private async Task<IPartnerCredentials> GetPartnerCenterCredentialsAsync()
|
||||
{
|
||||
// Attempt to obtain the Partner Center token from the cache.
|
||||
IPartnerCredentials credentials =
|
||||
await provider.Cache.FetchAsync<Models.PartnerCenterToken>(
|
||||
CacheDatabaseType.Authentication, PartnerCenterCacheKey).ConfigureAwait(false);
|
||||
|
||||
if (credentials != null && !credentials.IsExpired())
|
||||
{
|
||||
return credentials;
|
||||
}
|
||||
|
||||
// The access token has expired, so a new one must be requested.
|
||||
credentials = await PartnerCredentials.Instance.GenerateByApplicationCredentialsAsync(
|
||||
provider.Configuration.PartnerCenterApplicationId,
|
||||
provider.Configuration.PartnerCenterApplicationSecret.ToUnsecureString(),
|
||||
provider.Configuration.PartnerCenterAccountId).ConfigureAwait(false);
|
||||
|
||||
await provider.Cache.StoreAsync(CacheDatabaseType.Authentication, PartnerCenterCacheKey, credentials).ConfigureAwait(false);
|
||||
|
||||
return credentials;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="ApplicationCredential.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Models
|
||||
{
|
||||
using System.Security;
|
||||
|
||||
/// <summary>
|
||||
/// Represents credentials to be used to acquire an access token.
|
||||
/// </summary>
|
||||
public sealed class ApplicationCredential
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifier of the client requesting the token.
|
||||
/// </summary>
|
||||
public string ApplicationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Secret of the client requesting the token.
|
||||
/// </summary>
|
||||
public SecureString ApplicationSecret { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a flag indicating whether or not distributed token cache
|
||||
/// should be utilized for token acquistion.
|
||||
/// </summary>
|
||||
public bool UseCache { get; set; }
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Models
|
|||
/// Represents an access token used to access the Partner Center API.
|
||||
/// </summary>
|
||||
/// <seealso cref="Microsoft.Store.PartnerCenter.IPartnerCredentials" />
|
||||
public class PartnerCenterTokenModel : IPartnerCredentials
|
||||
public class PartnerCenterToken : IPartnerCredentials
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the expiry time in UTC for the token.
|
|
@ -0,0 +1,248 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="AccessTokenProvider.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Providers
|
||||
{
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Cache;
|
||||
using IdentityModel.Clients.ActiveDirectory;
|
||||
using Extensions;
|
||||
using Models;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the ability to manage access tokens.
|
||||
/// </summary>
|
||||
internal sealed class AccessTokenProvider : IAccessTokenProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to the core bot providers.
|
||||
/// </summary>
|
||||
private readonly IBotProvider provider;
|
||||
|
||||
/// <summary>
|
||||
/// Type of the assertion representing the user when performing app + user authentication.
|
||||
/// </summary>
|
||||
private const string AssertionType = "urn:ietf:params:oauth:grant-type:jwt-bearer";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="AccessTokenProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="provider">Provides access to core explorer providers.</param>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public AccessTokenProvider(IBotProvider provider)
|
||||
{
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
public async Task<AuthenticationResult> AcquireTokenByAuthorizationCodeAsync(string authority, string code, ApplicationCredential credential, Uri redirectUri, string resource)
|
||||
{
|
||||
AuthenticationContext authContext;
|
||||
ISecureClientSecret secret;
|
||||
|
||||
authority.AssertNotEmpty(nameof(authority));
|
||||
code.AssertNotEmpty(nameof(code));
|
||||
redirectUri.AssertNotNull(nameof(redirectUri));
|
||||
resource.AssertNotEmpty(nameof(resource));
|
||||
|
||||
try
|
||||
{
|
||||
authContext = new AuthenticationContext(authority);
|
||||
secret = new SecureClientSecret(credential.ApplicationSecret);
|
||||
|
||||
return await authContext.AcquireTokenByAuthorizationCodeAsync(
|
||||
code,
|
||||
redirectUri,
|
||||
new ClientCredential(
|
||||
credential.ApplicationId,
|
||||
secret),
|
||||
resource).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
authContext = null;
|
||||
secret = null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<AuthenticationResult> AcquireTokenSilentAsync(string authority, string resource, string clientId, UserIdentifier objectUserId)
|
||||
{
|
||||
AuthenticationContext authContext;
|
||||
AuthenticationResult authResult;
|
||||
|
||||
authority.AssertNotEmpty(nameof(authority));
|
||||
resource.AssertNotEmpty(nameof(resource));
|
||||
objectUserId.AssertNotNull(nameof(objectUserId));
|
||||
|
||||
try
|
||||
{
|
||||
authContext = new AuthenticationContext(authority);
|
||||
|
||||
authResult = await authContext.AcquireTokenSilentAsync(
|
||||
resource,
|
||||
clientId,
|
||||
objectUserId).ConfigureAwait(false);
|
||||
|
||||
return authResult;
|
||||
}
|
||||
finally
|
||||
{
|
||||
authContext = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an access token from the authority.
|
||||
/// </summary>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <param name="resource">Identifier of the target resource that is the recipient of the requested token.</param>
|
||||
/// <param name="credential">The application credential to use for token acquisition.</param>
|
||||
/// <returns>An instance of <see cref="AuthenticationResult"/> that represented the access token.</returns>
|
||||
/// <exception cref="System.ArgumentException">
|
||||
/// <paramref name="authority"/> is empty or null.
|
||||
/// or
|
||||
/// <paramref name="resource"/> is empty or null.
|
||||
/// </exception>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// <paramref name="credential"/> is null.
|
||||
/// </exception>
|
||||
public async Task<AuthenticationResult> GetAccessTokenAsync(string authority, string resource, ApplicationCredential credential)
|
||||
{
|
||||
AuthenticationContext authContext;
|
||||
AuthenticationResult authResult;
|
||||
DistributedTokenCache tokenCache;
|
||||
ISecureClientSecret secret;
|
||||
|
||||
authority.AssertNotEmpty(nameof(authority));
|
||||
resource.AssertNotEmpty(nameof(resource));
|
||||
credential.AssertNotNull(nameof(credential));
|
||||
|
||||
try
|
||||
{
|
||||
if (credential.UseCache)
|
||||
{
|
||||
tokenCache = new DistributedTokenCache(provider, resource, $"AppOnly::{authority}::{resource}");
|
||||
authContext = new AuthenticationContext(authority, tokenCache);
|
||||
}
|
||||
else
|
||||
{
|
||||
authContext = new AuthenticationContext(authority);
|
||||
}
|
||||
|
||||
secret = new SecureClientSecret(credential.ApplicationSecret);
|
||||
|
||||
authResult = await authContext.AcquireTokenAsync(
|
||||
resource,
|
||||
new ClientCredential(
|
||||
credential.ApplicationId,
|
||||
secret)).ConfigureAwait(false);
|
||||
|
||||
return authResult;
|
||||
}
|
||||
finally
|
||||
{
|
||||
authContext = null;
|
||||
authResult = null;
|
||||
secret = null;
|
||||
tokenCache = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an access token from the authority.
|
||||
/// </summary>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <param name="resource">Identifier of the target resource that is the recipient of the requested token.</param>
|
||||
/// <param name="credential">The application credential to use for token acquisition.</param>
|
||||
/// <param name="token">Assertion token representing the user.</param>
|
||||
/// <returns>An instance of <see cref="AuthenticationResult"/> that represented the access token.</returns>
|
||||
/// <exception cref="System.ArgumentException">
|
||||
/// <paramref name="authority"/> is empty or null.
|
||||
/// or
|
||||
/// <paramref name="resource"/> is empty or null.
|
||||
/// or
|
||||
/// <paramref name="token"/> is empty or null.
|
||||
/// </exception>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// <paramref name="credential"/> is null.
|
||||
/// </exception>
|
||||
public async Task<AuthenticationResult> GetAccessTokenAsync(string authority, string resource, ApplicationCredential credential, string token)
|
||||
{
|
||||
AuthenticationContext authContext;
|
||||
AuthenticationResult authResult;
|
||||
DistributedTokenCache tokenCache;
|
||||
ISecureClientSecret secret;
|
||||
|
||||
authority.AssertNotEmpty(nameof(authority));
|
||||
resource.AssertNotEmpty(nameof(authority));
|
||||
credential.AssertNotNull(nameof(credential));
|
||||
token.AssertNotEmpty(nameof(token));
|
||||
|
||||
try
|
||||
{
|
||||
if (credential.UseCache)
|
||||
{
|
||||
tokenCache = new DistributedTokenCache(provider, resource);
|
||||
authContext = new AuthenticationContext(authority, tokenCache);
|
||||
}
|
||||
else
|
||||
{
|
||||
authContext = new AuthenticationContext(authority);
|
||||
}
|
||||
|
||||
secret = new SecureClientSecret(credential.ApplicationSecret);
|
||||
|
||||
authResult = await authContext.AcquireTokenAsync(
|
||||
resource,
|
||||
new ClientCredential(
|
||||
credential.ApplicationId,
|
||||
secret),
|
||||
new UserAssertion(token, AssertionType)).ConfigureAwait(false);
|
||||
|
||||
return authResult;
|
||||
}
|
||||
finally
|
||||
{
|
||||
authContext = null;
|
||||
tokenCache = null;
|
||||
secret = null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> GetAuthorizationRequestUrlAsync(string authority, string resource, string clientId, Uri redirectUri, string extraQueryParameters)
|
||||
{
|
||||
AuthenticationContext authContext;
|
||||
Uri authUri;
|
||||
|
||||
authority.AssertNotEmpty(nameof(authority));
|
||||
resource.AssertNotEmpty(nameof(resource));
|
||||
redirectUri.AssertNotNull(nameof(redirectUri));
|
||||
|
||||
try
|
||||
{
|
||||
authContext = new AuthenticationContext(authority);
|
||||
|
||||
authUri = await authContext.GetAuthorizationRequestUrlAsync(
|
||||
resource,
|
||||
clientId,
|
||||
redirectUri,
|
||||
UserIdentifier.AnyUser,
|
||||
extraQueryParameters);
|
||||
|
||||
return authUri.ToString();
|
||||
}
|
||||
finally
|
||||
{
|
||||
authContext = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,39 +1,39 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="BotService.cs" company="Microsoft">
|
||||
// <copyright file="BotProvider.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Providers
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Autofac;
|
||||
using Cache;
|
||||
using Configuration;
|
||||
using Intents;
|
||||
using Microsoft.Bot.Builder.Dialogs;
|
||||
using Microsoft.Bot.Connector;
|
||||
using Office;
|
||||
using Security;
|
||||
using Logic;
|
||||
using Logic.Office;
|
||||
using Telemetry;
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to core services.
|
||||
/// </summary>
|
||||
/// <seealso cref="IBotService" />
|
||||
/// <seealso cref="IBotProvider" />
|
||||
[Serializable]
|
||||
public class BotService : IBotService
|
||||
public class BotProvider : IBotProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the ability to manage access tokens.
|
||||
/// </summary>
|
||||
private static IAccessTokenProvider accessToken;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the ability to cache often used objects.
|
||||
/// </summary>
|
||||
private static ICacheService cache;
|
||||
private static ICacheProvider cache;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the ability to access various configurations.
|
||||
/// </summary>
|
||||
private static IConfiguration configuration;
|
||||
private static IConfigurationProvider configuration;
|
||||
|
||||
/// <summary>
|
||||
/// A flag indicating whether or the service has been initialized.
|
||||
|
@ -48,7 +48,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
/// <summary>
|
||||
/// Provides localization functionality for the bot.
|
||||
/// </summary>
|
||||
private static ILocalizationService localization;
|
||||
private static ILocalizationProvider localization;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the ability to perform various partner operations.
|
||||
|
@ -65,25 +65,25 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
/// </summary>
|
||||
private static ITelemetryProvider telemetry;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the ability to manage access tokens.
|
||||
/// </summary>
|
||||
private static ITokenManagement tokenManagement;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the ability to retrieve and store data in a secure vault.
|
||||
/// </summary>
|
||||
private static IVaultService vault;
|
||||
private static IVaultProvider vault;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the a reference to the token management service.
|
||||
/// </summary>
|
||||
public IAccessTokenProvider AccessToken => accessToken ?? (accessToken = new AccessTokenProvider(this));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the service that provides caching functionality.
|
||||
/// </summary>
|
||||
public ICacheService Cache => cache ?? (cache = new CacheService(this));
|
||||
public ICacheProvider Cache => cache ?? (cache = new RedisCacheProvider(this));
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the available configurations.
|
||||
/// </summary>
|
||||
public IConfiguration Configuration => configuration ?? (configuration = new Configuration(this));
|
||||
public IConfigurationProvider Configuration => configuration ?? (configuration = new ConfigurationProvider(this));
|
||||
|
||||
/// <summary>
|
||||
/// Gets a flag indicating whether or the service has been initialized.
|
||||
|
@ -98,7 +98,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
/// <summary>
|
||||
/// Gets the service that provide localization functionality.
|
||||
/// </summary>
|
||||
public ILocalizationService Localization => localization;
|
||||
public ILocalizationProvider Localization => localization;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the partner operations.
|
||||
|
@ -135,15 +135,10 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the a reference to the token management service.
|
||||
/// </summary>
|
||||
public ITokenManagement TokenManagement => tokenManagement ?? (tokenManagement = new TokenManagement(this));
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the vault service.
|
||||
/// </summary>
|
||||
public IVaultService Vault => vault ?? (vault = new VaultService(this));
|
||||
public IVaultProvider Vault => vault ?? (vault = new KeyVaultProvider(this));
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the application core services.
|
||||
|
@ -151,35 +146,13 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
/// <returns>A <see cref="Task"/> that represents the asynchronous operations.</returns>
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
ContainerBuilder builder;
|
||||
|
||||
try
|
||||
{
|
||||
builder = new ContainerBuilder();
|
||||
|
||||
intent = new IntentService(this);
|
||||
localization = new LocalizationService(this);
|
||||
localization = new LocalizationProvider(this);
|
||||
|
||||
intent.Initialize();
|
||||
await localization.InitializeAsync();
|
||||
|
||||
initialized = true;
|
||||
|
||||
builder.Register(c =>
|
||||
{
|
||||
return new MicrosoftAppCredentials(
|
||||
Configuration.MicrosoftAppId,
|
||||
Configuration.MicrosoftAppPassword);
|
||||
}).SingleInstance();
|
||||
|
||||
#pragma warning disable 0618
|
||||
builder.Update(Conversation.Container);
|
||||
#pragma warning restore 0618
|
||||
}
|
||||
finally
|
||||
{
|
||||
builder = null;
|
||||
}
|
||||
await Configuration.InitializeAsync().ConfigureAwait(false);
|
||||
await localization.InitializeAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="ConfigurationProvider.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Providers
|
||||
{
|
||||
using System.Configuration;
|
||||
using System.Security;
|
||||
using System.Threading.Tasks;
|
||||
using Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulates configurations utilized by the bot.
|
||||
/// </summary>
|
||||
public class ConfigurationProvider : IConfigurationProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to core services.
|
||||
/// </summary>
|
||||
private readonly IBotProvider provider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConfigurationProvider" /> class.
|
||||
/// </summary>
|
||||
/// <param name="provider">Provides access to core services.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public ConfigurationProvider(IBotProvider provider)
|
||||
{
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Azure Active Directory endpoint.
|
||||
/// </summary>
|
||||
public string ActiveDirectoryEndpoint { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Azure AD application identifier.
|
||||
/// </summary>
|
||||
public string ApplicationId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Azure AD application secret.
|
||||
/// </summary>
|
||||
public SecureString ApplicationSecret { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Azure AD application tenant identifier.
|
||||
/// </summary>
|
||||
public string ApplicationTenantId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the access key for the instance of Cosmos DB.
|
||||
/// </summary>
|
||||
public SecureString CosmosDbAccessKey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Azure Cosmos DB endpoint address.
|
||||
/// </summary>
|
||||
public string CosmosDbEndpoint { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Microsoft Graph endpoint.
|
||||
/// </summary>
|
||||
public string GraphEndpoint { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Application Insights instrumentation key.
|
||||
/// </summary>
|
||||
public string InstrumentationKey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the endpoint address for the instance of Azure Key Vault.
|
||||
/// </summary>
|
||||
public string KeyVaultEndpoint { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the LUIS API key.
|
||||
/// </summary>
|
||||
public SecureString LuisApiKey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the LUIS application identifier.
|
||||
/// </summary>
|
||||
public string LuisAppId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Microsoft application identifier.
|
||||
/// </summary>
|
||||
public string MicrosoftAppId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Microsoft application password.
|
||||
/// </summary>
|
||||
public SecureString MicrosoftAppPassword { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Office 365 management endpoint address.
|
||||
/// </summary>
|
||||
public string OfficeManagementEndpoint { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Partner Center account identifier.
|
||||
/// </summary>
|
||||
public string PartnerCenterAccountId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Partner Center API endpoint.
|
||||
/// </summary>
|
||||
public string PartnerCenterEndpoint { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Partner Center application identifier value.
|
||||
/// </summary>
|
||||
public string PartnerCenterApplicationId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Partner Center application secret value.
|
||||
/// </summary>
|
||||
public SecureString PartnerCenterApplicationSecret { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the question and answer knowledgebase identifier.
|
||||
/// </summary>
|
||||
public string QnAKnowledgebaseId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets question and answer subscription subscription key.
|
||||
/// </summary>
|
||||
public SecureString QnASubscriptionKey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Redis Cache connection string.
|
||||
/// </summary>
|
||||
public SecureString RedisCacheConnectionString { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Performs the necessary initialization operations.
|
||||
/// </summary>
|
||||
/// <returns>An instance of the <see cref="Task"/> class that represents the asynchronous operation.</returns>
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
ActiveDirectoryEndpoint = ConfigurationManager.AppSettings["ActiveDirectoryEndpoint"];
|
||||
ApplicationId = ConfigurationManager.AppSettings["ApplicationId"];
|
||||
ApplicationTenantId = ConfigurationManager.AppSettings["ApplicationTenantId"];
|
||||
CosmosDbEndpoint = ConfigurationManager.AppSettings["CosmosDbEndpoint"];
|
||||
GraphEndpoint = ConfigurationManager.AppSettings["MicrosoftGraphEndpoint"];
|
||||
InstrumentationKey = ConfigurationManager.AppSettings["InstrumentationKey"];
|
||||
KeyVaultEndpoint = ConfigurationManager.AppSettings["KeyVaultEndpoint"];
|
||||
LuisAppId = ConfigurationManager.AppSettings["LuisAppId"];
|
||||
MicrosoftAppId = ConfigurationManager.AppSettings["MicrosoftAppId"];
|
||||
OfficeManagementEndpoint = ConfigurationManager.AppSettings["OfficeManagementEndpoint"];
|
||||
PartnerCenterAccountId = ConfigurationManager.AppSettings["PartnerCenter.AccountId"];
|
||||
PartnerCenterEndpoint = ConfigurationManager.AppSettings["PartnerCenterEndpoint"];
|
||||
PartnerCenterApplicationId = ConfigurationManager.AppSettings["PartnerCenter.ApplicationId"];
|
||||
QnAKnowledgebaseId = ConfigurationManager.AppSettings["QnAKnowledgebaseId"];
|
||||
|
||||
ApplicationSecret = await provider.Vault.GetAsync("ApplicationSecret").ConfigureAwait(false);
|
||||
CosmosDbAccessKey = await provider.Vault.GetAsync("CosmosDbAccessKey").ConfigureAwait(false);
|
||||
LuisApiKey = await provider.Vault.GetAsync("LuisApiKey").ConfigureAwait(false);
|
||||
MicrosoftAppPassword = await provider.Vault.GetAsync("MicrosoftAppPassword").ConfigureAwait(false);
|
||||
PartnerCenterApplicationSecret = await provider.Vault.GetAsync("PartnerCenterApplicationSecret").ConfigureAwait(false);
|
||||
QnASubscriptionKey = await provider.Vault.GetAsync("QnASubscriptionKey").ConfigureAwait(false);
|
||||
RedisCacheConnectionString = await provider.Vault.GetAsync("RedisCacheConnectionString").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="IAccessTokenProvider.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Providers
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using IdentityModel.Clients.ActiveDirectory;
|
||||
using Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the ability to manage access tokens.
|
||||
/// </summary>
|
||||
public interface IAccessTokenProvider
|
||||
{
|
||||
Task<AuthenticationResult> AcquireTokenByAuthorizationCodeAsync(string authority, string code, ApplicationCredential credential, Uri redirectUri, string resource);
|
||||
|
||||
Task<AuthenticationResult> AcquireTokenSilentAsync(string authority, string resource, string clientId, UserIdentifier objectUserId);
|
||||
/// <summary>
|
||||
/// Gets an access token from the authority.
|
||||
/// </summary>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <param name="resource">Identifier of the target resource that is the recipient of the requested token.</param>
|
||||
/// <param name="credential">The credentials to use for token acquisition.</param>
|
||||
/// <returns>An instance of <see cref="AuthenticationResult"/> that represented the access token.</returns>
|
||||
Task<AuthenticationResult> GetAccessTokenAsync(string authority, string resource, ApplicationCredential credential);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an access token from the authority.
|
||||
/// </summary>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <param name="resource">Identifier of the target resource that is the recipient of the requested token.</param>
|
||||
/// <param name="credential">The credentials to use for token acquisition.</param>
|
||||
/// <param name="token">Assertion token representing the user.</param>
|
||||
/// <returns>An instance of <see cref="AuthenticationResult"/> that represented the access token.</returns>
|
||||
Task<AuthenticationResult> GetAccessTokenAsync(string authority, string resource, ApplicationCredential credential, string token);
|
||||
|
||||
Task<string> GetAuthorizationRequestUrlAsync(string authority, string resource, string clientId, Uri redirectUri, string extraQueryParameters);
|
||||
}
|
||||
}
|
|
@ -1,33 +1,36 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="IBotService.cs" company="Microsoft">
|
||||
// <copyright file="IBotProvider.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Providers
|
||||
{
|
||||
using System.Threading.Tasks;
|
||||
using Cache;
|
||||
using Configuration;
|
||||
using Intents;
|
||||
using Office;
|
||||
using Security;
|
||||
using Logic;
|
||||
using Logic.Office;
|
||||
using Telemetry;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the core service that powers the application.
|
||||
/// </summary>
|
||||
public interface IBotService
|
||||
public interface IBotProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the a reference to the token management service.
|
||||
/// </summary>
|
||||
IAccessTokenProvider AccessToken { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the service that provides caching functionality.
|
||||
/// </summary>
|
||||
ICacheService Cache { get; }
|
||||
ICacheProvider Cache { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the available configurations.
|
||||
/// </summary>
|
||||
IConfiguration Configuration { get; }
|
||||
IConfigurationProvider Configuration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether or the service has been initialized.
|
||||
|
@ -42,7 +45,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
/// <summary>
|
||||
/// Gets the service that provides localization functionality.
|
||||
/// </summary>
|
||||
ILocalizationService Localization { get; }
|
||||
ILocalizationProvider Localization { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the partner operations.
|
||||
|
@ -59,15 +62,10 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
/// </summary>
|
||||
ITelemetryProvider Telemetry { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the a reference to the token management service.
|
||||
/// </summary>
|
||||
ITokenManagement TokenManagement { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the vault service.
|
||||
/// </summary>
|
||||
IVaultService Vault { get; }
|
||||
IVaultProvider Vault { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the bot service and all the dependent services.
|
|
@ -1,24 +1,20 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="ICacheService.cs" company="Microsoft">
|
||||
// <copyright file="ICacheProvider.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Providers
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Cache;
|
||||
|
||||
/// <summary>
|
||||
/// Provides quick access to frequently utilized resources.
|
||||
/// Represents a way to cache data structures.
|
||||
/// </summary>
|
||||
public interface ICacheService
|
||||
public interface ICacheProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether caching is enabled.
|
||||
/// </summary>
|
||||
bool IsEnabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Removes all entities from the specified cache database.
|
||||
/// </summary>
|
||||
|
@ -32,10 +28,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
/// <param name="database">Cache database type where the data is stored.</param>
|
||||
/// <param name="key">A unique identifier for the cache entry.</param>
|
||||
/// <returns>A <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="key"/> is null.
|
||||
/// </exception>
|
||||
Task DeleteAsync(CacheDatabaseType database, string key);
|
||||
Task DeleteAsync(CacheDatabaseType database, string key = null);
|
||||
|
||||
/// <summary>
|
||||
/// Fetches the specified entity from the cache.
|
||||
|
@ -46,9 +39,6 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
/// <returns>
|
||||
/// The entity associated with the specified key.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="key"/> is null.
|
||||
/// </exception>
|
||||
TEntity Fetch<TEntity>(CacheDatabaseType database, string key) where TEntity : class;
|
||||
|
||||
/// <summary>
|
||||
|
@ -60,9 +50,6 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
/// <returns>
|
||||
/// The entity associated with the specified key.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="key"/> is null.
|
||||
/// </exception>
|
||||
Task<TEntity> FetchAsync<TEntity>(CacheDatabaseType database, string key) where TEntity : class;
|
||||
|
||||
/// <summary>
|
||||
|
@ -74,12 +61,6 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
/// <param name="entity">The object to be cached.</param>
|
||||
/// <param name="expiration">When the cached object expires.</param>
|
||||
/// <returns>A <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="key"/> is null.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// entity
|
||||
/// </exception>
|
||||
Task StoreAsync<TEntity>(CacheDatabaseType database, string key, TEntity entity, TimeSpan? expiration = null)
|
||||
where TEntity : class;
|
||||
|
||||
|
@ -94,10 +75,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
/// </summary>
|
||||
/// <param name="database">Cache database type where the data is stored.</param>
|
||||
/// <param name="key">A unique identifier for the cache entry.</param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="key"/> is null.
|
||||
/// </exception>
|
||||
void Delete(CacheDatabaseType database, string key);
|
||||
void Delete(CacheDatabaseType database, string key = null);
|
||||
|
||||
/// <summary>
|
||||
/// Stores the specified entity in the cache.
|
||||
|
@ -107,12 +85,6 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
/// <param name="key">A unique identifier for the cache entry.</param>
|
||||
/// <param name="entity">The object to be cached.</param>
|
||||
/// <param name="expiration">When the cached object expires.</param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="key"/> is null.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// entity
|
||||
/// </exception>
|
||||
void Store<TEntity>(CacheDatabaseType database, string key, TEntity entity, TimeSpan? expiration = null)
|
||||
where TEntity : class;
|
||||
}
|
|
@ -1,15 +1,18 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="IConfiguration.cs" company="Microsoft">
|
||||
// <copyright file="IConfigurationProvider.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Configuration
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Providers
|
||||
{
|
||||
using System.Security;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the ability to reference various configurations.
|
||||
/// </summary>
|
||||
public interface IConfiguration
|
||||
public interface IConfigurationProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the Azure Active Directory endpoint.
|
||||
|
@ -24,13 +27,23 @@ namespace Microsoft.Store.PartnerCenter.Bot.Configuration
|
|||
/// <summary>
|
||||
/// Gets the Azure AD application secret.
|
||||
/// </summary>
|
||||
string ApplicationSecret { get; }
|
||||
SecureString ApplicationSecret { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Azure AD application tenant identifier.
|
||||
/// </summary>
|
||||
string ApplicationTenantId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the access key for the instance of Cosmos Db.
|
||||
/// </summary>
|
||||
SecureString CosmosDbAccessKey { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Azure Cosmos DB endpoint address.
|
||||
/// </summary>
|
||||
string CosmosDbEndpoint { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Microsoft Graph endpoint.
|
||||
/// </summary>
|
||||
|
@ -42,14 +55,19 @@ namespace Microsoft.Store.PartnerCenter.Bot.Configuration
|
|||
string InstrumentationKey { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the LUIS application identifier.
|
||||
/// Gets the endpoint address for the instance of Azure Key Vault.
|
||||
/// </summary>
|
||||
string LuisAppId { get; }
|
||||
string KeyVaultEndpoint { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the LUIS API key.
|
||||
/// </summary>
|
||||
string LuisApiKey { get; }
|
||||
SecureString LuisApiKey { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the LUIS application identifier.
|
||||
/// </summary>
|
||||
string LuisAppId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Microsoft application identifier.
|
||||
|
@ -59,7 +77,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Configuration
|
|||
/// <summary>
|
||||
/// Gets the Microsoft application password.
|
||||
/// </summary>
|
||||
string MicrosoftAppPassword { get; }
|
||||
SecureString MicrosoftAppPassword { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Office 365 management endpoint address.
|
||||
|
@ -67,9 +85,9 @@ namespace Microsoft.Store.PartnerCenter.Bot.Configuration
|
|||
string OfficeManagementEndpoint { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Partner Center API endpoint.
|
||||
/// Gets the Partner Center account identifier.
|
||||
/// </summary>
|
||||
string PartnerCenterEndpoint { get; }
|
||||
string PartnerCenterAccountId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Partner Center application identifier value.
|
||||
|
@ -79,12 +97,12 @@ namespace Microsoft.Store.PartnerCenter.Bot.Configuration
|
|||
/// <summary>
|
||||
/// Gets the Partner Center application secret value.
|
||||
/// </summary>
|
||||
string PartnerCenterApplicationSecret { get; }
|
||||
SecureString PartnerCenterApplicationSecret { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Partner Center application tenant identifier.
|
||||
/// Gets the Partner Center API endpoint.
|
||||
/// </summary>
|
||||
string PartnerCenterApplicationTenantId { get; }
|
||||
string PartnerCenterEndpoint { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the question and answer knowledgebase identifier.
|
||||
|
@ -94,31 +112,17 @@ namespace Microsoft.Store.PartnerCenter.Bot.Configuration
|
|||
/// <summary>
|
||||
/// Gets question and answer subscription subscription key.
|
||||
/// </summary>
|
||||
string QnASubscriptionKey { get; }
|
||||
SecureString QnASubscriptionKey { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Redis Cache connection string.
|
||||
/// </summary>
|
||||
string RedisCacheConnectionString { get; }
|
||||
SecureString RedisCacheConnectionString { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vault application certificate thumbprint.
|
||||
/// Performs the necessary initialization operations.
|
||||
/// </summary>
|
||||
string VaultApplicationCertThumbprint { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vault application identifier.
|
||||
/// </summary>
|
||||
string VaultApplicationId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vault application tenant identifier.
|
||||
/// </summary>
|
||||
string VaultApplicationTenantId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the base address for the vault.
|
||||
/// </summary>
|
||||
string VaultBaseAddress { get; }
|
||||
/// <returns>An instance of the <see cref="Task"/> class that represents the asynchronous operation.</returns>
|
||||
Task InitializeAsync();
|
||||
}
|
||||
}
|
|
@ -1,17 +1,17 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="ILocalizationService.cs" company="Microsoft">
|
||||
// <copyright file="ILocalizationProvider.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Providers
|
||||
{
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the localization service used by the portal.
|
||||
/// Represents the localization service used by the bot.
|
||||
/// </summary>
|
||||
public interface ILocalizationService
|
||||
public interface ILocalizationProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the country ISO2 code.
|
|
@ -0,0 +1,32 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="IVaultProvider.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Providers
|
||||
{
|
||||
using System.Security;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a secure mechanism for retrieving and store information.
|
||||
/// </summary>
|
||||
public interface IVaultProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified entity from the vault.
|
||||
/// </summary>
|
||||
/// <param name="identifier">Identifier of the entity to be retrieved.</param>
|
||||
/// <returns>The value retrieved from the vault.</returns>
|
||||
Task<SecureString> GetAsync(string identifier);
|
||||
|
||||
/// <summary>
|
||||
/// Stores the specified value in the vault.
|
||||
/// </summary>
|
||||
/// <param name="identifier">Identifier of the entity to be stored.</param>
|
||||
/// <param name="value">The value to be stored.</param>
|
||||
/// <returns>An instance of <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
Task StoreAsync(string identifier, SecureString value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="KeyVaultProvider.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Providers
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security;
|
||||
using System.Threading.Tasks;
|
||||
using Azure.KeyVault;
|
||||
using Azure.KeyVault.Models;
|
||||
using Azure.Services.AppAuthentication;
|
||||
using Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a secure mechanism for retrieving and store information.
|
||||
/// </summary>
|
||||
internal sealed class KeyVaultProvider : IVaultProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to core bot providers.
|
||||
/// </summary>
|
||||
private readonly IBotProvider provider;
|
||||
|
||||
/// <summary>
|
||||
/// Error code returned when a secret is not found in the vault.
|
||||
/// </summary>
|
||||
private const string NotFoundErrorCode = "SecretNotFound";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="KeyVaultProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="provider">Provides access to core bot providers.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public KeyVaultProvider(IBotProvider provider)
|
||||
{
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified entity from the vault.
|
||||
/// </summary>
|
||||
/// <param name="identifier">Identifier of the entity to be retrieved.</param>
|
||||
/// <returns>The value retrieved from the vault.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="identifier"/> is empty or null.
|
||||
/// </exception>
|
||||
public async Task<SecureString> GetAsync(string identifier)
|
||||
{
|
||||
DateTime executionTime;
|
||||
Dictionary<string, double> eventMetrics;
|
||||
Dictionary<string, string> eventProperties;
|
||||
SecretBundle bundle;
|
||||
|
||||
identifier.AssertNotEmpty(nameof(identifier));
|
||||
|
||||
try
|
||||
{
|
||||
executionTime = DateTime.Now;
|
||||
|
||||
using (IKeyVaultClient client = GetAzureKeyVaultClient())
|
||||
{
|
||||
try
|
||||
{
|
||||
bundle = await client.GetSecretAsync(provider.Configuration.KeyVaultEndpoint, identifier).ConfigureAwait(false);
|
||||
}
|
||||
catch (KeyVaultErrorException ex)
|
||||
{
|
||||
if (ex.Body.Error.Code.Equals(NotFoundErrorCode, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
bundle = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Track the event measurements for analysis.
|
||||
eventMetrics = new Dictionary<string, double>
|
||||
{
|
||||
{ "ElapsedMilliseconds", DateTime.Now.Subtract(executionTime).TotalMilliseconds }
|
||||
};
|
||||
|
||||
// Capture the request for the customer summary for analysis.
|
||||
eventProperties = new Dictionary<string, string>
|
||||
{
|
||||
{ "Identifier", identifier }
|
||||
};
|
||||
|
||||
provider.Telemetry.TrackEvent("KeyVault/GetAsync", eventProperties, eventMetrics);
|
||||
|
||||
return bundle?.Value.ToSecureString();
|
||||
}
|
||||
finally
|
||||
{
|
||||
bundle = null;
|
||||
eventMetrics = null;
|
||||
eventProperties = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores the specified value in the vault.
|
||||
/// </summary>
|
||||
/// <param name="identifier">Identifier of the entity to be stored.</param>
|
||||
/// <param name="value">The value to be stored.</param>
|
||||
/// <returns>An instance of <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="identifier"/> is empty or null.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="value"/> is null.
|
||||
/// </exception>
|
||||
public async Task StoreAsync(string identifier, SecureString value)
|
||||
{
|
||||
DateTime executionTime;
|
||||
Dictionary<string, double> eventMetrics;
|
||||
Dictionary<string, string> eventProperties;
|
||||
|
||||
identifier.AssertNotEmpty(nameof(identifier));
|
||||
value.AssertNotNull(nameof(value));
|
||||
|
||||
try
|
||||
{
|
||||
executionTime = DateTime.Now;
|
||||
|
||||
using (IKeyVaultClient client = GetAzureKeyVaultClient())
|
||||
{
|
||||
await client.SetSecretAsync(
|
||||
provider.Configuration.KeyVaultEndpoint, identifier, value.ToUnsecureString()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// Track the event measurements for analysis.
|
||||
eventMetrics = new Dictionary<string, double>
|
||||
{
|
||||
{ "ElapsedMilliseconds", DateTime.Now.Subtract(executionTime).TotalMilliseconds }
|
||||
};
|
||||
|
||||
// Capture the request for the customer summary for analysis.
|
||||
eventProperties = new Dictionary<string, string>
|
||||
{
|
||||
{ "Identifier", identifier }
|
||||
};
|
||||
|
||||
provider.Telemetry.TrackEvent("KeyVault/StoreAsync", eventProperties, eventMetrics);
|
||||
}
|
||||
finally
|
||||
{
|
||||
eventMetrics = null;
|
||||
eventProperties = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an aptly configured instance of the <see cref="KeyVaultClient"/> class.
|
||||
/// </summary>
|
||||
/// <returns>An aptly populated instane of the <see cref="KeyVaultClient"/> class.</returns>
|
||||
private static KeyVaultClient GetAzureKeyVaultClient()
|
||||
{
|
||||
return new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(new AzureServiceTokenProvider().KeyVaultTokenCallback));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,39 +1,39 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="LocalizationService.cs" company="Microsoft">
|
||||
// <copyright file="LocalizationProvider.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Providers
|
||||
{
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using PartnerCenter.Models.CountryValidationRules;
|
||||
using PartnerCenter.Models.Partners;
|
||||
using Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides localization for the bot.
|
||||
/// </summary>
|
||||
/// <seealso cref="ILocalizationService" />
|
||||
public class LocalizationService : ILocalizationService
|
||||
/// <seealso cref="ILocalizationProvider" />
|
||||
public class LocalizationProvider : ILocalizationProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to core application services.
|
||||
/// </summary>
|
||||
private readonly IBotService service;
|
||||
private readonly IBotProvider provider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalizationService"/> class.
|
||||
/// Initializes a new instance of the <see cref="LocalizationProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core application services.</param>
|
||||
/// <param name="provider">Provides access to core application services.</param>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// <paramref name="service"/> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public LocalizationService(IBotService service)
|
||||
public LocalizationProvider(IBotProvider provider)
|
||||
{
|
||||
service.AssertNotNull(nameof(service));
|
||||
|
||||
this.service = service;
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -58,25 +58,27 @@ namespace Microsoft.Store.PartnerCenter.Bot.Logic
|
|||
try
|
||||
{
|
||||
// Obtain the business profile for the configured partner.
|
||||
businessProfile = await this.service.PartnerOperations.GetLegalBusinessProfileAsync();
|
||||
businessProfile = await provider.PartnerOperations
|
||||
.GetLegalBusinessProfileAsync().ConfigureAwait(false);
|
||||
|
||||
this.CountryIso2Code = businessProfile.Address.Country;
|
||||
CountryIso2Code = businessProfile.Address.Country;
|
||||
|
||||
try
|
||||
{
|
||||
// Obtain the country validation rules for the configured partner.
|
||||
countryValidationRules = await this.service.PartnerOperations.GetCountryValidationRulesAsync(this.CountryIso2Code);
|
||||
countryValidationRules = await provider.PartnerOperations
|
||||
.GetCountryValidationRulesAsync(CountryIso2Code).ConfigureAwait(false);
|
||||
|
||||
this.Locale = countryValidationRules.DefaultCulture;
|
||||
Locale = countryValidationRules.DefaultCulture;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Default the region to en-US.
|
||||
this.Locale = "en-US";
|
||||
Locale = "en-US";
|
||||
}
|
||||
|
||||
// Set the culture to partner locale.
|
||||
Resources.Culture = new CultureInfo(this.Locale);
|
||||
Resources.Culture = new CultureInfo(Locale);
|
||||
}
|
||||
finally
|
||||
{
|
|
@ -1,34 +1,30 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="CacheService.cs" company="Microsoft">
|
||||
// <copyright file="RedisCacheProvider.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Providers
|
||||
{
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Configuration;
|
||||
using Logic;
|
||||
using Cache;
|
||||
using Extensions;
|
||||
using Newtonsoft.Json;
|
||||
using Security;
|
||||
using StackExchange.Redis;
|
||||
|
||||
/// <summary>
|
||||
/// Provides quick access to frequently utilized resources.
|
||||
/// Provides the ability to cache resources using an instance of Redis Cache.
|
||||
/// </summary>
|
||||
public class CacheService : ICacheService
|
||||
internal sealed class RedisCacheProvider : ICacheProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to core services.
|
||||
/// Provides access to the core bot providers.
|
||||
/// </summary>
|
||||
private readonly IBotService service;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the ability to protect data.
|
||||
/// </summary>
|
||||
private readonly IDataProtector protector;
|
||||
private readonly IBotProvider provider;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the ability to interact with an instance of Redis Cache.
|
||||
|
@ -36,49 +32,18 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
private IConnectionMultiplexer connection;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CacheService" /> class.
|
||||
/// Initializes a new instance of the <see cref="RedisCacheProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core services.</param>
|
||||
/// <param name="provider">Provides access to the core bot providers.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="service" /> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public CacheService(IBotService service)
|
||||
public RedisCacheProvider(IBotProvider provider)
|
||||
{
|
||||
service.AssertNotNull(nameof(service));
|
||||
|
||||
this.protector = new MachineKeyDataProtector(new[] { typeof(CacheService).FullName });
|
||||
this.service = service;
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CacheService"/> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core services.</param>
|
||||
/// <param name="connection">Connection to utilized to communicate with the Redis Cache instance.</param>
|
||||
/// <param name="dataProtector">Provides protection for data being cached.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="service"/> is null.
|
||||
/// or
|
||||
/// <paramref name="connection"/> is null.
|
||||
/// or
|
||||
/// <paramref name="dataProtector"/> is null.
|
||||
/// </exception>
|
||||
public CacheService(IBotService service, IConnectionMultiplexer connection, IDataProtector dataProtector)
|
||||
{
|
||||
service.AssertNotNull(nameof(service));
|
||||
connection.AssertNotNull(nameof(connection));
|
||||
dataProtector.AssertNotNull(nameof(dataProtector));
|
||||
|
||||
this.connection = connection;
|
||||
this.protector = dataProtector;
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether caching is enabled.
|
||||
/// </summary>
|
||||
public bool IsEnabled => !string.IsNullOrEmpty(this.service.Configuration.RedisCacheConnectionString);
|
||||
|
||||
/// <summary>
|
||||
/// Removes all entities from the specified cache database.
|
||||
/// </summary>
|
||||
|
@ -86,35 +51,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
/// <returns>A <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public async Task ClearAsync(CacheDatabaseType database)
|
||||
{
|
||||
EndPoint[] endpoints;
|
||||
IServer server;
|
||||
|
||||
try
|
||||
{
|
||||
if (!this.IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.connection == null)
|
||||
{
|
||||
this.connection = await ConnectionMultiplexer.ConnectAsync(
|
||||
this.service.Configuration.RedisCacheConnectionString);
|
||||
}
|
||||
|
||||
endpoints = this.connection.GetEndPoints(true);
|
||||
|
||||
foreach (EndPoint ep in endpoints)
|
||||
{
|
||||
server = this.connection.GetServer(ep);
|
||||
await server.FlushDatabaseAsync((int)database);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
endpoints = null;
|
||||
server = null;
|
||||
}
|
||||
await Task.FromResult(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -122,20 +59,13 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
/// </summary>
|
||||
/// <param name="database">Cache database type where the data is stored.</param>
|
||||
/// <param name="key">The key of the entity to be deleted.</param>
|
||||
/// <returns>A <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
/// <returns>An instance of <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="key"/> is empty or null.
|
||||
/// </exception>
|
||||
public async Task DeleteAsync(CacheDatabaseType database, string key)
|
||||
public async Task DeleteAsync(CacheDatabaseType database, string key = null)
|
||||
{
|
||||
key.AssertNotEmpty(nameof(key));
|
||||
|
||||
if (!this.IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IDatabase cache = this.GetCacheReference(database);
|
||||
IDatabase cache = await GetCacheReferenceAsync(database);
|
||||
await cache.KeyDeleteAsync(key);
|
||||
}
|
||||
|
||||
|
@ -155,16 +85,10 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
{
|
||||
key.AssertNotEmpty(nameof(key));
|
||||
|
||||
if (!this.IsEnabled)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
IDatabase cache = this.GetCacheReference(database);
|
||||
IDatabase cache = GetCacheReference(database);
|
||||
RedisValue value = cache.StringGet(key);
|
||||
|
||||
return value.HasValue ?
|
||||
JsonConvert.DeserializeObject<TEntity>(this.protector.Unprotect(value)) : null;
|
||||
return value.HasValue ? DecompressEntity<TEntity>(value) : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -183,15 +107,10 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
{
|
||||
key.AssertNotEmpty(nameof(key));
|
||||
|
||||
if (!this.IsEnabled)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
IDatabase cache = this.GetCacheReference(database);
|
||||
IDatabase cache = await GetCacheReferenceAsync(database);
|
||||
RedisValue value = await cache.StringGetAsync(key);
|
||||
|
||||
return value.HasValue ? JsonConvert.DeserializeObject<TEntity>(this.protector.Unprotect(value)) : null;
|
||||
return value.HasValue ? DecompressEntity<TEntity>(value) : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -215,14 +134,10 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
key.AssertNotEmpty(nameof(key));
|
||||
entity.AssertNotNull(nameof(entity));
|
||||
|
||||
if (!this.IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
IDatabase cache = await GetCacheReferenceAsync(database);
|
||||
|
||||
IDatabase cache = await this.GetCacheReferenceAsync(database);
|
||||
await cache.StringSetAsync(
|
||||
key, this.protector.Protect(JsonConvert.SerializeObject(entity)), expiration);
|
||||
key, CompressEntity(entity), expiration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -231,35 +146,6 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
/// <param name="database">Cache database type where the data is stored.</param>
|
||||
public void Clear(CacheDatabaseType database)
|
||||
{
|
||||
EndPoint[] endpoints;
|
||||
IServer server;
|
||||
|
||||
try
|
||||
{
|
||||
if (!this.IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.connection == null)
|
||||
{
|
||||
this.connection = ConnectionMultiplexer.Connect(
|
||||
this.service.Configuration.RedisCacheConnectionString);
|
||||
}
|
||||
|
||||
endpoints = this.connection.GetEndPoints(true);
|
||||
|
||||
foreach (EndPoint ep in endpoints)
|
||||
{
|
||||
server = this.connection.GetServer(ep);
|
||||
server.FlushDatabase((int)database);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
endpoints = null;
|
||||
server = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -270,16 +156,9 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="key"/> is empty or null.
|
||||
/// </exception>
|
||||
public void Delete(CacheDatabaseType database, string key)
|
||||
public void Delete(CacheDatabaseType database, string key = null)
|
||||
{
|
||||
key.AssertNotEmpty(nameof(key));
|
||||
|
||||
if (!this.IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IDatabase cache = this.GetCacheReference(database);
|
||||
IDatabase cache = GetCacheReference(database);
|
||||
cache.KeyDelete(key);
|
||||
}
|
||||
|
||||
|
@ -287,7 +166,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
/// Stores the specified entity in the cache.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">The type of the entity in cache.</typeparam>
|
||||
/// <param name="cacheDatabase">Cache database type where the data should be stored.</param>
|
||||
/// <param name="database">Cache database type where the data should be stored.</param>
|
||||
/// <param name="key">A unique identifier for the cache entry.</param>
|
||||
/// <param name="entity">The object to be cached.</param>
|
||||
/// <param name="expiration">When the entity in the cache should expire.</param>
|
||||
|
@ -297,20 +176,89 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
/// <exception cref="ArgumentNullException">
|
||||
/// entity
|
||||
/// </exception>
|
||||
public void Store<TEntity>(CacheDatabaseType cacheDatabase, string key, TEntity entity, TimeSpan? expiration = null)
|
||||
public void Store<TEntity>(CacheDatabaseType database, string key, TEntity entity, TimeSpan? expiration = null)
|
||||
where TEntity : class
|
||||
{
|
||||
key.AssertNotEmpty(nameof(key));
|
||||
entity.AssertNotNull(nameof(entity));
|
||||
|
||||
if (!this.IsEnabled)
|
||||
{
|
||||
return;
|
||||
IDatabase cache = GetCacheReference(database);
|
||||
|
||||
cache.StringSet(
|
||||
key, CompressEntity(entity), expiration);
|
||||
}
|
||||
|
||||
IDatabase cache = this.GetCacheReference(cacheDatabase);
|
||||
cache.StringSet(
|
||||
key, this.protector.Protect(JsonConvert.SerializeObject(entity)), expiration);
|
||||
/// <summary>
|
||||
/// Compresses the specified entity.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">The type of the entity to be compressed.</typeparam>
|
||||
/// <param name="entity">The entity to be compressed.</param>
|
||||
/// <returns>A base 64 string that represents the compressed entity.</returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="entity"/> is null.
|
||||
/// </exception>
|
||||
private static string CompressEntity<TEntity>(TEntity entity)
|
||||
{
|
||||
MemoryStream memoryStream;
|
||||
byte[] buffer;
|
||||
|
||||
entity.AssertNotNull(nameof(entity));
|
||||
|
||||
try
|
||||
{
|
||||
buffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(entity));
|
||||
|
||||
memoryStream = new MemoryStream();
|
||||
|
||||
using (GZipStream stream = new GZipStream(memoryStream, CompressionMode.Compress))
|
||||
{
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
return Convert.ToBase64String(memoryStream.ToArray());
|
||||
}
|
||||
finally
|
||||
{
|
||||
buffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses the entity represented by the specified value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">The type of the entity to be decompressed.</typeparam>
|
||||
/// <param name="value">A base 64 string that represented the compressed entity.</param>
|
||||
/// <returns>The decompressed and deserialized instance of the entity.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="value"/> is empty or null.
|
||||
/// </exception>
|
||||
public static TEntity DecompressEntity<TEntity>(string value)
|
||||
{
|
||||
MemoryStream memoryStream;
|
||||
byte[] buffer;
|
||||
|
||||
value.AssertNotEmpty(nameof(value));
|
||||
|
||||
try
|
||||
{
|
||||
buffer = Convert.FromBase64String(value);
|
||||
|
||||
memoryStream = new MemoryStream(buffer, 0, buffer.Length);
|
||||
|
||||
using (MemoryStream decompressedStream = new MemoryStream())
|
||||
{
|
||||
using (GZipStream stream = new GZipStream(memoryStream, CompressionMode.Decompress))
|
||||
{
|
||||
stream.CopyTo(decompressedStream);
|
||||
}
|
||||
|
||||
return JsonConvert.DeserializeObject<TEntity>(Encoding.UTF8.GetString(decompressedStream.ToArray()));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
buffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -320,22 +268,13 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
/// <returns>A reference to the appropriate cache database.</returns>
|
||||
private IDatabase GetCacheReference(CacheDatabaseType database)
|
||||
{
|
||||
if (this.connection != null)
|
||||
if (connection == null)
|
||||
{
|
||||
return this.connection.GetDatabase((int)database);
|
||||
connection = ConnectionMultiplexer.Connect(
|
||||
provider.Configuration.RedisCacheConnectionString.ToUnsecureString());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
this.connection = ConnectionMultiplexer.Connect(
|
||||
this.service.Configuration.RedisCacheConnectionString);
|
||||
|
||||
return this.connection.GetDatabase((int)database);
|
||||
}
|
||||
catch (RedisConnectionException ex)
|
||||
{
|
||||
throw new CacheException(Resources.RedisCacheConnectionException, ex);
|
||||
}
|
||||
return connection.GetDatabase((int)database);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -345,22 +284,13 @@ namespace Microsoft.Store.PartnerCenter.Bot.Cache
|
|||
/// <returns>A reference to the appropriate cache database.</returns>
|
||||
private async Task<IDatabase> GetCacheReferenceAsync(CacheDatabaseType database)
|
||||
{
|
||||
if (this.connection != null)
|
||||
if (connection == null)
|
||||
{
|
||||
return this.connection.GetDatabase((int)database);
|
||||
connection = await ConnectionMultiplexer.ConnectAsync(
|
||||
provider.Configuration.RedisCacheConnectionString.ToUnsecureString());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
this.connection = await ConnectionMultiplexer.ConnectAsync(
|
||||
this.service.Configuration.RedisCacheConnectionString);
|
||||
|
||||
return this.connection.GetDatabase((int)database);
|
||||
}
|
||||
catch (RedisConnectionException ex)
|
||||
{
|
||||
throw new CacheException(Resources.RedisCacheConnectionException, ex);
|
||||
}
|
||||
return connection.GetDatabase((int)database);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,9 +8,11 @@ namespace Microsoft.Store.PartnerCenter.Bot.Security
|
|||
{
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Configuration;
|
||||
using Graph;
|
||||
using Logic;
|
||||
using IdentityModel.Clients.ActiveDirectory;
|
||||
using Extensions;
|
||||
using Models;
|
||||
using Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Authentication provider for the Microsoft Graph service client.
|
||||
|
@ -31,7 +33,7 @@ namespace Microsoft.Store.PartnerCenter.Bot.Security
|
|||
/// <summary>
|
||||
/// Provides access to core services.
|
||||
/// </summary>
|
||||
private readonly IBotService service;
|
||||
private readonly IBotProvider provider;
|
||||
|
||||
/// <summary>
|
||||
/// The customer identifier utilized to scope the Microsoft Graph requests.
|
||||
|
@ -41,21 +43,21 @@ namespace Microsoft.Store.PartnerCenter.Bot.Security
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AuthenticationProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core services.</param>
|
||||
/// <param name="provider">Provides access to core services.</param>
|
||||
/// <param name="customerId">Identifier for customer whose resources are being accessed.</param>
|
||||
/// <exception cref="System.ArgumentException">
|
||||
/// <paramref name="customerId"/> is empty or null.
|
||||
/// </exception>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// <paramref name="service"/> is null.
|
||||
/// <paramref name="provider"/> is null.
|
||||
/// </exception>
|
||||
public AuthenticationProvider(IBotService service, string customerId)
|
||||
public AuthenticationProvider(IBotProvider provider, string customerId)
|
||||
{
|
||||
service.AssertNotNull(nameof(service));
|
||||
provider.AssertNotNull(nameof(provider));
|
||||
customerId.AssertNotEmpty(nameof(customerId));
|
||||
|
||||
this.customerId = customerId;
|
||||
this.service = service;
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -65,11 +67,26 @@ namespace Microsoft.Store.PartnerCenter.Bot.Security
|
|||
/// <returns>A <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
|
||||
{
|
||||
AuthenticationToken token = await this.service.TokenManagement.GetAppOnlyTokenAsync(
|
||||
$"{this.service.Configuration.ActiveDirectoryEndpoint}/{this.customerId}",
|
||||
this.service.Configuration.GraphEndpoint);
|
||||
AuthenticationResult authResult;
|
||||
|
||||
request.Headers.Add(AuthHeaderName, $"{TokenType} {token.Token}");
|
||||
try
|
||||
{
|
||||
authResult = await provider.AccessToken.GetAccessTokenAsync(
|
||||
$"{provider.Configuration.ActiveDirectoryEndpoint}/{customerId}",
|
||||
provider.Configuration.GraphEndpoint,
|
||||
new ApplicationCredential
|
||||
{
|
||||
ApplicationId = provider.Configuration.ApplicationId,
|
||||
ApplicationSecret = provider.Configuration.ApplicationSecret,
|
||||
UseCache = true
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
request.Headers.Add(AuthHeaderName, $"{TokenType} {authResult.AccessToken}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
authResult = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,8 +11,9 @@ namespace Microsoft.Store.PartnerCenter.Bot.Security
|
|||
using System.Threading.Tasks;
|
||||
using System.Web.Http.Controllers;
|
||||
using Autofac;
|
||||
using Logic;
|
||||
using Extensions;
|
||||
using Microsoft.Bot.Connector;
|
||||
using Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Provides custom authentication for the bot itself.
|
||||
|
@ -31,10 +32,10 @@ namespace Microsoft.Store.PartnerCenter.Bot.Security
|
|||
{
|
||||
using (ILifetimeScope scope = WebApiApplication.Container.BeginLifetimeScope())
|
||||
{
|
||||
IBotService service = scope.Resolve<IBotService>();
|
||||
IBotProvider provider = scope.Resolve<IBotProvider>();
|
||||
|
||||
MicrosoftAppId = service.Configuration.MicrosoftAppId;
|
||||
MicrosoftAppPassword = service.Configuration.MicrosoftAppPassword;
|
||||
MicrosoftAppId = provider.Configuration.MicrosoftAppId;
|
||||
MicrosoftAppPassword = provider.Configuration.MicrosoftAppPassword.ToUnsecureString();
|
||||
}
|
||||
|
||||
return base.OnActionExecutingAsync(actionContext, cancellationToken);
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="DataProtectorException.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Security
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Security.Permissions;
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when an error is encountered when protecting, or unprotecting, data.
|
||||
/// </summary>
|
||||
/// <seealso cref="Exception" />
|
||||
[Serializable]
|
||||
public class DataProtectorException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataProtectorException"/> class.
|
||||
/// </summary>
|
||||
public DataProtectorException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataProtectorException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public DataProtectorException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataProtectorException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.</param>
|
||||
public DataProtectorException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataProtectorException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
protected DataProtectorException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, sets the <see cref="T:System.Runtime.Serialization.SerializationInfo" /> with information about the exception.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
/// <PermissionSet>
|
||||
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Read="*AllFiles*" PathDiscovery="*AllFiles*" />
|
||||
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="SerializationFormatter" />
|
||||
/// </PermissionSet>
|
||||
[SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
|
||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
base.GetObjectData(info, context);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="IDataProtector.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Security
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a data protection strategy.
|
||||
/// </summary>
|
||||
public interface IDataProtector
|
||||
{
|
||||
/// <summary>
|
||||
/// Protects the specified data by encrypting.
|
||||
/// </summary>
|
||||
/// <param name="data">The data to be encrypted.</param>
|
||||
/// <returns>Base64 encoded string that represented the protected data.</returns>
|
||||
/// <exception cref="System.ArgumentException">
|
||||
/// <paramref name="data"/> is empty or null.
|
||||
/// </exception>
|
||||
string Protect(string data);
|
||||
|
||||
/// <summary>
|
||||
/// Unprotects the specified data, which was protected by the <see cref="Protect(string)"/> method.
|
||||
/// </summary>
|
||||
/// <param name="data">The cipher text data to unprotect.</param>
|
||||
/// <returns>The decrypted data in plaintext.</returns>
|
||||
/// <exception cref="System.ArgumentException">
|
||||
/// <paramref name="data"/> is empty or null.
|
||||
/// </exception>
|
||||
string Unprotect(string data);
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="ITokenManagement.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Security
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using IdentityModel.Clients.ActiveDirectory;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a management interface for retrieving access tokens.
|
||||
/// </summary>
|
||||
public interface ITokenManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// Acquires an access token without asking for user credential.
|
||||
/// </summary>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <param name="resource">Identifier of the client requesting the token.</param>
|
||||
/// <param name="objectUserId">Identifier of the user that is requesting the token.</param>
|
||||
/// <returns>An instance of <see cref="AuthenticationToken"/> that represents the access token.</returns>
|
||||
Task<AuthenticationResult> AcquireTokenSilentAsync(string authority, string resource, UserIdentifier objectUserId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an access token from the authority using app only authentication.
|
||||
/// </summary>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <param name="resource">Identifier of the target resource that is the recipient of the requested token.</param>
|
||||
/// <returns>An instance of <see cref="AuthenticationToken"/> that represented the access token.</returns>
|
||||
Task<AuthenticationToken> GetAppOnlyTokenAsync(string authority, string resource);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an access token from the authority using app only authentication.
|
||||
/// </summary>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <param name="resource">Identifier of the target resource that is the recipient of the requested token.</param>
|
||||
/// <param name="scope">Permissions the requested token will need.</param>
|
||||
/// <returns>A string that represented the access token.</returns>
|
||||
Task<string> GetAppOnlyTokenAsync(string authority, string resource, string scope);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an access token from the authority using app + user authentication.
|
||||
/// </summary>
|
||||
/// <param name="princiapl">Security principal for the calling user.</param>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <param name="resource">Identifier of the target resource that is the recipient of the requested token.</param>
|
||||
/// <returns>An instance of <see cref="AuthenticationToken"/> that represented the access token.</returns>
|
||||
Task<AuthenticationToken> GetAppPlusUserTokenAsync(CustomerPrincipal princiapl, string authority, string resource);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the URL of the authorization endpoint including the query parameters.
|
||||
/// </summary>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <param name="redirectUri">Address to return to upon receiving a response from the authority.</param>
|
||||
/// <param name="resource">Identifier of the target resource that is the recipient of the requested token.</param>
|
||||
/// <param name="extraQueryParameters">Data that will be appended as is to the query string in the HTTP authentication request to the authority.</param>
|
||||
/// <returns>URL of the authorization endpoint including the query parameters.</returns>
|
||||
Task<string> GetAuthorizationRequestUrlAsync(string authority, Uri redirectUri, string resource, string extraQueryParameters);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance of <see cref="IPartnerCredentials"/> used to access the Partner Center API.
|
||||
/// </summary>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <returns>
|
||||
/// An instance of <see cref="IPartnerCredentials" /> that represents the access token.
|
||||
/// </returns>
|
||||
Task<IPartnerCredentials> GetPartnerCenterAppOnlyCredentialsAsync(string authority);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance of <see cref="IPartnerCredentials"/> used to access the Partner Center API.
|
||||
/// </summary>
|
||||
/// <param name="principal">Security principal for the calling user.</param>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <returns>
|
||||
/// An instance of <see cref="IPartnerCredentials" /> that represents the access token.
|
||||
/// </returns>
|
||||
Task<IPartnerCredentials> GetPartnerCenterAppPlusUserCredentialsAsync(CustomerPrincipal principal, string authority);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an access token utilizing an authorization code.
|
||||
/// </summary>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <param name="code">Authorization code received from the service authorization endpoint.</param>
|
||||
/// <param name="resource">Identifier of the target resource that is the recipient of the requested token.</param>
|
||||
/// <param name="redirectUri">Redirect URI used for obtain the authorization code.</param>
|
||||
/// <returns>An instance of <see cref="AuthenticationToken"/> that represented the access token.</returns>
|
||||
Task<AuthenticationResult> GetTokenByAuthorizationCodeAsync(string authority, string code, string resource, Uri redirectUri);
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="IVaultService.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Security
|
||||
{
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a secure mechanism for retrieving and store information.
|
||||
/// </summary>
|
||||
public interface IVaultService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the vault service is enabled or not.
|
||||
/// </summary>
|
||||
bool IsEnabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified entity from the vault.
|
||||
/// </summary>
|
||||
/// <param name="identifier">Identifier of the entity to be retrieved.</param>
|
||||
/// <returns>The value retrieved from the vault.</returns>
|
||||
/// <exception cref="System.ArgumentException">
|
||||
/// <paramref name="identifier"/> is empty or null.
|
||||
/// </exception>
|
||||
string Get(string identifier);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified entity from the vault.
|
||||
/// </summary>
|
||||
/// <param name="identifier">Identifier of the entity to be retrieved.</param>
|
||||
/// <returns>The value retrieved from the vault.</returns>
|
||||
/// <exception cref="System.ArgumentException">
|
||||
/// <paramref name="identifier"/> is empty or null.
|
||||
/// </exception>
|
||||
Task<string> GetAsync(string identifier);
|
||||
|
||||
/// <summary>
|
||||
/// Stores the specified value in the vault.
|
||||
/// </summary>
|
||||
/// <param name="identifier">Identifier of the entity to be stored.</param>
|
||||
/// <param name="value">The value to stored.</param>
|
||||
/// <exception cref="System.ArgumentException">
|
||||
/// <paramref name="identifier"/> is empty or null.
|
||||
/// or
|
||||
/// <paramref name="value"/> is empty or null.
|
||||
/// </exception>
|
||||
void Store(string identifier, string value);
|
||||
|
||||
/// <summary>
|
||||
/// Stores the specified value in the vault.
|
||||
/// </summary>
|
||||
/// <param name="identifier">Identifier of the entity to be stored.</param>
|
||||
/// <param name="value">The value to stored.</param>
|
||||
/// <returns>An instance of <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
/// <exception cref="System.ArgumentException">
|
||||
/// <paramref name="identifier"/> is empty or null.
|
||||
/// or
|
||||
/// <paramref name="value"/> is empty or null.
|
||||
/// </exception>
|
||||
Task StoreAsync(string identifier, string value);
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="MachineKeyDataProtector.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Security
|
||||
{
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Web.Security;
|
||||
using Logic;
|
||||
|
||||
/// <summary>
|
||||
/// Provides data protection using the machine encryption.
|
||||
/// </summary>
|
||||
/// <seealso cref="IDataProtector" />
|
||||
internal sealed class MachineKeyDataProtector : IDataProtector
|
||||
{
|
||||
/// <summary>
|
||||
/// The purposes of the data being protected.
|
||||
/// </summary>
|
||||
private readonly string[] purposes;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MachineKeyDataProtector"/> class.
|
||||
/// </summary>
|
||||
/// <param name="purposes">The purpose of the data being protected.</param>
|
||||
public MachineKeyDataProtector(string[] purposes)
|
||||
{
|
||||
this.purposes = purposes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Protects the specified data by encrypting.
|
||||
/// </summary>
|
||||
/// <param name="data">The data to be encrypted.</param>
|
||||
/// <returns>Base64 encoded string that represented the protected data.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="data"/> is empty or null.
|
||||
/// </exception>
|
||||
public string Protect(string data)
|
||||
{
|
||||
byte[] buffer;
|
||||
|
||||
data.AssertNotEmpty(nameof(data));
|
||||
|
||||
try
|
||||
{
|
||||
buffer = Encoding.ASCII.GetBytes(data);
|
||||
return Convert.ToBase64String(MachineKey.Protect(buffer, purposes));
|
||||
}
|
||||
finally
|
||||
{
|
||||
buffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unprotects the specified data, which was protected by the <see cref="Protect(string)"/> method.
|
||||
/// </summary>
|
||||
/// <param name="data">The cipher text data to unprotect.</param>
|
||||
/// <returns>The decrypted data in plaintext.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="data"/> is empty or null.
|
||||
/// </exception>
|
||||
public string Unprotect(string data)
|
||||
{
|
||||
byte[] buffer;
|
||||
byte[] decrypt;
|
||||
|
||||
data.AssertNotEmpty(nameof(data));
|
||||
|
||||
try
|
||||
{
|
||||
buffer = Convert.FromBase64String(data);
|
||||
decrypt = MachineKey.Unprotect(buffer, purposes);
|
||||
|
||||
if (decrypt == null)
|
||||
{
|
||||
throw new DataProtectorException(Resources.DataProtectionUnprotectException);
|
||||
}
|
||||
|
||||
return Encoding.ASCII.GetString(decrypt);
|
||||
}
|
||||
finally
|
||||
{
|
||||
buffer = null;
|
||||
decrypt = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
namespace Microsoft.Store.PartnerCenter.Bot.Security
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Logic;
|
||||
using Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the ability to verify the authenticated user has the appropriate permissions.
|
||||
|
|
|
@ -1,455 +0,0 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="TokenManagement.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Security
|
||||
{
|
||||
using System;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading.Tasks;
|
||||
using Cache;
|
||||
using Extensions;
|
||||
using IdentityModel.Clients.ActiveDirectory;
|
||||
using Logic;
|
||||
using Models;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the ability to manage access tokens.
|
||||
/// </summary>
|
||||
/// <seealso cref="ITokenManagement" />
|
||||
public class TokenManagement : ITokenManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of the assertion representing the user when performing app + user authentication.
|
||||
/// </summary>
|
||||
private const string AssertionType = "urn:ietf:params:oauth:grant-type:jwt-bearer";
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to core services.
|
||||
/// </summary>
|
||||
private readonly IBotService service;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TokenManagement"/> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core services.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="service"/> is null.
|
||||
/// </exception>
|
||||
public TokenManagement(IBotService service)
|
||||
{
|
||||
service.AssertNotNull(nameof(service));
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Acquires an access token without asking for user credential.
|
||||
/// </summary>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <param name="resource">Identifier of the client requesting the token.</param>
|
||||
/// <param name="objectUserId">Identifier of the user that is requesting the token.</param>
|
||||
/// <returns>An instance of <see cref="AuthenticationToken"/> that represents the access token.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="authority"/> is empty or null.
|
||||
/// or
|
||||
/// <paramref name="resource"/> is empty or null.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="objectUserId"/> is null.
|
||||
/// </exception>
|
||||
public async Task<AuthenticationResult> AcquireTokenSilentAsync(string authority, string resource, UserIdentifier objectUserId)
|
||||
{
|
||||
AuthenticationContext authContext;
|
||||
AuthenticationResult authResult;
|
||||
|
||||
authority.AssertNotEmpty(nameof(authority));
|
||||
resource.AssertNotEmpty(nameof(resource));
|
||||
objectUserId.AssertNotNull(nameof(objectUserId));
|
||||
|
||||
try
|
||||
{
|
||||
authContext = new AuthenticationContext(authority);
|
||||
|
||||
authResult = await authContext.AcquireTokenSilentAsync(
|
||||
resource,
|
||||
service.Configuration.ApplicationId,
|
||||
objectUserId);
|
||||
|
||||
return authResult;
|
||||
}
|
||||
finally
|
||||
{
|
||||
authContext = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an access token from the authority using app only authentication.
|
||||
/// </summary>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <param name="resource">Identifier of the target resource that is the recipient of the requested token.</param>
|
||||
/// <returns>An instance of <see cref="AuthenticationToken"/> that represented the access token.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="authority"/> is empty or null.
|
||||
/// or
|
||||
/// <paramref name="resource"/> is empty or null.
|
||||
/// </exception>
|
||||
public async Task<AuthenticationToken> GetAppOnlyTokenAsync(string authority, string resource)
|
||||
{
|
||||
AuthenticationContext authContext;
|
||||
AuthenticationResult authResult;
|
||||
DistributedTokenCache tokenCache;
|
||||
|
||||
authority.AssertNotEmpty(nameof(authority));
|
||||
resource.AssertNotEmpty(nameof(resource));
|
||||
|
||||
try
|
||||
{
|
||||
if (service.Cache.IsEnabled)
|
||||
{
|
||||
tokenCache = new DistributedTokenCache(service, resource, $"AppOnly::{resource}");
|
||||
authContext = new AuthenticationContext(authority, tokenCache);
|
||||
}
|
||||
else
|
||||
{
|
||||
authContext = new AuthenticationContext(authority);
|
||||
}
|
||||
|
||||
authResult = await authContext.AcquireTokenAsync(
|
||||
resource,
|
||||
new ClientCredential(
|
||||
service.Configuration.ApplicationId,
|
||||
service.Configuration.ApplicationSecret));
|
||||
|
||||
return new AuthenticationToken(authResult.AccessToken, authResult.ExpiresOn);
|
||||
}
|
||||
finally
|
||||
{
|
||||
authContext = null;
|
||||
authResult = null;
|
||||
tokenCache = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an access token from the authority using app only authentication.
|
||||
/// </summary>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <param name="resource">Identifier of the target resource that is the recipient of the requested token.</param>
|
||||
/// <param name="scope">Permissions the requested token will need.</param>
|
||||
/// <returns>A string that represented the access token.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="authority"/> is empty or null.
|
||||
/// or
|
||||
/// <paramref name="resource"/> is empty or null.
|
||||
/// </exception>
|
||||
public async Task<string> GetAppOnlyTokenAsync(string authority, string resource, string scope)
|
||||
{
|
||||
AuthenticationContext authContext;
|
||||
AuthenticationResult authResult;
|
||||
X509Certificate2 certificate;
|
||||
|
||||
authority.AssertNotEmpty(nameof(authority));
|
||||
resource.AssertNotEmpty(nameof(resource));
|
||||
|
||||
try
|
||||
{
|
||||
authContext = new AuthenticationContext(authority);
|
||||
|
||||
certificate = FindCertificateByThumbprint(service.Configuration.VaultApplicationCertThumbprint);
|
||||
|
||||
authResult = await authContext.AcquireTokenAsync(
|
||||
resource,
|
||||
new ClientAssertionCertificate(
|
||||
service.Configuration.VaultApplicationId,
|
||||
certificate));
|
||||
|
||||
return authResult.AccessToken;
|
||||
}
|
||||
finally
|
||||
{
|
||||
authContext = null;
|
||||
authResult = null;
|
||||
certificate = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an access token from the authority using app + user authentication.
|
||||
/// </summary>
|
||||
/// <param name="principal">Security principal for the calling user.</param>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <param name="resource">Identifier of the target resource that is the recipient of the requested token.</param>
|
||||
/// <returns>An instance of <see cref="AuthenticationToken"/> that represented the access token.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="authority"/> is empty or null.
|
||||
/// or
|
||||
/// <paramref name="resource"/> is empty or null.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="principal"/> is null.
|
||||
/// </exception>
|
||||
public async Task<AuthenticationToken> GetAppPlusUserTokenAsync(CustomerPrincipal principal, string authority, string resource)
|
||||
{
|
||||
AuthenticationContext authContext;
|
||||
AuthenticationResult authResult;
|
||||
DistributedTokenCache tokenCache;
|
||||
string key;
|
||||
|
||||
authority.AssertNotEmpty(nameof(authority));
|
||||
principal.AssertNotNull(nameof(principal));
|
||||
resource.AssertNotEmpty(nameof(resource));
|
||||
|
||||
try
|
||||
{
|
||||
key = $"AppPlusUser::{resource}::{principal.ObjectId}";
|
||||
|
||||
if (service.Cache.IsEnabled)
|
||||
{
|
||||
tokenCache = new DistributedTokenCache(service, resource, key);
|
||||
authContext = new AuthenticationContext(authority, tokenCache);
|
||||
}
|
||||
else
|
||||
{
|
||||
authContext = new AuthenticationContext(authority);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
authResult = await authContext.AcquireTokenAsync(
|
||||
resource,
|
||||
new ClientCredential(
|
||||
service.Configuration.ApplicationId,
|
||||
service.Configuration.ApplicationSecret),
|
||||
new UserAssertion(principal.AccessToken, AssertionType));
|
||||
}
|
||||
catch (AdalServiceException ex)
|
||||
{
|
||||
if (ex.ErrorCode.Equals("AADSTS70002", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
await service.Cache.DeleteAsync(CacheDatabaseType.Authentication, key);
|
||||
|
||||
authResult = await authContext.AcquireTokenAsync(
|
||||
resource,
|
||||
new ClientCredential(
|
||||
service.Configuration.ApplicationId,
|
||||
service.Configuration.ApplicationSecret),
|
||||
new UserAssertion(principal.AccessToken, AssertionType));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return new AuthenticationToken(authResult.AccessToken, authResult.ExpiresOn);
|
||||
}
|
||||
finally
|
||||
{
|
||||
authContext = null;
|
||||
authResult = null;
|
||||
tokenCache = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the URL of the authorization endpoint including the query parameters.
|
||||
/// </summary>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <param name="redirectUri">Address to return to upon receiving a response from the authority.</param>
|
||||
/// <param name="resource">Identifier of the target resource that is the recipient of the requested token.</param>
|
||||
/// <param name="extraQueryParameters">Data that will be appended as is to the query string in the HTTP authentication request to the authority.</param>
|
||||
/// <returns>URL of the authorization endpoint including the query parameters.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="authority"/> is empty or null.
|
||||
/// or
|
||||
/// <paramref name="resource"/> is empty or null.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="redirectUri"/> is null.
|
||||
/// </exception>
|
||||
public async Task<string> GetAuthorizationRequestUrlAsync(string authority, Uri redirectUri, string resource, string extraQueryParameters)
|
||||
{
|
||||
AuthenticationContext authContext;
|
||||
Uri authUri;
|
||||
|
||||
authority.AssertNotEmpty(nameof(authority));
|
||||
resource.AssertNotEmpty(nameof(resource));
|
||||
redirectUri.AssertNotNull(nameof(redirectUri));
|
||||
|
||||
try
|
||||
{
|
||||
authContext = new AuthenticationContext(authority);
|
||||
|
||||
authUri = await authContext.GetAuthorizationRequestUrlAsync(
|
||||
resource,
|
||||
service.Configuration.ApplicationId,
|
||||
redirectUri,
|
||||
UserIdentifier.AnyUser,
|
||||
extraQueryParameters);
|
||||
|
||||
return authUri.ToString();
|
||||
}
|
||||
finally
|
||||
{
|
||||
authContext = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance of <see cref="IPartnerCredentials"/> used to access the Partner Center Managed API.
|
||||
/// </summary>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <returns>
|
||||
/// An instance of <see cref="IPartnerCredentials" /> that represents the access token.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="authority"/> is empty or null.
|
||||
/// </exception>
|
||||
/// <remarks>This function will use app only authentication to obtain the credentials.</remarks>
|
||||
public async Task<IPartnerCredentials> GetPartnerCenterAppOnlyCredentialsAsync(string authority)
|
||||
{
|
||||
authority.AssertNotEmpty(nameof(authority));
|
||||
|
||||
// Attempt to obtain the Partner Center token from the cache.
|
||||
IPartnerCredentials credentials =
|
||||
await service.Cache.FetchAsync<PartnerCenterTokenModel>(
|
||||
CacheDatabaseType.Authentication, BotConstants.PartnerCenterAppOnlyKey);
|
||||
|
||||
if (credentials != null && !credentials.IsExpired())
|
||||
{
|
||||
return credentials;
|
||||
}
|
||||
|
||||
// The access token has expired, so a new one must be requested.
|
||||
credentials = await PartnerCredentials.Instance.GenerateByApplicationCredentialsAsync(
|
||||
service.Configuration.PartnerCenterApplicationId,
|
||||
service.Configuration.PartnerCenterApplicationSecret,
|
||||
service.Configuration.PartnerCenterApplicationTenantId);
|
||||
|
||||
await service.Cache.StoreAsync(
|
||||
CacheDatabaseType.Authentication, BotConstants.PartnerCenterAppOnlyKey, credentials);
|
||||
|
||||
return credentials;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance of <see cref="IPartnerCredentials"/> used to access the Partner Center API.
|
||||
/// </summary>
|
||||
/// <param name="principal">Security principal for the calling user.</param>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <returns>
|
||||
/// An instance of <see cref="IPartnerCredentials" /> that represents the access token.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="authority"/> is empty or null.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="principal"/> is null.
|
||||
/// </exception>
|
||||
public async Task<IPartnerCredentials> GetPartnerCenterAppPlusUserCredentialsAsync(CustomerPrincipal principal, string authority)
|
||||
{
|
||||
authority.AssertNotEmpty(nameof(authority));
|
||||
principal.AssertNotNull(nameof(principal));
|
||||
|
||||
string key = $"AppPlusUser::PartnerCenter::{principal.ObjectId}";
|
||||
|
||||
IPartnerCredentials credentials =
|
||||
await service.Cache.FetchAsync<PartnerCenterTokenModel>(
|
||||
CacheDatabaseType.Authentication, key);
|
||||
|
||||
if (credentials != null && !credentials.IsExpired())
|
||||
{
|
||||
return credentials;
|
||||
}
|
||||
|
||||
AuthenticationToken token = await GetAppPlusUserTokenAsync(
|
||||
principal,
|
||||
authority,
|
||||
service.Configuration.PartnerCenterEndpoint);
|
||||
|
||||
credentials = await PartnerCredentials.Instance.GenerateByUserCredentialsAsync(
|
||||
service.Configuration.PartnerCenterApplicationId, token);
|
||||
|
||||
await service.Cache.StoreAsync(
|
||||
CacheDatabaseType.Authentication, key, credentials);
|
||||
|
||||
return credentials;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an access token utilizing an authorization code.
|
||||
/// </summary>
|
||||
/// <param name="authority">Address of the authority to issue the token.</param>
|
||||
/// <param name="code">Authorization code received from the service authorization endpoint.</param>
|
||||
/// <param name="resource">Identifier of the target resource that is the recipient of the requested token.</param>
|
||||
/// <param name="redirectUri">Redirect URI used for obtain the authorization code.</param>
|
||||
/// <returns>An instance of <see cref="AuthenticationResult"/> that represented the access token.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="authority"/> is empty or null.
|
||||
/// or
|
||||
/// <paramref name="code"/> is empty or null.
|
||||
/// or
|
||||
/// <paramref name="resource"/> is empty or null.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="redirectUri"/> is null.
|
||||
/// </exception>
|
||||
public async Task<AuthenticationResult> GetTokenByAuthorizationCodeAsync(string authority, string code, string resource, Uri redirectUri)
|
||||
{
|
||||
AuthenticationContext authContext;
|
||||
|
||||
authority.AssertNotEmpty(nameof(authority));
|
||||
code.AssertNotEmpty(nameof(code));
|
||||
redirectUri.AssertNotNull(nameof(redirectUri));
|
||||
resource.AssertNotEmpty(nameof(resource));
|
||||
|
||||
try
|
||||
{
|
||||
authContext = new AuthenticationContext(authority);
|
||||
|
||||
return await authContext.AcquireTokenByAuthorizationCodeAsync(
|
||||
code,
|
||||
redirectUri,
|
||||
new ClientCredential(
|
||||
service.Configuration.ApplicationId,
|
||||
service.Configuration.ApplicationSecret),
|
||||
resource);
|
||||
}
|
||||
finally
|
||||
{
|
||||
authContext = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Locates a certificate by thumbprint.
|
||||
/// </summary>
|
||||
/// <param name="thumbprint">Thumbprint of the certificate to be located.</param>
|
||||
/// <returns>An instance of <see cref="X509Certificate2"/> that represents the certificate.</returns>
|
||||
private static X509Certificate2 FindCertificateByThumbprint(string thumbprint)
|
||||
{
|
||||
X509Store store = null;
|
||||
X509Certificate2Collection col;
|
||||
|
||||
thumbprint.AssertNotNull(nameof(thumbprint));
|
||||
|
||||
try
|
||||
{
|
||||
store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
|
||||
store.Open(OpenFlags.ReadOnly);
|
||||
|
||||
col = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
|
||||
return col.Count == 0 ? null : col[0];
|
||||
}
|
||||
finally
|
||||
{
|
||||
col = null;
|
||||
store?.Close();
|
||||
store = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,229 +0,0 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="VaultService.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Store.PartnerCenter.Bot.Security
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Azure.KeyVault;
|
||||
using Azure.KeyVault.Models;
|
||||
using Logic;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a secure mechanism for retrieving and store information.
|
||||
/// </summary>
|
||||
/// <seealso cref="IVaultService" />
|
||||
public sealed class VaultService : IVaultService
|
||||
{
|
||||
/// <summary>
|
||||
/// Error code returned when a secret is not found in the vault.
|
||||
/// </summary>
|
||||
private const string NotFoundErrorCode = "SecretNotFound";
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to core services.
|
||||
/// </summary>
|
||||
private readonly IBotService service;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="VaultService"/> class.
|
||||
/// </summary>
|
||||
/// <param name="service">Provides access to core services.</param>
|
||||
public VaultService(IBotService service)
|
||||
{
|
||||
service.AssertNotNull(nameof(service));
|
||||
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the vault service is enabled or not.
|
||||
/// </summary>
|
||||
public bool IsEnabled => !string.IsNullOrEmpty(this.service.Configuration.VaultBaseAddress);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified entity from the vault.
|
||||
/// </summary>
|
||||
/// <param name="identifier">Identifier of the entity to be retrieved.</param>
|
||||
/// <returns>The value retrieved from the vault.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="identifier"/> is empty or null.
|
||||
/// </exception>
|
||||
public string Get(string identifier)
|
||||
{
|
||||
identifier.AssertNotEmpty(nameof(identifier));
|
||||
|
||||
return SynchronousExecute(() => this.GetAsync(identifier));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified entity from the vault.
|
||||
/// </summary>
|
||||
/// <param name="identifier">Identifier of the entity to be retrieved.</param>
|
||||
/// <returns>The value retrieved from the vault.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="identifier"/> is empty or null.
|
||||
/// </exception>
|
||||
public async Task<string> GetAsync(string identifier)
|
||||
{
|
||||
DateTime startTime;
|
||||
Dictionary<string, double> eventMetrics;
|
||||
Dictionary<string, string> eventProperties;
|
||||
SecretBundle bundle;
|
||||
|
||||
identifier.AssertNotEmpty(nameof(identifier));
|
||||
|
||||
try
|
||||
{
|
||||
startTime = DateTime.Now;
|
||||
|
||||
if (!this.IsEnabled)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (IKeyVaultClient client = new KeyVaultClient(this.service.TokenManagement.GetAppOnlyTokenAsync))
|
||||
{
|
||||
try
|
||||
{
|
||||
bundle = await client.GetSecretAsync(this.service.Configuration.VaultBaseAddress, identifier);
|
||||
}
|
||||
catch (KeyVaultErrorException ex)
|
||||
{
|
||||
if (ex.Body.Error.Code.Equals(NotFoundErrorCode, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
bundle = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Track the event measurements for analysis.
|
||||
eventMetrics = new Dictionary<string, double>
|
||||
{
|
||||
{ "ElapsedMilliseconds", DateTime.Now.Subtract(startTime).TotalMilliseconds }
|
||||
};
|
||||
|
||||
// Capture the request for the customer summary for analysis.
|
||||
eventProperties = new Dictionary<string, string>
|
||||
{
|
||||
{ "Identifier", identifier }
|
||||
};
|
||||
|
||||
this.service.Telemetry.TrackEvent("Vault/GetAsync", eventProperties, eventMetrics);
|
||||
|
||||
return bundle?.Value;
|
||||
}
|
||||
finally
|
||||
{
|
||||
bundle = null;
|
||||
eventMetrics = null;
|
||||
eventProperties = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores the specified value in the vault.
|
||||
/// </summary>
|
||||
/// <param name="identifier">Identifier of the entity to be stored.</param>
|
||||
/// <param name="value">The value to stored.</param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="identifier"/> is empty or null.
|
||||
/// or
|
||||
/// <paramref name="value"/> is empty or null.
|
||||
/// </exception>
|
||||
public void Store(string identifier, string value)
|
||||
{
|
||||
identifier.AssertNotEmpty(nameof(identifier));
|
||||
value.AssertNotEmpty(nameof(value));
|
||||
|
||||
this.StoreAsync(identifier, value).RunSynchronously();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores the specified value in the vault.
|
||||
/// </summary>
|
||||
/// <param name="identifier">Identifier of the entity to be stored.</param>
|
||||
/// <param name="value">The value to stored.</param>
|
||||
/// <returns>An instance of <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="identifier"/> is empty or null.
|
||||
/// or
|
||||
/// <paramref name="value"/> is empty or null.
|
||||
/// </exception>
|
||||
public async Task StoreAsync(string identifier, string value)
|
||||
{
|
||||
DateTime startTime;
|
||||
Dictionary<string, double> eventMetrics;
|
||||
Dictionary<string, string> eventProperties;
|
||||
|
||||
identifier.AssertNotEmpty(nameof(identifier));
|
||||
value.AssertNotEmpty(nameof(value));
|
||||
|
||||
try
|
||||
{
|
||||
startTime = DateTime.Now;
|
||||
|
||||
if (!this.IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (IKeyVaultClient client = new KeyVaultClient(this.service.TokenManagement.GetAppOnlyTokenAsync))
|
||||
{
|
||||
await client.SetSecretAsync(
|
||||
this.service.Configuration.VaultBaseAddress, identifier, value);
|
||||
}
|
||||
|
||||
// Track the event measurements for analysis.
|
||||
eventMetrics = new Dictionary<string, double>
|
||||
{
|
||||
{ "ElapsedMilliseconds", DateTime.Now.Subtract(startTime).TotalMilliseconds }
|
||||
};
|
||||
|
||||
// Capture the request for the customer summary for analysis.
|
||||
eventProperties = new Dictionary<string, string>
|
||||
{
|
||||
{ "Identifier", identifier }
|
||||
};
|
||||
|
||||
this.service.Telemetry.TrackEvent("Vault/StoreAsync", eventProperties, eventMetrics);
|
||||
}
|
||||
finally
|
||||
{
|
||||
eventMetrics = null;
|
||||
eventProperties = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes an asynchronous method synchronously
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to be returned.</typeparam>
|
||||
/// <param name="operation">The asynchronous operation to be executed.</param>
|
||||
/// <returns>The result from the operation.</returns>
|
||||
private static T SynchronousExecute<T>(Func<Task<T>> operation)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Task.Run(async () => await operation()).Result;
|
||||
}
|
||||
catch (AggregateException ex)
|
||||
{
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
throw ex.InnerException;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
For more information on how to configure your ASP.NET application, please visit
|
||||
http://go.microsoft.com/fwlink/?LinkId=301879
|
||||
-->
|
||||
<configuration>
|
||||
<configSections>
|
||||
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
|
||||
</configSections>
|
||||
<appSettings>
|
||||
<!-- AAD endpoint used during authentication requests -->
|
||||
<add key="ActiveDirectoryEndpoint" value="https://login.microsoftonline.com" />
|
||||
|
@ -18,34 +17,22 @@
|
|||
<add key="ApplicationId" value="" />
|
||||
<!-- Specify the AAD tenant identifier here -->
|
||||
<add key="ApplicationTenantId" value="" />
|
||||
|
||||
<!-- Specify the identifer for the bot here -->
|
||||
<add key="BotId" value="" />
|
||||
<!-- Specify the Microsoft application identifier for the bot here -->
|
||||
<add key="MicrosoftAppId" value="" />
|
||||
|
||||
<!-- Specify the Application Insights instrumentation key here -->
|
||||
<add key="InstrumentationKey" value="" />
|
||||
|
||||
<!-- Specify the Azure Key Vault endpoint address here -->
|
||||
<add key="KeyVaultEndpoint" value="" />
|
||||
<!-- Specify the LUIS application identifier here -->
|
||||
<add key="LuisAppId" value="" />
|
||||
|
||||
<!-- Specify the Partner Center account identifier here -->
|
||||
<add key="PartnerCenter.AccountId" value="" />
|
||||
<!-- Specify the Partner Center AAD application identifier here -->
|
||||
<add key="PartnerCenterApplicationId" value="" />
|
||||
<!-- Specify the Partner Center AAD tenant identifier here -->
|
||||
<add key="PartnerCenterApplicationTenantId" value="" />
|
||||
|
||||
<add key="PartnerCenter.ApplicationId" value="" />
|
||||
<!-- Specify the QnA knowledge base identifier here -->
|
||||
<add key="QnAKnowledgebaseId" value="" />
|
||||
|
||||
<!-- Specify the thumbprint of the certificate used access Azure Key Vault here -->
|
||||
<add key="VaultApplicationCertThumbprint" value="" />
|
||||
<!-- Specify the Azure Key Vault AAD application identifier here -->
|
||||
<add key="VaultApplicationId" value="" />
|
||||
<!-- Specify the Azure Key Vault AAD application tenant identifier here -->
|
||||
<add key="VaultApplicationTenantId" value="" />
|
||||
<!-- Specify the Azure Key Vault base address here -->
|
||||
<add key="VaultBaseAddress" value="" />
|
||||
</appSettings>
|
||||
<system.web>
|
||||
<customErrors mode="Off" />
|
||||
|
@ -58,68 +45,88 @@
|
|||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
|
||||
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
|
||||
<bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
|
||||
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Autofac" publicKeyToken="17863af14b0044da" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.4.0.0" newVersion="3.5.0.0" />
|
||||
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.1.1.2" newVersion="4.1.1.2" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.IdentityModel.Clients.ActiveDirectory" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.13.9.1126" newVersion="3.13.9.1126" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.19.2.6005" newVersion="3.19.2.6005" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.AI.Agent.Intercept" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.0.7.0" newVersion="2.0.7.0" />
|
||||
<assemblyIdentity name="Microsoft.IdentityModel.Clients.ActiveDirectory.Platform" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.19.2.6005" newVersion="3.19.2.6005" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-11.0.0.0" newVersion="11.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.2.4.0" newVersion="5.2.4.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Bot.Builder" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.8.0.0" newVersion="3.8.0.0" />
|
||||
<assemblyIdentity name="Autofac" publicKeyToken="17863af14b0044da" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.6.2.0" newVersion="4.6.2.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Bot.Connector" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.8.0.0" newVersion="3.8.0.0" />
|
||||
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.2.4.0" newVersion="5.2.4.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.1" newVersion="4.0.2.1" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.IdentityModel.Tokens.Jwt" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.40306.1554" newVersion="4.0.40306.1554" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.2.1.0" newVersion="5.2.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.IdentityModel.Protocol.Extensions" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-1.0.40306.1554" newVersion="1.0.40306.1554" />
|
||||
<assemblyIdentity name="Microsoft.IdentityModel.Tokens" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.2.1.0" newVersion="5.2.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.1.1.1" newVersion="4.1.1.1" />
|
||||
<assemblyIdentity name="Microsoft.IdentityModel.Protocols" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.2.1.0" newVersion="5.2.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.IdentityModel.Protocols.OpenIdConnect" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.2.1.0" newVersion="5.2.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Bot.Builder" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.14.1.1" newVersion="3.14.1.1" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Bot.Connector" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.14.1.1" newVersion="3.14.1.1" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Bot.Builder.Autofac" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.8.0.0" newVersion="3.8.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.14.1.1" newVersion="3.14.1.1" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Azure.KeyVault.Core" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Azure.Documents.Client" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-1.21.0.0" newVersion="1.21.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.1.0.0" newVersion="9.1.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Bot.Builder.History" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.14.1.1" newVersion="3.14.1.1" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.5.1.0" newVersion="2.5.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.AspNet.TelemetryCorrelation" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-1.0.1.0" newVersion="1.0.1.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
|
@ -134,7 +141,10 @@
|
|||
<modules>
|
||||
<remove name="ApplicationInsightsWebTracking" />
|
||||
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler" />
|
||||
<remove name="TelemetryCorrelationHttpModule" />
|
||||
<add name="TelemetryCorrelationHttpModule" type="Microsoft.AspNet.TelemetryCorrelation.TelemetryCorrelationHttpModule, Microsoft.AspNet.TelemetryCorrelation" preCondition="integratedMode,managedHandler" />
|
||||
</modules>
|
||||
|
||||
<handlers>
|
||||
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
|
||||
<remove name="OPTIONSVerbHandler" />
|
||||
|
@ -142,4 +152,14 @@
|
|||
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
|
||||
</handlers>
|
||||
</system.webServer>
|
||||
<entityFramework>
|
||||
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
|
||||
<parameters>
|
||||
<parameter value="mssqllocaldb" />
|
||||
</parameters>
|
||||
</defaultConnectionFactory>
|
||||
<providers>
|
||||
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
|
||||
</providers>
|
||||
</entityFramework>
|
||||
</configuration>
|
|
@ -1,36 +1,51 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Autofac" version="3.5.2" targetFramework="net461" />
|
||||
<package id="Autofac.WebApi2" version="4.0.1" targetFramework="net461" />
|
||||
<package id="Autofac" version="4.6.2" targetFramework="net461" />
|
||||
<package id="Autofac.WebApi2" version="4.1.0" targetFramework="net461" />
|
||||
<package id="Chronic.Signed" version="0.3.2" targetFramework="net461" />
|
||||
<package id="Microsoft.ApplicationInsights" version="2.3.0" targetFramework="net461" />
|
||||
<package id="Microsoft.ApplicationInsights.Agent.Intercept" version="2.0.7" targetFramework="net461" />
|
||||
<package id="Microsoft.ApplicationInsights.DependencyCollector" version="2.3.0" targetFramework="net461" />
|
||||
<package id="Microsoft.ApplicationInsights.PerfCounterCollector" version="2.3.0" targetFramework="net461" />
|
||||
<package id="Microsoft.ApplicationInsights.Web" version="2.3.0" targetFramework="net461" />
|
||||
<package id="Microsoft.ApplicationInsights.WindowsServer" version="2.3.0" targetFramework="net461" />
|
||||
<package id="Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel" version="2.3.0" targetFramework="net461" />
|
||||
<package id="Microsoft.AspNet.WebApi" version="5.2.3" targetFramework="net461" />
|
||||
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net461" />
|
||||
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net461" />
|
||||
<package id="Microsoft.AspNet.WebApi.WebHost" version="5.2.3" targetFramework="net461" />
|
||||
<package id="Microsoft.Azure.KeyVault" version="2.0.6" targetFramework="net461" />
|
||||
<package id="Microsoft.Azure.KeyVault.WebKey" version="2.0.5" targetFramework="net461" />
|
||||
<package id="Microsoft.Bot.Builder" version="3.8.0" targetFramework="net461" />
|
||||
<package id="Microsoft.Bot.Builder.CognitiveServices" version="1.1.0" targetFramework="net461" />
|
||||
<package id="Microsoft.Graph" version="1.3.0" targetFramework="net461" />
|
||||
<package id="Microsoft.Graph.Core" version="1.4.0" targetFramework="net461" />
|
||||
<package id="Microsoft.IdentityModel.Clients.ActiveDirectory" version="3.13.9" targetFramework="net461" />
|
||||
<package id="EntityFramework" version="6.2.0" targetFramework="net461" />
|
||||
<package id="Microsoft.ApplicationInsights" version="2.5.1" targetFramework="net461" />
|
||||
<package id="Microsoft.ApplicationInsights.Agent.Intercept" version="2.4.0" targetFramework="net461" />
|
||||
<package id="Microsoft.ApplicationInsights.DependencyCollector" version="2.5.1" targetFramework="net461" />
|
||||
<package id="Microsoft.ApplicationInsights.PerfCounterCollector" version="2.5.1" targetFramework="net461" />
|
||||
<package id="Microsoft.ApplicationInsights.SnapshotCollector" version="1.2.1" targetFramework="net461" />
|
||||
<package id="Microsoft.ApplicationInsights.Web" version="2.5.1" targetFramework="net461" />
|
||||
<package id="Microsoft.ApplicationInsights.WindowsServer" version="2.5.1" targetFramework="net461" />
|
||||
<package id="Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel" version="2.5.1" targetFramework="net461" />
|
||||
<package id="Microsoft.AspNet.TelemetryCorrelation" version="1.0.1" targetFramework="net461" />
|
||||
<package id="Microsoft.AspNet.WebApi" version="5.2.4" targetFramework="net461" />
|
||||
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.4" targetFramework="net461" />
|
||||
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.4" targetFramework="net461" />
|
||||
<package id="Microsoft.AspNet.WebApi.WebHost" version="5.2.4" targetFramework="net461" />
|
||||
<package id="Microsoft.Azure.DocumentDB" version="1.21.1" targetFramework="net461" />
|
||||
<package id="Microsoft.Azure.KeyVault" version="2.3.2" targetFramework="net461" />
|
||||
<package id="Microsoft.Azure.KeyVault.Core" version="2.0.4" targetFramework="net461" />
|
||||
<package id="Microsoft.Azure.KeyVault.WebKey" version="2.0.7" targetFramework="net461" />
|
||||
<package id="Microsoft.Azure.Services.AppAuthentication" version="1.1.0-preview" targetFramework="net461" />
|
||||
<package id="Microsoft.Bot.Builder" version="3.14.1.1" targetFramework="net461" />
|
||||
<package id="Microsoft.Bot.Builder.Azure" version="3.2.5" targetFramework="net461" />
|
||||
<package id="Microsoft.Bot.Builder.CognitiveServices" version="1.1.2" targetFramework="net461" />
|
||||
<package id="Microsoft.Bot.Builder.History" version="3.14.1.1" targetFramework="net461" />
|
||||
<package id="Microsoft.Bot.Connector" version="3.14.1.1" targetFramework="net461" />
|
||||
<package id="Microsoft.Graph" version="1.8.1" targetFramework="net461" />
|
||||
<package id="Microsoft.Graph.Core" version="1.8.1" targetFramework="net461" />
|
||||
<package id="Microsoft.IdentityModel.Clients.ActiveDirectory" version="3.19.2" targetFramework="net461" />
|
||||
<package id="Microsoft.IdentityModel.Logging" version="5.2.1" targetFramework="net461" />
|
||||
<package id="Microsoft.IdentityModel.Protocol.Extensions" version="1.0.4.403061554" targetFramework="net461" />
|
||||
<package id="Microsoft.Rest.ClientRuntime" version="2.3.7" targetFramework="net461" />
|
||||
<package id="Microsoft.Rest.ClientRuntime.Azure" version="3.3.6" targetFramework="net461" />
|
||||
<package id="Microsoft.Store.PartnerCenter" version="1.4.0" targetFramework="net461" />
|
||||
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net461" />
|
||||
<package id="StackExchange.Redis.StrongName" version="1.2.3" targetFramework="net461" />
|
||||
<package id="System.IdentityModel.Tokens.Jwt" version="4.0.4.403061554" targetFramework="net461" />
|
||||
<package id="System.Net.Http" version="4.3.2" targetFramework="net461" />
|
||||
<package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net461" />
|
||||
<package id="Microsoft.IdentityModel.Protocols" version="5.2.1" targetFramework="net461" />
|
||||
<package id="Microsoft.IdentityModel.Protocols.OpenIdConnect" version="5.2.1" targetFramework="net461" />
|
||||
<package id="Microsoft.IdentityModel.Tokens" version="5.2.1" targetFramework="net461" />
|
||||
<package id="Microsoft.Rest.ClientRuntime" version="2.3.11" targetFramework="net461" />
|
||||
<package id="Microsoft.Rest.ClientRuntime.Azure" version="3.3.12" targetFramework="net461" />
|
||||
<package id="Microsoft.Store.PartnerCenter" version="1.7.1" targetFramework="net461" />
|
||||
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net461" />
|
||||
<package id="StackExchange.Redis.StrongName" version="1.2.6" targetFramework="net461" />
|
||||
<package id="System.Diagnostics.DiagnosticSource" version="4.4.1" targetFramework="net461" />
|
||||
<package id="System.IdentityModel.Tokens.Jwt" version="5.2.1" targetFramework="net461" />
|
||||
<package id="System.Net.Http" version="4.3.3" targetFramework="net461" />
|
||||
<package id="System.Security.Cryptography.Algorithms" version="4.3.1" targetFramework="net461" />
|
||||
<package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Security.Cryptography.X509Certificates" version="4.3.2" targetFramework="net461" />
|
||||
<package id="WindowsAzure.Storage" version="9.1.0" targetFramework="net461" />
|
||||
</packages>
|
Загрузка…
Ссылка в новой задаче