Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ReleasePrep][2023.01.01]RI of dev into main #9345

Merged
merged 9 commits into from
Jan 11, 2023
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/NuGetGallery.Core/Frameworks/SupportedFrameworks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Credential> ResetPasswordWithToken(string username, string token, string newPassword)
Expand Down Expand Up @@ -501,6 +504,10 @@ public virtual async Task<Credential> ResetPasswordWithToken(string username, st
user.FailedLoginCount = 0;
user.LastFailedLoginUtc = null;
await Entities.SaveChangesAsync();

await Auditing.SaveAuditRecordAsync(new UserAuditRecord(
user, AuditedUserAction.AddCredential, cred));

return cred;
}

Expand Down Expand Up @@ -590,6 +597,10 @@ public virtual async Task<bool> ChangePassword(User user, string oldPassword, st

// Save changes
await Entities.SaveChangesAsync();

await Auditing.SaveAuditRecordAsync(new UserAuditRecord(
user, AuditedUserAction.AddCredential, passwordCredential));

return true;
}

Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -1024,15 +1032,20 @@ private async Task MigrateCredentials(User user, List<Credential> 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));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
};

Expand Down Expand Up @@ -257,7 +258,7 @@ private Task RedirectToIdentityProvider(RedirectToIdentityProviderNotification<O
// Set the redirect_uri token for the alternate domains of same gallery instance
if (_alternateSiteRootList != null && _alternateSiteRootList.Contains(notification.Request.Uri.Host))
{
notification.ProtocolMessage.RedirectUri = "https://" + notification.Request.Uri.Host + "/" + _callbackPath ;
notification.ProtocolMessage.RedirectUri = "https://" + notification.Request.Uri.Host + "/" + _callbackPath;
}

// We always want to show the options to select account when signing in and while changing account.
Expand All @@ -271,5 +272,13 @@ private AuthenticationProperties GetAuthenticationPropertiesFromProtocolMessage(
var authenticationPropertiesEncodedString = message.State.Split('=');
return options.StateDataFormat.Unprotect(authenticationPropertiesEncodedString[1]);
}

private Task AuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
// Explicitly set the access_token to null. The access_token is used for authorized requests to AAD on
// behalf of the end user. We do not use this feature. We only use the id_token.
context.HandleCodeRedemption(accessToken: null, idToken: context.JwtSecurityToken.RawData);
return Task.CompletedTask;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ namespace NuGetGallery.Authentication.Providers.AzureActiveDirectoryV2
public class AzureActiveDirectoryV2AuthenticatorConfiguration : AuthenticatorConfiguration
{
public string ClientId { get; set; }
public string ClientSecret { get; set; }

public AzureActiveDirectoryV2AuthenticatorConfiguration()
{
Expand All @@ -31,7 +30,7 @@ public override void ApplyToOwinSecurityOptions(AuthenticationOptions options)
// the auth flow.
openIdOptions.AuthenticationMode = AuthenticationMode.Passive;

// Make sure ClientId and ClientSecret is configured
// Make sure ClientId is configured
if (String.IsNullOrEmpty(ClientId))
{
throw new ConfigurationErrorsException(String.Format(
Expand All @@ -40,16 +39,7 @@ public override void ApplyToOwinSecurityOptions(AuthenticationOptions options)
"Auth.CommonAuth.ClientId"));
}

if (String.IsNullOrEmpty(ClientSecret))
{
throw new ConfigurationErrorsException(String.Format(
CultureInfo.CurrentCulture,
ServicesStrings.MissingRequiredConfigurationValue,
"Auth.CommonAuth.ClientSecret"));
}

openIdOptions.ClientId = ClientId;
openIdOptions.ClientSecret = ClientSecret;
openIdOptions.Authority = String.Format(CultureInfo.InvariantCulture, AzureActiveDirectoryV2Authenticator.Authority, AzureActiveDirectoryV2Authenticator.V2CommonTenant);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"codefactor.io",
"coveralls.io",
"dev.azure.com",
"flat.badgen.net",
"gitlab.com",
"img.shields.io",
"i.imgur.com",
Expand Down
2 changes: 1 addition & 1 deletion src/NuGetGallery/Scripts/gallery/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@

nuget.setPopovers = function () {
var popoverElement = $(this);
var popoverElementDom = popoverElement.get(0);
var popoverElementDom = this;
var originalLabel = popoverElementDom.ariaLabel;
var popoverHideTimeMS = 2000;
var popoverFadeTimeMS = 200;
Expand Down
2 changes: 1 addition & 1 deletion src/NuGetGallery/Services/UploadPackageMissingReadme.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace NuGetGallery
{
/// <summary>
/// Represents a package ID reservation conflict
/// Represents package missing an embedded README.
/// </summary>
public class UploadPackageMissingReadme : IValidationMessage
{
Expand Down
13 changes: 7 additions & 6 deletions src/NuGetGallery/Views/Packages/UploadPackage.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
@if (Model.IsUserLocked)
{
@ViewHelpers.AlertDanger(
@<text>
@ServicesStrings.UserAccountIsLocked
</text>)
@<text>
@ServicesStrings.UserAccountIsLocked
</text>)
}
else
{
Expand All @@ -33,6 +33,10 @@
<a href="https://docs.microsoft.com/nuget/create-packages/package-authoring-best-practices" alt="Best Practices" aria-label="Read here about publishing packages">Best Practices</a> page.
</p>
</div>

<div id="validation-failure-container" class="hidden">
</div>

<div id="upload-package-container">
<h2>
<a href="#" role="button" data-toggle="collapse" data-target="#upload-package-form"
Expand Down Expand Up @@ -61,9 +65,6 @@
<div id="upload-progress-bar-container" class="progress hidden">
<div id="upload-progress-bar" class="progress-bar active" role="progressbar" aria-valuemin="0" aria-valuemax="100"></div>
</div>

<div id="validation-failure-container" class="hidden">
</div>
</div>
</div>

Expand Down
4 changes: 3 additions & 1 deletion src/NuGetGallery/Views/Users/Packages.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,11 @@
{
var title = "This version has at least one known vulnerability. Click on the package name for details.";
<td class="package-icon-cell">
<span class="package-warning-icon" aria-label="@title" data-content="@title" data-bind="visible: IsVulnerable" tabindex="0">
<!-- ko if: IsVulnerable -->
<span class="package-warning-icon" aria-label="@title" data-content="@title" tabindex="0">
<i class="ms-Icon ms-Icon--Warning package-icon"></i>
</span>
<!-- /ko -->
</td>
}
<td class="text-right align-middle package-controls">
Expand Down
1 change: 0 additions & 1 deletion src/NuGetGallery/Web.config
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@
<add key="Auth.MicrosoftAccount.ClientSecret" value=""/>
<add key="Auth.AzureActiveDirectoryV2.Enabled" value="false"/>
<add key="Auth.AzureActiveDirectoryV2.ClientId" value=""/>
<add key="Auth.AzureActiveDirectoryV2.ClientSecret" value=""/>
<add key="Auth.AzureActiveDirectory.Enabled" value="false"/>
<add key="Auth.AzureActiveDirectory.ClientId" value=""/>
<add key="Auth.AzureActiveDirectory.Authority" value=""/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<IEntitiesContext>();
var auditingService = new Mock<IAuditingService>();
var credentialBuilder = new CredentialBuilder();

var authService = new AuthenticationService(
entitiesContext.Object,
Get<IAppConfiguration>(),
Get<IDiagnosticsService>(),
auditingService.Object,
Enumerable.Empty<Authenticator>(),
credentialBuilder,
Get<ICredentialValidator>(),
Get<IDateTimeProvider>(),
Get<ITelemetryService>(),
Get<IContentObjectService>(),
Get<IFeatureFlagService>());
var operations = new List<string>();

var fakes = Get<Fakes>();
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<AuditRecord>()))
.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<string> { nameof(IEntitiesContext.SaveChangesAsync), nameof(IAuditingService.SaveAuditRecordAsync) },
operations);
}
}

public class TheDescribeCredentialMethod : TestContainer
Expand Down