From 6dbaab5598812af4c174534e56e8b3f8a54beda2 Mon Sep 17 00:00:00 2001 From: Isaiah Williams Date: Sat, 7 Jul 2018 09:09:12 -0500 Subject: [PATCH] Corrected authentication issue with creating and deleting users. Also, performed minor code cleanup. --- src/Explorer/Controllers/ManageController.cs | 6 +- src/Explorer/Explorer.csproj | 4 +- src/Explorer/GlobalSuppressions.cs | 9 ++ ...{ResourceManager.cs => AzureManagement.cs} | 8 +- src/Explorer/Logic/GraphClient.cs | 12 +- src/Explorer/Logic/HttpService.cs | 8 +- src/Explorer/Logic/Office/Result.cs | 12 +- src/Explorer/Logic/PartnerOperations.cs | 4 +- src/Explorer/Models/NewUserModel.cs | 3 +- src/Explorer/Models/PartnerCenterToken.cs | 2 +- src/Explorer/Views/Users/Create.cshtml | 141 ++++++++++-------- 11 files changed, 123 insertions(+), 86 deletions(-) create mode 100644 src/Explorer/GlobalSuppressions.cs rename src/Explorer/Logic/Azure/{ResourceManager.cs => AzureManagement.cs} (96%) diff --git a/src/Explorer/Controllers/ManageController.cs b/src/Explorer/Controllers/ManageController.cs index 65347db..59f5b20 100644 --- a/src/Explorer/Controllers/ManageController.cs +++ b/src/Explorer/Controllers/ManageController.cs @@ -167,7 +167,7 @@ namespace Microsoft.Store.PartnerCenter.Explorer.Controllers token = await GetAccessTokenAsync( $"{Provider.Configuration.ActiveDirectoryEndpoint}/{model.CustomerId}").ConfigureAwait(false); - using (ResourceManager manager = new ResourceManager(Provider, token.AccessToken)) + using (AzureManagement manager = new AzureManagement(Provider, token.AccessToken)) { await manager.ApplyTemplateAsync( model.SubscriptionId, @@ -215,7 +215,7 @@ namespace Microsoft.Store.PartnerCenter.Explorer.Controllers token = await GetAccessTokenAsync( $"{Provider.Configuration.ActiveDirectoryEndpoint}/{customerId}").ConfigureAwait(false); - using (ResourceManager manager = new ResourceManager(Provider, token.AccessToken)) + using (AzureManagement manager = new AzureManagement(Provider, token.AccessToken)) { groups = await manager.GetResourceGroupsAsync(subscriptionId).ConfigureAwait(false); return Json(groups, JsonRequestBehavior.AllowGet); @@ -249,7 +249,7 @@ namespace Microsoft.Store.PartnerCenter.Explorer.Controllers token = await GetAccessTokenAsync( $"{Provider.Configuration.ActiveDirectoryEndpoint}/{customerId}").ConfigureAwait(false); - using (ResourceManager manager = new ResourceManager(Provider, token.AccessToken)) + using (AzureManagement manager = new AzureManagement(Provider, token.AccessToken)) { deployments = await manager.GetDeploymentsAsync(subscriptionId, resourceGroupName).ConfigureAwait(false); diff --git a/src/Explorer/Explorer.csproj b/src/Explorer/Explorer.csproj index 3404499..f1baa19 100644 --- a/src/Explorer/Explorer.csproj +++ b/src/Explorer/Explorer.csproj @@ -312,6 +312,7 @@ + @@ -382,7 +383,7 @@ - + @@ -477,6 +478,7 @@ + diff --git a/src/Explorer/GlobalSuppressions.cs b/src/Explorer/GlobalSuppressions.cs new file mode 100644 index 0000000..a282507 --- /dev/null +++ b/src/Explorer/GlobalSuppressions.cs @@ -0,0 +1,9 @@ + +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "This is requred function name.", Scope = "member", Target = "~M:Microsoft.Store.PartnerCenter.Explorer.MvcApplication.Application_Start")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "This function is called externally.", Scope = "member", Target = "~M:Microsoft.Store.PartnerCenter.Explorer.MvcApplication.Application_Start")] + diff --git a/src/Explorer/Logic/Azure/ResourceManager.cs b/src/Explorer/Logic/Azure/AzureManagement.cs similarity index 96% rename from src/Explorer/Logic/Azure/ResourceManager.cs rename to src/Explorer/Logic/Azure/AzureManagement.cs index 8c29c23..50adb76 100644 --- a/src/Explorer/Logic/Azure/ResourceManager.cs +++ b/src/Explorer/Logic/Azure/AzureManagement.cs @@ -1,5 +1,5 @@ // ----------------------------------------------------------------------- -// +// // Copyright (c) Microsoft Corporation. All rights reserved. // // ----------------------------------------------------------------------- @@ -20,7 +20,7 @@ namespace Microsoft.Store.PartnerCenter.Explorer.Logic.Azure /// Facilitates interactions with the Azure Resource Manager API. /// /// - public class ResourceManager : IDisposable + public class AzureManagement : IDisposable { /// /// Provides access to core services. @@ -38,7 +38,7 @@ namespace Microsoft.Store.PartnerCenter.Explorer.Logic.Azure private bool disposed; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Provides access to core services. /// A valid JSON Web Token (JWT). @@ -48,7 +48,7 @@ namespace Microsoft.Store.PartnerCenter.Explorer.Logic.Azure /// /// is null. /// - public ResourceManager(IExplorerProvider provider, string token) + public AzureManagement(IExplorerProvider provider, string token) { provider.AssertNotNull(nameof(provider)); token.AssertNotEmpty(nameof(token)); diff --git a/src/Explorer/Logic/GraphClient.cs b/src/Explorer/Logic/GraphClient.cs index 626350e..b85a773 100644 --- a/src/Explorer/Logic/GraphClient.cs +++ b/src/Explorer/Logic/GraphClient.cs @@ -9,6 +9,7 @@ namespace Microsoft.Store.PartnerCenter.Explorer.Logic using System; using System.Collections.Generic; using System.Linq; + using System.Net.Http; using System.Threading.Tasks; using Graph; using Models; @@ -21,6 +22,11 @@ namespace Microsoft.Store.PartnerCenter.Explorer.Logic /// public class GraphClient : IGraphClient { + /// + /// Static instance of the class. + /// + private static HttpProvider httpProvider = new HttpProvider(new HttpClientHandler(), false); + /// /// Provides access to core services. /// @@ -55,7 +61,7 @@ namespace Microsoft.Store.PartnerCenter.Explorer.Logic this.customerId = customerId; this.provider = provider; - client = new GraphServiceClient(new AuthenticationProvider(this.provider, customerId)); + client = new GraphServiceClient(new AuthenticationProvider(this.provider, customerId), httpProvider); } /// @@ -121,8 +127,8 @@ namespace Microsoft.Store.PartnerCenter.Explorer.Logic if (customerId.Equals(provider.Configuration.PartnerCenterAccountId, StringComparison.InvariantCultureIgnoreCase)) { groups = directoryGroups.CurrentPage.OfType().Where( - g => g.DisplayName.Equals("AdminAgents", StringComparison.InvariantCultureIgnoreCase) - || g.DisplayName.Equals("HelpdeskAgents", StringComparison.InvariantCultureIgnoreCase) + g => g.DisplayName.Equals("AdminAgents", StringComparison.InvariantCultureIgnoreCase) + || g.DisplayName.Equals("HelpdeskAgents", StringComparison.InvariantCultureIgnoreCase) || g.DisplayName.Equals("SalesAgent", StringComparison.InvariantCultureIgnoreCase)).ToList(); if (groups.Count > 0) diff --git a/src/Explorer/Logic/HttpService.cs b/src/Explorer/Logic/HttpService.cs index 56c638d..8abf512 100644 --- a/src/Explorer/Logic/HttpService.cs +++ b/src/Explorer/Logic/HttpService.cs @@ -48,12 +48,12 @@ namespace Microsoft.Store.PartnerCenter.Explorer.Logic try { - using (HttpClient client = new HttpClient()) + using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUri)) { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - response = await client.GetAsync(requestUri).ConfigureAwait(false); + response = await client.SendAsync(request).ConfigureAwait(false); if (!response.IsSuccessStatusCode) { diff --git a/src/Explorer/Logic/Office/Result.cs b/src/Explorer/Logic/Office/Result.cs index b03078c..4c63554 100644 --- a/src/Explorer/Logic/Office/Result.cs +++ b/src/Explorer/Logic/Office/Result.cs @@ -13,8 +13,16 @@ namespace Microsoft.Store.PartnerCenter.Explorer.Logic.Office /// Represents a result from the Service Communication API. /// /// Type return from the Service Communication API. - internal class Result + public class Result { + /// + /// Initializes a new instance of the class. + /// + public Result() + { + Value = new List(); + } + /// /// Gets or sets the OData context. /// @@ -24,6 +32,6 @@ namespace Microsoft.Store.PartnerCenter.Explorer.Logic.Office /// /// Gets or sets the value returned from the API. /// - public List Value { get; set; } + public List Value { get; } } } \ No newline at end of file diff --git a/src/Explorer/Logic/PartnerOperations.cs b/src/Explorer/Logic/PartnerOperations.cs index e6306cd..e823b80 100644 --- a/src/Explorer/Logic/PartnerOperations.cs +++ b/src/Explorer/Logic/PartnerOperations.cs @@ -294,7 +294,7 @@ namespace Microsoft.Store.PartnerCenter.Explorer.Logic { executionTime = DateTime.Now; correlationId = Guid.NewGuid(); - operations = await GetAppOperationsAsync(correlationId).ConfigureAwait(false); + operations = await GetUserOperationsAsync(correlationId).ConfigureAwait(false); principal = new CustomerPrincipal(ClaimsPrincipal.Current); @@ -420,7 +420,7 @@ namespace Microsoft.Store.PartnerCenter.Explorer.Logic { startTime = DateTime.Now; correlationId = Guid.NewGuid(); - operations = await GetAppOperationsAsync(correlationId).ConfigureAwait(false); + operations = await GetUserOperationsAsync(correlationId).ConfigureAwait(false); principal = new CustomerPrincipal(ClaimsPrincipal.Current); diff --git a/src/Explorer/Models/NewUserModel.cs b/src/Explorer/Models/NewUserModel.cs index 72b4005..c452668 100644 --- a/src/Explorer/Models/NewUserModel.cs +++ b/src/Explorer/Models/NewUserModel.cs @@ -48,7 +48,8 @@ namespace Microsoft.Store.PartnerCenter.Explorer.Models /// /// Gets the usage location. /// - public string UsageLocation => "US"; + [Required] + public string UsageLocation { get; set; } /// /// Gets or sets the name of the user principal. diff --git a/src/Explorer/Models/PartnerCenterToken.cs b/src/Explorer/Models/PartnerCenterToken.cs index c9de4ba..201caed 100644 --- a/src/Explorer/Models/PartnerCenterToken.cs +++ b/src/Explorer/Models/PartnerCenterToken.cs @@ -12,7 +12,7 @@ namespace Microsoft.Store.PartnerCenter.Explorer.Models /// Represents an access token used to access Partner Center. /// /// - internal sealed class PartnerCenterToken : IPartnerCredentials + public sealed class PartnerCenterToken : IPartnerCredentials { /// /// Gets the expiry time in UTC for the token. diff --git a/src/Explorer/Views/Users/Create.cshtml b/src/Explorer/Views/Users/Create.cshtml index 9c55513..758c5a4 100644 --- a/src/Explorer/Views/Users/Create.cshtml +++ b/src/Explorer/Views/Users/Create.cshtml @@ -10,78 +10,89 @@ @using (Ajax.BeginForm("Create", "Users", new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, OnSuccess = "OnUserSuccessCallback", UpdateTargetId = "users" })) { -
-
-   +
+
+   +
+
+
+ @Html.LabelFor(item => Model.DisplayName)
-
-
- @Html.LabelFor(item => Model.DisplayName) -
-
- @Html.TextBoxFor(item => Model.DisplayName, new { @class = "form-control" }) -
-
- @Html.ValidationMessageFor(item => Model.DisplayName) -
+
+ @Html.TextBoxFor(item => Model.DisplayName, new { @class = "form-control" })
-
-
- @Html.LabelFor(item => Model.FirstName) -
-
- @Html.TextBoxFor(item => Model.FirstName, new { @class = "form-control" }) -
-
- @Html.ValidationMessageFor(item => item.FirstName) -
+
+ @Html.ValidationMessageFor(item => Model.DisplayName)
-
-
- @Html.LabelFor(item => item.LastName) -
-
- @Html.TextBoxFor(item => item.LastName, new { @class = "form-control" }) -
-
- @Html.ValidationMessageFor(item => item.LastName) -
+
+
+
+ @Html.LabelFor(item => Model.FirstName)
-
-
- @Html.LabelFor(item => item.UserPrincipalName) -
-
- @Html.TextBoxFor(item => item.UserPrincipalName, new { @class = "form-control" }) -
-
- @Html.ValidationMessageFor(item => item.UserPrincipalName) -
+
+ @Html.TextBoxFor(item => Model.FirstName, new { @class = "form-control" })
-
-
- @Html.LabelFor(item => item.Password) -
-
- @Html.PasswordFor(item => item.Password, new { @class = "form-control", placeholder = "Password" }) -
-
- @Html.ValidationMessageFor(item => item.Password) -
+
+ @Html.ValidationMessageFor(item => item.FirstName)
-
-
- - -
+
+
+
+ @Html.LabelFor(item => item.LastName)
-
+
+ @Html.TextBoxFor(item => item.LastName, new { @class = "form-control" }) +
+
+ @Html.ValidationMessageFor(item => item.LastName) +
+
+
+
+ @Html.LabelFor(item => item.UserPrincipalName) +
+
+ @Html.TextBoxFor(item => item.UserPrincipalName, new { @class = "form-control" }) +
+
+ @Html.ValidationMessageFor(item => item.UserPrincipalName) +
+
+
+
+ @Html.LabelFor(item => item.Password) +
+
+ @Html.PasswordFor(item => item.Password, new { @class = "form-control", placeholder = "Password" }) +
+
+ @Html.ValidationMessageFor(item => item.Password) +
+
+
+
+ @Html.LabelFor(item => item.UsageLocation) +
+
+ @Html.TextBoxFor(item => item.UsageLocation, new { @class = "form-control", placeholder = "US" }) +
+
+ @Html.ValidationMessageFor(item => item.UsageLocation) +
+
+
+
+ + +
+
+
} @Scripts.Render("~/bundles/jqueryval") \ No newline at end of file