Nuget package update and refactored the code to support the latest version of the bot builder

This commit is contained in:
Isaiah Williams 2018-04-04 04:43:04 +00:00
Родитель daf9b4320e
Коммит 3ff892ac83
68 изменённых файлов: 2176 добавлений и 2453 удалений

13
CHANGELOG.md Normal file
Просмотреть файл

@ -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*
* ...

1
CODE_OF_CONDUCT.md Normal file
Просмотреть файл

@ -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.
```
Summary of the changes (Less than 80 chars)
- Detail 1
- Detail 2
## <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.
#bugnumber (in this specific format)
```
* **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
```
That's it! Thank you for your contribution!

5
ISSUE_TEMPLATE.md Normal file
Просмотреть файл

@ -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

Просмотреть файл

@ -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)

Просмотреть файл

@ -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>