diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.md b/.github/ISSUE_TEMPLATE/BUG_REPORT.md new file mode 100644 index 0000000..53976f4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.md @@ -0,0 +1,28 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- + +# Steps to reproduce + +> What steps can reproduce the defect? +> Please share the setup, sample project, version of Java etc. + +## Expected behavior + +> Share the expected output + +## Actual behavior + +> What is the behavior observed? + +## Diagnostic logs + +> Please share test platform diagnostics logs. +> The logs may contain test assembly paths, kindly review and mask those before sharing. + +## Environment + +> Please share additional details about your environment. +> Version \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md new file mode 100644 index 0000000..b9bec0c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md @@ -0,0 +1,19 @@ +--- +name: Feature request +about: Suggest an idea for this project + +--- + +# Feature Request + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I am always frustrated when [...] + +**Describe the solution you would like** +A clear and concise description of what you want to happen. + +**Describe alternatives you have considered** +A clear and concise description of any alternative solutions or features you have considered. + +**Additional context** +Add any other context or screenshots about the feature request here. \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..ddc7f75 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,7 @@ +# Description + +Please add a meaningful description for this change. Ensure the PR has required unit tests. + +## Related issue + +Kindly link any related issues (e.g. Fixes #xyz). \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..8be1d1f --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Code of Conduct + +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. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..d922ad2 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,80 @@ +# Contributing to Partner Center .NET Samples + +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. View the [Contributor License Agreement](https://cla.microsoft.com) for more details. + +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. + +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. + +- [Code of Conduct](#code-of-conduct) +- [Issues and Bugs](#finding-issues) +- [Feature Requests](#requesting-features) +- [Submission Guidelines](#submission-guidelines) + +## Code of Conduct + +Help us keep this project open and inclusive. Please read and follow our [Code of Conduct](https://opensource.microsoft.com/codeofconduct/). + +## Finding Issues + +If you find a bug in the source code or a mistake in the documentation, you can help us by +[submitting an issue](#submitting-an-issue) to the GitHub Repository. Even better, you can +[submit a Pull Request](#submitting-a-pull-request) with a fix. + +## Requesting Features + +You can *request* a new feature by [submitting an issue](#submitting-an-issue) to the GitHub +Repository. If you would like to *implement* a new feature, please submit an issue with +a proposal for your work first, to be sure that we can use it. + +**Small Features** can be crafted and directly [submitted as a Pull Request](#submitting-a-pull-request). + +## Submission Guidelines + +### 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 add 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 an 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-DotNet-Samples/issues/new). + +### Submitting a Pull Request + +Before you submit your Pull Request (PR) consider the following guidelines: + +- [Search the repository](https://github.com/Microsoft/Partner-Center-DotNet-Samples/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 is it! Thank you for your contribution! \ No newline at end of file diff --git a/README.md b/README.md index 72f1506..27e0d05 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ +# Partner Center .NET Samples -# Contributing +This repository contains samples for the Partner Center .NET SDK. -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. +## Samples List -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. +| Sample Name | Description | +|-------------|-------------| +| [SDK Samples](sdk/README.md) | Console application that demonstrates each scenario the Partner Center .NET SDK is capable of performing.| +| [Secure App Model](secure-app-model/README.md) | A set of projects that demonstrate how a Control Panel Vendor (CPV) and a Cloud Solution Provider (CSP) should implement the Secure App Model. | -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. +## Reporting Security Issues + +Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) at [secure@microsoft.com](mailto:secure@microsoft.com). You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the [MSRC PGP](https://technet.microsoft.com/en-us/security/dn606155) key, can be found in the [Security TechCenter](https://technet.microsoft.com/en-us/security/default). \ No newline at end of file diff --git a/Source/Partner Center SDK Samples/Settings.StyleCop b/Source/Partner Center SDK Samples/Settings.StyleCop deleted file mode 100644 index 18ba2fd..0000000 --- a/Source/Partner Center SDK Samples/Settings.StyleCop +++ /dev/null @@ -1,15 +0,0 @@ - - - - - False - - \.g\.cs$ - \.generated\.cs$ - \.g\.i\.cs$ - TemporaryGeneratedFile_.*\.cs$ - - - - - \ No newline at end of file diff --git a/nuget.config b/nuget.config deleted file mode 100644 index d596277..0000000 --- a/nuget.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/sdk/README.md b/sdk/README.md new file mode 100644 index 0000000..8959d37 --- /dev/null +++ b/sdk/README.md @@ -0,0 +1,87 @@ +# SDK Samples + +## Overview + +Console application that demonstrates each scenario the Partner Center .NET SDK is capable of performing. + +## Getting Started + +Before you build the application, update the values in the *App.config* file to reflect the Azure AD authentication information you created in [Partner Center authentication](https://docs.microsoft.com/partner-center/develop/partner-center-authentication). Specifically, you should use your integration sandbox account settings during early development or for testing in production. + +Under **ScenarioSettings** in the *App.config* file, you can set parameters that will be automatically passed into the scenarios that you run. + +To modify the list of scenarios that are run, comment out lines in **MainScenarios** or in an individual **Get Scenarios** method found in the *Program.cs* file. + +## Prerequisites + +With previous versions of this sample there were configurations for user credentials. Those configurations have been removed, and now you will be prompted to enter the credentials upon execution. This change was made due to the upcoming requirement to use multi-factor authentication (MFA) when access Partner Center and the Partner Center API using app + user authentication. Please note that the approach used by this application to obtain user credentials is just one way this operation can be performed. It is recommended that you review the [Secure App Model](../secure-app-model/README.md) sample for more information. + +### Azure Active Directory + +Perform the following task to correctly configure the Azure AD application for use with this sample project. + +1. Sign in to the [Partner Center](https://partner.microsoft.com/cloud-solution-provider/csp-partner) using credentials that have *Admin Agent* and *Global Admin* privileges +2. Click on _Dashboard_ at the top of the page, then click on the cog icon in the upper right, and then click the _Partner settings_. +3. Add a new native application if one does not exist already. +4. Sign in to the [Azure management portal](https://portal.azure.com) using the same credentials from step 1. +5. Click on the _Azure Active Directory_ icon in the toolbar. +6. Click _App registrations_ -> Select _All apps_ from the drop down -> Click on the application created in step 3. +7. Click _Settings_ and then click _Redirect URIs_ +8. Add **urn:ietf:wg:oauth:2.0:oob** as one of the available Redirect URIs. Be sure to click the _Save_ button to ensure the changes are saved. + +## What to Change + +### PartnerServiceSettings + +If you are connecting to one of the sovereign clouds you will need to modify the values below. These values should not be modified if you are connecting to the commercial cloud. + +- PartnerServiceApiEndpoint +- AuthenticationAuthorityEndpoint +- GraphEndpoint + +All thees settings are necessary for the sample API calls to properly function. + +### AppAuthentication + +The following settings must be modified, so that each scenario functions as excepted + +- **ApplicationId**: Your Azure Active Directory application identifier, used for authentication +- **ApplicationSecret**: The application secret, associated with the specified Azure AD application +- **Domain**: The Azure Active Directory domain where the Azure AD application was created + +### UserAuthentication + +The following settings must be updated, so that each scenario functions as expected + +- **ApplicationId**: Your Azure Active Directory application identifier, used for authentication + +Do not change the following configurations + +- RedirectUrl +- ResourceUrl + +### ScenarioSettings + +Do not change the following setting + +- **CustomerDomainSuffix**: The domain suffix used when creating a new customer + +The following settings can be updated, if left blank information will need to be inputted when running a scenario where it is necessary + +- **CustomerIdToDelete**: The ID of the customer used for deletion. +- **DefaultCustomerId**: The customer ID to use in customer-related scenarios. +- **DefaultInvoiceId**: The invoice ID to use in invoice scenarios. +- **PartnerMpnId**: The partner MPN ID to use in indirect partner scenarios. +- **DefaultServiceRequestId**: The service request ID to use in service request scenarios. +- **DefaultSupportTopicId**: The support topic ID to use in service request scenarios. +- **DefaultOfferId**: The offer ID to use in offer scenarios. +- **DefaultOrderId**: The order ID to use in order scenarios. +- **DefaultSubscriptionId**: The subscription ID to use in subscription scenarios. + +Each of the following of settings specify the amount of entries per page when retrieving paged content. These settings can be modified if desired + +- CustomerPageSize +- DefaultOfferPageSize +- InvoicePageSize +- ServiceRequestPageSize +- SubscriptionPageSize \ No newline at end of file diff --git a/Partner Center SDK Samples.sln b/sdk/SdkSamples.sln similarity index 61% rename from Partner Center SDK Samples.sln rename to sdk/SdkSamples.sln index af8a941..cdb3831 100644 --- a/Partner Center SDK Samples.sln +++ b/sdk/SdkSamples.sln @@ -1,14 +1,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.28010.2050 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Partner Center SDK Samples", "Source\Partner Center SDK Samples\Partner Center SDK Samples.csproj", "{AC93E5CC-549C-4F13-9675-A86860918C2B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9445B986-021F-44FE-BA08-92C43301F8AF}" - ProjectSection(SolutionItems) = preProject - nuget.config = nuget.config - EndProjectSection +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SdkSamples", "SdkSamples\SdkSamples.csproj", "{AC93E5CC-549C-4F13-9675-A86860918C2B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -24,4 +19,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8E53D670-F5E9-41B1-B567-8819B3AD3DCF} + EndGlobalSection EndGlobal diff --git a/Source/Partner Center SDK Samples/AggregatePartnerScenario.cs b/sdk/SdkSamples/AggregatePartnerScenario.cs similarity index 100% rename from Source/Partner Center SDK Samples/AggregatePartnerScenario.cs rename to sdk/SdkSamples/AggregatePartnerScenario.cs diff --git a/Source/Partner Center SDK Samples/Agreements/CreateCustomerAgreement.cs b/sdk/SdkSamples/Agreements/CreateCustomerAgreement.cs similarity index 100% rename from Source/Partner Center SDK Samples/Agreements/CreateCustomerAgreement.cs rename to sdk/SdkSamples/Agreements/CreateCustomerAgreement.cs diff --git a/Source/Partner Center SDK Samples/Agreements/GetAgreementDetails.cs b/sdk/SdkSamples/Agreements/GetAgreementDetails.cs similarity index 100% rename from Source/Partner Center SDK Samples/Agreements/GetAgreementDetails.cs rename to sdk/SdkSamples/Agreements/GetAgreementDetails.cs diff --git a/Source/Partner Center SDK Samples/Agreements/GetAllCustomersAgreements.cs b/sdk/SdkSamples/Agreements/GetAllCustomersAgreements.cs similarity index 100% rename from Source/Partner Center SDK Samples/Agreements/GetAllCustomersAgreements.cs rename to sdk/SdkSamples/Agreements/GetAllCustomersAgreements.cs diff --git a/Source/Partner Center SDK Samples/Agreements/GetCustomerAgreements.cs b/sdk/SdkSamples/Agreements/GetCustomerAgreements.cs similarity index 100% rename from Source/Partner Center SDK Samples/Agreements/GetCustomerAgreements.cs rename to sdk/SdkSamples/Agreements/GetCustomerAgreements.cs diff --git a/Source/Partner Center SDK Samples/Agreements/ImportCustomersAgreement.cs b/sdk/SdkSamples/Agreements/ImportCustomersAgreement.cs similarity index 100% rename from Source/Partner Center SDK Samples/Agreements/ImportCustomersAgreement.cs rename to sdk/SdkSamples/Agreements/ImportCustomersAgreement.cs diff --git a/Source/Partner Center SDK Samples/Analytics/GetCustomerLicensesDeploymentAnalytics.cs b/sdk/SdkSamples/Analytics/GetCustomerLicensesDeploymentAnalytics.cs similarity index 100% rename from Source/Partner Center SDK Samples/Analytics/GetCustomerLicensesDeploymentAnalytics.cs rename to sdk/SdkSamples/Analytics/GetCustomerLicensesDeploymentAnalytics.cs diff --git a/Source/Partner Center SDK Samples/Analytics/GetCustomerLicensesUsageAnalytics.cs b/sdk/SdkSamples/Analytics/GetCustomerLicensesUsageAnalytics.cs similarity index 100% rename from Source/Partner Center SDK Samples/Analytics/GetCustomerLicensesUsageAnalytics.cs rename to sdk/SdkSamples/Analytics/GetCustomerLicensesUsageAnalytics.cs diff --git a/Source/Partner Center SDK Samples/Analytics/GetPartnerLicensesDeploymentAnalytics.cs b/sdk/SdkSamples/Analytics/GetPartnerLicensesDeploymentAnalytics.cs similarity index 100% rename from Source/Partner Center SDK Samples/Analytics/GetPartnerLicensesDeploymentAnalytics.cs rename to sdk/SdkSamples/Analytics/GetPartnerLicensesDeploymentAnalytics.cs diff --git a/Source/Partner Center SDK Samples/Analytics/GetPartnerLicensesUsageAnalytics.cs b/sdk/SdkSamples/Analytics/GetPartnerLicensesUsageAnalytics.cs similarity index 100% rename from Source/Partner Center SDK Samples/Analytics/GetPartnerLicensesUsageAnalytics.cs rename to sdk/SdkSamples/Analytics/GetPartnerLicensesUsageAnalytics.cs diff --git a/Source/Partner Center SDK Samples/App.config b/sdk/SdkSamples/App.config similarity index 96% rename from Source/Partner Center SDK Samples/App.config rename to sdk/SdkSamples/App.config index c573633..d44f55e 100644 --- a/Source/Partner Center SDK Samples/App.config +++ b/sdk/SdkSamples/App.config @@ -8,7 +8,7 @@ - + @@ -26,9 +26,6 @@ - - - @@ -119,7 +116,7 @@ - + @@ -129,8 +126,8 @@ - + - \ No newline at end of file + diff --git a/Source/Partner Center SDK Samples/Auditing/QueryAuditRecords.cs b/sdk/SdkSamples/Auditing/QueryAuditRecords.cs similarity index 100% rename from Source/Partner Center SDK Samples/Auditing/QueryAuditRecords.cs rename to sdk/SdkSamples/Auditing/QueryAuditRecords.cs diff --git a/Source/Partner Center SDK Samples/Auditing/SearchAuditRecords.cs b/sdk/SdkSamples/Auditing/SearchAuditRecords.cs similarity index 100% rename from Source/Partner Center SDK Samples/Auditing/SearchAuditRecords.cs rename to sdk/SdkSamples/Auditing/SearchAuditRecords.cs diff --git a/Source/Partner Center SDK Samples/Auditing/SearchAuditRecordsByCustomerId.cs b/sdk/SdkSamples/Auditing/SearchAuditRecordsByCustomerId.cs similarity index 100% rename from Source/Partner Center SDK Samples/Auditing/SearchAuditRecordsByCustomerId.cs rename to sdk/SdkSamples/Auditing/SearchAuditRecordsByCustomerId.cs diff --git a/Source/Partner Center SDK Samples/Auditing/SearchAuditRecordsByResourceType.cs b/sdk/SdkSamples/Auditing/SearchAuditRecordsByResourceType.cs similarity index 100% rename from Source/Partner Center SDK Samples/Auditing/SearchAuditRecordsByResourceType.cs rename to sdk/SdkSamples/Auditing/SearchAuditRecordsByResourceType.cs diff --git a/Source/Partner Center SDK Samples/BasePartnerScenario.cs b/sdk/SdkSamples/BasePartnerScenario.cs similarity index 100% rename from Source/Partner Center SDK Samples/BasePartnerScenario.cs rename to sdk/SdkSamples/BasePartnerScenario.cs diff --git a/Source/Partner Center SDK Samples/Carts/CheckoutCart.cs b/sdk/SdkSamples/Carts/CheckoutCart.cs similarity index 100% rename from Source/Partner Center SDK Samples/Carts/CheckoutCart.cs rename to sdk/SdkSamples/Carts/CheckoutCart.cs diff --git a/Source/Partner Center SDK Samples/Carts/CreateCart.cs b/sdk/SdkSamples/Carts/CreateCart.cs similarity index 100% rename from Source/Partner Center SDK Samples/Carts/CreateCart.cs rename to sdk/SdkSamples/Carts/CreateCart.cs diff --git a/Source/Partner Center SDK Samples/Carts/CreateCartAddonWithExistingSubscription.cs b/sdk/SdkSamples/Carts/CreateCartAddonWithExistingSubscription.cs similarity index 100% rename from Source/Partner Center SDK Samples/Carts/CreateCartAddonWithExistingSubscription.cs rename to sdk/SdkSamples/Carts/CreateCartAddonWithExistingSubscription.cs diff --git a/Source/Partner Center SDK Samples/Carts/CreateCartWithAddons.cs b/sdk/SdkSamples/Carts/CreateCartWithAddons.cs similarity index 100% rename from Source/Partner Center SDK Samples/Carts/CreateCartWithAddons.cs rename to sdk/SdkSamples/Carts/CreateCartWithAddons.cs diff --git a/Source/Partner Center SDK Samples/Carts/UpdateCart.cs b/sdk/SdkSamples/Carts/UpdateCart.cs similarity index 100% rename from Source/Partner Center SDK Samples/Carts/UpdateCart.cs rename to sdk/SdkSamples/Carts/UpdateCart.cs diff --git a/Source/Partner Center SDK Samples/Configuration/ApplicationAuthenticationSection.cs b/sdk/SdkSamples/Configuration/ApplicationAuthenticationSection.cs similarity index 100% rename from Source/Partner Center SDK Samples/Configuration/ApplicationAuthenticationSection.cs rename to sdk/SdkSamples/Configuration/ApplicationAuthenticationSection.cs diff --git a/Source/Partner Center SDK Samples/Configuration/ConfigurationManager.cs b/sdk/SdkSamples/Configuration/ConfigurationManager.cs similarity index 100% rename from Source/Partner Center SDK Samples/Configuration/ConfigurationManager.cs rename to sdk/SdkSamples/Configuration/ConfigurationManager.cs diff --git a/Source/Partner Center SDK Samples/Configuration/PartnerServiceSettingsSection.cs b/sdk/SdkSamples/Configuration/PartnerServiceSettingsSection.cs similarity index 100% rename from Source/Partner Center SDK Samples/Configuration/PartnerServiceSettingsSection.cs rename to sdk/SdkSamples/Configuration/PartnerServiceSettingsSection.cs diff --git a/Source/Partner Center SDK Samples/Configuration/ScenarioSettingsSection.cs b/sdk/SdkSamples/Configuration/ScenarioSettingsSection.cs similarity index 100% rename from Source/Partner Center SDK Samples/Configuration/ScenarioSettingsSection.cs rename to sdk/SdkSamples/Configuration/ScenarioSettingsSection.cs diff --git a/Source/Partner Center SDK Samples/Configuration/Section.cs b/sdk/SdkSamples/Configuration/Section.cs similarity index 100% rename from Source/Partner Center SDK Samples/Configuration/Section.cs rename to sdk/SdkSamples/Configuration/Section.cs diff --git a/Source/Partner Center SDK Samples/Configuration/UserAuthenticationSection.cs b/sdk/SdkSamples/Configuration/UserAuthenticationSection.cs similarity index 78% rename from Source/Partner Center SDK Samples/Configuration/UserAuthenticationSection.cs rename to sdk/SdkSamples/Configuration/UserAuthenticationSection.cs index c29a076..c34e1d9 100644 --- a/Source/Partner Center SDK Samples/Configuration/UserAuthenticationSection.cs +++ b/sdk/SdkSamples/Configuration/UserAuthenticationSection.cs @@ -53,27 +53,5 @@ namespace Microsoft.Store.PartnerCenter.Samples.Configuration return new Uri(this.ConfigurationSection["RedirectUrl"]); } } - - /// - /// Gets AAD user name. - /// - public string UserName - { - get - { - return this.ConfigurationSection["UserName"]; - } - } - - /// - /// Gets AAD password. - /// - public string Password - { - get - { - return this.ConfigurationSection["Password"]; - } - } } } diff --git a/Source/Partner Center SDK Samples/Context/ScenarioContext.cs b/sdk/SdkSamples/Context/ScenarioContext.cs similarity index 75% rename from Source/Partner Center SDK Samples/Context/ScenarioContext.cs rename to sdk/SdkSamples/Context/ScenarioContext.cs index 9a5401c..4a26a96 100644 --- a/Source/Partner Center SDK Samples/Context/ScenarioContext.cs +++ b/sdk/SdkSamples/Context/ScenarioContext.cs @@ -9,21 +9,25 @@ namespace Microsoft.Store.PartnerCenter.Samples.Context using System; using System.Threading.Tasks; using Configuration; + using Extensions; using Helpers; using IdentityModel.Clients.ActiveDirectory; - using Store.PartnerCenter; - using Store.PartnerCenter.Extensions; /// /// Scenario context implementation class. /// public class ScenarioContext : IScenarioContext { + /// + /// The redirect URI used when performing app + user authentication. + /// + private static readonly Uri redirectUri = new Uri("urn:ietf:wg:oauth:2.0:oob"); + /// /// A lazy reference to an user based partner operations. /// private IAggregatePartner userPartnerOperations = null; - + /// /// A lazy reference to an application based partner operations. /// @@ -50,11 +54,11 @@ namespace Microsoft.Store.PartnerCenter.Samples.Context this.ConsoleHelper.StartProgress("Authenticating application"); IPartnerCredentials appCredentials = PartnerCredentials.Instance.GenerateByApplicationCredentials( - this.Configuration.ApplicationAuthentication.ApplicationId, - this.Configuration.ApplicationAuthentication.ApplicationSecret, - this.Configuration.ApplicationAuthentication.Domain, - this.Configuration.PartnerService.AuthenticationAuthorityEndpoint.OriginalString, - this.Configuration.PartnerService.GraphEndpoint.OriginalString); + this.Configuration.ApplicationAuthentication.ApplicationId, + this.Configuration.ApplicationAuthentication.ApplicationSecret, + this.Configuration.ApplicationAuthentication.Domain, + this.Configuration.PartnerService.AuthenticationAuthorityEndpoint.OriginalString, + this.Configuration.PartnerService.GraphEndpoint.OriginalString); this.ConsoleHelper.StopProgress(); this.ConsoleHelper.Success("Authenticated!"); @@ -69,24 +73,12 @@ namespace Microsoft.Store.PartnerCenter.Samples.Context /// /// Gets a configuration instance. /// - public ConfigurationManager Configuration - { - get - { - return ConfigurationManager.Instance; - } - } + public ConfigurationManager Configuration => ConfigurationManager.Instance; /// /// Gets a console helper instance. /// - public ConsoleHelper ConsoleHelper - { - get - { - return ConsoleHelper.Instance; - } - } + public ConsoleHelper ConsoleHelper => ConsoleHelper.Instance; /// /// Gets a partner operations instance which is user based authenticated. @@ -98,7 +90,8 @@ namespace Microsoft.Store.PartnerCenter.Samples.Context if (this.userPartnerOperations == null) { this.ConsoleHelper.StartProgress("Authenticating user"); - var aadAuthenticationResult = this.LoginUserToAad(); + + AuthenticationResult aadAuthenticationResult = this.LoginUserToAad(); // Authenticate by user context with the partner service IPartnerCredentials userCredentials = PartnerCredentials.Instance.GenerateByUserCredentials( @@ -110,7 +103,7 @@ namespace Microsoft.Store.PartnerCenter.Samples.Context { // token has expired, re-Login to Azure Active Directory this.ConsoleHelper.StartProgress("Token expired. Re-authenticating user"); - var aadToken = this.LoginUserToAad(); + AuthenticationResult aadToken = this.LoginUserToAad(); this.ConsoleHelper.StopProgress(); // give the partner SDK the new add token information @@ -133,21 +126,19 @@ namespace Microsoft.Store.PartnerCenter.Samples.Context /// The user authentication result. private AuthenticationResult LoginUserToAad() { - var addAuthority = new UriBuilder(this.Configuration.PartnerService.AuthenticationAuthorityEndpoint) + UriBuilder addAuthority = new UriBuilder(this.Configuration.PartnerService.AuthenticationAuthorityEndpoint) { Path = this.Configuration.PartnerService.CommonDomain }; - UserCredential userCredentials = new UserCredential( - this.Configuration.UserAuthentication.UserName, - this.Configuration.UserAuthentication.Password); - AuthenticationContext authContext = new AuthenticationContext(addAuthority.Uri.AbsoluteUri); - return authContext.AcquireToken( - this.Configuration.UserAuthentication.ResourceUrl.OriginalString, - this.Configuration.UserAuthentication.ApplicationId, - userCredentials); + return Task.Run(() => authContext.AcquireTokenAsync( + Configuration.UserAuthentication.ResourceUrl.OriginalString, + Configuration.UserAuthentication.ApplicationId, + redirectUri, + new PlatformParameters(PromptBehavior.Always), + UserIdentifier.AnyUser)).Result; } } } \ No newline at end of file diff --git a/Source/Partner Center SDK Samples/CustomerDirectoryRoles/AddUserMemberToDirectoryRole.cs b/sdk/SdkSamples/CustomerDirectoryRoles/AddUserMemberToDirectoryRole.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerDirectoryRoles/AddUserMemberToDirectoryRole.cs rename to sdk/SdkSamples/CustomerDirectoryRoles/AddUserMemberToDirectoryRole.cs diff --git a/Source/Partner Center SDK Samples/CustomerDirectoryRoles/GetCustomerDirectoryRoleUserMembers.cs b/sdk/SdkSamples/CustomerDirectoryRoles/GetCustomerDirectoryRoleUserMembers.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerDirectoryRoles/GetCustomerDirectoryRoleUserMembers.cs rename to sdk/SdkSamples/CustomerDirectoryRoles/GetCustomerDirectoryRoleUserMembers.cs diff --git a/Source/Partner Center SDK Samples/CustomerDirectoryRoles/GetCustomerDirectoryRoles.cs b/sdk/SdkSamples/CustomerDirectoryRoles/GetCustomerDirectoryRoles.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerDirectoryRoles/GetCustomerDirectoryRoles.cs rename to sdk/SdkSamples/CustomerDirectoryRoles/GetCustomerDirectoryRoles.cs diff --git a/Source/Partner Center SDK Samples/CustomerDirectoryRoles/RemoveCustomerUserMemberFromDirectoryRole.cs b/sdk/SdkSamples/CustomerDirectoryRoles/RemoveCustomerUserMemberFromDirectoryRole.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerDirectoryRoles/RemoveCustomerUserMemberFromDirectoryRole.cs rename to sdk/SdkSamples/CustomerDirectoryRoles/RemoveCustomerUserMemberFromDirectoryRole.cs diff --git a/Source/Partner Center SDK Samples/CustomerProducts/GetCustomerAvailabilities.cs b/sdk/SdkSamples/CustomerProducts/GetCustomerAvailabilities.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerProducts/GetCustomerAvailabilities.cs rename to sdk/SdkSamples/CustomerProducts/GetCustomerAvailabilities.cs diff --git a/Source/Partner Center SDK Samples/CustomerProducts/GetCustomerAvailabilitiesByTargetSegment.cs b/sdk/SdkSamples/CustomerProducts/GetCustomerAvailabilitiesByTargetSegment.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerProducts/GetCustomerAvailabilitiesByTargetSegment.cs rename to sdk/SdkSamples/CustomerProducts/GetCustomerAvailabilitiesByTargetSegment.cs diff --git a/Source/Partner Center SDK Samples/CustomerProducts/GetCustomerAvailability.cs b/sdk/SdkSamples/CustomerProducts/GetCustomerAvailability.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerProducts/GetCustomerAvailability.cs rename to sdk/SdkSamples/CustomerProducts/GetCustomerAvailability.cs diff --git a/Source/Partner Center SDK Samples/CustomerProducts/GetCustomerProduct.cs b/sdk/SdkSamples/CustomerProducts/GetCustomerProduct.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerProducts/GetCustomerProduct.cs rename to sdk/SdkSamples/CustomerProducts/GetCustomerProduct.cs diff --git a/Source/Partner Center SDK Samples/CustomerProducts/GetCustomerProducts.cs b/sdk/SdkSamples/CustomerProducts/GetCustomerProducts.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerProducts/GetCustomerProducts.cs rename to sdk/SdkSamples/CustomerProducts/GetCustomerProducts.cs diff --git a/Source/Partner Center SDK Samples/CustomerProducts/GetCustomerProductsByTargetSegment.cs b/sdk/SdkSamples/CustomerProducts/GetCustomerProductsByTargetSegment.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerProducts/GetCustomerProductsByTargetSegment.cs rename to sdk/SdkSamples/CustomerProducts/GetCustomerProductsByTargetSegment.cs diff --git a/Source/Partner Center SDK Samples/CustomerProducts/GetCustomerSku.cs b/sdk/SdkSamples/CustomerProducts/GetCustomerSku.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerProducts/GetCustomerSku.cs rename to sdk/SdkSamples/CustomerProducts/GetCustomerSku.cs diff --git a/Source/Partner Center SDK Samples/CustomerProducts/GetCustomerSkuDownloadOptions.cs b/sdk/SdkSamples/CustomerProducts/GetCustomerSkuDownloadOptions.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerProducts/GetCustomerSkuDownloadOptions.cs rename to sdk/SdkSamples/CustomerProducts/GetCustomerSkuDownloadOptions.cs diff --git a/Source/Partner Center SDK Samples/CustomerProducts/GetCustomerSkus.cs b/sdk/SdkSamples/CustomerProducts/GetCustomerSkus.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerProducts/GetCustomerSkus.cs rename to sdk/SdkSamples/CustomerProducts/GetCustomerSkus.cs diff --git a/Source/Partner Center SDK Samples/CustomerProducts/GetCustomerSkusByTargetSegment.cs b/sdk/SdkSamples/CustomerProducts/GetCustomerSkusByTargetSegment.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerProducts/GetCustomerSkusByTargetSegment.cs rename to sdk/SdkSamples/CustomerProducts/GetCustomerSkusByTargetSegment.cs diff --git a/Source/Partner Center SDK Samples/CustomerServiceCosts/GetCustomerServiceCostsLineItems.cs b/sdk/SdkSamples/CustomerServiceCosts/GetCustomerServiceCostsLineItems.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerServiceCosts/GetCustomerServiceCostsLineItems.cs rename to sdk/SdkSamples/CustomerServiceCosts/GetCustomerServiceCostsLineItems.cs diff --git a/Source/Partner Center SDK Samples/CustomerServiceCosts/GetCustomerServiceCostsSummary.cs b/sdk/SdkSamples/CustomerServiceCosts/GetCustomerServiceCostsSummary.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerServiceCosts/GetCustomerServiceCostsSummary.cs rename to sdk/SdkSamples/CustomerServiceCosts/GetCustomerServiceCostsSummary.cs diff --git a/Source/Partner Center SDK Samples/CustomerSubscribedSkus/GetCustomerSubscribedSkus.cs b/sdk/SdkSamples/CustomerSubscribedSkus/GetCustomerSubscribedSkus.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerSubscribedSkus/GetCustomerSubscribedSkus.cs rename to sdk/SdkSamples/CustomerSubscribedSkus/GetCustomerSubscribedSkus.cs diff --git a/Source/Partner Center SDK Samples/CustomerUser/CreateCustomerUser.cs b/sdk/SdkSamples/CustomerUser/CreateCustomerUser.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerUser/CreateCustomerUser.cs rename to sdk/SdkSamples/CustomerUser/CreateCustomerUser.cs diff --git a/Source/Partner Center SDK Samples/CustomerUser/CustomerUserAssignGroup1Licenses.cs b/sdk/SdkSamples/CustomerUser/CustomerUserAssignGroup1Licenses.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerUser/CustomerUserAssignGroup1Licenses.cs rename to sdk/SdkSamples/CustomerUser/CustomerUserAssignGroup1Licenses.cs diff --git a/Source/Partner Center SDK Samples/CustomerUser/CustomerUserAssignGroup2Licenses.cs b/sdk/SdkSamples/CustomerUser/CustomerUserAssignGroup2Licenses.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerUser/CustomerUserAssignGroup2Licenses.cs rename to sdk/SdkSamples/CustomerUser/CustomerUserAssignGroup2Licenses.cs diff --git a/Source/Partner Center SDK Samples/CustomerUser/CustomerUserAssignLicenses.cs b/sdk/SdkSamples/CustomerUser/CustomerUserAssignLicenses.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerUser/CustomerUserAssignLicenses.cs rename to sdk/SdkSamples/CustomerUser/CustomerUserAssignLicenses.cs diff --git a/Source/Partner Center SDK Samples/CustomerUser/CustomerUserAssignedGroup1AndGroup2Licenses.cs b/sdk/SdkSamples/CustomerUser/CustomerUserAssignedGroup1AndGroup2Licenses.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerUser/CustomerUserAssignedGroup1AndGroup2Licenses.cs rename to sdk/SdkSamples/CustomerUser/CustomerUserAssignedGroup1AndGroup2Licenses.cs diff --git a/Source/Partner Center SDK Samples/CustomerUser/CustomerUserAssignedGroup1Licenses.cs b/sdk/SdkSamples/CustomerUser/CustomerUserAssignedGroup1Licenses.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerUser/CustomerUserAssignedGroup1Licenses.cs rename to sdk/SdkSamples/CustomerUser/CustomerUserAssignedGroup1Licenses.cs diff --git a/Source/Partner Center SDK Samples/CustomerUser/CustomerUserAssignedGroup2Licenses.cs b/sdk/SdkSamples/CustomerUser/CustomerUserAssignedGroup2Licenses.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerUser/CustomerUserAssignedGroup2Licenses.cs rename to sdk/SdkSamples/CustomerUser/CustomerUserAssignedGroup2Licenses.cs diff --git a/Source/Partner Center SDK Samples/CustomerUser/CustomerUserAssignedLicenses.cs b/sdk/SdkSamples/CustomerUser/CustomerUserAssignedLicenses.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerUser/CustomerUserAssignedLicenses.cs rename to sdk/SdkSamples/CustomerUser/CustomerUserAssignedLicenses.cs diff --git a/Source/Partner Center SDK Samples/CustomerUser/CustomerUserRestore.cs b/sdk/SdkSamples/CustomerUser/CustomerUserRestore.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerUser/CustomerUserRestore.cs rename to sdk/SdkSamples/CustomerUser/CustomerUserRestore.cs diff --git a/Source/Partner Center SDK Samples/CustomerUser/DeleteCustomerUser.cs b/sdk/SdkSamples/CustomerUser/DeleteCustomerUser.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerUser/DeleteCustomerUser.cs rename to sdk/SdkSamples/CustomerUser/DeleteCustomerUser.cs diff --git a/Source/Partner Center SDK Samples/CustomerUser/GetCustomerInactiveUsers.cs b/sdk/SdkSamples/CustomerUser/GetCustomerInactiveUsers.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerUser/GetCustomerInactiveUsers.cs rename to sdk/SdkSamples/CustomerUser/GetCustomerInactiveUsers.cs diff --git a/Source/Partner Center SDK Samples/CustomerUser/GetCustomerUserCollection.cs b/sdk/SdkSamples/CustomerUser/GetCustomerUserCollection.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerUser/GetCustomerUserCollection.cs rename to sdk/SdkSamples/CustomerUser/GetCustomerUserCollection.cs diff --git a/Source/Partner Center SDK Samples/CustomerUser/GetCustomerUserDetails.cs b/sdk/SdkSamples/CustomerUser/GetCustomerUserDetails.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerUser/GetCustomerUserDetails.cs rename to sdk/SdkSamples/CustomerUser/GetCustomerUserDetails.cs diff --git a/Source/Partner Center SDK Samples/CustomerUser/GetCustomerUserDirectoryRoles.cs b/sdk/SdkSamples/CustomerUser/GetCustomerUserDirectoryRoles.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerUser/GetCustomerUserDirectoryRoles.cs rename to sdk/SdkSamples/CustomerUser/GetCustomerUserDirectoryRoles.cs diff --git a/Source/Partner Center SDK Samples/CustomerUser/GetPagedCustomerUsers.cs b/sdk/SdkSamples/CustomerUser/GetPagedCustomerUsers.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerUser/GetPagedCustomerUsers.cs rename to sdk/SdkSamples/CustomerUser/GetPagedCustomerUsers.cs diff --git a/Source/Partner Center SDK Samples/CustomerUser/SortCustomerUsers.cs b/sdk/SdkSamples/CustomerUser/SortCustomerUsers.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerUser/SortCustomerUsers.cs rename to sdk/SdkSamples/CustomerUser/SortCustomerUsers.cs diff --git a/Source/Partner Center SDK Samples/CustomerUser/UpdateCustomerUser.cs b/sdk/SdkSamples/CustomerUser/UpdateCustomerUser.cs similarity index 100% rename from Source/Partner Center SDK Samples/CustomerUser/UpdateCustomerUser.cs rename to sdk/SdkSamples/CustomerUser/UpdateCustomerUser.cs diff --git a/Source/Partner Center SDK Samples/Customers/CheckDomainAvailability.cs b/sdk/SdkSamples/Customers/CheckDomainAvailability.cs similarity index 100% rename from Source/Partner Center SDK Samples/Customers/CheckDomainAvailability.cs rename to sdk/SdkSamples/Customers/CheckDomainAvailability.cs diff --git a/Source/Partner Center SDK Samples/Customers/CreateCustomer.cs b/sdk/SdkSamples/Customers/CreateCustomer.cs similarity index 100% rename from Source/Partner Center SDK Samples/Customers/CreateCustomer.cs rename to sdk/SdkSamples/Customers/CreateCustomer.cs diff --git a/Source/Partner Center SDK Samples/Customers/DeleteCustomerFromTipAccount.cs b/sdk/SdkSamples/Customers/DeleteCustomerFromTipAccount.cs similarity index 100% rename from Source/Partner Center SDK Samples/Customers/DeleteCustomerFromTipAccount.cs rename to sdk/SdkSamples/Customers/DeleteCustomerFromTipAccount.cs diff --git a/Source/Partner Center SDK Samples/Customers/DeletePartnerCustomerRelationship.cs b/sdk/SdkSamples/Customers/DeletePartnerCustomerRelationship.cs similarity index 100% rename from Source/Partner Center SDK Samples/Customers/DeletePartnerCustomerRelationship.cs rename to sdk/SdkSamples/Customers/DeletePartnerCustomerRelationship.cs diff --git a/Source/Partner Center SDK Samples/Customers/FilterCustomers.cs b/sdk/SdkSamples/Customers/FilterCustomers.cs similarity index 100% rename from Source/Partner Center SDK Samples/Customers/FilterCustomers.cs rename to sdk/SdkSamples/Customers/FilterCustomers.cs diff --git a/Source/Partner Center SDK Samples/Customers/GetCustomerDetails.cs b/sdk/SdkSamples/Customers/GetCustomerDetails.cs similarity index 100% rename from Source/Partner Center SDK Samples/Customers/GetCustomerDetails.cs rename to sdk/SdkSamples/Customers/GetCustomerDetails.cs diff --git a/Source/Partner Center SDK Samples/Customers/GetCustomerManagedServices.cs b/sdk/SdkSamples/Customers/GetCustomerManagedServices.cs similarity index 100% rename from Source/Partner Center SDK Samples/Customers/GetCustomerManagedServices.cs rename to sdk/SdkSamples/Customers/GetCustomerManagedServices.cs diff --git a/Source/Partner Center SDK Samples/Customers/GetCustomerQualification.cs b/sdk/SdkSamples/Customers/GetCustomerQualification.cs similarity index 100% rename from Source/Partner Center SDK Samples/Customers/GetCustomerQualification.cs rename to sdk/SdkSamples/Customers/GetCustomerQualification.cs diff --git a/Source/Partner Center SDK Samples/Customers/GetCustomerRelationshipRequest.cs b/sdk/SdkSamples/Customers/GetCustomerRelationshipRequest.cs similarity index 100% rename from Source/Partner Center SDK Samples/Customers/GetCustomerRelationshipRequest.cs rename to sdk/SdkSamples/Customers/GetCustomerRelationshipRequest.cs diff --git a/Source/Partner Center SDK Samples/Customers/GetPagedCustomers.cs b/sdk/SdkSamples/Customers/GetPagedCustomers.cs similarity index 100% rename from Source/Partner Center SDK Samples/Customers/GetPagedCustomers.cs rename to sdk/SdkSamples/Customers/GetPagedCustomers.cs diff --git a/Source/Partner Center SDK Samples/Customers/UpdateCustomerBillingProfile.cs b/sdk/SdkSamples/Customers/UpdateCustomerBillingProfile.cs similarity index 100% rename from Source/Partner Center SDK Samples/Customers/UpdateCustomerBillingProfile.cs rename to sdk/SdkSamples/Customers/UpdateCustomerBillingProfile.cs diff --git a/Source/Partner Center SDK Samples/Customers/UpdateCustomerQualification.cs b/sdk/SdkSamples/Customers/UpdateCustomerQualification.cs similarity index 100% rename from Source/Partner Center SDK Samples/Customers/UpdateCustomerQualification.cs rename to sdk/SdkSamples/Customers/UpdateCustomerQualification.cs diff --git a/Source/Partner Center SDK Samples/Customers/UpdateCustomerQualificationWithGCC.cs b/sdk/SdkSamples/Customers/UpdateCustomerQualificationWithGCC.cs similarity index 100% rename from Source/Partner Center SDK Samples/Customers/UpdateCustomerQualificationWithGCC.cs rename to sdk/SdkSamples/Customers/UpdateCustomerQualificationWithGCC.cs diff --git a/Source/Partner Center SDK Samples/Customers/ValidateCustomerAddress.cs b/sdk/SdkSamples/Customers/ValidateCustomerAddress.cs similarity index 100% rename from Source/Partner Center SDK Samples/Customers/ValidateCustomerAddress.cs rename to sdk/SdkSamples/Customers/ValidateCustomerAddress.cs diff --git a/Source/Partner Center SDK Samples/DevicesDeployment/CreateConfigurationPolicy.cs b/sdk/SdkSamples/DevicesDeployment/CreateConfigurationPolicy.cs similarity index 100% rename from Source/Partner Center SDK Samples/DevicesDeployment/CreateConfigurationPolicy.cs rename to sdk/SdkSamples/DevicesDeployment/CreateConfigurationPolicy.cs diff --git a/Source/Partner Center SDK Samples/DevicesDeployment/CreateDeviceBatch.cs b/sdk/SdkSamples/DevicesDeployment/CreateDeviceBatch.cs similarity index 100% rename from Source/Partner Center SDK Samples/DevicesDeployment/CreateDeviceBatch.cs rename to sdk/SdkSamples/DevicesDeployment/CreateDeviceBatch.cs diff --git a/Source/Partner Center SDK Samples/DevicesDeployment/CreateDevices.cs b/sdk/SdkSamples/DevicesDeployment/CreateDevices.cs similarity index 100% rename from Source/Partner Center SDK Samples/DevicesDeployment/CreateDevices.cs rename to sdk/SdkSamples/DevicesDeployment/CreateDevices.cs diff --git a/Source/Partner Center SDK Samples/DevicesDeployment/DeleteConfigurationPolicy.cs b/sdk/SdkSamples/DevicesDeployment/DeleteConfigurationPolicy.cs similarity index 100% rename from Source/Partner Center SDK Samples/DevicesDeployment/DeleteConfigurationPolicy.cs rename to sdk/SdkSamples/DevicesDeployment/DeleteConfigurationPolicy.cs diff --git a/Source/Partner Center SDK Samples/DevicesDeployment/DeleteDevice.cs b/sdk/SdkSamples/DevicesDeployment/DeleteDevice.cs similarity index 100% rename from Source/Partner Center SDK Samples/DevicesDeployment/DeleteDevice.cs rename to sdk/SdkSamples/DevicesDeployment/DeleteDevice.cs diff --git a/Source/Partner Center SDK Samples/DevicesDeployment/GetAllConfigurationPolicies.cs b/sdk/SdkSamples/DevicesDeployment/GetAllConfigurationPolicies.cs similarity index 100% rename from Source/Partner Center SDK Samples/DevicesDeployment/GetAllConfigurationPolicies.cs rename to sdk/SdkSamples/DevicesDeployment/GetAllConfigurationPolicies.cs diff --git a/Source/Partner Center SDK Samples/DevicesDeployment/GetBatchUploadStatus.cs b/sdk/SdkSamples/DevicesDeployment/GetBatchUploadStatus.cs similarity index 100% rename from Source/Partner Center SDK Samples/DevicesDeployment/GetBatchUploadStatus.cs rename to sdk/SdkSamples/DevicesDeployment/GetBatchUploadStatus.cs diff --git a/Source/Partner Center SDK Samples/DevicesDeployment/GetDevices.cs b/sdk/SdkSamples/DevicesDeployment/GetDevices.cs similarity index 100% rename from Source/Partner Center SDK Samples/DevicesDeployment/GetDevices.cs rename to sdk/SdkSamples/DevicesDeployment/GetDevices.cs diff --git a/Source/Partner Center SDK Samples/DevicesDeployment/GetDevicesBatches.cs b/sdk/SdkSamples/DevicesDeployment/GetDevicesBatches.cs similarity index 100% rename from Source/Partner Center SDK Samples/DevicesDeployment/GetDevicesBatches.cs rename to sdk/SdkSamples/DevicesDeployment/GetDevicesBatches.cs diff --git a/Source/Partner Center SDK Samples/DevicesDeployment/UpdateConfigurationPolicy.cs b/sdk/SdkSamples/DevicesDeployment/UpdateConfigurationPolicy.cs similarity index 100% rename from Source/Partner Center SDK Samples/DevicesDeployment/UpdateConfigurationPolicy.cs rename to sdk/SdkSamples/DevicesDeployment/UpdateConfigurationPolicy.cs diff --git a/Source/Partner Center SDK Samples/DevicesDeployment/UpdateDevicesPolicy.cs b/sdk/SdkSamples/DevicesDeployment/UpdateDevicesPolicy.cs similarity index 100% rename from Source/Partner Center SDK Samples/DevicesDeployment/UpdateDevicesPolicy.cs rename to sdk/SdkSamples/DevicesDeployment/UpdateDevicesPolicy.cs diff --git a/Source/Partner Center SDK Samples/Entitlements/GetEntitlements.cs b/sdk/SdkSamples/Entitlements/GetEntitlements.cs similarity index 100% rename from Source/Partner Center SDK Samples/Entitlements/GetEntitlements.cs rename to sdk/SdkSamples/Entitlements/GetEntitlements.cs diff --git a/Source/Partner Center SDK Samples/Helpers/ConsoleHelper.cs b/sdk/SdkSamples/Helpers/ConsoleHelper.cs similarity index 100% rename from Source/Partner Center SDK Samples/Helpers/ConsoleHelper.cs rename to sdk/SdkSamples/Helpers/ConsoleHelper.cs diff --git a/Source/Partner Center SDK Samples/IPartnerScenario.cs b/sdk/SdkSamples/IPartnerScenario.cs similarity index 100% rename from Source/Partner Center SDK Samples/IPartnerScenario.cs rename to sdk/SdkSamples/IPartnerScenario.cs diff --git a/Source/Partner Center SDK Samples/IScenarioContext.cs b/sdk/SdkSamples/IScenarioContext.cs similarity index 100% rename from Source/Partner Center SDK Samples/IScenarioContext.cs rename to sdk/SdkSamples/IScenarioContext.cs diff --git a/Source/Partner Center SDK Samples/IndirectModel/CreateCustomerForIndirectReseller.cs b/sdk/SdkSamples/IndirectModel/CreateCustomerForIndirectReseller.cs similarity index 100% rename from Source/Partner Center SDK Samples/IndirectModel/CreateCustomerForIndirectReseller.cs rename to sdk/SdkSamples/IndirectModel/CreateCustomerForIndirectReseller.cs diff --git a/Source/Partner Center SDK Samples/IndirectModel/GetCustomersOfIndirectReseller.cs b/sdk/SdkSamples/IndirectModel/GetCustomersOfIndirectReseller.cs similarity index 100% rename from Source/Partner Center SDK Samples/IndirectModel/GetCustomersOfIndirectReseller.cs rename to sdk/SdkSamples/IndirectModel/GetCustomersOfIndirectReseller.cs diff --git a/Source/Partner Center SDK Samples/IndirectModel/GetIndirectResellers.cs b/sdk/SdkSamples/IndirectModel/GetIndirectResellers.cs similarity index 100% rename from Source/Partner Center SDK Samples/IndirectModel/GetIndirectResellers.cs rename to sdk/SdkSamples/IndirectModel/GetIndirectResellers.cs diff --git a/Source/Partner Center SDK Samples/IndirectModel/GetIndirectResellersOfCustomer.cs b/sdk/SdkSamples/IndirectModel/GetIndirectResellersOfCustomer.cs similarity index 100% rename from Source/Partner Center SDK Samples/IndirectModel/GetIndirectResellersOfCustomer.cs rename to sdk/SdkSamples/IndirectModel/GetIndirectResellersOfCustomer.cs diff --git a/Source/Partner Center SDK Samples/IndirectModel/GetSubscriptionsByMpnId.cs b/sdk/SdkSamples/IndirectModel/GetSubscriptionsByMpnId.cs similarity index 100% rename from Source/Partner Center SDK Samples/IndirectModel/GetSubscriptionsByMpnId.cs rename to sdk/SdkSamples/IndirectModel/GetSubscriptionsByMpnId.cs diff --git a/Source/Partner Center SDK Samples/IndirectModel/PlaceOrderForCustomer.cs b/sdk/SdkSamples/IndirectModel/PlaceOrderForCustomer.cs similarity index 100% rename from Source/Partner Center SDK Samples/IndirectModel/PlaceOrderForCustomer.cs rename to sdk/SdkSamples/IndirectModel/PlaceOrderForCustomer.cs diff --git a/Source/Partner Center SDK Samples/IndirectModel/VerifyPartnerMpnId.cs b/sdk/SdkSamples/IndirectModel/VerifyPartnerMpnId.cs similarity index 100% rename from Source/Partner Center SDK Samples/IndirectModel/VerifyPartnerMpnId.cs rename to sdk/SdkSamples/IndirectModel/VerifyPartnerMpnId.cs diff --git a/Source/Partner Center SDK Samples/IndirectPartners/GetSubscriptionsByMpnId.cs b/sdk/SdkSamples/IndirectPartners/GetSubscriptionsByMpnId.cs similarity index 100% rename from Source/Partner Center SDK Samples/IndirectPartners/GetSubscriptionsByMpnId.cs rename to sdk/SdkSamples/IndirectPartners/GetSubscriptionsByMpnId.cs diff --git a/Source/Partner Center SDK Samples/IndirectPartners/VerifyPartnerMpnId.cs b/sdk/SdkSamples/IndirectPartners/VerifyPartnerMpnId.cs similarity index 100% rename from Source/Partner Center SDK Samples/IndirectPartners/VerifyPartnerMpnId.cs rename to sdk/SdkSamples/IndirectPartners/VerifyPartnerMpnId.cs diff --git a/Source/Partner Center SDK Samples/Invoice/GetAccountBalance.cs b/sdk/SdkSamples/Invoice/GetAccountBalance.cs similarity index 100% rename from Source/Partner Center SDK Samples/Invoice/GetAccountBalance.cs rename to sdk/SdkSamples/Invoice/GetAccountBalance.cs diff --git a/Source/Partner Center SDK Samples/Invoice/GetInvoice.cs b/sdk/SdkSamples/Invoice/GetInvoice.cs similarity index 100% rename from Source/Partner Center SDK Samples/Invoice/GetInvoice.cs rename to sdk/SdkSamples/Invoice/GetInvoice.cs diff --git a/Source/Partner Center SDK Samples/Invoice/GetInvoiceLineItems.cs b/sdk/SdkSamples/Invoice/GetInvoiceLineItems.cs similarity index 100% rename from Source/Partner Center SDK Samples/Invoice/GetInvoiceLineItems.cs rename to sdk/SdkSamples/Invoice/GetInvoiceLineItems.cs diff --git a/Source/Partner Center SDK Samples/Invoice/GetInvoiceStatement.cs b/sdk/SdkSamples/Invoice/GetInvoiceStatement.cs similarity index 100% rename from Source/Partner Center SDK Samples/Invoice/GetInvoiceStatement.cs rename to sdk/SdkSamples/Invoice/GetInvoiceStatement.cs diff --git a/Source/Partner Center SDK Samples/Invoice/GetInvoiceSummaries.cs b/sdk/SdkSamples/Invoice/GetInvoiceSummaries.cs similarity index 100% rename from Source/Partner Center SDK Samples/Invoice/GetInvoiceSummaries.cs rename to sdk/SdkSamples/Invoice/GetInvoiceSummaries.cs diff --git a/Source/Partner Center SDK Samples/Invoice/GetPagedInvoices.cs b/sdk/SdkSamples/Invoice/GetPagedInvoices.cs similarity index 100% rename from Source/Partner Center SDK Samples/Invoice/GetPagedInvoices.cs rename to sdk/SdkSamples/Invoice/GetPagedInvoices.cs diff --git a/Source/Partner Center SDK Samples/Offers/GetCustomerOfferCategories.cs b/sdk/SdkSamples/Offers/GetCustomerOfferCategories.cs similarity index 100% rename from Source/Partner Center SDK Samples/Offers/GetCustomerOfferCategories.cs rename to sdk/SdkSamples/Offers/GetCustomerOfferCategories.cs diff --git a/Source/Partner Center SDK Samples/Offers/GetCustomerOffers.cs b/sdk/SdkSamples/Offers/GetCustomerOffers.cs similarity index 100% rename from Source/Partner Center SDK Samples/Offers/GetCustomerOffers.cs rename to sdk/SdkSamples/Offers/GetCustomerOffers.cs diff --git a/Source/Partner Center SDK Samples/Offers/GetOffer.cs b/sdk/SdkSamples/Offers/GetOffer.cs similarity index 100% rename from Source/Partner Center SDK Samples/Offers/GetOffer.cs rename to sdk/SdkSamples/Offers/GetOffer.cs diff --git a/Source/Partner Center SDK Samples/Offers/GetOfferCategories.cs b/sdk/SdkSamples/Offers/GetOfferCategories.cs similarity index 100% rename from Source/Partner Center SDK Samples/Offers/GetOfferCategories.cs rename to sdk/SdkSamples/Offers/GetOfferCategories.cs diff --git a/Source/Partner Center SDK Samples/Offers/GetOffers.cs b/sdk/SdkSamples/Offers/GetOffers.cs similarity index 100% rename from Source/Partner Center SDK Samples/Offers/GetOffers.cs rename to sdk/SdkSamples/Offers/GetOffers.cs diff --git a/Source/Partner Center SDK Samples/Offers/GetPagedOffers.cs b/sdk/SdkSamples/Offers/GetPagedOffers.cs similarity index 100% rename from Source/Partner Center SDK Samples/Offers/GetPagedOffers.cs rename to sdk/SdkSamples/Offers/GetPagedOffers.cs diff --git a/Source/Partner Center SDK Samples/Orders/CreateAzureReservationOrder.cs b/sdk/SdkSamples/Orders/CreateAzureReservationOrder.cs similarity index 100% rename from Source/Partner Center SDK Samples/Orders/CreateAzureReservationOrder.cs rename to sdk/SdkSamples/Orders/CreateAzureReservationOrder.cs diff --git a/Source/Partner Center SDK Samples/Orders/CreateOrder.cs b/sdk/SdkSamples/Orders/CreateOrder.cs similarity index 100% rename from Source/Partner Center SDK Samples/Orders/CreateOrder.cs rename to sdk/SdkSamples/Orders/CreateOrder.cs diff --git a/Source/Partner Center SDK Samples/Orders/GetOrderDetails.cs b/sdk/SdkSamples/Orders/GetOrderDetails.cs similarity index 100% rename from Source/Partner Center SDK Samples/Orders/GetOrderDetails.cs rename to sdk/SdkSamples/Orders/GetOrderDetails.cs diff --git a/Source/Partner Center SDK Samples/Orders/GetOrderProvisioningStatus.cs b/sdk/SdkSamples/Orders/GetOrderProvisioningStatus.cs similarity index 100% rename from Source/Partner Center SDK Samples/Orders/GetOrderProvisioningStatus.cs rename to sdk/SdkSamples/Orders/GetOrderProvisioningStatus.cs diff --git a/Source/Partner Center SDK Samples/Orders/GetOrders.cs b/sdk/SdkSamples/Orders/GetOrders.cs similarity index 100% rename from Source/Partner Center SDK Samples/Orders/GetOrders.cs rename to sdk/SdkSamples/Orders/GetOrders.cs diff --git a/Source/Partner Center SDK Samples/Orders/GetOrdersByBillingCycleType.cs b/sdk/SdkSamples/Orders/GetOrdersByBillingCycleType.cs similarity index 100% rename from Source/Partner Center SDK Samples/Orders/GetOrdersByBillingCycleType.cs rename to sdk/SdkSamples/Orders/GetOrdersByBillingCycleType.cs diff --git a/Source/Partner Center SDK Samples/Orders/UpdateOrder.cs b/sdk/SdkSamples/Orders/UpdateOrder.cs similarity index 100% rename from Source/Partner Center SDK Samples/Orders/UpdateOrder.cs rename to sdk/SdkSamples/Orders/UpdateOrder.cs diff --git a/Source/Partner Center SDK Samples/Products/CheckInventory.cs b/sdk/SdkSamples/Products/CheckInventory.cs similarity index 100% rename from Source/Partner Center SDK Samples/Products/CheckInventory.cs rename to sdk/SdkSamples/Products/CheckInventory.cs diff --git a/Source/Partner Center SDK Samples/Products/GetAvailabilities.cs b/sdk/SdkSamples/Products/GetAvailabilities.cs similarity index 100% rename from Source/Partner Center SDK Samples/Products/GetAvailabilities.cs rename to sdk/SdkSamples/Products/GetAvailabilities.cs diff --git a/Source/Partner Center SDK Samples/Products/GetAvailabilitiesByTargetSegment.cs b/sdk/SdkSamples/Products/GetAvailabilitiesByTargetSegment.cs similarity index 100% rename from Source/Partner Center SDK Samples/Products/GetAvailabilitiesByTargetSegment.cs rename to sdk/SdkSamples/Products/GetAvailabilitiesByTargetSegment.cs diff --git a/Source/Partner Center SDK Samples/Products/GetAvailability.cs b/sdk/SdkSamples/Products/GetAvailability.cs similarity index 100% rename from Source/Partner Center SDK Samples/Products/GetAvailability.cs rename to sdk/SdkSamples/Products/GetAvailability.cs diff --git a/Source/Partner Center SDK Samples/Products/GetProduct.cs b/sdk/SdkSamples/Products/GetProduct.cs similarity index 100% rename from Source/Partner Center SDK Samples/Products/GetProduct.cs rename to sdk/SdkSamples/Products/GetProduct.cs diff --git a/Source/Partner Center SDK Samples/Products/GetProducts.cs b/sdk/SdkSamples/Products/GetProducts.cs similarity index 100% rename from Source/Partner Center SDK Samples/Products/GetProducts.cs rename to sdk/SdkSamples/Products/GetProducts.cs diff --git a/Source/Partner Center SDK Samples/Products/GetProductsByTargetSegment.cs b/sdk/SdkSamples/Products/GetProductsByTargetSegment.cs similarity index 100% rename from Source/Partner Center SDK Samples/Products/GetProductsByTargetSegment.cs rename to sdk/SdkSamples/Products/GetProductsByTargetSegment.cs diff --git a/Source/Partner Center SDK Samples/Products/GetSku.cs b/sdk/SdkSamples/Products/GetSku.cs similarity index 100% rename from Source/Partner Center SDK Samples/Products/GetSku.cs rename to sdk/SdkSamples/Products/GetSku.cs diff --git a/Source/Partner Center SDK Samples/Products/GetSkuDownloadOptions.cs b/sdk/SdkSamples/Products/GetSkuDownloadOptions.cs similarity index 100% rename from Source/Partner Center SDK Samples/Products/GetSkuDownloadOptions.cs rename to sdk/SdkSamples/Products/GetSkuDownloadOptions.cs diff --git a/Source/Partner Center SDK Samples/Products/GetSkus.cs b/sdk/SdkSamples/Products/GetSkus.cs similarity index 100% rename from Source/Partner Center SDK Samples/Products/GetSkus.cs rename to sdk/SdkSamples/Products/GetSkus.cs diff --git a/Source/Partner Center SDK Samples/Products/GetSkusByTargetSegment.cs b/sdk/SdkSamples/Products/GetSkusByTargetSegment.cs similarity index 100% rename from Source/Partner Center SDK Samples/Products/GetSkusByTargetSegment.cs rename to sdk/SdkSamples/Products/GetSkusByTargetSegment.cs diff --git a/Source/Partner Center SDK Samples/Profile/GetBillingProfile.cs b/sdk/SdkSamples/Profile/GetBillingProfile.cs similarity index 100% rename from Source/Partner Center SDK Samples/Profile/GetBillingProfile.cs rename to sdk/SdkSamples/Profile/GetBillingProfile.cs diff --git a/Source/Partner Center SDK Samples/Profile/GetMPNProfile.cs b/sdk/SdkSamples/Profile/GetMPNProfile.cs similarity index 100% rename from Source/Partner Center SDK Samples/Profile/GetMPNProfile.cs rename to sdk/SdkSamples/Profile/GetMPNProfile.cs diff --git a/Source/Partner Center SDK Samples/Profile/GetOrganizationProfile.cs b/sdk/SdkSamples/Profile/GetOrganizationProfile.cs similarity index 100% rename from Source/Partner Center SDK Samples/Profile/GetOrganizationProfile.cs rename to sdk/SdkSamples/Profile/GetOrganizationProfile.cs diff --git a/Source/Partner Center SDK Samples/Profile/GetSupportProfile.cs b/sdk/SdkSamples/Profile/GetSupportProfile.cs similarity index 100% rename from Source/Partner Center SDK Samples/Profile/GetSupportProfile.cs rename to sdk/SdkSamples/Profile/GetSupportProfile.cs diff --git a/Source/Partner Center SDK Samples/Profile/GetlegalBusinessProfile.cs b/sdk/SdkSamples/Profile/GetlegalBusinessProfile.cs similarity index 100% rename from Source/Partner Center SDK Samples/Profile/GetlegalBusinessProfile.cs rename to sdk/SdkSamples/Profile/GetlegalBusinessProfile.cs diff --git a/Source/Partner Center SDK Samples/Profile/UpdateBillingProfile.cs b/sdk/SdkSamples/Profile/UpdateBillingProfile.cs similarity index 100% rename from Source/Partner Center SDK Samples/Profile/UpdateBillingProfile.cs rename to sdk/SdkSamples/Profile/UpdateBillingProfile.cs diff --git a/Source/Partner Center SDK Samples/Profile/UpdateLegalBusinessProfile.cs b/sdk/SdkSamples/Profile/UpdateLegalBusinessProfile.cs similarity index 100% rename from Source/Partner Center SDK Samples/Profile/UpdateLegalBusinessProfile.cs rename to sdk/SdkSamples/Profile/UpdateLegalBusinessProfile.cs diff --git a/Source/Partner Center SDK Samples/Profile/UpdateOrganizationProfile.cs b/sdk/SdkSamples/Profile/UpdateOrganizationProfile.cs similarity index 100% rename from Source/Partner Center SDK Samples/Profile/UpdateOrganizationProfile.cs rename to sdk/SdkSamples/Profile/UpdateOrganizationProfile.cs diff --git a/Source/Partner Center SDK Samples/Profile/UpdateSupportProfile.cs b/sdk/SdkSamples/Profile/UpdateSupportProfile.cs similarity index 100% rename from Source/Partner Center SDK Samples/Profile/UpdateSupportProfile.cs rename to sdk/SdkSamples/Profile/UpdateSupportProfile.cs diff --git a/Source/Partner Center SDK Samples/Program.cs b/sdk/SdkSamples/Program.cs similarity index 100% rename from Source/Partner Center SDK Samples/Program.cs rename to sdk/SdkSamples/Program.cs diff --git a/Source/Partner Center SDK Samples/Properties/AssemblyInfo.cs b/sdk/SdkSamples/Properties/AssemblyInfo.cs similarity index 100% rename from Source/Partner Center SDK Samples/Properties/AssemblyInfo.cs rename to sdk/SdkSamples/Properties/AssemblyInfo.cs diff --git a/Source/Partner Center SDK Samples/RateCards/GetAzureRateCard.cs b/sdk/SdkSamples/RateCards/GetAzureRateCard.cs similarity index 100% rename from Source/Partner Center SDK Samples/RateCards/GetAzureRateCard.cs rename to sdk/SdkSamples/RateCards/GetAzureRateCard.cs diff --git a/Source/Partner Center SDK Samples/RateCards/GetAzureSharedRateCard.cs b/sdk/SdkSamples/RateCards/GetAzureSharedRateCard.cs similarity index 100% rename from Source/Partner Center SDK Samples/RateCards/GetAzureSharedRateCard.cs rename to sdk/SdkSamples/RateCards/GetAzureSharedRateCard.cs diff --git a/Source/Partner Center SDK Samples/RatedUsage/GetCustomerSubscriptionsUsage.cs b/sdk/SdkSamples/RatedUsage/GetCustomerSubscriptionsUsage.cs similarity index 100% rename from Source/Partner Center SDK Samples/RatedUsage/GetCustomerSubscriptionsUsage.cs rename to sdk/SdkSamples/RatedUsage/GetCustomerSubscriptionsUsage.cs diff --git a/Source/Partner Center SDK Samples/RatedUsage/GetCustomerUsageSummary.cs b/sdk/SdkSamples/RatedUsage/GetCustomerUsageSummary.cs similarity index 100% rename from Source/Partner Center SDK Samples/RatedUsage/GetCustomerUsageSummary.cs rename to sdk/SdkSamples/RatedUsage/GetCustomerUsageSummary.cs diff --git a/Source/Partner Center SDK Samples/RatedUsage/GetSubscriptionResourceUsage.cs b/sdk/SdkSamples/RatedUsage/GetSubscriptionResourceUsage.cs similarity index 100% rename from Source/Partner Center SDK Samples/RatedUsage/GetSubscriptionResourceUsage.cs rename to sdk/SdkSamples/RatedUsage/GetSubscriptionResourceUsage.cs diff --git a/Source/Partner Center SDK Samples/RatedUsage/GetSubscriptionUsageRecords.cs b/sdk/SdkSamples/RatedUsage/GetSubscriptionUsageRecords.cs similarity index 100% rename from Source/Partner Center SDK Samples/RatedUsage/GetSubscriptionUsageRecords.cs rename to sdk/SdkSamples/RatedUsage/GetSubscriptionUsageRecords.cs diff --git a/Source/Partner Center SDK Samples/RatedUsage/GetSubscriptionUsageSummary.cs b/sdk/SdkSamples/RatedUsage/GetSubscriptionUsageSummary.cs similarity index 100% rename from Source/Partner Center SDK Samples/RatedUsage/GetSubscriptionUsageSummary.cs rename to sdk/SdkSamples/RatedUsage/GetSubscriptionUsageSummary.cs diff --git a/Source/Partner Center SDK Samples/ScenarioExecution/AggregateScenarioExecutionStrategy.cs b/sdk/SdkSamples/ScenarioExecution/AggregateScenarioExecutionStrategy.cs similarity index 100% rename from Source/Partner Center SDK Samples/ScenarioExecution/AggregateScenarioExecutionStrategy.cs rename to sdk/SdkSamples/ScenarioExecution/AggregateScenarioExecutionStrategy.cs diff --git a/Source/Partner Center SDK Samples/ScenarioExecution/IScenarioExecutionStrategy.cs b/sdk/SdkSamples/ScenarioExecution/IScenarioExecutionStrategy.cs similarity index 100% rename from Source/Partner Center SDK Samples/ScenarioExecution/IScenarioExecutionStrategy.cs rename to sdk/SdkSamples/ScenarioExecution/IScenarioExecutionStrategy.cs diff --git a/Source/Partner Center SDK Samples/ScenarioExecution/PromptExecutionStrategy.cs b/sdk/SdkSamples/ScenarioExecution/PromptExecutionStrategy.cs similarity index 100% rename from Source/Partner Center SDK Samples/ScenarioExecution/PromptExecutionStrategy.cs rename to sdk/SdkSamples/ScenarioExecution/PromptExecutionStrategy.cs diff --git a/Source/Partner Center SDK Samples/Partner Center SDK Samples.csproj b/sdk/SdkSamples/SdkSamples.csproj similarity index 84% rename from Source/Partner Center SDK Samples/Partner Center SDK Samples.csproj rename to sdk/SdkSamples/SdkSamples.csproj index 97a8649..e06ad6f 100644 --- a/Source/Partner Center SDK Samples/Partner Center SDK Samples.csproj +++ b/sdk/SdkSamples/SdkSamples.csproj @@ -9,7 +9,7 @@ Properties Microsoft.Store.PartnerCenter.Samples Microsoft.Store.PartnerCenter.Samples - v4.5.1 + v4.6.1 512 true @@ -25,9 +25,10 @@ DEBUG;TRACE prompt 4 - true + false bin\Debug\Microsoft.Store.PartnerCenter.Samples.xml true + false AnyCPU @@ -41,28 +42,27 @@ bin\Release\Microsoft.Store.PartnerCenter.Samples.xml - - ..\..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.2.29.0\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll - - - ..\..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.2.29.0\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll + + ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.4.4.0\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll - ..\..\packages\Microsoft.Store.PartnerCenter.1.10.0\lib\Net45\Microsoft.Store.PartnerCenter.dll + ..\packages\Microsoft.Store.PartnerCenter.1.10.0\lib\Net45\Microsoft.Store.PartnerCenter.dll - ..\..\packages\Microsoft.Store.PartnerCenter.1.10.0\lib\Net45\Microsoft.Store.PartnerCenter.Extensions.dll + ..\packages\Microsoft.Store.PartnerCenter.1.10.0\lib\Net45\Microsoft.Store.PartnerCenter.Extensions.dll - ..\..\packages\Microsoft.Store.PartnerCenter.1.10.0\lib\Net45\Microsoft.Store.PartnerCenter.Models.dll + ..\packages\Microsoft.Store.PartnerCenter.1.10.0\lib\Net45\Microsoft.Store.PartnerCenter.Models.dll - ..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll - True + ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + + + @@ -243,25 +243,8 @@ Designer - - Designer - - + - - - - 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}. - - - - \ No newline at end of file diff --git a/Source/Partner Center SDK Samples/ServiceIncidents/GetServiceIncidents.cs b/sdk/SdkSamples/ServiceIncidents/GetServiceIncidents.cs similarity index 100% rename from Source/Partner Center SDK Samples/ServiceIncidents/GetServiceIncidents.cs rename to sdk/SdkSamples/ServiceIncidents/GetServiceIncidents.cs diff --git a/Source/Partner Center SDK Samples/ServiceRequests/CreatePartnerServiceRequest.cs b/sdk/SdkSamples/ServiceRequests/CreatePartnerServiceRequest.cs similarity index 100% rename from Source/Partner Center SDK Samples/ServiceRequests/CreatePartnerServiceRequest.cs rename to sdk/SdkSamples/ServiceRequests/CreatePartnerServiceRequest.cs diff --git a/Source/Partner Center SDK Samples/ServiceRequests/GetCustomerServiceRequests.cs b/sdk/SdkSamples/ServiceRequests/GetCustomerServiceRequests.cs similarity index 100% rename from Source/Partner Center SDK Samples/ServiceRequests/GetCustomerServiceRequests.cs rename to sdk/SdkSamples/ServiceRequests/GetCustomerServiceRequests.cs diff --git a/Source/Partner Center SDK Samples/ServiceRequests/GetPagedPartnerServiceRequests.cs b/sdk/SdkSamples/ServiceRequests/GetPagedPartnerServiceRequests.cs similarity index 100% rename from Source/Partner Center SDK Samples/ServiceRequests/GetPagedPartnerServiceRequests.cs rename to sdk/SdkSamples/ServiceRequests/GetPagedPartnerServiceRequests.cs diff --git a/Source/Partner Center SDK Samples/ServiceRequests/GetPartnerServiceRequestDetails.cs b/sdk/SdkSamples/ServiceRequests/GetPartnerServiceRequestDetails.cs similarity index 100% rename from Source/Partner Center SDK Samples/ServiceRequests/GetPartnerServiceRequestDetails.cs rename to sdk/SdkSamples/ServiceRequests/GetPartnerServiceRequestDetails.cs diff --git a/Source/Partner Center SDK Samples/ServiceRequests/GetServiceRequestSupportTopics.cs b/sdk/SdkSamples/ServiceRequests/GetServiceRequestSupportTopics.cs similarity index 100% rename from Source/Partner Center SDK Samples/ServiceRequests/GetServiceRequestSupportTopics.cs rename to sdk/SdkSamples/ServiceRequests/GetServiceRequestSupportTopics.cs diff --git a/Source/Partner Center SDK Samples/ServiceRequests/UpdatePartnerServiceRequest.cs b/sdk/SdkSamples/ServiceRequests/UpdatePartnerServiceRequest.cs similarity index 100% rename from Source/Partner Center SDK Samples/ServiceRequests/UpdatePartnerServiceRequest.cs rename to sdk/SdkSamples/ServiceRequests/UpdatePartnerServiceRequest.cs diff --git a/Source/Partner Center SDK Samples/Subscriptions/AddSubscriptionAddOn.cs b/sdk/SdkSamples/Subscriptions/AddSubscriptionAddOn.cs similarity index 100% rename from Source/Partner Center SDK Samples/Subscriptions/AddSubscriptionAddOn.cs rename to sdk/SdkSamples/Subscriptions/AddSubscriptionAddOn.cs diff --git a/Source/Partner Center SDK Samples/Subscriptions/ConvertTrialSubscription.cs b/sdk/SdkSamples/Subscriptions/ConvertTrialSubscription.cs similarity index 100% rename from Source/Partner Center SDK Samples/Subscriptions/ConvertTrialSubscription.cs rename to sdk/SdkSamples/Subscriptions/ConvertTrialSubscription.cs diff --git a/Source/Partner Center SDK Samples/Subscriptions/GetSubscription.cs b/sdk/SdkSamples/Subscriptions/GetSubscription.cs similarity index 100% rename from Source/Partner Center SDK Samples/Subscriptions/GetSubscription.cs rename to sdk/SdkSamples/Subscriptions/GetSubscription.cs diff --git a/Source/Partner Center SDK Samples/Subscriptions/GetSubscriptionProvisioningStatus.cs b/sdk/SdkSamples/Subscriptions/GetSubscriptionProvisioningStatus.cs similarity index 100% rename from Source/Partner Center SDK Samples/Subscriptions/GetSubscriptionProvisioningStatus.cs rename to sdk/SdkSamples/Subscriptions/GetSubscriptionProvisioningStatus.cs diff --git a/Source/Partner Center SDK Samples/Subscriptions/GetSubscriptionSupportContact.cs b/sdk/SdkSamples/Subscriptions/GetSubscriptionSupportContact.cs similarity index 100% rename from Source/Partner Center SDK Samples/Subscriptions/GetSubscriptionSupportContact.cs rename to sdk/SdkSamples/Subscriptions/GetSubscriptionSupportContact.cs diff --git a/Source/Partner Center SDK Samples/Subscriptions/GetSubscriptions.cs b/sdk/SdkSamples/Subscriptions/GetSubscriptions.cs similarity index 100% rename from Source/Partner Center SDK Samples/Subscriptions/GetSubscriptions.cs rename to sdk/SdkSamples/Subscriptions/GetSubscriptions.cs diff --git a/Source/Partner Center SDK Samples/Subscriptions/GetSubscriptionsByOrder.cs b/sdk/SdkSamples/Subscriptions/GetSubscriptionsByOrder.cs similarity index 100% rename from Source/Partner Center SDK Samples/Subscriptions/GetSubscriptionsByOrder.cs rename to sdk/SdkSamples/Subscriptions/GetSubscriptionsByOrder.cs diff --git a/Source/Partner Center SDK Samples/Subscriptions/UpdateSubscription.cs b/sdk/SdkSamples/Subscriptions/UpdateSubscription.cs similarity index 100% rename from Source/Partner Center SDK Samples/Subscriptions/UpdateSubscription.cs rename to sdk/SdkSamples/Subscriptions/UpdateSubscription.cs diff --git a/Source/Partner Center SDK Samples/Subscriptions/UpdateSubscriptionSupportContact.cs b/sdk/SdkSamples/Subscriptions/UpdateSubscriptionSupportContact.cs similarity index 100% rename from Source/Partner Center SDK Samples/Subscriptions/UpdateSubscriptionSupportContact.cs rename to sdk/SdkSamples/Subscriptions/UpdateSubscriptionSupportContact.cs diff --git a/Source/Partner Center SDK Samples/Subscriptions/UpgradeSubscription.cs b/sdk/SdkSamples/Subscriptions/UpgradeSubscription.cs similarity index 100% rename from Source/Partner Center SDK Samples/Subscriptions/UpgradeSubscription.cs rename to sdk/SdkSamples/Subscriptions/UpgradeSubscription.cs diff --git a/Source/Partner Center SDK Samples/Utilization/GetAzureSubscriptionUtilization.cs b/sdk/SdkSamples/Utilization/GetAzureSubscriptionUtilization.cs similarity index 100% rename from Source/Partner Center SDK Samples/Utilization/GetAzureSubscriptionUtilization.cs rename to sdk/SdkSamples/Utilization/GetAzureSubscriptionUtilization.cs diff --git a/Source/Partner Center SDK Samples/Validations/AddressValidation.cs b/sdk/SdkSamples/Validations/AddressValidation.cs similarity index 100% rename from Source/Partner Center SDK Samples/Validations/AddressValidation.cs rename to sdk/SdkSamples/Validations/AddressValidation.cs diff --git a/Source/Partner Center SDK Samples/Validations/ValidateAddress.cs b/sdk/SdkSamples/Validations/ValidateAddress.cs similarity index 100% rename from Source/Partner Center SDK Samples/Validations/ValidateAddress.cs rename to sdk/SdkSamples/Validations/ValidateAddress.cs diff --git a/Source/Partner Center SDK Samples/packages.config b/sdk/SdkSamples/packages.config similarity index 57% rename from Source/Partner Center SDK Samples/packages.config rename to sdk/SdkSamples/packages.config index 64ae55c..ccf8ec4 100644 --- a/Source/Partner Center SDK Samples/packages.config +++ b/sdk/SdkSamples/packages.config @@ -1,7 +1,6 @@ - - - - + + + \ No newline at end of file diff --git a/secure-app-model/README.md b/secure-app-model/README.md new file mode 100644 index 0000000..e52e17a --- /dev/null +++ b/secure-app-model/README.md @@ -0,0 +1,5 @@ +# Secure App Model + +## Overview + +Microsoft is introducing a secure, scalable framework for authenticating Cloud Solution Provider (CSP) and Control Panel Vendors (CPV) using multi-factor authentication (MFA). This new model will elevate security for operations involving the Partner Center API. This will help all parties including Microsoft and partners to protect their infrastructure and customer data from security risk. \ No newline at end of file diff --git a/secure-app-model/keyvault/CPVApplication.sln b/secure-app-model/keyvault/CPVApplication.sln new file mode 100644 index 0000000..1cacae0 --- /dev/null +++ b/secure-app-model/keyvault/CPVApplication.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2026 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CPVApplication", "CPVApplication\CPVApplication.csproj", "{9283AF7A-6CA9-4373-93CB-F5D2130E1889}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9283AF7A-6CA9-4373-93CB-F5D2130E1889}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9283AF7A-6CA9-4373-93CB-F5D2130E1889}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9283AF7A-6CA9-4373-93CB-F5D2130E1889}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9283AF7A-6CA9-4373-93CB-F5D2130E1889}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3CBE281A-E046-480F-8FC6-34536B951BEC} + EndGlobalSection +EndGlobal diff --git a/secure-app-model/keyvault/CPVApplication/App.config b/secure-app-model/keyvault/CPVApplication/App.config new file mode 100644 index 0000000..685ab40 --- /dev/null +++ b/secure-app-model/keyvault/CPVApplication/App.config @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/secure-app-model/keyvault/CPVApplication/CPVApplication.csproj b/secure-app-model/keyvault/CPVApplication/CPVApplication.csproj new file mode 100644 index 0000000..22ccb8b --- /dev/null +++ b/secure-app-model/keyvault/CPVApplication/CPVApplication.csproj @@ -0,0 +1,111 @@ + + + + + Debug + AnyCPU + {9283AF7A-6CA9-4373-93CB-F5D2130E1889} + Exe + CPVApplication + CPVApplication + v4.6.1 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + packages\Microsoft.Azure.KeyVault.3.0.2\lib\net452\Microsoft.Azure.KeyVault.dll + + + packages\Microsoft.Azure.KeyVault.WebKey.3.0.2\lib\net452\Microsoft.Azure.KeyVault.WebKey.dll + + + packages\Microsoft.IdentityModel.Clients.ActiveDirectory.4.4.1\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll + + + packages\Microsoft.Rest.ClientRuntime.2.3.18\lib\net452\Microsoft.Rest.ClientRuntime.dll + + + packages\Microsoft.Rest.ClientRuntime.Azure.3.3.18\lib\net452\Microsoft.Rest.ClientRuntime.Azure.dll + + + packages\Microsoft.Store.PartnerCenter.1.10.0\lib\Net45\Microsoft.Store.PartnerCenter.dll + + + packages\Microsoft.Store.PartnerCenter.1.10.0\lib\Net45\Microsoft.Store.PartnerCenter.Extensions.dll + + + packages\Microsoft.Store.PartnerCenter.1.10.0\lib\Net45\Microsoft.Store.PartnerCenter.Models.dll + + + packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll + + + + + + + + + packages\System.Net.Http.4.3.4\lib\net46\System.Net.Http.dll + + + + + + + packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net461\System.Security.Cryptography.Algorithms.dll + + + packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll + + + packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll + + + packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net461\System.Security.Cryptography.X509Certificates.dll + + + + + + + + + + + + + + + + + + + Designer + + + Designer + + + + \ No newline at end of file diff --git a/secure-app-model/keyvault/CPVApplication/CPVApplication.sln b/secure-app-model/keyvault/CPVApplication/CPVApplication.sln new file mode 100644 index 0000000..0c79432 --- /dev/null +++ b/secure-app-model/keyvault/CPVApplication/CPVApplication.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.136 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CPVApplication", "CPVApplication.csproj", "{9283AF7A-6CA9-4373-93CB-F5D2130E1889}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9283AF7A-6CA9-4373-93CB-F5D2130E1889}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9283AF7A-6CA9-4373-93CB-F5D2130E1889}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9283AF7A-6CA9-4373-93CB-F5D2130E1889}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9283AF7A-6CA9-4373-93CB-F5D2130E1889}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3A06FFB3-C870-4EE7-A03D-0ECB09F84BB6} + EndGlobalSection +EndGlobal diff --git a/secure-app-model/keyvault/CPVApplication/Program.cs b/secure-app-model/keyvault/CPVApplication/Program.cs new file mode 100644 index 0000000..83b1a26 --- /dev/null +++ b/secure-app-model/keyvault/CPVApplication/Program.cs @@ -0,0 +1,185 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ----------------------------------------------------------------------- + +namespace CPVApplication +{ + using System; + using System.Configuration; + using System.Threading.Tasks; + using CPVApplication.Utilities; + using Microsoft.Store.PartnerCenter; + using Microsoft.Store.PartnerCenter.Extensions; + using Microsoft.Store.PartnerCenter.Models; + using Microsoft.Store.PartnerCenter.Models.Users; + using Newtonsoft.Json; + using Newtonsoft.Json.Linq; + + internal class Program + { + /// + /// The following code assumes that the context of the partner is pre determined by some external code + /// (based on marketplace application logic: either by product or by partner context) + /// + + private static readonly string CPVApplicationId = ConfigurationManager.AppSettings["ida:CPVApplicationId"]; + private static readonly string CPVApplicationSecret = ConfigurationManager.AppSettings["ida:CPVApplicationSecret"]; + + private static void Main(string[] args) + { + Task.Run(() => Run()).Wait(); + } + + private static async Task Run() + { + // The following properties indicate which partner and customer context the calls are going to be made. + string PartnerId = ""; + string CustomerId = ""; + + Console.WriteLine(" ===================== Partner center API calls ============================", DateTime.Now); + IAggregatePartner ops = await GetUserPartnerOperationsAsync(PartnerId); + SeekBasedResourceCollection customerUsers = ops.Customers.ById(CustomerId).Users.Get(); + Console.WriteLine(JsonConvert.SerializeObject(customerUsers)); + + Console.WriteLine(" ===================== Partner graph API calls ============================", DateTime.Now); + Tuple tokenResult = await LoginToGraph(PartnerId); + JObject mydetails = await ApiCalls.GetAsync(tokenResult.Item1, "https://graph.microsoft.com/v1.0/me"); + Console.WriteLine(JsonConvert.SerializeObject(mydetails)); + + // The customer graph calls require customer admin to consent the application + Console.WriteLine(" ===================== Customer consent for graph API calls ============================", DateTime.Now); + + // Enable consent + Tuple tokenPartnerResult = await LoginToPartnerCenter(PartnerId); + JObject contents = new JObject + { + // Provide your application display name + ["displayName"] = "CPV Marketplace", + + // Provide your application id + ["applicationId"] = CPVApplicationId, + + // Provide your application grants + ["applicationGrants"] = new JArray( + JObject.Parse("{\"enterpriseApplicationId\": \"00000002-0000-0000-c000-000000000000\", \"scope\":\"Domain.ReadWrite.All,User.ReadWrite.All,Directory.Read.All\"}"), // for graph api access, Directory.Read.All + JObject.Parse("{\"enterpriseApplicationId\": \"797f4846-ba00-4fd7-ba43-dac1f8f63013\", \"scope\":\"user_impersonation\"}")) // for ARM api access + }; + + /** The following steps have to performed once in per customer tenant if your application is Control panel vendor application and requires customer tenant graph access **/ + + // delete the previous grant into customer tenant + JObject consentDeletion = await ApiCalls.DeleteAsync( + tokenPartnerResult.Item1, + string.Format("https://api.partnercenter.microsoft.com/v1/customers/{0}/applicationconsents/{1}", CustomerId, CPVApplicationId)); + Console.WriteLine(JsonConvert.SerializeObject(consentDeletion)); + + // create new grants for the application given the setting in application grants payload. + JObject consentCreation = await ApiCalls.PostAsync( + tokenPartnerResult.Item1, + string.Format("https://api.partnercenter.microsoft.com/v1/customers/{0}/applicationconsents", CustomerId), + contents.ToString()); + Console.WriteLine(JsonConvert.SerializeObject(consentCreation)); + + + Console.WriteLine(" ===================== Customer graph API calls ============================", DateTime.Now); + + Tuple tokenCustomerResult = await LoginToCustomerGraph(PartnerId, CustomerId); + JObject customerDomainsUsingGraph = await ApiCalls.GetAsync(tokenCustomerResult.Item1, "https://graph.windows.net/" + CustomerId + "/domains?api-version=1.6"); + Console.WriteLine(JsonConvert.SerializeObject(customerDomainsUsingGraph)); + + Console.ReadLine(); + } + + public static async Task> LoginToPartnerCenter(string tenantId) + { + KeyVaultProvider provider = new KeyVaultProvider(); + string refreshToken = await provider.GetSecretAsync(tenantId); + + JObject token = await AuthorizationUtilities.GetAADTokenFromRefreshToken( + "https://login.microsoftonline.com/" + tenantId, + "https://api.partnercenter.microsoft.com", + CPVApplicationId, + CPVApplicationSecret, + refreshToken); + + return new Tuple(token["access_token"].ToString(), DateTimeOffset.UtcNow + TimeSpan.FromTicks(long.Parse(token["expires_on"].ToString()))); + } + + /// + /// Generates a token for Partner tenant using partner user refresh token + /// The expiry time calculation explined below is not strong. + /// please use stardard ADAL library to gain access token by refresh token, which provides strongly typed classes with proper expirty time calculation. + /// + /// partner tenant id + /// + /// Access token and expiry time. + /// + public static async Task> LoginToGraph(string partnerTenantId) + { + KeyVaultProvider provider = new KeyVaultProvider(); + string refreshToken = await provider.GetSecretAsync(partnerTenantId); + + JObject token = await AuthorizationUtilities.GetAADTokenFromRefreshToken( + "https://login.microsoftonline.com/" + partnerTenantId, + "https://graph.microsoft.com", + CPVApplicationId, + CPVApplicationSecret, + refreshToken); + + return new Tuple(token["access_token"].ToString(), DateTimeOffset.UtcNow + TimeSpan.FromTicks(long.Parse(token["expires_on"].ToString()))); + } + + /// + /// Generates a token for Customer tenant using partner user refresh token + /// The expiry time calculation explined below is not strong. + /// please use stardard ADAL library to gain access token by refresh token, which provides strongly typed classes with proper expirty time calculation. + /// + /// partner tenant id + /// customer tenant id + /// + /// Access token and expiry time. + /// + public static async Task> LoginToCustomerGraph(string partnerTenantId, string customerTenantId) + { + KeyVaultProvider provider = new KeyVaultProvider(); + string refreshToken = await provider.GetSecretAsync(partnerTenantId); + + JObject token = await AuthorizationUtilities.GetAADTokenFromRefreshToken( + "https://login.microsoftonline.com/" + customerTenantId, + "https://graph.windows.net", + CPVApplicationId, + CPVApplicationSecret, + refreshToken); + + return new Tuple(token["access_token"].ToString(), DateTimeOffset.UtcNow + TimeSpan.FromTicks(long.Parse(token["expires_on"].ToString()))); + } + + /// + /// Using Partner center .NET SDK to make partner center calls. If you are using java application, you can either us REST calls or use Java SDK to make partner center calls + /// In all the mentioned cases, the token generated by LoginToPartnerCenter method should work. + /// + /// partner tenant id + /// SDK reference for contextual calls + public static async Task GetUserPartnerOperationsAsync(string PartnerId) + { + Tuple aadAuthenticationResult = await LoginToPartnerCenter(PartnerId); + + // Authenticate by user context with the partner service + IPartnerCredentials userCredentials = PartnerCredentials.Instance.GenerateByUserCredentials( + CPVApplicationId, + new AuthenticationToken( + aadAuthenticationResult.Item1, + aadAuthenticationResult.Item2), + async delegate + { + // token has expired, re-Login to Azure Active Directory + Tuple aadToken = await LoginToPartnerCenter(PartnerId); + return new AuthenticationToken(aadToken.Item1, aadToken.Item2); + }); + + return PartnerService.Instance.CreatePartnerOperations(userCredentials); + } + } +} \ No newline at end of file diff --git a/secure-app-model/keyvault/CPVApplication/Properties/AssemblyInfo.cs b/secure-app-model/keyvault/CPVApplication/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..310c068 --- /dev/null +++ b/secure-app-model/keyvault/CPVApplication/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CPVApplication")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CPVApplication")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("9283af7a-6ca9-4373-93cb-f5d2130e1889")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/secure-app-model/keyvault/CPVApplication/Utilities/ApiCalls.cs b/secure-app-model/keyvault/CPVApplication/Utilities/ApiCalls.cs new file mode 100644 index 0000000..8c4061e --- /dev/null +++ b/secure-app-model/keyvault/CPVApplication/Utilities/ApiCalls.cs @@ -0,0 +1,140 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ----------------------------------------------------------------------- + +namespace CPVApplication.Utilities +{ + using System.IO; + using System.Net; + using System.Text; + using System.Threading.Tasks; + using Newtonsoft.Json.Linq; + + public static class ApiCalls + { + /// + /// Gets response for a REST api + /// + /// authorization token + /// REST url for the resource + /// response from the rest url + public static async Task GetAsync(string token, string url) + { + WebRequest request = WebRequest.Create(url); + request.Method = "GET"; + request.Headers.Add("Authorization", "Bearer " + token); + + try + { + WebResponse response = await request.GetResponseAsync(); + using (StreamReader reader = new StreamReader(response.GetResponseStream())) + { + string responseContent = reader.ReadToEnd(); + JObject adResponse = + Newtonsoft.Json.JsonConvert.DeserializeObject(responseContent); + return adResponse; + } + } + catch (WebException webException) + { + if (webException.Response != null) + { + using (StreamReader reader = new StreamReader(webException.Response.GetResponseStream())) + { + string responseContent = reader.ReadToEnd(); + return Newtonsoft.Json.JsonConvert.DeserializeObject(responseContent); ; + } + } + } + + return null; + } + + /// + /// Post for a REST api + /// + /// authorization token + /// REST url for the resource + /// content + /// response from the rest url + public static async Task PostAsync(string token, string url, string content) + { + byte[] data = Encoding.UTF8.GetBytes(content); + WebRequest request = WebRequest.Create(url); + request.Method = "POST"; + request.ContentType = "application/json"; + request.Headers.Add("Authorization", "Bearer " + token); + request.ContentLength = data.Length; + + using (Stream stream = request.GetRequestStream()) + { + stream.Write(data, 0, data.Length); + } + + try + { + WebResponse response = await request.GetResponseAsync(); + using (StreamReader reader = new StreamReader(response.GetResponseStream())) + { + string responseContent = reader.ReadToEnd(); + JObject adResponse = + Newtonsoft.Json.JsonConvert.DeserializeObject(responseContent); + return adResponse; + } + } + catch (WebException webException) + { + if (webException.Response != null) + { + using (StreamReader reader = new StreamReader(webException.Response.GetResponseStream())) + { + string responseContent = reader.ReadToEnd(); + return Newtonsoft.Json.JsonConvert.DeserializeObject(responseContent); ; + } + } + } + + return null; + } + + /// + /// Delete response for a REST api + /// + /// authorization token + /// REST url for the resource + /// response from the rest url + public static async Task DeleteAsync(string token, string url) + { + WebRequest request = WebRequest.Create(url); + request.Method = "DELETE"; + request.Headers.Add("Authorization", "Bearer " + token); + + try + { + WebResponse response = await request.GetResponseAsync(); + using (StreamReader reader = new StreamReader(response.GetResponseStream())) + { + string responseContent = reader.ReadToEnd(); + JObject adResponse = + Newtonsoft.Json.JsonConvert.DeserializeObject(responseContent); + return adResponse; + } + } + catch (WebException webException) + { + if (webException.Response != null) + { + using (StreamReader reader = new StreamReader(webException.Response.GetResponseStream())) + { + string responseContent = reader.ReadToEnd(); + return Newtonsoft.Json.JsonConvert.DeserializeObject(responseContent); ; + } + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/secure-app-model/keyvault/CPVApplication/Utilities/AuthorizationUtilities.cs b/secure-app-model/keyvault/CPVApplication/Utilities/AuthorizationUtilities.cs new file mode 100644 index 0000000..8ec5149 --- /dev/null +++ b/secure-app-model/keyvault/CPVApplication/Utilities/AuthorizationUtilities.cs @@ -0,0 +1,108 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ----------------------------------------------------------------------- + +namespace CPVApplication.Utilities +{ + using System.IO; + using System.Net; + using System.Threading.Tasks; + using System.Web; + using Newtonsoft.Json.Linq; + + public static class AuthorizationUtilities + { + /// + /// Gets AAD token in refresh token flow + /// + /// AAD authority + /// Token audience + /// Marketplace application id + /// Marketplace application secret + /// refresh token + /// + public static async Task GetAADTokenFromRefreshToken(string authority, string audience, string clientId, string clientSecret, string refreshToken) + { + string loginUrl = string.Format("{0}/oauth2/token", authority); + + var request = WebRequest.Create(loginUrl); + + request.Method = "POST"; + request.ContentType = "application/x-www-form-urlencoded"; + + string content = string.Format( + "resource={0}&client_id={1}&client_secret={2}&grant_type=refresh_token&refresh_token={3}&scope=openid", + HttpUtility.UrlEncode(audience), + HttpUtility.UrlEncode(clientId), + HttpUtility.UrlEncode(clientSecret), + HttpUtility.UrlEncode(refreshToken)); + + return await GetResponse(request, content); + } + + /// + /// Gets AAD token in application only token in non-interactive service principal flow + /// + /// AAD authority + /// Token audience + /// AAD application id + /// AAD application secret + public static async Task GetADAppToken(string authority, string audience, string clientId, string clientSecret) + { + string loginUrl = string.Format("{0}/oauth2/token", authority); + + var request = WebRequest.Create(loginUrl); + + request.Method = "POST"; + request.ContentType = "application/x-www-form-urlencoded"; + + string content = string.Format( + "resource={0}&client_id={1}&client_secret={2}&grant_type=client_credentials", + HttpUtility.UrlEncode(audience), + HttpUtility.UrlEncode(clientId), + HttpUtility.UrlEncode(clientSecret)); + + return await GetResponse(request, content); + } + + /// + /// Helper function to execute webrequest and parse response as JObject + /// + /// web request + /// request content + /// + private static async Task GetResponse(WebRequest request, string content) + { + using (var writer = new StreamWriter(request.GetRequestStream())) + { + writer.Write(content); + } + + try + { + var response = await request.GetResponseAsync(); + using (var reader = new StreamReader(response.GetResponseStream())) + { + var responseContent = reader.ReadToEnd(); + var adResponse = + Newtonsoft.Json.JsonConvert.DeserializeObject(responseContent); + return adResponse; + } + } + catch (WebException webException) + { + if (webException.Response != null) + { + using (var reader = new StreamReader(webException.Response.GetResponseStream())) + { + var responseContent = reader.ReadToEnd(); + } + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/secure-app-model/keyvault/CPVApplication/Utilities/KeyVaultProvider.cs b/secure-app-model/keyvault/CPVApplication/Utilities/KeyVaultProvider.cs new file mode 100644 index 0000000..b9f2a78 --- /dev/null +++ b/secure-app-model/keyvault/CPVApplication/Utilities/KeyVaultProvider.cs @@ -0,0 +1,71 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ----------------------------------------------------------------------- + +namespace CPVApplication.Utilities +{ + using System.Configuration; + using System.Net.Http; + using System.Threading.Tasks; + using Microsoft.Azure.KeyVault; + using Microsoft.Azure.KeyVault.Models; + using Newtonsoft.Json.Linq; + + /// + /// Provider for accessing secrets from the Azure KeyVault + /// + public class KeyVaultProvider + { + /// + /// The client used to perform HTTP operations. + /// + private static readonly HttpClient httpClient = new HttpClient(); + + /// + /// The base address for the instance of Azure Key Vault. + /// + private readonly string BaseUrl = ConfigurationManager.AppSettings["KeyVaultEndpoint"]; + + /// + /// The identifier for the Azure AD application configured to access the instance of Azure Key Vault. + /// + private readonly string KeyVaultClientId = ConfigurationManager.AppSettings["ida:KeyVaultClientId"]; + + /// + /// The application secret for the Azure AD application configured to access the instance of Azure Key Vault. + /// + private readonly string KeyVaultClientSecret = ConfigurationManager.AppSettings["ida:KeyVaultClientSecret"]; + + /// + /// The client used to interact with the instance of Azure Key Vault. + /// + private readonly KeyVaultClient keyVaultClient; + + /// + /// Initializes a new instance of the class. + /// + public KeyVaultProvider() + { + keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(GetToken), httpClient); + } + + /// + /// Gets the specified entity from the vault. + /// + /// Identifier of the entity to be retrieved. + /// The value retrieved from the vault. + public async Task GetSecretAsync(string key) + { + SecretBundle secret = await keyVaultClient.GetSecretAsync(BaseUrl, key.Replace("@", string.Empty).Replace(".", string.Empty)).ConfigureAwait(false); + return secret.Value; + } + + private async Task GetToken(string authority, string resource, string scope) + { + JObject tokenResult = await AuthorizationUtilities.GetADAppToken(authority, resource, KeyVaultClientId, KeyVaultClientSecret); + return tokenResult["access_token"].ToString(); + } + } +} \ No newline at end of file diff --git a/secure-app-model/keyvault/CPVApplication/packages.config b/secure-app-model/keyvault/CPVApplication/packages.config new file mode 100644 index 0000000..5d1d7c9 --- /dev/null +++ b/secure-app-model/keyvault/CPVApplication/packages.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/secure-app-model/keyvault/CSPApplication.sln b/secure-app-model/keyvault/CSPApplication.sln new file mode 100644 index 0000000..25f3c88 --- /dev/null +++ b/secure-app-model/keyvault/CSPApplication.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2026 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSPApplication", "CSPApplication\CSPApplication.csproj", "{9283AF7A-6CA9-4373-93CB-F5D2130E1889}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9283AF7A-6CA9-4373-93CB-F5D2130E1889}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9283AF7A-6CA9-4373-93CB-F5D2130E1889}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9283AF7A-6CA9-4373-93CB-F5D2130E1889}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9283AF7A-6CA9-4373-93CB-F5D2130E1889}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3CBE281A-E046-480F-8FC6-34536B951BEC} + EndGlobalSection +EndGlobal diff --git a/secure-app-model/keyvault/CSPApplication/App.config b/secure-app-model/keyvault/CSPApplication/App.config new file mode 100644 index 0000000..78c4ad3 --- /dev/null +++ b/secure-app-model/keyvault/CSPApplication/App.config @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/secure-app-model/keyvault/CSPApplication/CSPApplication.csproj b/secure-app-model/keyvault/CSPApplication/CSPApplication.csproj new file mode 100644 index 0000000..0406a45 --- /dev/null +++ b/secure-app-model/keyvault/CSPApplication/CSPApplication.csproj @@ -0,0 +1,109 @@ + + + + + Debug + AnyCPU + {9283AF7A-6CA9-4373-93CB-F5D2130E1889} + Exe + CPVApplication + CPVApplication + v4.6.1 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.Azure.KeyVault.3.0.1\lib\net452\Microsoft.Azure.KeyVault.dll + + + ..\packages\Microsoft.Azure.KeyVault.WebKey.3.0.1\lib\net452\Microsoft.Azure.KeyVault.WebKey.dll + + + ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.4.4.0\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll + + + ..\packages\Microsoft.Rest.ClientRuntime.2.3.18\lib\net452\Microsoft.Rest.ClientRuntime.dll + + + ..\packages\Microsoft.Rest.ClientRuntime.Azure.3.3.18\lib\net452\Microsoft.Rest.ClientRuntime.Azure.dll + + + ..\packages\Microsoft.Store.PartnerCenter.1.10.0\lib\Net45\Microsoft.Store.PartnerCenter.dll + + + ..\packages\Microsoft.Store.PartnerCenter.1.10.0\lib\Net45\Microsoft.Store.PartnerCenter.Extensions.dll + + + ..\packages\Microsoft.Store.PartnerCenter.1.10.0\lib\Net45\Microsoft.Store.PartnerCenter.Models.dll + + + ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + + + + + + + + + ..\packages\System.Net.Http.4.3.4\lib\net46\System.Net.Http.dll + + + + + + + ..\packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net461\System.Security.Cryptography.Algorithms.dll + + + ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll + + + ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll + + + ..\packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net461\System.Security.Cryptography.X509Certificates.dll + + + + + + + + + + + + + + + + + + + Designer + + + + + \ No newline at end of file diff --git a/secure-app-model/keyvault/CSPApplication/Program.cs b/secure-app-model/keyvault/CSPApplication/Program.cs new file mode 100644 index 0000000..0cfc32a --- /dev/null +++ b/secure-app-model/keyvault/CSPApplication/Program.cs @@ -0,0 +1,150 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ----------------------------------------------------------------------- + +namespace CSPApplication +{ + using System; + using System.Configuration; + using System.Threading.Tasks; + using CSPApplication.Utilities; + using Microsoft.Store.PartnerCenter; + using Microsoft.Store.PartnerCenter.Extensions; + using Microsoft.Store.PartnerCenter.Models; + using Microsoft.Store.PartnerCenter.Models.Users; + using Newtonsoft.Json; + + internal class Program + { + /// + /// The following code assumes that the context of the partner is pre determined by some external code + /// (based on marketplace application logic: either by product or by partner context) + /// + + private static readonly string CSPApplicationId = ConfigurationManager.AppSettings["ida:CSPApplicationId"]; + private static readonly string CSPApplicationSecret = ConfigurationManager.AppSettings["ida:CSPApplicationSecret"]; + + private static void Main(string[] args) + { + Task.Run(() => Run()).Wait(); + } + + private static async Task Run() + { + // The following properties indicate which partner and customer context the calls are going to be made. + string PartnerId = ""; + string CustomerId = ""; + + Console.WriteLine(" ===================== Partner center API calls ============================", DateTime.Now); + IAggregatePartner ops = await GetUserPartnerOperationsAsync(PartnerId); + SeekBasedResourceCollection customerUsers = ops.Customers.ById(CustomerId).Users.Get(); + Console.WriteLine(JsonConvert.SerializeObject(customerUsers)); + + Console.WriteLine(" ===================== Partner graph API calls ============================", DateTime.Now); + Tuple tokenResult = await LoginToGraph(PartnerId); + Newtonsoft.Json.Linq.JObject mydetails = await ApiCalls.GetAsync(tokenResult.Item1, "https://graph.microsoft.com/v1.0/me"); + Console.WriteLine(JsonConvert.SerializeObject(mydetails)); + + // CSP partner applications are pre-consented into customer tenant, so they do not need a custom consent from customer. + // direct token acquire to a customer tenant should work. + Console.WriteLine(" ===================== Customer graph API calls ============================", DateTime.Now); + Tuple tokenCustomerResult = await LoginToCustomerGraph(PartnerId, CustomerId); + Newtonsoft.Json.Linq.JObject customerDomainsUsingGraph = await ApiCalls.GetAsync(tokenCustomerResult.Item1, "https://graph.windows.net/" + CustomerId + "/domains?api-version=1.6"); + Console.WriteLine(JsonConvert.SerializeObject(customerDomainsUsingGraph)); + + Console.ReadLine(); + } + + public static async Task> LoginToPartnerCenter(string tenantId) + { + KeyVaultProvider provider = new KeyVaultProvider(); + string refreshToken = await provider.GetSecretAsync(tenantId); + + Newtonsoft.Json.Linq.JObject token = await AuthorizationUtilities.GetAADTokenFromRefreshToken( + "https://login.microsoftonline.com/" + tenantId, + "https://api.partnercenter.microsoft.com", + CSPApplicationId, + CSPApplicationSecret, + refreshToken); + + return new Tuple(token["access_token"].ToString(), DateTimeOffset.UtcNow + TimeSpan.FromTicks(long.Parse(token["expires_on"].ToString()))); + } + + /// + /// Generates a token for Partner tenant using partner user refresh token + /// The expiry time calculation explined below is not strong. + /// please use stardard ADAL library to gain access token by refresh token, which provides strongly typed classes with proper expirty time calculation. + /// + /// partner tenant id + /// + /// Access token and expiry time. + /// + public static async Task> LoginToGraph(string partnerTenantId) + { + KeyVaultProvider provider = new KeyVaultProvider(); + string refreshToken = await provider.GetSecretAsync(partnerTenantId); + + Newtonsoft.Json.Linq.JObject token = await AuthorizationUtilities.GetAADTokenFromRefreshToken( + "https://login.microsoftonline.com/" + partnerTenantId, + "https://graph.microsoft.com", + CSPApplicationId, + CSPApplicationSecret, + refreshToken); + + return new Tuple(token["access_token"].ToString(), DateTimeOffset.UtcNow + TimeSpan.FromTicks(long.Parse(token["expires_on"].ToString()))); + } + + /// + /// Generates a token for Customer tenant using partner user refresh token + /// The expiry time calculation explined below is not strong. + /// please use stardard ADAL library to gain access token by refresh token, which provides strongly typed classes with proper expirty time calculation. + /// + /// partner tenant id + /// customer tenant id + /// + /// Access token and expiry time. + /// + public static async Task> LoginToCustomerGraph(string partnerTenantId, string customerTenantId) + { + KeyVaultProvider provider = new KeyVaultProvider(); + string refreshToken = await provider.GetSecretAsync(partnerTenantId); + + Newtonsoft.Json.Linq.JObject token = await AuthorizationUtilities.GetAADTokenFromRefreshToken( + "https://login.microsoftonline.com/" + customerTenantId, + "https://graph.windows.net", + CSPApplicationId, + CSPApplicationSecret, + refreshToken); + + return new Tuple(token["access_token"].ToString(), DateTimeOffset.UtcNow + TimeSpan.FromTicks(long.Parse(token["expires_on"].ToString()))); + } + + /// + /// Using Partner center .NET SDK to make partner center calls. If you are using java application, you can either us REST calls or use Java SDK to make partner center calls + /// In all the mentioned cases, the token generated by LoginToPartnerCenter method should work. + /// + /// partner tenant id + /// SDK reference for contextual calls + public static async Task GetUserPartnerOperationsAsync(string PartnerId) + { + Tuple aadAuthenticationResult = await LoginToPartnerCenter(PartnerId); + + // Authenticate by user context with the partner service + IPartnerCredentials userCredentials = PartnerCredentials.Instance.GenerateByUserCredentials( + CSPApplicationId, + new AuthenticationToken( + aadAuthenticationResult.Item1, + aadAuthenticationResult.Item2), + async delegate + { + // token has expired, re-Login to Azure Active Directory + Tuple aadToken = await LoginToPartnerCenter(PartnerId); + return new AuthenticationToken(aadToken.Item1, aadToken.Item2); + }); + + return PartnerService.Instance.CreatePartnerOperations(userCredentials); + } + } +} \ No newline at end of file diff --git a/secure-app-model/keyvault/CSPApplication/Properties/AssemblyInfo.cs b/secure-app-model/keyvault/CSPApplication/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a379108 --- /dev/null +++ b/secure-app-model/keyvault/CSPApplication/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CSPApplication")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CSPApplication")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("9283af7a-6ca9-4373-93cb-f5d2130e1889")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/secure-app-model/keyvault/CSPApplication/Utilities/ApiCalls.cs b/secure-app-model/keyvault/CSPApplication/Utilities/ApiCalls.cs new file mode 100644 index 0000000..278c8cb --- /dev/null +++ b/secure-app-model/keyvault/CSPApplication/Utilities/ApiCalls.cs @@ -0,0 +1,141 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ----------------------------------------------------------------------- + +namespace CSPApplication.Utilities +{ + using System.IO; + using System.Net; + using System.Text; + using System.Threading.Tasks; + using Newtonsoft.Json.Linq; + + public static class ApiCalls + { + /// + /// Gets response for a REST api + /// + /// authorization token + /// REST url for the resource + /// response from the rest url + public static async Task GetAsync(string token, string url) + { + WebRequest request = WebRequest.Create(url); + request.Method = "GET"; + request.Headers.Add("Authorization", "Bearer " + token); + + try + { + WebResponse response = await request.GetResponseAsync(); + + using (StreamReader reader = new StreamReader(response.GetResponseStream())) + { + string responseContent = reader.ReadToEnd(); + JObject adResponse = + Newtonsoft.Json.JsonConvert.DeserializeObject(responseContent); + return adResponse; + } + } + catch (WebException webException) + { + if (webException.Response != null) + { + using (StreamReader reader = new StreamReader(webException.Response.GetResponseStream())) + { + string responseContent = reader.ReadToEnd(); + return Newtonsoft.Json.JsonConvert.DeserializeObject(responseContent); ; + } + } + } + + return null; + } + + /// + /// Post for a REST api + /// + /// authorization token + /// REST url for the resource + /// content + /// response from the rest url + public static async Task PostAsync(string token, string url, string content) + { + byte[] data = Encoding.UTF8.GetBytes(content); + WebRequest request = WebRequest.Create(url); + request.Method = "POST"; + request.ContentType = "application/json"; + request.Headers.Add("Authorization", "Bearer " + token); + request.ContentLength = data.Length; + + using (Stream stream = request.GetRequestStream()) + { + stream.Write(data, 0, data.Length); + } + + try + { + WebResponse response = await request.GetResponseAsync(); + using (StreamReader reader = new StreamReader(response.GetResponseStream())) + { + string responseContent = reader.ReadToEnd(); + JObject adResponse = + Newtonsoft.Json.JsonConvert.DeserializeObject(responseContent); + return adResponse; + } + } + catch (WebException webException) + { + if (webException.Response != null) + { + using (StreamReader reader = new StreamReader(webException.Response.GetResponseStream())) + { + string responseContent = reader.ReadToEnd(); + return Newtonsoft.Json.JsonConvert.DeserializeObject(responseContent); ; + } + } + } + + return null; + } + + /// + /// Delete response for a REST api + /// + /// authorization token + /// REST url for the resource + /// response from the rest url + public static async Task DeleteAsync(string token, string url) + { + WebRequest request = WebRequest.Create(url); + request.Method = "DELETE"; + request.Headers.Add("Authorization", "Bearer " + token); + + try + { + WebResponse response = await request.GetResponseAsync(); + using (StreamReader reader = new StreamReader(response.GetResponseStream())) + { + string responseContent = reader.ReadToEnd(); + JObject adResponse = + Newtonsoft.Json.JsonConvert.DeserializeObject(responseContent); + return adResponse; + } + } + catch (WebException webException) + { + if (webException.Response != null) + { + using (StreamReader reader = new StreamReader(webException.Response.GetResponseStream())) + { + string responseContent = reader.ReadToEnd(); + return Newtonsoft.Json.JsonConvert.DeserializeObject(responseContent); ; + } + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/secure-app-model/keyvault/CSPApplication/Utilities/AuthorizationUtilities.cs b/secure-app-model/keyvault/CSPApplication/Utilities/AuthorizationUtilities.cs new file mode 100644 index 0000000..fc936b3 --- /dev/null +++ b/secure-app-model/keyvault/CSPApplication/Utilities/AuthorizationUtilities.cs @@ -0,0 +1,108 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ----------------------------------------------------------------------- + +namespace CSPApplication.Utilities +{ + using System.IO; + using System.Net; + using System.Threading.Tasks; + using System.Web; + using Newtonsoft.Json.Linq; + + public static class AuthorizationUtilities + { + /// + /// Gets AAD token in refresh token flow + /// + /// AAD authority + /// Token audience + /// Marketplace application id + /// Marketplace application secret + /// refresh token + /// + public static async Task GetAADTokenFromRefreshToken(string authority, string audience, string clientId, string clientSecret, string refreshToken) + { + string loginUrl = string.Format("{0}/oauth2/token", authority); + + WebRequest request = WebRequest.Create(loginUrl); + + request.Method = "POST"; + request.ContentType = "application/x-www-form-urlencoded"; + + string content = string.Format( + "resource={0}&client_id={1}&client_secret={2}&grant_type=refresh_token&refresh_token={3}&scope=openid", + HttpUtility.UrlEncode(audience), + HttpUtility.UrlEncode(clientId), + HttpUtility.UrlEncode(clientSecret), + HttpUtility.UrlEncode(refreshToken)); + + return await GetResponse(request, content); + } + + /// + /// Gets AAD token in application only token in non-interactive service principal flow + /// + /// AAD authority + /// Token audience + /// AAD application id + /// AAD application secret + public static async Task GetADAppToken(string authority, string audience, string clientId, string clientSecret) + { + string loginUrl = string.Format("{0}/oauth2/token", authority); + + WebRequest request = WebRequest.Create(loginUrl); + + request.Method = "POST"; + request.ContentType = "application/x-www-form-urlencoded"; + + string content = string.Format( + "resource={0}&client_id={1}&client_secret={2}&grant_type=client_credentials", + HttpUtility.UrlEncode(audience), + HttpUtility.UrlEncode(clientId), + HttpUtility.UrlEncode(clientSecret)); + + return await GetResponse(request, content); + } + + /// + /// Helper function to execute webrequest and parse response as JObject + /// + /// web request + /// request content + /// + private static async Task GetResponse(WebRequest request, string content) + { + using (StreamWriter writer = new StreamWriter(request.GetRequestStream())) + { + writer.Write(content); + } + + try + { + WebResponse response = await request.GetResponseAsync(); + using (StreamReader reader = new StreamReader(response.GetResponseStream())) + { + string responseContent = reader.ReadToEnd(); + JObject adResponse = + Newtonsoft.Json.JsonConvert.DeserializeObject(responseContent); + return adResponse; + } + } + catch (WebException webException) + { + if (webException.Response != null) + { + using (StreamReader reader = new StreamReader(webException.Response.GetResponseStream())) + { + string responseContent = reader.ReadToEnd(); + } + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/secure-app-model/keyvault/CSPApplication/Utilities/KeyVaultProvider.cs b/secure-app-model/keyvault/CSPApplication/Utilities/KeyVaultProvider.cs new file mode 100644 index 0000000..8c520b2 --- /dev/null +++ b/secure-app-model/keyvault/CSPApplication/Utilities/KeyVaultProvider.cs @@ -0,0 +1,37 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ----------------------------------------------------------------------- + +namespace CSPApplication.Utilities +{ + using System.Configuration; + using System.Threading.Tasks; + using Microsoft.Azure.KeyVault; + using Microsoft.Azure.KeyVault.Models; + using Newtonsoft.Json.Linq; + + /// + /// Provider for accessing secrets from the Azure KeyVault + /// + public class KeyVaultProvider + { + private readonly string KeyVaultClientId = ConfigurationManager.AppSettings["ida:KeyVaultClientId"]; + private readonly string KeyVaultClientSecret = ConfigurationManager.AppSettings["ida:KeyVaultClientSecret"]; + private readonly string BaseUrl = ConfigurationManager.AppSettings["KeyVaultEndpoint"]; + + public async Task GetSecretAsync(string key) + { + KeyVaultClient keyVault = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(this.GetToken), new System.Net.Http.HttpClient()); + SecretBundle secret = await keyVault.GetSecretAsync(this.BaseUrl, key.Replace("@", string.Empty).Replace(".", string.Empty)); + return secret.Value; + } + + private async Task GetToken(string authority, string resource, string scope) + { + JObject tokenResult = await AuthorizationUtilities.GetADAppToken(authority, resource, this.KeyVaultClientId, this.KeyVaultClientSecret); + return tokenResult["access_token"].ToString(); + } + } +} \ No newline at end of file diff --git a/secure-app-model/keyvault/CSPApplication/packages.config b/secure-app-model/keyvault/CSPApplication/packages.config new file mode 100644 index 0000000..647dbb5 --- /dev/null +++ b/secure-app-model/keyvault/CSPApplication/packages.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/secure-app-model/keyvault/PartnerConsent.sln b/secure-app-model/keyvault/PartnerConsent.sln new file mode 100644 index 0000000..076127e --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2026 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PartnerConsent", "PartnerConsent\PartnerConsent.csproj", "{CCD6D318-8E1F-4825-9A2F-AFA6DF76933C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CCD6D318-8E1F-4825-9A2F-AFA6DF76933C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CCD6D318-8E1F-4825-9A2F-AFA6DF76933C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CCD6D318-8E1F-4825-9A2F-AFA6DF76933C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CCD6D318-8E1F-4825-9A2F-AFA6DF76933C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {52BF6D3C-3E96-42D3-A9C7-0F0C18A3D962} + EndGlobalSection +EndGlobal diff --git a/secure-app-model/keyvault/PartnerConsent/App_Start/BundleConfig.cs b/secure-app-model/keyvault/PartnerConsent/App_Start/BundleConfig.cs new file mode 100644 index 0000000..6d6c4ae --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/App_Start/BundleConfig.cs @@ -0,0 +1,19 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ----------------------------------------------------------------------- + +namespace PartnerConsent +{ + using System.Web.Optimization; + + public static class BundleConfig + { + public static void RegisterBundles(BundleCollection bundles) + { + bundles.Add(new StyleBundle("~/Content/css").Include( + "~/Content/site.css")); + } + } +} \ No newline at end of file diff --git a/secure-app-model/keyvault/PartnerConsent/App_Start/FilterConfig.cs b/secure-app-model/keyvault/PartnerConsent/App_Start/FilterConfig.cs new file mode 100644 index 0000000..1e9e6ad --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/App_Start/FilterConfig.cs @@ -0,0 +1,18 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ----------------------------------------------------------------------- + +namespace PartnerConsent +{ + using System.Web.Mvc; + + public static class FilterConfig + { + public static void RegisterGlobalFilters(GlobalFilterCollection filters) + { + filters.Add(new HandleErrorAttribute()); + } + } +} \ No newline at end of file diff --git a/secure-app-model/keyvault/PartnerConsent/App_Start/RouteConfig.cs b/secure-app-model/keyvault/PartnerConsent/App_Start/RouteConfig.cs new file mode 100644 index 0000000..fbce47c --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/App_Start/RouteConfig.cs @@ -0,0 +1,29 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ----------------------------------------------------------------------- + +namespace PartnerConsent +{ + using System.Web.Mvc; + using System.Web.Routing; + + public static class RouteConfig + { + /// + /// Register ASP.NET routes for the web application + /// + /// + public static void RegisterRoutes(RouteCollection routes) + { + routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); + + routes.MapRoute( + name: "Default", + url: "{controller}/{action}/{id}", + defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } + ); + } + } +} \ No newline at end of file diff --git a/secure-app-model/keyvault/PartnerConsent/App_Start/Startup.Auth.cs b/secure-app-model/keyvault/PartnerConsent/App_Start/Startup.Auth.cs new file mode 100644 index 0000000..340a9d5 --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/App_Start/Startup.Auth.cs @@ -0,0 +1,94 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ----------------------------------------------------------------------- + +namespace PartnerConsent +{ + using System.Configuration; + using System.IdentityModel.Claims; + using System.Threading.Tasks; + using Microsoft.Owin.Security; + using Microsoft.Owin.Security.Cookies; + using Microsoft.Owin.Security.Notifications; + using Microsoft.Owin.Security.OpenIdConnect; + using Owin; + using Utilities; + + public partial class Startup + { + private static string CSPApplicationId = ConfigurationManager.AppSettings["ida:CSPApplicationId"]; + private static string CSPApplicationSecret = ConfigurationManager.AppSettings["ida:CSPApplicationSecret"]; + private static string AuthorityCommon = ConfigurationManager.AppSettings["ida:AADInstance"] + "common"; + + /// + /// Configure authentication pipeline + /// + /// Owin app builder + public void ConfigureAuth(IAppBuilder app) + { + app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType); + + app.UseCookieAuthentication(new CookieAuthenticationOptions { }); + + app.UseOpenIdConnectAuthentication( + new OpenIdConnectAuthenticationOptions + { + ClientId = CSPApplicationId, + Authority = AuthorityCommon, + TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters + { + // instead of using the default validation (validating against a single issuer value, as we do in line of business apps), + // we inject our own multitenant validation logic + ValidateIssuer = false, + // If the app needs access to the entire organization, then add the logic + // of validating the Issuer here. + // IssuerValidator + }, + Notifications = new OpenIdConnectAuthenticationNotifications() + { + SecurityTokenValidated = (context) => + { + // If your authentication logic is based on users then add your logic here + return Task.FromResult(0); + }, + AuthenticationFailed = (context) => + { + // Pass in the context back to the app + context.OwinContext.Response.Redirect("/Home/Error"); + context.HandleResponse(); // Suppress the exception + return Task.FromResult(0); + }, + AuthorizationCodeReceived = HandleAuthorizationCodeReceivedNotification + } + }); + } + + /// + /// Recieve the AuthCode and exchange it for an access token and refresh token + /// + /// + private static async Task HandleAuthorizationCodeReceivedNotification(AuthorizationCodeReceivedNotification notificationMessage) + { + string signInUserId = notificationMessage.AuthenticationTicket.Identity.FindFirst(ClaimTypes.Upn).Value; + string partnerTenantId = notificationMessage.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value; + + // Acquire a token using AuthCode + Newtonsoft.Json.Linq.JObject tokenResult = await AuthorizationUtilities.GetAADTokenFromAuthCode( + "https://login.microsoftonline.com/" + partnerTenantId, + "https://api.partnercenter.microsoft.com", + CSPApplicationId, + CSPApplicationSecret, + notificationMessage.Code, + notificationMessage.OwinContext.Request.Uri.ToString()); + + string refreshToken = tokenResult["refresh_token"].ToString(); + + // Store the refresh token using partner tenant id as the key. + // Marketplace application will use the partner tenant id as a key to retrive the refresh token to get authenticated against the user. + KeyVaultProvider provider = new KeyVaultProvider(); + await provider.AddSecretAsync(partnerTenantId, refreshToken); + } + } +} diff --git a/secure-app-model/keyvault/PartnerConsent/ApplicationInsights.config b/secure-app-model/keyvault/PartnerConsent/ApplicationInsights.config new file mode 100644 index 0000000..75e12df --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/ApplicationInsights.config @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/secure-app-model/keyvault/PartnerConsent/Content/Site.css b/secure-app-model/keyvault/PartnerConsent/Content/Site.css new file mode 100644 index 0000000..6ea5d8f --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/Content/Site.css @@ -0,0 +1,24 @@ +body { + padding-top: 50px; + padding-bottom: 20px; +} + +/* Set padding to keep content from hitting the edges */ +.body-content { + padding-left: 15px; + padding-right: 15px; +} + +/* Override the default bootstrap behavior where horizontal description lists + will truncate terms that are too long to fit in the left column +*/ +.dl-horizontal dt { + white-space: normal; +} + +/* Set width on the form input elements since they're 100% wide by default */ +input, +select, +textarea { + max-width: 280px; +} diff --git a/secure-app-model/keyvault/PartnerConsent/Controllers/HomeController.cs b/secure-app-model/keyvault/PartnerConsent/Controllers/HomeController.cs new file mode 100644 index 0000000..012b1a3 --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/Controllers/HomeController.cs @@ -0,0 +1,33 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ----------------------------------------------------------------------- + +namespace PartnerConsent.Controllers +{ + using System.Web.Mvc; + + [Authorize] + public class HomeController : Controller + { + /// + /// Homepage - this is an example of response the consent application can present + /// + /// + public ActionResult Index() + { + return View(); + } + + /// + /// Error page controller to capture exceptions. + /// This is default implementation and must be tuned to specific experience for an application + /// + /// + public ActionResult Error() + { + return View(); + } + } +} \ No newline at end of file diff --git a/secure-app-model/keyvault/PartnerConsent/Global.asax b/secure-app-model/keyvault/PartnerConsent/Global.asax new file mode 100644 index 0000000..54faa36 --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/Global.asax @@ -0,0 +1 @@ +<%@ Application Codebehind="Global.asax.cs" Inherits="PartnerConsent.MvcApplication" Language="C#" %> diff --git a/secure-app-model/keyvault/PartnerConsent/Global.asax.cs b/secure-app-model/keyvault/PartnerConsent/Global.asax.cs new file mode 100644 index 0000000..aff9e5c --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/Global.asax.cs @@ -0,0 +1,23 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ----------------------------------------------------------------------- + +namespace PartnerConsent +{ + using System.Web.Mvc; + using System.Web.Optimization; + using System.Web.Routing; + + public class MvcApplication : System.Web.HttpApplication + { + protected void Application_Start() + { + AreaRegistration.RegisterAllAreas(); + FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); + RouteConfig.RegisterRoutes(RouteTable.Routes); + BundleConfig.RegisterBundles(BundleTable.Bundles); + } + } +} \ No newline at end of file diff --git a/secure-app-model/keyvault/PartnerConsent/PartnerConsent.csproj b/secure-app-model/keyvault/PartnerConsent/PartnerConsent.csproj new file mode 100644 index 0000000..9e16958 --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/PartnerConsent.csproj @@ -0,0 +1,241 @@ + + + + + + + Debug + AnyCPU + + + 2.0 + {CCD6D318-8E1F-4825-9A2F-AFA6DF76933C} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + PartnerConsent + PartnerConsent + v4.6.1 + false + true + + 44395 + + + + + + + 0 + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + true + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + ..\packages\Antlr.3.5.0.2\lib\Antlr3.Runtime.dll + + + ..\packages\Microsoft.Azure.KeyVault.3.0.1\lib\net452\Microsoft.Azure.KeyVault.dll + + + ..\packages\Microsoft.Azure.KeyVault.WebKey.3.0.1\lib\net452\Microsoft.Azure.KeyVault.WebKey.dll + + + ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.1\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + + + + ..\packages\Microsoft.IdentityModel.JsonWebTokens.5.3.0\lib\net461\Microsoft.IdentityModel.JsonWebTokens.dll + + + ..\packages\Microsoft.IdentityModel.Logging.5.3.0\lib\net461\Microsoft.IdentityModel.Logging.dll + + + ..\packages\Microsoft.IdentityModel.Protocol.Extensions.1.0.4.403061554\lib\net45\Microsoft.IdentityModel.Protocol.Extensions.dll + + + ..\packages\Microsoft.IdentityModel.Protocols.5.3.0\lib\net461\Microsoft.IdentityModel.Protocols.dll + + + ..\packages\Microsoft.IdentityModel.Protocols.OpenIdConnect.5.3.0\lib\net461\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll + + + ..\packages\Microsoft.IdentityModel.Tokens.5.3.0\lib\net461\Microsoft.IdentityModel.Tokens.dll + + + ..\packages\Microsoft.Owin.4.0.0\lib\net451\Microsoft.Owin.dll + + + ..\packages\Microsoft.Owin.Host.SystemWeb.4.0.0\lib\net451\Microsoft.Owin.Host.SystemWeb.dll + + + ..\packages\Microsoft.Owin.Security.4.0.0\lib\net451\Microsoft.Owin.Security.dll + + + ..\packages\Microsoft.Owin.Security.Cookies.4.0.0\lib\net451\Microsoft.Owin.Security.Cookies.dll + + + ..\packages\Microsoft.Owin.Security.OpenIdConnect.4.0.0\lib\net451\Microsoft.Owin.Security.OpenIdConnect.dll + + + ..\packages\Microsoft.Rest.ClientRuntime.2.3.18\lib\net452\Microsoft.Rest.ClientRuntime.dll + + + ..\packages\Microsoft.Rest.ClientRuntime.Azure.3.3.18\lib\net452\Microsoft.Rest.ClientRuntime.Azure.dll + + + ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll + + + ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + + + ..\packages\Owin.1.0\lib\net40\Owin.dll + + + + + + + ..\packages\System.IdentityModel.Tokens.Jwt.5.3.0\lib\net461\System.IdentityModel.Tokens.Jwt.dll + + + + + + + + + + + + + + ..\packages\Microsoft.AspNet.WebPages.3.2.6\lib\net45\System.Web.Helpers.dll + + + ..\packages\Microsoft.AspNet.Mvc.5.2.6\lib\net45\System.Web.Mvc.dll + + + ..\packages\Microsoft.AspNet.Web.Optimization.1.1.3\lib\net40\System.Web.Optimization.dll + + + ..\packages\Microsoft.AspNet.Razor.3.2.6\lib\net45\System.Web.Razor.dll + + + + ..\packages\Microsoft.AspNet.WebPages.3.2.6\lib\net45\System.Web.WebPages.dll + + + ..\packages\Microsoft.AspNet.WebPages.3.2.6\lib\net45\System.Web.WebPages.Deployment.dll + + + ..\packages\Microsoft.AspNet.WebPages.3.2.6\lib\net45\System.Web.WebPages.Razor.dll + + + + + + + + + + + ..\packages\WebGrease.1.6.0\lib\WebGrease.dll + + + + + + + + + + + Global.asax + + + + + + + + + + + + Web.config + + + Web.config + + + + + + + + + + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + + + + + + + True + True + 3376 + / + https://localhost:44395/ + False + False + + + False + + + + + + + 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}. + + + + + + \ No newline at end of file diff --git a/secure-app-model/keyvault/PartnerConsent/Properties/AssemblyInfo.cs b/secure-app-model/keyvault/PartnerConsent/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..aed4309 --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PartnerConsent")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PartnerConsent")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("4fcd2826-5f2b-45ba-9268-172736b3e212")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/secure-app-model/keyvault/PartnerConsent/Startup.cs b/secure-app-model/keyvault/PartnerConsent/Startup.cs new file mode 100644 index 0000000..0f607e0 --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/Startup.cs @@ -0,0 +1,20 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ----------------------------------------------------------------------- + +[assembly: Microsoft.Owin.OwinStartup(typeof(PartnerConsent.Startup))] + +namespace PartnerConsent +{ + using Owin; + + public partial class Startup + { + public void Configuration(IAppBuilder app) + { + ConfigureAuth(app); + } + } +} \ No newline at end of file diff --git a/secure-app-model/keyvault/PartnerConsent/Utilities/AuthorizationUtilities.cs b/secure-app-model/keyvault/PartnerConsent/Utilities/AuthorizationUtilities.cs new file mode 100644 index 0000000..22d1f24 --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/Utilities/AuthorizationUtilities.cs @@ -0,0 +1,110 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ----------------------------------------------------------------------- + +namespace PartnerConsent.Utilities +{ + using System.IO; + using System.Net; + using System.Threading.Tasks; + using System.Web; + using Newtonsoft.Json.Linq; + + public static class AuthorizationUtilities + { + /// + /// Gets AAD token in auth-code interactive login flow + /// + /// AAD authority + /// Token audience + /// Marketplace application id + /// Marketplace application secret + /// Auth-Code recieved from AAD in interactive login flow + /// Marketplace application redirect url + /// + public static async Task GetAADTokenFromAuthCode(string authority, string audience, string clientId, string clientSecret, string authCode, string redirectUrl) + { + string loginUrl = string.Format("{0}/oauth2/token", authority); + WebRequest request = WebRequest.Create(loginUrl); + + request.Method = "POST"; + request.ContentType = "application/x-www-form-urlencoded"; + + // Use grant_type=authorization_code to get a token using authorization code + string content = string.Format( + "resource={0}&client_id={1}&client_secret={2}&grant_type=authorization_code&code={3}&redirect_uri={4}", + HttpUtility.UrlEncode(audience), + HttpUtility.UrlEncode(clientId), + HttpUtility.UrlEncode(clientSecret), + HttpUtility.UrlEncode(authCode), + HttpUtility.UrlEncode(redirectUrl)); + + return await GetResponse(request, content); + } + + /// + /// Gets AAD token in application only token in non-interactive service principal flow + /// + /// AAD authority + /// Token audience + /// AAD application id + /// AAD application secret + public static async Task GetADAppToken(string authority, string audience, string clientId, string clientSecret) + { + string loginUrl = string.Format("{0}/oauth2/token", authority); + + WebRequest request = WebRequest.Create(loginUrl); + + request.Method = "POST"; + request.ContentType = "application/x-www-form-urlencoded"; + + string content = string.Format( + "resource={0}&client_id={1}&client_secret={2}&grant_type=client_credentials", + HttpUtility.UrlEncode(audience), + HttpUtility.UrlEncode(clientId), + HttpUtility.UrlEncode(clientSecret)); + + return await GetResponse(request, content); + } + + /// + /// Helper function to execute webrequest and parse response as JObject + /// + /// web request + /// request content + /// + private static async Task GetResponse(WebRequest request, string content) + { + using (StreamWriter writer = new StreamWriter(request.GetRequestStream())) + { + writer.Write(content); + } + + try + { + WebResponse response = await request.GetResponseAsync(); + using (StreamReader reader = new StreamReader(response.GetResponseStream())) + { + string responseContent = reader.ReadToEnd(); + JObject adResponse = + Newtonsoft.Json.JsonConvert.DeserializeObject(responseContent); + return adResponse; + } + } + catch (WebException webException) + { + if (webException.Response != null) + { + using (StreamReader reader = new StreamReader(webException.Response.GetResponseStream())) + { + string responseContent = reader.ReadToEnd(); + } + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/secure-app-model/keyvault/PartnerConsent/Utilities/KeyVaultProvider.cs b/secure-app-model/keyvault/PartnerConsent/Utilities/KeyVaultProvider.cs new file mode 100644 index 0000000..ba96ee9 --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/Utilities/KeyVaultProvider.cs @@ -0,0 +1,65 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ----------------------------------------------------------------------- + +namespace PartnerConsent.Utilities +{ + using System.Configuration; + using System.Threading.Tasks; + using Microsoft.Azure.KeyVault; + using Microsoft.Azure.KeyVault.Models; + + + /// + /// Provider for accessing secrets from the Azure KeyVault + /// + public class KeyVaultProvider + { + private readonly string KeyVaultClientId = ConfigurationManager.AppSettings["ida:KeyVaultClientId"]; + private readonly string KeyVaultClientSecret = ConfigurationManager.AppSettings["ida:KeyVaultClientSecret"]; + private readonly string BaseUrl = ConfigurationManager.AppSettings["KeyVaultEndpoint"]; + + /// + /// Get secret value from azure key vault + /// + /// key name + /// key value in string format + public async Task GetSecretAsync(string key) + { + KeyVaultClient keyVault = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(this.GetToken), new System.Net.Http.HttpClient()); + + string secretIdentifier = this.BaseUrl + "/secrets/" + key.Replace("@", string.Empty).Replace(".", string.Empty); + SecretBundle secret = await keyVault.GetSecretAsync(secretIdentifier); + + return secret.Value; + } + + /// + /// Add or overrides a key value in Azure key vault + /// + /// key name + /// secret value + /// + public async Task AddSecretAsync(string key, string value) + { + KeyVaultClient keyVault = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(this.GetToken), new System.Net.Http.HttpClient()); + await keyVault.SetSecretAsync(this.BaseUrl, key.Replace("@", string.Empty).Replace(".", string.Empty), value); + } + + /// + /// Get application token for the app which is given access to Azure keyvault + /// Called by delegate in azure key vault SDK + /// + /// AAD authority + /// Azure key vault resource + /// authorization scope + /// + private async Task GetToken(string authority, string resource, string scope) + { + Newtonsoft.Json.Linq.JObject tokenResult = await AuthorizationUtilities.GetADAppToken(authority, resource, this.KeyVaultClientId, this.KeyVaultClientSecret); + return tokenResult["access_token"].ToString(); + } + } +} \ No newline at end of file diff --git a/secure-app-model/keyvault/PartnerConsent/Views/Home/Index.cshtml b/secure-app-model/keyvault/PartnerConsent/Views/Home/Index.cshtml new file mode 100644 index 0000000..6170a0a --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/Views/Home/Index.cshtml @@ -0,0 +1,19 @@ +@inherits System.Web.Mvc.WebViewPage + + + Partner Consent + + + + + Consent successful + + Partner consent to application has been granted. + For more information on how to manage the application in your Active directory tenant, please check here + You can manage applications at https://myapps.microsoft.com + + + close + + + \ No newline at end of file diff --git a/secure-app-model/keyvault/PartnerConsent/Views/Shared/Error.cshtml b/secure-app-model/keyvault/PartnerConsent/Views/Shared/Error.cshtml new file mode 100644 index 0000000..be55b17 --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/Views/Shared/Error.cshtml @@ -0,0 +1,9 @@ +@model System.Web.Mvc.HandleErrorInfo + +@{ + ViewBag.Title = "Error"; +} + +Error. +An error occurred while processing your request. + diff --git a/secure-app-model/keyvault/PartnerConsent/Views/Shared/_Layout.cshtml b/secure-app-model/keyvault/PartnerConsent/Views/Shared/_Layout.cshtml new file mode 100644 index 0000000..34933de --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/Views/Shared/_Layout.cshtml @@ -0,0 +1,16 @@ + + + + + + Partner Consent + @Styles.Render("~/Content/css") + @Scripts.Render("~/bundles/modernizr") + + + + @RenderBody() + + @RenderSection("scripts", required: false) + + diff --git a/secure-app-model/keyvault/PartnerConsent/Views/Web.config b/secure-app-model/keyvault/PartnerConsent/Views/Web.config new file mode 100644 index 0000000..e3f2a4b --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/Views/Web.config @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/secure-app-model/keyvault/PartnerConsent/Views/_ViewStart.cshtml b/secure-app-model/keyvault/PartnerConsent/Views/_ViewStart.cshtml new file mode 100644 index 0000000..2de6241 --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "~/Views/Shared/_Layout.cshtml"; +} diff --git a/secure-app-model/keyvault/PartnerConsent/Web.Debug.config b/secure-app-model/keyvault/PartnerConsent/Web.Debug.config new file mode 100644 index 0000000..d7712aa --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/Web.Debug.config @@ -0,0 +1,30 @@ + + + + + + + + + + diff --git a/secure-app-model/keyvault/PartnerConsent/Web.Release.config b/secure-app-model/keyvault/PartnerConsent/Web.Release.config new file mode 100644 index 0000000..28a4d5f --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/Web.Release.config @@ -0,0 +1,31 @@ + + + + + + + + + + + diff --git a/secure-app-model/keyvault/PartnerConsent/Web.config b/secure-app-model/keyvault/PartnerConsent/Web.config new file mode 100644 index 0000000..b9d2535 --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/Web.config @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/secure-app-model/keyvault/PartnerConsent/favicon.ico b/secure-app-model/keyvault/PartnerConsent/favicon.ico new file mode 100644 index 0000000..a3a7999 Binary files /dev/null and b/secure-app-model/keyvault/PartnerConsent/favicon.ico differ diff --git a/secure-app-model/keyvault/PartnerConsent/packages.config b/secure-app-model/keyvault/PartnerConsent/packages.config new file mode 100644 index 0000000..8f9c433 --- /dev/null +++ b/secure-app-model/keyvault/PartnerConsent/packages.config @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file
+ Partner consent to application has been granted. + For more information on how to manage the application in your Active directory tenant, please check here + You can manage applications at https://myapps.microsoft.com +
+ close +