From e619f2c95c9c1a7e35a02a0c9b7582e2ff7d7e5d Mon Sep 17 00:00:00 2001 From: Lynn Dai Date: Wed, 5 Oct 2022 15:50:02 -0700 Subject: [PATCH 1/5] fix broken support link --- .../Configuration/GalleryConfiguration.cs | 1 + .../Providers/AccountDeleteUrlHelper.cs | 6 +- src/GalleryTools/App.config | 1 + .../Configuration/AppConfiguration.cs | 5 + .../Configuration/ConfigurationService.cs | 37 +++++ .../Configuration/IAppConfiguration.cs | 5 + .../IGalleryConfigurationService.cs | 5 + .../PackageOwnershipManagementService.cs | 10 +- .../Providers/IUrlHelper.cs | 9 +- .../Admin/Controllers/ApiKeysController.cs | 4 +- .../Controllers/AuthenticationController.cs | 3 +- .../Controllers/OrganizationsController.cs | 6 +- .../Controllers/UsersController.cs | 9 +- src/NuGetGallery/UrlHelperExtensions.cs | 133 +++++++++++------- src/NuGetGallery/UrlHelperWrapper.cs | 12 +- src/NuGetGallery/Web.config | 3 +- .../App_Start/ConfigurationServiceFacts.cs | 41 +++++- .../AuthenticationControllerFacts.cs | 8 +- .../Framework/UnitTestBindings.cs | 1 + .../TestUtils/TestUtility.cs | 1 + 20 files changed, 220 insertions(+), 80 deletions(-) diff --git a/src/AccountDeleter/Configuration/GalleryConfiguration.cs b/src/AccountDeleter/Configuration/GalleryConfiguration.cs index 11a99cb66e..d6a95bea8a 100644 --- a/src/AccountDeleter/Configuration/GalleryConfiguration.cs +++ b/src/AccountDeleter/Configuration/GalleryConfiguration.cs @@ -115,5 +115,6 @@ public string SiteRoot public int? MaxIoThreads { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } public string InternalMicrosoftTenantKey { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } public string AdminSenderUser { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public string SupportEmailSiteRoot { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } } } diff --git a/src/AccountDeleter/Providers/AccountDeleteUrlHelper.cs b/src/AccountDeleter/Providers/AccountDeleteUrlHelper.cs index d44ed96636..3624f1d84c 100644 --- a/src/AccountDeleter/Providers/AccountDeleteUrlHelper.cs +++ b/src/AccountDeleter/Providers/AccountDeleteUrlHelper.cs @@ -7,7 +7,7 @@ namespace NuGetGallery.AccountDeleter { public class AccountDeleteUrlHelper : IUrlHelper { - public string ConfirmPendingOwnershipRequest(string id, string username, string confirmationCode, bool relativeUrl) + public string ConfirmPendingOwnershipRequest(string id, string username, string confirmationCode, bool relativeUrl, bool supportEmail) { throw new NotImplementedException(); } @@ -17,12 +17,12 @@ public string ManagePackageOwnership(string id, bool relativeUrl) throw new NotImplementedException(); } - public string Package(string id, string version, bool relativeUrl) + public string Package(string id, string version, bool relativeUrl, bool supportEmail) { throw new NotImplementedException(); } - public string RejectPendingOwnershipRequest(string id, string username, string confirmationCode, bool relativeUrl) + public string RejectPendingOwnershipRequest(string id, string username, string confirmationCode, bool relativeUrl, bool supportEmail) { throw new NotImplementedException(); } diff --git a/src/GalleryTools/App.config b/src/GalleryTools/App.config index 11892f2d4e..b1dc6b78f3 100644 --- a/src/GalleryTools/App.config +++ b/src/GalleryTools/App.config @@ -25,6 +25,7 @@ + diff --git a/src/NuGetGallery.Services/Configuration/AppConfiguration.cs b/src/NuGetGallery.Services/Configuration/AppConfiguration.cs index a03ee49846..2eef4c2632 100644 --- a/src/NuGetGallery.Services/Configuration/AppConfiguration.cs +++ b/src/NuGetGallery.Services/Configuration/AppConfiguration.cs @@ -210,6 +210,11 @@ public class AppConfiguration : IAppConfiguration /// public string SiteRoot { get; set; } + /// + /// Gets the protocol-independent support email site root + /// + public string SupportEmailSiteRoot { get; set; } + /// /// Private key for verifying recaptcha user response. /// diff --git a/src/NuGetGallery.Services/Configuration/ConfigurationService.cs b/src/NuGetGallery.Services/Configuration/ConfigurationService.cs index d55706f978..67f87cf056 100644 --- a/src/NuGetGallery.Services/Configuration/ConfigurationService.cs +++ b/src/NuGetGallery.Services/Configuration/ConfigurationService.cs @@ -26,6 +26,7 @@ public class ConfigurationService : IGalleryConfigurationService, IConfiguration private readonly Lazy _httpSiteRootThunk; private readonly Lazy _httpsSiteRootThunk; + private readonly Lazy _httpsEmailSupportSiteRootThunk; private readonly Lazy _lazyAppConfiguration; private readonly Lazy _lazyFeatureConfiguration; private readonly Lazy _lazyServiceBusConfiguration; @@ -59,6 +60,7 @@ public ConfigurationService() { _httpSiteRootThunk = new Lazy(GetHttpSiteRoot); _httpsSiteRootThunk = new Lazy(GetHttpsSiteRoot); + _httpsEmailSupportSiteRootThunk = new Lazy(GetHttpsSupportEmailSiteRoot); _lazyAppConfiguration = new Lazy(() => ResolveSettings().Result); _lazyFeatureConfiguration = new Lazy(() => ResolveFeatures().Result); @@ -89,6 +91,15 @@ public string GetSiteRoot(bool useHttps) return useHttps ? _httpsSiteRootThunk.Value : _httpSiteRootThunk.Value; } + /// + /// Gets the support email site root using the specified protocol + /// + /// + public string GetSupportEmailSiteRoot() + { + return _httpsEmailSupportSiteRootThunk.Value; + } + public Task Get() where T : NuGet.Services.Configuration.Configuration, new() { // Get the prefix specified by the ConfigurationKeyPrefixAttribute on the class if it exists. @@ -242,5 +253,31 @@ private string GetHttpsSiteRoot() return "https://" + siteRoot.Substring(7); } + + private string GetHttpsSupportEmailSiteRoot() + { + var siteRoot = Current.SupportEmailSiteRoot; + + if (siteRoot == null) + { + // No SiteRoot configured in settings. + // Fallback to detected site root. + var request = GetCurrentRequest(); + siteRoot = request.Url.GetLeftPart(UriPartial.Authority) + '/'; + } + + if (!siteRoot.StartsWith("http://", StringComparison.OrdinalIgnoreCase) + && !siteRoot.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidOperationException("The configured site root must start with either http:// or https://."); + } + + if (siteRoot.StartsWith("http://", StringComparison.OrdinalIgnoreCase)) + { + siteRoot = "https://" + siteRoot.Substring(7); + } + + return siteRoot; + } } } \ No newline at end of file diff --git a/src/NuGetGallery.Services/Configuration/IAppConfiguration.cs b/src/NuGetGallery.Services/Configuration/IAppConfiguration.cs index 0e30dbd6bb..01f0219939 100644 --- a/src/NuGetGallery.Services/Configuration/IAppConfiguration.cs +++ b/src/NuGetGallery.Services/Configuration/IAppConfiguration.cs @@ -231,6 +231,11 @@ public interface IAppConfiguration : IMessageServiceConfiguration /// string SiteRoot { get; set; } + /// + /// Gets the protocol-independent support email site root + /// + string SupportEmailSiteRoot { get; set; } + /// /// Private key for verifying recaptcha user response. /// diff --git a/src/NuGetGallery.Services/Configuration/IGalleryConfigurationService.cs b/src/NuGetGallery.Services/Configuration/IGalleryConfigurationService.cs index 07260407e2..8dbc1b68bc 100644 --- a/src/NuGetGallery.Services/Configuration/IGalleryConfigurationService.cs +++ b/src/NuGetGallery.Services/Configuration/IGalleryConfigurationService.cs @@ -17,6 +17,11 @@ public interface IGalleryConfigurationService /// If true, the root will be returned in HTTPS form, otherwise, HTTP. string GetSiteRoot(bool useHttps); + /// + /// Gets the support email site root using the specified protocol + /// + string GetSupportEmailSiteRoot(); + /// /// Populate the properties of from configuration. /// diff --git a/src/NuGetGallery.Services/PackageManagement/PackageOwnershipManagementService.cs b/src/NuGetGallery.Services/PackageManagement/PackageOwnershipManagementService.cs index 3ea8e4f596..9c27e6d971 100644 --- a/src/NuGetGallery.Services/PackageManagement/PackageOwnershipManagementService.cs +++ b/src/NuGetGallery.Services/PackageManagement/PackageOwnershipManagementService.cs @@ -49,7 +49,7 @@ public async Task AddPackageOwnerWithMessagesAsync(PackageRegistration packageRe { await AddPackageOwnerAsync(packageRegistration, user, commitChanges: true); - var packageUrl = _urlHelper.Package(packageRegistration.Id, version: null, relativeUrl: false); + var packageUrl = _urlHelper.Package(packageRegistration.Id, version: null, relativeUrl: false, supportEmail: true); // Accumulate the tasks so that they are sent in parallel and as many messages as possible are sent even if // one fails (i.e. throws an exception). @@ -155,7 +155,7 @@ public async Task AddPackageOwnershipRequestWithMessagesAsy var encodedMessage = HttpUtility.HtmlEncode(message ?? string.Empty); - var packageUrl = _urlHelper.Package(packageRegistration.Id, version: null, relativeUrl: false); + var packageUrl = _urlHelper.Package(packageRegistration.Id, version: null, relativeUrl: false, supportEmail: true); var ownerRequest = await AddPackageOwnershipRequestAsync( packageRegistration, requestingOwner, newOwner); @@ -164,13 +164,15 @@ public async Task AddPackageOwnershipRequestWithMessagesAsy packageRegistration.Id, newOwner.Username, ownerRequest.ConfirmationCode, - relativeUrl: false); + relativeUrl: false, + supportEmail: true); var rejectionUrl = _urlHelper.RejectPendingOwnershipRequest( packageRegistration.Id, newOwner.Username, ownerRequest.ConfirmationCode, - relativeUrl: false); + relativeUrl: false, + supportEmail: true); var manageUrl = _urlHelper.ManagePackageOwnership( packageRegistration.Id, diff --git a/src/NuGetGallery.Services/Providers/IUrlHelper.cs b/src/NuGetGallery.Services/Providers/IUrlHelper.cs index 1bc927fb49..3894eb63df 100644 --- a/src/NuGetGallery.Services/Providers/IUrlHelper.cs +++ b/src/NuGetGallery.Services/Providers/IUrlHelper.cs @@ -17,8 +17,9 @@ public interface IUrlHelper /// The package ID to link to. /// The specific package version to link to. Can be null. /// True to return a relative URL, false to return an absolute URL. + /// True to return a supportEmail root site URL, false to return an root site URL. /// The relative or absolute URL as a string. - string Package(string id, string version, bool relativeUrl); + string Package(string id, string version, bool relativeUrl, bool supportEmail); /// /// Produces a URL to the package ownership request confirmation page. @@ -27,8 +28,9 @@ public interface IUrlHelper /// The username of the ownership request recipient (new owner). /// The confirmation code (secret) associated with the request. /// True to return a relative URL, false to return an absolute URL. + /// True to return a supportEmail root site URL, false to return an root site URL. /// The relative or absolute URL as a string. - string ConfirmPendingOwnershipRequest(string id, string username, string confirmationCode, bool relativeUrl); + string ConfirmPendingOwnershipRequest(string id, string username, string confirmationCode, bool relativeUrl, bool supportEmail); /// /// Produces a URL to the package ownership request rejection page. @@ -37,8 +39,9 @@ public interface IUrlHelper /// The username of the ownership request recipient (new owner). /// The confirmation code (secret) associated with the request. /// True to return a relative URL, false to return an absolute URL. + /// True to return a supportEmail root site URL, false to return an root site URL. /// The relative or absolute URL as a string. - string RejectPendingOwnershipRequest(string id, string username, string confirmationCode, bool relativeUrl); + string RejectPendingOwnershipRequest(string id, string username, string confirmationCode, bool relativeUrl, bool supportEmail); /// /// Produces a URL to manage the ownership of an existing package. diff --git a/src/NuGetGallery/Areas/Admin/Controllers/ApiKeysController.cs b/src/NuGetGallery/Areas/Admin/Controllers/ApiKeysController.cs index 24bdd779dc..b90d4b3645 100644 --- a/src/NuGetGallery/Areas/Admin/Controllers/ApiKeysController.cs +++ b/src/NuGetGallery/Areas/Admin/Controllers/ApiKeysController.cs @@ -130,8 +130,8 @@ public async Task Revoke(RevokeApiKeysRequest revokeApiKeysRequest credential: apiKeyCredential, leakedUrl: apiKeyInfo.LeakedUrl, revocationSource: apiKeyInfo.RevocationSource, - manageApiKeyUrl: Url.ManageMyApiKeys(relativeUrl: false), - contactUrl: Url.Contact(relativeUrl: false)); + manageApiKeyUrl: Url.ManageMyApiKeys(relativeUrl: false, supportEmail: true), + contactUrl: Url.Contact(relativeUrl: false, supportEmail: true)); await _messageService.SendMessageAsync(credentialRevokedMessage); await _authenticationService.RevokeApiKeyCredential(apiKeyCredential, revocationSourceKey, commitChanges: false); diff --git a/src/NuGetGallery/Controllers/AuthenticationController.cs b/src/NuGetGallery/Controllers/AuthenticationController.cs index 5b6a633392..9bc921256a 100644 --- a/src/NuGetGallery/Controllers/AuthenticationController.cs +++ b/src/NuGetGallery/Controllers/AuthenticationController.cs @@ -330,7 +330,8 @@ public virtual async Task Register(LogOnViewModel model, string re Url.ConfirmEmail( user.User.Username, user.User.EmailConfirmationToken, - relativeUrl: false)); + relativeUrl: false, + supportEmail: true)); await _messageService.SendMessageAsync(message); } diff --git a/src/NuGetGallery/Controllers/OrganizationsController.cs b/src/NuGetGallery/Controllers/OrganizationsController.cs index 817714c0db..ae2ddc694b 100644 --- a/src/NuGetGallery/Controllers/OrganizationsController.cs +++ b/src/NuGetGallery/Controllers/OrganizationsController.cs @@ -152,9 +152,9 @@ public async Task AddMember(string accountName, string memberName, b request.NewMember, currentUser, request.IsAdmin, - profileUrl: Url.User(account, relativeUrl: false), - confirmationUrl: Url.AcceptOrganizationMembershipRequest(request, relativeUrl: false), - rejectionUrl: Url.RejectOrganizationMembershipRequest(request, relativeUrl: false)); + profileUrl: Url.User(account, relativeUrl: false, supportEmail: true), + confirmationUrl: Url.AcceptOrganizationMembershipRequest(request, relativeUrl: false, supportEmail: true), + rejectionUrl: Url.RejectOrganizationMembershipRequest(request, relativeUrl: false, supportEmail: true)); await MessageService.SendMessageAsync(organizationMembershipRequestMessage); var organizationMembershipRequestInitiatedMessage = new OrganizationMembershipRequestInitiatedMessage( diff --git a/src/NuGetGallery/Controllers/UsersController.cs b/src/NuGetGallery/Controllers/UsersController.cs index b4bc42c76c..4a5e0f7369 100644 --- a/src/NuGetGallery/Controllers/UsersController.cs +++ b/src/NuGetGallery/Controllers/UsersController.cs @@ -203,9 +203,9 @@ public virtual async Task TransformToOrganization(TransformAccount _config, accountToTransform, adminUser, - profileUrl: Url.User(accountToTransform, relativeUrl: false), - confirmationUrl: Url.ConfirmTransformAccount(accountToTransform, relativeUrl: false), - rejectionUrl: Url.RejectTransformAccount(accountToTransform, relativeUrl: false)); + profileUrl: Url.User(accountToTransform, relativeUrl: false, supportEmail: true), + confirmationUrl: Url.ConfirmTransformAccount(accountToTransform, relativeUrl: false, supportEmail: true), + rejectionUrl: Url.RejectTransformAccount(accountToTransform, relativeUrl: false, supportEmail: true)); await MessageService.SendMessageAsync(organizationTransformRequestMessage); var organizationTransformInitiatedMessage = new OrganizationTransformInitiatedMessage( @@ -1257,7 +1257,8 @@ private async Task SendPasswordResetEmailAsync(User user, bool for user.Username, user.PasswordResetToken, forgotPassword, - relativeUrl: false); + relativeUrl: false, + supportEmail: true); var message = new PasswordResetInstructionsMessage( MessageServiceConfiguration, diff --git a/src/NuGetGallery/UrlHelperExtensions.cs b/src/NuGetGallery/UrlHelperExtensions.cs index fd49b5d555..2cbfd6cc34 100644 --- a/src/NuGetGallery/UrlHelperExtensions.cs +++ b/src/NuGetGallery/UrlHelperExtensions.cs @@ -8,6 +8,7 @@ using System.Web.Mvc; using System.Web.Routing; using NuGet.Services.Entities; +using NuGet.Services.Logging; using NuGetGallery.Areas.Admin; using NuGetGallery.Areas.Admin.Controllers; using NuGetGallery.Configuration; @@ -68,6 +69,11 @@ internal static string GetSiteRoot(bool useHttps) return _configuration.GetSiteRoot(useHttps); } + internal static string GetSupportEmailSiteRoot(bool useHttps) + { + return _configuration.GetSupportEmailSiteRoot(); + } + public static string GetCanonicalLinkUrl(this UrlHelper url) { var current = url.RequestContext.HttpContext.Request.Url; @@ -89,6 +95,12 @@ private static string GetConfiguredSiteHostName() return new Uri(siteRoot).Host; } + private static string GetConfiguredSupportEmailSiteHostName() + { + var siteRoot = GetSupportEmailSiteRoot(useHttps: true); + return new Uri(siteRoot).Host; + } + public static string Home(this UrlHelper url, bool relativeUrl = true) { return GetRouteLink(url, RouteName.Home, relativeUrl); @@ -228,9 +240,9 @@ public static RouteUrlTemplate PackageRegistrationTemplate return new RouteUrlTemplate(linkGenerator, routesGenerator); } - public static string Package(this UrlHelper url, string id, bool relativeUrl = true) + public static string Package(this UrlHelper url, string id, bool relativeUrl = true, bool supportEmail = false) { - return url.Package(id, version: null, relativeUrl: relativeUrl); + return url.Package(id, version: null, relativeUrl: relativeUrl, supportEmail: supportEmail); } public static string Package( @@ -238,7 +250,8 @@ public static string Package( string id, string version, bool relativeUrl = true, - bool preview = false) + bool preview = false, + bool supportEmail = false) { var normalized = (version != null) ? NuGetVersionFormatter.Normalize(version) : version; @@ -251,7 +264,8 @@ public static string Package( { "id", id }, { "version", normalized }, { "preview", preview ? "1" : null } - }); + }, + supportEmail: supportEmail); // Ensure trailing slashes for versionless package URLs, as a fix for package filenames that look like known file extensions return version == null ? EnsureTrailingSlash(result) : result; @@ -592,7 +606,8 @@ public static string User( this UrlHelper url, User user, int page = 1, - bool relativeUrl = true) + bool relativeUrl = true, + bool supportEmail = false) { var routeValues = new RouteValueDictionary { @@ -604,7 +619,7 @@ public static string User( routeValues.Add("page", page); } - return GetActionLink(url, "Profiles", "Users", relativeUrl, routeValues); + return GetActionLink(url, "Profiles", "Users", relativeUrl, routeValues, supportEmail: supportEmail); } public static string Avatar( @@ -965,13 +980,14 @@ private static string GetAuthenticationRoute(this UrlHelper url, string action, interceptReturnUrl: false); } - public static string ManageMyApiKeys(this UrlHelper url, bool relativeUrl = true) + public static string ManageMyApiKeys(this UrlHelper url, bool relativeUrl = true, bool supportEmail = false) { return GetActionLink( url, nameof(UsersController.ApiKeys), "Users", - relativeUrl); + relativeUrl, + supportEmail: supportEmail); } public static string ManageMyOrganizations(this UrlHelper url, bool relativeUrl = true) @@ -1016,35 +1032,37 @@ public static string AddOrganizationMember(this UrlHelper url, string accountNam }); } - public static string AcceptOrganizationMembershipRequest(this UrlHelper url, MembershipRequest request, bool relativeUrl = true) + public static string AcceptOrganizationMembershipRequest(this UrlHelper url, MembershipRequest request, bool relativeUrl = true, bool supportEmail = false) { - return url.AcceptOrganizationMembershipRequest(request.Organization.Username, request.ConfirmationToken, relativeUrl); + return url.AcceptOrganizationMembershipRequest(request.Organization.Username, request.ConfirmationToken, relativeUrl, supportEmail); } - public static string RejectOrganizationMembershipRequest(this UrlHelper url, MembershipRequest request, bool relativeUrl = true) + public static string RejectOrganizationMembershipRequest(this UrlHelper url, MembershipRequest request, bool relativeUrl = true, bool supportEmail = false) { - return url.RejectOrganizationMembershipRequest(request.Organization.Username, request.ConfirmationToken, relativeUrl); + return url.RejectOrganizationMembershipRequest(request.Organization.Username, request.ConfirmationToken, relativeUrl, supportEmail); } - public static string AcceptOrganizationMembershipRequest(this UrlHelper url, string organizationUsername, string confirmationToken, bool relativeUrl = true) + public static string AcceptOrganizationMembershipRequest(this UrlHelper url, string organizationUsername, string confirmationToken, bool relativeUrl = true, bool supportEmail = false) { return url.HandleOrganizationMembershipRequest( nameof(OrganizationsController.ConfirmMemberRequest), organizationUsername, confirmationToken, - relativeUrl); + relativeUrl, + supportEmail); } - public static string RejectOrganizationMembershipRequest(this UrlHelper url, string organizationUsername, string confirmationToken, bool relativeUrl = true) + public static string RejectOrganizationMembershipRequest(this UrlHelper url, string organizationUsername, string confirmationToken, bool relativeUrl = true, bool supportEmail = false) { return url.HandleOrganizationMembershipRequest( nameof(OrganizationsController.RejectMemberRequest), organizationUsername, confirmationToken, - relativeUrl); + relativeUrl, + supportEmail); } - private static string HandleOrganizationMembershipRequest(this UrlHelper url, string actionName, string organizationUsername, string confirmationToken, bool relativeUrl = true) + private static string HandleOrganizationMembershipRequest(this UrlHelper url, string actionName, string organizationUsername, string confirmationToken, bool relativeUrl = true, bool supportEmail = false) { return GetActionLink(url, actionName, @@ -1054,7 +1072,8 @@ private static string HandleOrganizationMembershipRequest(this UrlHelper url, st { { "accountName", organizationUsername }, { "confirmationToken", confirmationToken } - }); + }, + supportEmail: supportEmail); } public static string CancelOrganizationMembershipRequest(this UrlHelper url, string accountName, bool relativeUrl = true) @@ -1199,7 +1218,8 @@ public static string ConfirmPendingOwnershipRequest( string packageId, string username, string confirmationCode, - bool relativeUrl = true) + bool relativeUrl = true, + bool supportEmail = false) { return HandlePendingOwnershipRequest( url, @@ -1207,7 +1227,8 @@ public static string ConfirmPendingOwnershipRequest( packageId, username, confirmationCode, - relativeUrl); + relativeUrl, + supportEmail); } public static RouteUrlTemplate RejectPendingOwnershipRequestTemplate( @@ -1225,7 +1246,8 @@ public static string RejectPendingOwnershipRequest( string packageId, string username, string confirmationCode, - bool relativeUrl = true) + bool relativeUrl = true, + bool supportEmail = false) { return HandlePendingOwnershipRequest( url, @@ -1233,7 +1255,8 @@ public static string RejectPendingOwnershipRequest( packageId, username, confirmationCode, - relativeUrl); + relativeUrl, + supportEmail); } private static RouteUrlTemplate HandlePendingOwnershipRequestTemplate( @@ -1264,7 +1287,8 @@ private static string HandlePendingOwnershipRequest( string packageId, string username, string confirmationCode, - bool relativeUrl = true) + bool relativeUrl = true, + bool supportEmail = false) { var routeValues = new RouteValueDictionary { @@ -1273,14 +1297,15 @@ private static string HandlePendingOwnershipRequest( ["token"] = confirmationCode }; - return GetActionLink(url, actionName, "Packages", relativeUrl, routeValues); + return GetActionLink(url, actionName, "Packages", relativeUrl, routeValues, supportEmail:supportEmail); } public static string ConfirmEmail( this UrlHelper url, string username, string token, - bool relativeUrl = true) + bool relativeUrl = true, + bool supportEmail = false) { var routeValues = new RouteValueDictionary { @@ -1288,14 +1313,15 @@ public static string ConfirmEmail( ["token"] = token }; - return GetActionLink(url, "Confirm", "Users", relativeUrl, routeValues); + return GetActionLink(url, "Confirm", "Users", relativeUrl, routeValues, supportEmail: supportEmail); } public static string ConfirmOrganizationEmail( this UrlHelper url, string username, string token, - bool relativeUrl = true) + bool relativeUrl = true, + bool supportEmail = false) { var routeValues = new RouteValueDictionary { @@ -1303,7 +1329,7 @@ public static string ConfirmOrganizationEmail( ["token"] = token }; - return GetActionLink(url, "Confirm", "Organizations", relativeUrl, routeValues); + return GetActionLink(url, "Confirm", "Organizations", relativeUrl, routeValues, supportEmail: supportEmail); } public static string ResetEmailOrPassword( @@ -1311,7 +1337,8 @@ public static string ResetEmailOrPassword( string username, string token, bool forgotPassword, - bool relativeUrl = true) + bool relativeUrl = true, + bool supportEmail = false) { var routeValues = new RouteValueDictionary { @@ -1320,7 +1347,7 @@ public static string ResetEmailOrPassword( ["forgot"] = forgotPassword }; - return GetActionLink(url, "ResetPassword", "Users", relativeUrl, routeValues); + return GetActionLink(url, "ResetPassword", "Users", relativeUrl, routeValues, supportEmail: supportEmail); } public static string VerifyPackage(this UrlHelper url, bool relativeUrl = true) @@ -1338,9 +1365,9 @@ public static string Downloads(this UrlHelper url, bool relativeUrl = true) return GetRouteLink(url, RouteName.Downloads, relativeUrl); } - public static string Contact(this UrlHelper url, bool relativeUrl = true) + public static string Contact(this UrlHelper url, bool relativeUrl = true, bool supportEmail = false) { - return GetActionLink(url, "Contact", "Pages", relativeUrl); + return GetActionLink(url, "Contact", "Pages", relativeUrl, supportEmail: supportEmail); } public static string ContactOwners(this UrlHelper url, IPackageVersionModel package, bool relativeUrl = true) @@ -1442,50 +1469,55 @@ public static string TransformAccount(this UrlHelper url, bool relativeUrl = tru relativeUrl); } - public static string ConfirmTransformAccount(this UrlHelper url, User accountToTransform, bool relativeUrl = true) + public static string ConfirmTransformAccount(this UrlHelper url, User accountToTransform, bool relativeUrl = true, bool supportEmail = false) { return url.HandleTransformAccount( nameof(UsersController.ConfirmTransformToOrganization), accountToTransform, - relativeUrl); + relativeUrl, + supportEmail); } - public static string RejectTransformAccount(this UrlHelper url, User accountToTransform, bool relativeUrl = true) + public static string RejectTransformAccount(this UrlHelper url, User accountToTransform, bool relativeUrl = true, bool supportEmail = false) { return url.HandleTransformAccount( nameof(UsersController.RejectTransformToOrganization), accountToTransform, - relativeUrl); + relativeUrl, + supportEmail); } - private static string HandleTransformAccount(this UrlHelper url, string action, User accountToTransform, bool relativeUrl = true) + private static string HandleTransformAccount(this UrlHelper url, string action, User accountToTransform, bool relativeUrl = true, bool supportEmail = false) { return url.HandleTransformAccount( action, accountToTransform.Username, accountToTransform.OrganizationMigrationRequest.ConfirmationToken, - relativeUrl); + relativeUrl, + supportEmail); } - public static string ConfirmTransformAccount(this UrlHelper url, string accountToTransformUsername, string confirmationToken, bool relativeUrl = true) + public static string ConfirmTransformAccount(this UrlHelper url, string accountToTransformUsername, string confirmationToken, bool relativeUrl = true, bool supportEmail = false) { return url.HandleTransformAccount( nameof(UsersController.ConfirmTransformToOrganization), accountToTransformUsername, confirmationToken, - relativeUrl); + relativeUrl, + supportEmail); } - public static string RejectTransformAccount(this UrlHelper url, string accountToTransformUsername, string confirmationToken, bool relativeUrl = true) + public static string RejectTransformAccount(this UrlHelper url, string accountToTransformUsername, string confirmationToken, bool relativeUrl = true, bool supportEmail = false) { return url.HandleTransformAccount( nameof(UsersController.RejectTransformToOrganization), accountToTransformUsername, confirmationToken, - relativeUrl); + relativeUrl, + supportEmail); } - private static string HandleTransformAccount(this UrlHelper url, string action, string accountToTransformUsername, string confirmationToken, bool relativeUrl = true) + private static string HandleTransformAccount(this UrlHelper url, string action, string accountToTransformUsername, string confirmationToken, bool relativeUrl = true, bool supportEmail = false) { return GetActionLink( url, @@ -1496,7 +1528,8 @@ private static string HandleTransformAccount(this UrlHelper url, string action, { { "accountNameToTransform", accountToTransformUsername }, { "token", confirmationToken } - }); + }, + supportEmail: supportEmail); } public static string CancelTransformAccount(this UrlHelper url, User accountToTransform, bool relativeUrl = true) @@ -1549,11 +1582,13 @@ public static string GetActionLink( bool relativeUrl, RouteValueDictionary routeValues = null, bool interceptReturnUrl = true, - string area = "" // Default to no area. Admin links should specify the "Admin" area explicitly. + string area = "", // Default to no area. Admin links should specify the "Admin" area explicitly. + bool supportEmail = false ) { var protocol = GetProtocol(url); - var hostName = GetConfiguredSiteHostName(); + + var hostName = supportEmail? GetConfiguredSupportEmailSiteHostName(): GetConfiguredSiteHostName(); routeValues = routeValues ?? new RouteValueDictionary(); if (!routeValues.ContainsKey(Area)) @@ -1600,10 +1635,12 @@ private static string GetRouteLink( UrlHelper url, string routeName, bool relativeUrl, - RouteValueDictionary routeValues = null) + RouteValueDictionary routeValues = null, + bool supportEmail = false) { var protocol = GetProtocol(url); - var hostName = GetConfiguredSiteHostName(); + + var hostName = supportEmail ? GetConfiguredSupportEmailSiteHostName() : GetConfiguredSiteHostName(); var routeLink = url.RouteUrl(routeName, routeValues, protocol, hostName); diff --git a/src/NuGetGallery/UrlHelperWrapper.cs b/src/NuGetGallery/UrlHelperWrapper.cs index 93048da74f..6be5794888 100644 --- a/src/NuGetGallery/UrlHelperWrapper.cs +++ b/src/NuGetGallery/UrlHelperWrapper.cs @@ -15,9 +15,9 @@ public UrlHelperWrapper(UrlHelper urlHelper) _urlHelper = urlHelper ?? throw new ArgumentNullException(nameof(urlHelper)); } - public string ConfirmPendingOwnershipRequest(string id, string username, string confirmationCode, bool relativeUrl) + public string ConfirmPendingOwnershipRequest(string id, string username, string confirmationCode, bool relativeUrl, bool supportEmail) { - return _urlHelper.ConfirmPendingOwnershipRequest(id, username, confirmationCode, relativeUrl); + return _urlHelper.ConfirmPendingOwnershipRequest(id, username, confirmationCode, relativeUrl, supportEmail); } public string ManagePackageOwnership(string id, bool relativeUrl) @@ -25,14 +25,14 @@ public string ManagePackageOwnership(string id, bool relativeUrl) return _urlHelper.ManagePackageOwnership(id, relativeUrl); } - public string Package(string id, string version, bool relativeUrl) + public string Package(string id, string version, bool relativeUrl, bool supportEmail) { - return _urlHelper.Package(id, version, relativeUrl); + return _urlHelper.Package(id, version, relativeUrl, supportEmail); } - public string RejectPendingOwnershipRequest(string id, string username, string confirmationCode, bool relativeUrl) + public string RejectPendingOwnershipRequest(string id, string username, string confirmationCode, bool relativeUrl, bool supportEmail) { - return _urlHelper.RejectPendingOwnershipRequest(id, username, confirmationCode, relativeUrl); + return _urlHelper.RejectPendingOwnershipRequest(id, username, confirmationCode, relativeUrl, supportEmail); } } } diff --git a/src/NuGetGallery/Web.config b/src/NuGetGallery/Web.config index 56c5379009..86a6a7b652 100644 --- a/src/NuGetGallery/Web.config +++ b/src/NuGetGallery/Web.config @@ -85,6 +85,7 @@ + @@ -673,4 +674,4 @@ - + \ No newline at end of file diff --git a/tests/NuGetGallery.Facts/App_Start/ConfigurationServiceFacts.cs b/tests/NuGetGallery.Facts/App_Start/ConfigurationServiceFacts.cs index 57b9e61b9f..d8e5047564 100644 --- a/tests/NuGetGallery.Facts/App_Start/ConfigurationServiceFacts.cs +++ b/tests/NuGetGallery.Facts/App_Start/ConfigurationServiceFacts.cs @@ -22,6 +22,7 @@ private class TestableConfigurationService : ConfigurationService public TestableConfigurationService() : base() { StubConfiguredSiteRoot = "http://aSiteRoot/"; + StubConfiguredSupportEmailSiteRoot = "http://aSupportEmailSiteRoot"; StubRequest = new Mock(); StubRequest.Setup(stub => stub.IsLocal).Returns(false); @@ -31,6 +32,7 @@ public TestableConfigurationService() : base() } public string StubConfiguredSiteRoot { get; set; } + public string StubConfiguredSupportEmailSiteRoot { get; set; } public Mock StubRequest { get; set; } protected override string GetAppSetting(string settingName) @@ -42,8 +44,14 @@ protected override string GetAppSetting(string settingName) return StubConfiguredSiteRoot; } + if (settingName == $"{SettingPrefix}{nameof(tempAppConfig.SupportEmailSiteRoot)}") + { + return StubConfiguredSupportEmailSiteRoot; + } + return string.Empty; } + } [Fact] @@ -82,7 +90,7 @@ public void WillUseTheConfiguredSiteRootWhenTheRequestIsLocal() } [Fact] - public void WillUseHttpUponRequestWhenConfiguredSiteRootIsHttps() + public void WillUseHttpUponRequestHtWhenConfiguredSiteRootIstps() { var configuration = new TestableConfigurationService(); configuration.StubConfiguredSiteRoot = "https://theSiteRoot/"; @@ -100,6 +108,37 @@ public void WillThrowIfConfiguredSiteRootIsNotHttpOrHttps() Assert.Throws(() => configuration.GetSiteRoot(useHttps: false)); } + + [Fact] + public void WillGetTheConfiguredHttpsSupportEmailSiteRoot() + { + var configuration = new TestableConfigurationService(); + configuration.StubConfiguredSupportEmailSiteRoot = "https://aSupportEmailSiteRoot"; + + var siteRoot = configuration.GetSupportEmailSiteRoot(); + + Assert.Equal("https://aSupportEmailSiteRoot", siteRoot); + } + + [Fact] + public void WillThrowIfConfiguredSupportEmailSiteRootIsNotHttpOrHttps() + { + var configuration = new TestableConfigurationService(); + configuration.StubConfiguredSupportEmailSiteRoot = "ftp://theSupportEmailSiteRoot/"; + + Assert.Throws(() => configuration.GetSupportEmailSiteRoot()); + } + + [Fact] + public void WillUseHttpsWhenConfiguredSiteRootIsHttp() + { + var configuration = new TestableConfigurationService(); + configuration.StubConfiguredSupportEmailSiteRoot = "http://aSupportEmailSiteRoot"; + + var siteRoot = configuration.GetSupportEmailSiteRoot(); + + Assert.Equal("https://aSupportEmailSiteRoot", siteRoot); + } } public class TheReadSettingMethod diff --git a/tests/NuGetGallery.Facts/Controllers/AuthenticationControllerFacts.cs b/tests/NuGetGallery.Facts/Controllers/AuthenticationControllerFacts.cs index 9e8da1fefa..bfd58b850e 100644 --- a/tests/NuGetGallery.Facts/Controllers/AuthenticationControllerFacts.cs +++ b/tests/NuGetGallery.Facts/Controllers/AuthenticationControllerFacts.cs @@ -804,7 +804,7 @@ public async Task WillCreateAndLogInTheUserWhenNotLinking() It.Is( msg => msg.User == authUser.User - && msg.ConfirmationUrl == TestUtility.GallerySiteRootHttps + "account/confirm/" + authUser.User.Username + "/" + authUser.User.EmailConfirmationToken), + && msg.ConfirmationUrl == TestUtility.GallerySupportEmailSiteRootHttps + "account/confirm/" + authUser.User.Username + "/" + authUser.User.EmailConfirmationToken), false, false)); @@ -892,7 +892,7 @@ public async Task WillNotAutoConfirmAndWillSendConfirmationEmailWhenNotExternalC UserInfo = new IdentityInformation("", "", authUser.User.UnconfirmedEmailAddress, "") }); - var confirmationUrl = TestUtility.GallerySiteRootHttps + "account/confirm/" + authUser.User.Username + "/" + authUser.User.EmailConfirmationToken; + var confirmationUrl = TestUtility.GallerySupportEmailSiteRootHttps + "account/confirm/" + authUser.User.Username + "/" + authUser.User.EmailConfirmationToken; var configurationService = GetConfigurationService(); var messageService = GetMock(); messageService @@ -960,7 +960,7 @@ public async Task WillNotAutoConfirmAndWillSendConfirmationEmailWhenModelRegiste UserInfo = new IdentityInformation("", "", "unconfirmed@example.com", "") }); - var confirmationUrl = TestUtility.GallerySiteRootHttps + "account/confirm/" + authUser.User.Username + "/" + authUser.User.EmailConfirmationToken; + var confirmationUrl = TestUtility.GallerySupportEmailSiteRootHttps + "account/confirm/" + authUser.User.Username + "/" + authUser.User.EmailConfirmationToken; var configurationService = GetConfigurationService(); var messageService = GetMock(); messageService @@ -1147,7 +1147,7 @@ public async Task GivenValidExternalAuth_ItCreatesAccountAndLinksCredential() It.Is( msg => msg.User == authUser.User - && msg.ConfirmationUrl == TestUtility.GallerySiteRootHttps + "account/confirm/" + authUser.User.Username + "/" + authUser.User.EmailConfirmationToken), + && msg.ConfirmationUrl == TestUtility.GallerySupportEmailSiteRootHttps + "account/confirm/" + authUser.User.Username + "/" + authUser.User.EmailConfirmationToken), false, false)); diff --git a/tests/NuGetGallery.Facts/Framework/UnitTestBindings.cs b/tests/NuGetGallery.Facts/Framework/UnitTestBindings.cs index 873421f80c..69cbdea97d 100644 --- a/tests/NuGetGallery.Facts/Framework/UnitTestBindings.cs +++ b/tests/NuGetGallery.Facts/Framework/UnitTestBindings.cs @@ -131,6 +131,7 @@ private static IGalleryConfigurationService CreateTestConfigurationService() // We configure HTTP site root, but require SSL. var configurationService = new TestGalleryConfigurationService(); configurationService.Current.SiteRoot = TestUtility.GallerySiteRootHttp; + configurationService.Current.SupportEmailSiteRoot = TestUtility.GallerySupportEmailSiteRootHttps; configurationService.Current.RequireSSL = true; configurationService.Current.GalleryOwner = new MailAddress("support@example.com"); diff --git a/tests/NuGetGallery.Facts/TestUtils/TestUtility.cs b/tests/NuGetGallery.Facts/TestUtils/TestUtility.cs index c55609672b..125668c3d3 100644 --- a/tests/NuGetGallery.Facts/TestUtils/TestUtility.cs +++ b/tests/NuGetGallery.Facts/TestUtils/TestUtility.cs @@ -21,6 +21,7 @@ public static class TestUtility public static readonly string GallerySiteRootHttp = $"http://{galleryHostName}/"; public static readonly string GallerySiteRootHttps = $"https://{galleryHostName}/"; + public static readonly string GallerySupportEmailSiteRootHttps = $"https://{galleryHostName}-supportEmail/"; public static readonly string FakeUserName = "theUsername"; public static readonly int FakeUserKey = _key++; From 45ae5a9f97f4a0cc4c2acaedb3d97dee0fc2e9fd Mon Sep 17 00:00:00 2001 From: Lynn Dai Date: Mon, 10 Oct 2022 14:43:32 -0700 Subject: [PATCH 2/5] fix unit test --- tests/NuGetGallery.Facts/Controllers/UsersControllerFacts.cs | 2 +- tests/NuGetGallery.Facts/TestUtils/TestUtility.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/NuGetGallery.Facts/Controllers/UsersControllerFacts.cs b/tests/NuGetGallery.Facts/Controllers/UsersControllerFacts.cs index e884659436..88b3ac0863 100644 --- a/tests/NuGetGallery.Facts/Controllers/UsersControllerFacts.cs +++ b/tests/NuGetGallery.Facts/Controllers/UsersControllerFacts.cs @@ -1609,7 +1609,7 @@ public async Task GivenNoOldPassword_ItSendsAPasswordSetEmail() await controller.ChangePassword(new UserAccountViewModel()); // Assert - Assert.Equal(TestUtility.GallerySiteRootHttps + "account/setpassword/test/t0k3n", actualConfirmUrl); + Assert.Equal(TestUtility.GallerySupportEmailSiteRootHttps + "account/setpassword/test/t0k3n", actualConfirmUrl); GetMock().VerifyAll(); } diff --git a/tests/NuGetGallery.Facts/TestUtils/TestUtility.cs b/tests/NuGetGallery.Facts/TestUtils/TestUtility.cs index 125668c3d3..ac027e798a 100644 --- a/tests/NuGetGallery.Facts/TestUtils/TestUtility.cs +++ b/tests/NuGetGallery.Facts/TestUtils/TestUtility.cs @@ -21,7 +21,7 @@ public static class TestUtility public static readonly string GallerySiteRootHttp = $"http://{galleryHostName}/"; public static readonly string GallerySiteRootHttps = $"https://{galleryHostName}/"; - public static readonly string GallerySupportEmailSiteRootHttps = $"https://{galleryHostName}-supportEmail/"; + public static readonly string GallerySupportEmailSiteRootHttps = $"https://{galleryHostName}/"; public static readonly string FakeUserName = "theUsername"; public static readonly int FakeUserKey = _key++; From d328bfdd1c477a91a00f6dfd4651cad4a6dfd09d Mon Sep 17 00:00:00 2001 From: Lynn Dai Date: Mon, 10 Oct 2022 16:29:29 -0700 Subject: [PATCH 3/5] fix parameter --- src/NuGetGallery/UrlHelperWrapper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NuGetGallery/UrlHelperWrapper.cs b/src/NuGetGallery/UrlHelperWrapper.cs index 6be5794888..dd8c18c892 100644 --- a/src/NuGetGallery/UrlHelperWrapper.cs +++ b/src/NuGetGallery/UrlHelperWrapper.cs @@ -27,7 +27,7 @@ public string ManagePackageOwnership(string id, bool relativeUrl) public string Package(string id, string version, bool relativeUrl, bool supportEmail) { - return _urlHelper.Package(id, version, relativeUrl, supportEmail); + return _urlHelper.Package(id, version, relativeUrl, supportEmail: supportEmail); } public string RejectPendingOwnershipRequest(string id, string username, string confirmationCode, bool relativeUrl, bool supportEmail) From 1927091be42f3a5ba0e7cc14b7d884e9e79113dd Mon Sep 17 00:00:00 2001 From: Lynn Dai Date: Mon, 10 Oct 2022 16:57:17 -0700 Subject: [PATCH 4/5] change local config --- src/NuGetGallery/Web.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NuGetGallery/Web.config b/src/NuGetGallery/Web.config index 86a6a7b652..02743e762a 100644 --- a/src/NuGetGallery/Web.config +++ b/src/NuGetGallery/Web.config @@ -85,7 +85,7 @@ - + From 10d44d293b1823682525fc074731cc022cfdca29 Mon Sep 17 00:00:00 2001 From: Lynn Dai Date: Tue, 11 Oct 2022 12:29:37 -0700 Subject: [PATCH 5/5] fix typo --- .../Configuration/ConfigurationService.cs | 33 ++++++++----------- src/NuGetGallery/UrlHelperExtensions.cs | 5 ++- .../App_Start/ConfigurationServiceFacts.cs | 2 +- 3 files changed, 16 insertions(+), 24 deletions(-) diff --git a/src/NuGetGallery.Services/Configuration/ConfigurationService.cs b/src/NuGetGallery.Services/Configuration/ConfigurationService.cs index 67f87cf056..fba91fe698 100644 --- a/src/NuGetGallery.Services/Configuration/ConfigurationService.cs +++ b/src/NuGetGallery.Services/Configuration/ConfigurationService.cs @@ -220,19 +220,7 @@ private string GetHttpSiteRoot() { var siteRoot = Current.SiteRoot; - if (siteRoot == null) - { - // No SiteRoot configured in settings. - // Fallback to detected site root. - var request = GetCurrentRequest(); - siteRoot = request.Url.GetLeftPart(UriPartial.Authority) + '/'; - } - - if (!siteRoot.StartsWith("http://", StringComparison.OrdinalIgnoreCase) - && !siteRoot.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) - { - throw new InvalidOperationException("The configured site root must start with either http:// or https://."); - } + CheckValidSiteRoot(siteRoot); if (siteRoot.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) { @@ -258,6 +246,18 @@ private string GetHttpsSupportEmailSiteRoot() { var siteRoot = Current.SupportEmailSiteRoot; + CheckValidSiteRoot(siteRoot); + + if (siteRoot.StartsWith("http://", StringComparison.OrdinalIgnoreCase)) + { + siteRoot = "https://" + siteRoot.Substring(7); + } + + return siteRoot; + } + + private void CheckValidSiteRoot(string siteRoot) + { if (siteRoot == null) { // No SiteRoot configured in settings. @@ -271,13 +271,6 @@ private string GetHttpsSupportEmailSiteRoot() { throw new InvalidOperationException("The configured site root must start with either http:// or https://."); } - - if (siteRoot.StartsWith("http://", StringComparison.OrdinalIgnoreCase)) - { - siteRoot = "https://" + siteRoot.Substring(7); - } - - return siteRoot; } } } \ No newline at end of file diff --git a/src/NuGetGallery/UrlHelperExtensions.cs b/src/NuGetGallery/UrlHelperExtensions.cs index 2cbfd6cc34..eb8c36227f 100644 --- a/src/NuGetGallery/UrlHelperExtensions.cs +++ b/src/NuGetGallery/UrlHelperExtensions.cs @@ -1297,7 +1297,7 @@ private static string HandlePendingOwnershipRequest( ["token"] = confirmationCode }; - return GetActionLink(url, actionName, "Packages", relativeUrl, routeValues, supportEmail:supportEmail); + return GetActionLink(url, actionName, "Packages", relativeUrl, routeValues, supportEmail: supportEmail); } public static string ConfirmEmail( @@ -1587,8 +1587,7 @@ public static string GetActionLink( ) { var protocol = GetProtocol(url); - - var hostName = supportEmail? GetConfiguredSupportEmailSiteHostName(): GetConfiguredSiteHostName(); + var hostName = supportEmail ? GetConfiguredSupportEmailSiteHostName(): GetConfiguredSiteHostName(); routeValues = routeValues ?? new RouteValueDictionary(); if (!routeValues.ContainsKey(Area)) diff --git a/tests/NuGetGallery.Facts/App_Start/ConfigurationServiceFacts.cs b/tests/NuGetGallery.Facts/App_Start/ConfigurationServiceFacts.cs index d8e5047564..88d0e8f157 100644 --- a/tests/NuGetGallery.Facts/App_Start/ConfigurationServiceFacts.cs +++ b/tests/NuGetGallery.Facts/App_Start/ConfigurationServiceFacts.cs @@ -90,7 +90,7 @@ public void WillUseTheConfiguredSiteRootWhenTheRequestIsLocal() } [Fact] - public void WillUseHttpUponRequestHtWhenConfiguredSiteRootIstps() + public void WillUseHttpUponRequestWhenConfiguredSiteRootIsHttps() { var configuration = new TestableConfigurationService(); configuration.StubConfiguredSiteRoot = "https://theSiteRoot/";