diff --git a/.github/ISSUE_TEMPLATE/NUGETORG_ISSUE.yml b/.github/ISSUE_TEMPLATE/NUGETORG_ISSUE.yml index 6f92bc63e7..cd498091ae 100644 --- a/.github/ISSUE_TEMPLATE/NUGETORG_ISSUE.yml +++ b/.github/ISSUE_TEMPLATE/NUGETORG_ISSUE.yml @@ -10,9 +10,9 @@ body: The more detail you provide, the more likely it will be for us to be able to identify what is going on and how to solve it! - ### For issues connecting to NuGet.org, please refer to [this guide](https://docs.microsoft.com/en-us/nuget/nuget-org/nuget-org-faq#nuget.org-not-accessible). + ### For issues connecting to NuGet.org, please refer to [this guide](https://docs.microsoft.com/nuget/nuget-org/nuget-org-faq#nuget.org-not-accessible). - ### For issues regarding your NuGet.org account, please refer to [this guide](https://docs.microsoft.com/en-us/nuget/nuget-org/nuget-org-faq#nuget.org-account-management). + ### For issues regarding your NuGet.org account, please refer to [this guide](https://docs.microsoft.com/nuget/nuget-org/nuget-org-faq#nuget.org-account-management). - type: dropdown id: impact attributes: diff --git a/README.md b/README.md index 85ce44c49a..ac4c0545ef 100644 --- a/README.md +++ b/README.md @@ -27,9 +27,26 @@ Now run the NuGet Gallery: Refer to [our documentation](./docs/) for information on how to develop the frontend, use AAD, and more. ## Deploy +### Deploy to Azure You will find instructions on how to deploy the Gallery to Azure [here](https://github.com/NuGet/NuGetGallery/blob/master/docs/Deploying/README.md). +### Deploy locally +After you succeed in running the NuGet Gallery, you can create a publish profile to deploy locally (such as your local Windows computer). + +The steps are: +1. Select the `NuGetGallery` project in Solution Explore of Visual Studio. +2. Right click the project, and then click `Publish` in the pop-up menu. Create a publish profile and make sure the Target is set to `Folder`. +3. Copy the contents of the `Target Location` to any folder you want. For the following example, assume the folder is `C:\ContosoSoftware\NuGetGallery`. +4. Execute the command below to start the web app (note that the parameter `/path` of iisexpress.exe only supports absolute paths on Windows). + ```cmd + "C:\Program Files\IIS Express\iisexpress.exe" /path:C:\ContosoSoftware\NuGetGallery + ``` + +Now you can access the local website with a web browser. The URL is `https://localhost`. + +After you deploy it, you don't need using Visual Studio to run it anymore. + ## Contribute If you find a bug with the gallery, please visit the [Issue tracker](https://github.com/NuGet/NuGetGallery/issues) and diff --git a/src/NuGetGallery.Core/Frameworks/SupportedFrameworks.cs b/src/NuGetGallery.Core/Frameworks/SupportedFrameworks.cs index 4c40002035..c9249437ce 100644 --- a/src/NuGetGallery.Core/Frameworks/SupportedFrameworks.cs +++ b/src/NuGetGallery.Core/Frameworks/SupportedFrameworks.cs @@ -25,6 +25,7 @@ public static class SupportedFrameworks public static readonly NuGetFramework MonoTouch = new NuGetFramework(FrameworkIdentifiers.MonoTouch, EmptyVersion); public static readonly NuGetFramework MonoMac = new NuGetFramework(FrameworkIdentifiers.MonoMac, EmptyVersion); public static readonly NuGetFramework Net48 = new NuGetFramework(FrameworkIdentifiers.Net, new Version(4, 8, 0, 0)); + public static readonly NuGetFramework Net481 = new NuGetFramework(FrameworkIdentifiers.Net, new Version(4, 8, 1, 0)); public static readonly NuGetFramework Net50Windows = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version5, "windows", EmptyVersion); public static readonly NuGetFramework Net60Android = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version6, "android", EmptyVersion); public static readonly NuGetFramework Net60Ios = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version6, "ios", EmptyVersion); @@ -57,7 +58,7 @@ static SupportedFrameworks() { MonoAndroid, MonoMac, MonoTouch, Native, - Net11, Net2, Net35, Net4, Net403, Net45, Net451, Net452, Net46, Net461, Net462, Net463, Net47, Net471, Net472, Net48, + Net11, Net2, Net35, Net4, Net403, Net45, Net451, Net452, Net46, Net461, Net462, Net463, Net47, Net471, Net472, Net48, Net481, Net50, Net50Windows, Net60, Net60Android, Net60Ios, Net60MacCatalyst, Net60MacOs, Net60TvOs, Net60Windows, Net70, Net70Android, Net70Ios, Net70MacCatalyst, Net70MacOs, Net70TvOs, Net70Windows, diff --git a/src/NuGetGallery.Services/Authentication/AuthenticationService.cs b/src/NuGetGallery.Services/Authentication/AuthenticationService.cs index 673bd85613..057b3190fa 100644 --- a/src/NuGetGallery.Services/Authentication/AuthenticationService.cs +++ b/src/NuGetGallery.Services/Authentication/AuthenticationService.cs @@ -473,6 +473,9 @@ public virtual async Task ReplaceCredential(User user, Credential credential) { await ReplaceCredentialInternal(user, credential); await Entities.SaveChangesAsync(); + + await Auditing.SaveAuditRecordAsync(new UserAuditRecord( + user, AuditedUserAction.AddCredential, credential)); } public virtual async Task ResetPasswordWithToken(string username, string token, string newPassword) @@ -501,6 +504,10 @@ public virtual async Task ResetPasswordWithToken(string username, st user.FailedLoginCount = 0; user.LastFailedLoginUtc = null; await Entities.SaveChangesAsync(); + + await Auditing.SaveAuditRecordAsync(new UserAuditRecord( + user, AuditedUserAction.AddCredential, cred)); + return cred; } @@ -590,6 +597,10 @@ public virtual async Task ChangePassword(User user, string oldPassword, st // Save changes await Entities.SaveChangesAsync(); + + await Auditing.SaveAuditRecordAsync(new UserAuditRecord( + user, AuditedUserAction.AddCredential, passwordCredential)); + return true; } @@ -623,10 +634,10 @@ public virtual async Task AddCredential(User user, Credential credential) throw new InvalidOperationException(ServicesStrings.OrganizationsCannotCreateCredentials); } - await Auditing.SaveAuditRecordAsync(new UserAuditRecord(user, AuditedUserAction.AddCredential, credential)); user.Credentials.Add(credential); await Entities.SaveChangesAsync(); + await Auditing.SaveAuditRecordAsync(new UserAuditRecord(user, AuditedUserAction.AddCredential, credential)); _telemetryService.TrackNewCredentialCreated(user, credential); } @@ -838,9 +849,6 @@ private async Task ReplaceCredentialInternal(User user, Credential credential) } user.Credentials.Add(credential); - - await Auditing.SaveAuditRecordAsync(new UserAuditRecord( - user, AuditedUserAction.AddCredential, credential)); } private static CredentialKind GetCredentialKind(string type) @@ -1024,15 +1032,20 @@ private async Task MigrateCredentials(User user, List creds, string await Auditing.SaveAuditRecordAsync(new UserAuditRecord(user, AuditedUserAction.RemoveCredential, toRemove)); // Now add one if there are no credentials left + Credential newCred = null; if (creds.Count == 0) { - var newCred = _credentialBuilder.CreatePasswordCredential(password); - await Auditing.SaveAuditRecordAsync(new UserAuditRecord(user, AuditedUserAction.AddCredential, newCred)); + newCred = _credentialBuilder.CreatePasswordCredential(password); user.Credentials.Add(newCred); } // Save changes, if any await Entities.SaveChangesAsync(); + + if (newCred != null) + { + await Auditing.SaveAuditRecordAsync(new UserAuditRecord(user, AuditedUserAction.AddCredential, newCred)); + } } } } \ No newline at end of file diff --git a/src/NuGetGallery.Services/Authentication/Providers/AzureActiveDirectoryV2/AzureActiveDirectoryV2Authenticator.cs b/src/NuGetGallery.Services/Authentication/Providers/AzureActiveDirectoryV2/AzureActiveDirectoryV2Authenticator.cs index d63331ed2f..dc2a60613d 100644 --- a/src/NuGetGallery.Services/Authentication/Providers/AzureActiveDirectoryV2/AzureActiveDirectoryV2Authenticator.cs +++ b/src/NuGetGallery.Services/Authentication/Providers/AzureActiveDirectoryV2/AzureActiveDirectoryV2Authenticator.cs @@ -101,12 +101,13 @@ protected override void AttachToOwinApp(IGalleryConfigurationService config, IAp RedirectUri = siteRoot + _callbackPath, PostLogoutRedirectUri = siteRoot, Scope = OpenIdConnectScope.OpenIdProfile + " email", - ResponseType = OpenIdConnectResponseType.CodeIdToken, + ResponseType = OpenIdConnectResponseType.IdToken, TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters() { ValidateIssuer = false }, Notifications = new OpenIdConnectAuthenticationNotifications { AuthenticationFailed = AuthenticationFailed, - RedirectToIdentityProvider = RedirectToIdentityProvider + RedirectToIdentityProvider = RedirectToIdentityProvider, + AuthorizationCodeReceived = AuthorizationCodeReceived, } }; @@ -257,7 +258,7 @@ private Task RedirectToIdentityProvider(RedirectToIdentityProviderNotification + + + } diff --git a/src/NuGetGallery/Web.config b/src/NuGetGallery/Web.config index c622639fb5..db6bd8ee50 100644 --- a/src/NuGetGallery/Web.config +++ b/src/NuGetGallery/Web.config @@ -101,7 +101,6 @@ - diff --git a/tests/NuGetGallery.Facts/Authentication/AuthenticationServiceFacts.cs b/tests/NuGetGallery.Facts/Authentication/AuthenticationServiceFacts.cs index 0817d1a5a4..bce12636d9 100644 --- a/tests/NuGetGallery.Facts/Authentication/AuthenticationServiceFacts.cs +++ b/tests/NuGetGallery.Facts/Authentication/AuthenticationServiceFacts.cs @@ -15,6 +15,8 @@ using NuGetGallery.Auditing; using NuGetGallery.Authentication.Providers; using NuGetGallery.Authentication.Providers.MicrosoftAccount; +using NuGetGallery.Configuration; +using NuGetGallery.Diagnostics; using NuGetGallery.Framework; using NuGetGallery.Infrastructure.Authentication; using Xunit; @@ -2071,6 +2073,51 @@ public async Task WritesAuditRecordForTheNewCredential() ar.AffectedCredential[0].Type == cred.Type && ar.AffectedCredential[0].Identity == cred.Identity)); } + + [Fact] + public async Task WritesAuditRecordAfterDbCommit() + { + // Arrange + var entitiesContext = new Mock(); + var auditingService = new Mock(); + var credentialBuilder = new CredentialBuilder(); + + var authService = new AuthenticationService( + entitiesContext.Object, + Get(), + Get(), + auditingService.Object, + Enumerable.Empty(), + credentialBuilder, + Get(), + Get(), + Get(), + Get(), + Get()); + var operations = new List(); + + var fakes = Get(); + var user = fakes.CreateUser("test", credentialBuilder.CreatePasswordCredential(Fakes.Password)); + var cred = credentialBuilder.CreateExternalCredential("flarg", "glarb", "blarb"); + Mock + .Get(authService.Auditing) + .Setup(x => x.SaveAuditRecordAsync(It.IsAny())) + .Returns(Task.CompletedTask) + .Callback(() => operations.Add(nameof(IAuditingService.SaveAuditRecordAsync))); + Mock + .Get(authService.Entities) + .Setup(x => x.SaveChangesAsync()) + .ReturnsAsync(() => 0) + .Callback(() => operations.Add(nameof(IEntitiesContext.SaveChangesAsync))); + + // Act + await authService.AddCredential(user, cred); + + // Assert + Assert.Equal( + new List { nameof(IEntitiesContext.SaveChangesAsync), nameof(IAuditingService.SaveAuditRecordAsync) }, + operations); + } } public class TheDescribeCredentialMethod : TestContainer diff --git a/tests/NuGetGallery.Facts/Services/PackageMetadataValidationServiceFacts.cs b/tests/NuGetGallery.Facts/Services/PackageMetadataValidationServiceFacts.cs index 85b2f208e0..24599eb16e 100644 --- a/tests/NuGetGallery.Facts/Services/PackageMetadataValidationServiceFacts.cs +++ b/tests/NuGetGallery.Facts/Services/PackageMetadataValidationServiceFacts.cs @@ -1500,8 +1500,8 @@ public async Task WarnsAboutPackagesWithoutReadmeWhenDisplayUploadWarningV2Enabl Assert.Null(result.Message); var warning = Assert.Single(result.Warnings); Assert.IsType(warning); - Assert.StartsWith("Readme missing. Go to https://learn.microsoft.com/en-us/nuget/create-packages/package-authoring-best-practices#readme learn How to include a readme file within the package.", warning.PlainTextMessage); - Assert.StartsWith("Readme missing. See how to include a readme file within the package, or add it as you upload.", warning.RawHtmlMessage); + Assert.StartsWith("Readme missing. Go to https://learn.microsoft.com/nuget/create-packages/package-authoring-best-practices#readme learn How to include a readme file within the package.", warning.PlainTextMessage); + Assert.StartsWith("Readme missing. See how to include a readme file within the package, or add it as you upload.", warning.RawHtmlMessage); } [Fact] @@ -1527,8 +1527,8 @@ public async Task WarnsAboutPackagesWithoutWhenEmbeddedReadmeNotEnabledAndDispla Assert.Null(result.Message); var warning = Assert.Single(result.Warnings); Assert.IsType(warning); - Assert.StartsWith("Readme missing. Go to https://learn.microsoft.com/en-us/nuget/create-packages/package-authoring-best-practices#readme learn How to include a readme file within the package.", warning.PlainTextMessage); - Assert.StartsWith("Readme missing. See how to include a readme file within the package, or add it as you upload.", warning.RawHtmlMessage); + Assert.StartsWith("Readme missing. Go to https://learn.microsoft.com/nuget/create-packages/package-authoring-best-practices#readme learn How to include a readme file within the package.", warning.PlainTextMessage); + Assert.StartsWith("Readme missing. See how to include a readme file within the package, or add it as you upload.", warning.RawHtmlMessage); } private async Task ValidatePackageWithReadme(string readmePath, byte[] readmeFileData)