public async Task StorePackageFileInBackupLocationAsync(Package package, Stream packageFile) { if (package == null) { throw new ArgumentNullException(nameof(package)); } if (packageFile == null) { throw new ArgumentNullException(nameof(packageFile)); } if (package.PackageRegistration == null || string.IsNullOrWhiteSpace(package.PackageRegistration.Id) || (string.IsNullOrWhiteSpace(package.NormalizedVersion) && string.IsNullOrWhiteSpace(package.Version))) { throw new ArgumentException(CoreStrings.PackageIsMissingRequiredData, nameof(package)); } string version; if (string.IsNullOrEmpty(package.NormalizedVersion)) { version = NuGetVersion.Parse(package.Version).ToNormalizedString(); } else { version = package.NormalizedVersion; } // Hash the provided stream instead of using the hash on the package. This is to avoid a backup with the // incorrect file name if the hash in the DB does not match the package (a potentially transient issue). var hash = CryptographyService.GenerateHash( packageFile, hashAlgorithmId: CoreConstants.Sha512HashAlgorithmId); packageFile.Position = 0; var fileName = BuildBackupFileName( package.PackageRegistration.Id, version, hash); // If the package already exists, don't even bother uploading it. The file name is based off of the hash so // we know the upload isn't necessary. if (await _fileStorageService.FileExistsAsync(CoreConstants.PackageBackupsFolderName, fileName)) { return; } try { await _fileStorageService.SaveFileAsync(CoreConstants.PackageBackupsFolderName, fileName, packageFile); } catch (InvalidOperationException) { // If the package file already exists, swallow the exception since we know the content is the same. return; } }
public void ResetsPasswordMigratesPasswordHash() { var user = new User { Username = "******", EmailAddress = "*****@*****.**", HashedPassword = CryptographyService.GenerateSaltedHash("thePassword", "SHA1"), PasswordHashAlgorithm = "SHA1", PasswordResetToken = "some-token", PasswordResetTokenExpirationDate = DateTime.UtcNow.AddDays(1), }; var userService = new TestableUserService(); userService.MockUserRepository .Setup(r => r.GetAll()) .Returns(new[] { user }.AsQueryable()); bool result = userService.ResetPasswordWithToken("user", "some-token", "new-password"); Assert.True(result); Assert.Equal("PBKDF2", user.PasswordHashAlgorithm); Assert.True(VerifyPasswordHash(user, "new-password")); Assert.Null(user.PasswordResetToken); Assert.Null(user.PasswordResetTokenExpirationDate); userService.MockUserRepository .Verify(u => u.CommitChanges()); }
public async Task <Package> ReflowAsync(string id, string version) { var package = _packageService.FindPackageByIdAndVersionStrict(id, version); if (package == null) { return(null); } EntitiesConfiguration.SuspendExecutionStrategy = true; using (var transaction = _entitiesContext.GetDatabase().BeginTransaction()) { // 1) Download package binary to memory using (var packageStream = await _packageFileService.DownloadPackageFileAsync(package)) { using (var packageArchive = new PackageArchiveReader(packageStream, leaveStreamOpen: false)) { // 2) Determine package metadata from binary var packageStreamMetadata = new PackageStreamMetadata { HashAlgorithm = CoreConstants.Sha512HashAlgorithmId, Hash = CryptographyService.GenerateHash(packageStream.AsSeekableStream()), Size = packageStream.Length, }; var packageMetadata = PackageMetadata.FromNuspecReader(packageArchive.GetNuspecReader()); // 3) Clear referenced objects that will be reflowed ClearSupportedFrameworks(package); ClearAuthors(package); ClearDependencies(package); // 4) Reflow the package var listed = package.Listed; package = _packageService.EnrichPackageFromNuGetPackage( package, packageArchive, packageMetadata, packageStreamMetadata, package.User); package.LastEdited = DateTime.UtcNow; package.Listed = listed; // 5) Update IsLatest so that reflow can correct concurrent updates (see Gallery #2514) await _packageService.UpdateIsLatestAsync(package.PackageRegistration, commitChanges : false); // 6) Save and profit await _entitiesContext.SaveChangesAsync(); } } // Commit transaction transaction.Commit(); } EntitiesConfiguration.SuspendExecutionStrategy = false; return(package); }
public static Credential CreatePasswordCredential(string password) { return(new Credential( type: CredentialTypes.Password.Pbkdf2, value: CryptographyService.GenerateSaltedHash( password, Constants.PBKDF2HashAlgorithmId))); }
User CreateUser(string username, string password, string emailAddress) { return(new User { Username = username, EmailAddress = emailAddress, HashedPassword = CryptographyService.GenerateSaltedHash(password, Constants.PBKDF2HashAlgorithmId), PasswordHashAlgorithm = Constants.PBKDF2HashAlgorithmId }); }
public static bool VerifyPasswordHash(string hash, string algorithm, string password) { bool canAuthenticate = CryptographyService.ValidateSaltedHash( hash, password, algorithm); bool sanity = CryptographyService.ValidateSaltedHash( hash, "not_the_password", algorithm); return(canAuthenticate && !sanity); }
public static bool VerifyPasswordHash(User user, string password) { bool canAuthenticate = CryptographyService.ValidateSaltedHash( user.HashedPassword, password, user.PasswordHashAlgorithm); bool sanity = CryptographyService.ValidateSaltedHash( user.HashedPassword, "not_the_password", user.PasswordHashAlgorithm); return(canAuthenticate && !sanity); }
public void UpdatesTheHashedPassword() { var hash = CryptographyService.GenerateSaltedHash("oldpwd", "PBKDF2"); var user = new User { Username = "******", HashedPassword = hash, PasswordHashAlgorithm = "PBKDF2" }; var service = new TestableUserService(); service.MockUserRepository .Setup(r => r.GetAll()).Returns(new[] { user }.AsQueryable()); var changed = service.ChangePassword("user", "oldpwd", "newpwd"); Assert.True(VerifyPasswordHash(user, "newpwd")); }
public void WillNotFindsUsersByEmailAddress() { var hash = CryptographyService.GenerateSaltedHash("thePassword", Constants.PBKDF2HashAlgorithmId); var user = new User { Username = "******", HashedPassword = hash, EmailAddress = "*****@*****.**" }; var service = new TestableUserService(); service.MockUserRepository .Setup(r => r.GetAll()) .Returns(new[] { user }.AsQueryable()); var foundByEmailAddress = service.FindByUsernameAndPassword("*****@*****.**", "thePassword"); Assert.Null(foundByEmailAddress); }
public void ReturnsTrueWhenSuccessful() { var crypto = new CryptographyService(); var user = new User { Username = "******", HashedPassword = "******" }; var userRepository = new Mock<IEntityRepository<User>>(); userRepository.Setup(r => r.GetAll()).Returns(new[] { user }.AsQueryable()); var cryptoService = new Mock<ICryptographyService>(); cryptoService.Setup(s => s.ValidateSaltedHash("old hash", "oldpwd", Constants.Sha1HashAlgorithmId)).Returns(true); cryptoService.Setup(s => s.GenerateSaltedHash("newpwd", Constants.Sha1HashAlgorithmId)).Returns("hash and bacon"); var service = CreateUsersService(userRepo: userRepository, cryptoSvc: cryptoService); var changed = service.ChangePassword("user", "oldpwd", "newpwd"); Assert.True(changed); Assert.Equal("hash and bacon", user.HashedPassword); }
public void ReturnsFalseIfPasswordDoesNotMatchUser_PBKDF2() { var user = new User { Username = "******", HashedPassword = CryptographyService.GenerateSaltedHash("oldpwd", "PBKDF2"), PasswordHashAlgorithm = "PBKDF2", }; var service = new TestableUserService(); service.MockUserRepository .Setup(r => r.GetAll()).Returns(new[] { user }.AsQueryable()); var changed = service.ChangePassword("user", "not_the_password", "newpwd"); Assert.False(changed); }
public static User CreateAUser( string username, string password, string emailAddress, string hashAlgorithm = Constants.PBKDF2HashAlgorithmId) { return(new User { Username = username, HashedPassword = String.IsNullOrEmpty(password) ? null : CryptographyService.GenerateSaltedHash(password, hashAlgorithm), PasswordHashAlgorithm = String.IsNullOrEmpty(password) ? null : hashAlgorithm, EmailAddress = emailAddress, }); }
public void MigratesPasswordIfHashAlgorithmIsNotPBKDF2() { var user = new User { Username = "******", HashedPassword = CryptographyService.GenerateSaltedHash("oldpwd", "SHA1"), PasswordHashAlgorithm = "SHA1" }; var service = new TestableUserService(); service.MockUserRepository .Setup(r => r.GetAll()).Returns(new[] { user }.AsQueryable()); var changed = service.ChangePassword("user", "oldpwd", "newpwd"); Assert.True(changed); Assert.True(VerifyPasswordHash(user, "newpwd")); Assert.Equal("PBKDF2", user.PasswordHashAlgorithm); }
public void ReturnsTrueWhenSuccessful() { var crypto = new CryptographyService(); var user = new User { Username = "******", HashedPassword = "******" }; var userRepository = new Mock <IEntityRepository <User> >(); userRepository.Setup(r => r.GetAll()).Returns(new[] { user }.AsQueryable()); var cryptoService = new Mock <ICryptographyService>(); cryptoService.Setup(s => s.ValidateSaltedHash("old hash", "oldpwd", Const.Sha1HashAlgorithmId)).Returns(true); cryptoService.Setup(s => s.GenerateSaltedHash("newpwd", Const.Sha1HashAlgorithmId)).Returns("hash and bacon"); var service = CreateUsersService(userRepo: userRepository, cryptoSvc: cryptoService); var changed = service.ChangePassword("user", "oldpwd", "newpwd"); Assert.True(changed); Assert.Equal("hash and bacon", user.HashedPassword); }
public async Task<PackageOwnerRequest> CreatePackageOwnerRequestAsync(PackageRegistration package, User currentOwner, User newOwner) { var existingRequest = FindExistingPackageOwnerRequest(package, newOwner); if (existingRequest != null) { return existingRequest; } var newRequest = new PackageOwnerRequest { PackageRegistrationKey = package.Key, RequestingOwnerKey = currentOwner.Key, NewOwnerKey = newOwner.Key, ConfirmationCode = CryptographyService.GenerateToken(), RequestDate = DateTime.UtcNow }; _packageOwnerRequestRepository.InsertOnCommit(newRequest); await _packageOwnerRequestRepository.CommitChangesAsync(); return newRequest; }
public async Task <PackageOwnerRequest> AddPackageOwnershipRequest(PackageRegistration package, User requestingOwner, User newOwner) { if (package == null) { throw new ArgumentNullException(nameof(package)); } if (requestingOwner == null) { throw new ArgumentNullException(nameof(requestingOwner)); } if (newOwner == null) { throw new ArgumentNullException(nameof(newOwner)); } var existingRequest = GetPackageOwnershipRequests(package: package, newOwner: newOwner).FirstOrDefault(); if (existingRequest != null) { return(existingRequest); } var newRequest = new PackageOwnerRequest { PackageRegistrationKey = package.Key, RequestingOwnerKey = requestingOwner.Key, NewOwnerKey = newOwner.Key, ConfirmationCode = CryptographyService.GenerateToken(), RequestDate = DateTime.UtcNow }; _packageOwnerRequestRepository.InsertOnCommit(newRequest); await _packageOwnerRequestRepository.CommitChangesAsync(); return(newRequest); }
public void FindsUsersUpdatesPasswordIfUsingLegacyHashAlgorithm() { var user = new User { Username = "******", HashedPassword = CryptographyService.GenerateSaltedHash("thePassword", "SHA1"), PasswordHashAlgorithm = "SHA1", EmailAddress = "*****@*****.**", }; var service = new TestableUserService(); service.MockUserRepository .Setup(r => r.GetAll()) .Returns(new[] { user }.AsQueryable()); service.MockUserRepository .Setup(r => r.CommitChanges()) .Verifiable(); service.FindByUsernameOrEmailAddressAndPassword("*****@*****.**", "thePassword"); Assert.Equal("PBKDF2", user.PasswordHashAlgorithm); Assert.True(VerifyPasswordHash(user, "thePassword")); service.MockUserRepository.Verify(r => r.CommitChanges(), Times.Once()); }
/// <summary> /// This method creates the symbol db entities and invokes the validations for the uploaded snupkg. /// It will send the message for validation and upload the snupkg to the "validations"/"symbols-packages" container /// based on the result. It will then update the references in the database for persistence with appropriate status. /// </summary> /// <param name="package">The package for which symbols package is to be uplloaded</param> /// <param name="symbolPackageStream">The symbols package stream metadata for the uploaded symbols package file.</param> /// <returns>The <see cref="PackageCommitResult"/> for the create and upload symbol package flow.</returns> public async Task <PackageCommitResult> CreateAndUploadSymbolsPackage(Package package, Stream symbolPackageStream) { var packageStreamMetadata = new PackageStreamMetadata { HashAlgorithm = CoreConstants.Sha512HashAlgorithmId, Hash = CryptographyService.GenerateHash( symbolPackageStream.AsSeekableStream(), CoreConstants.Sha512HashAlgorithmId), Size = symbolPackageStream.Length }; Stream symbolPackageFile = symbolPackageStream.AsSeekableStream(); var symbolPackage = _symbolPackageService.CreateSymbolPackage(package, packageStreamMetadata); await _validationService.StartValidationAsync(symbolPackage); if (symbolPackage.StatusKey != PackageStatus.Available && symbolPackage.StatusKey != PackageStatus.Validating) { throw new InvalidOperationException( $"The symbol package to commit must have either the {PackageStatus.Available} or {PackageStatus.Validating} package status."); } try { if (symbolPackage.StatusKey == PackageStatus.Validating) { await _symbolPackageFileService.SaveValidationPackageFileAsync(symbolPackage.Package, symbolPackageFile); } else if (symbolPackage.StatusKey == PackageStatus.Available) { if (!symbolPackage.Published.HasValue) { symbolPackage.Published = DateTime.UtcNow; } // Mark any other associated available symbol packages for deletion. var availableSymbolPackages = package .SymbolPackages .Where(sp => sp.StatusKey == PackageStatus.Available && sp != symbolPackage); var overwrite = false; if (availableSymbolPackages.Any()) { // Mark the currently available packages for deletion, and replace the file in the container. foreach (var availableSymbolPackage in availableSymbolPackages) { availableSymbolPackage.StatusKey = PackageStatus.Deleted; } overwrite = true; } // Caveat: This doesn't really affect our prod flow since the package is validating, however, when the async validation // is disabled there is a chance that there could be concurrency issues when pushing multiple symbols simultaneously. // This could result in an inconsistent data or multiple symbol entities marked as available. This could be sovled using etag // for saving files, however since it doesn't really affect nuget.org which happen have async validations flow I will leave it as is. await _symbolPackageFileService.SavePackageFileAsync(symbolPackage.Package, symbolPackageFile, overwrite); } try { // commit all changes to database as an atomic transaction await _entitiesContext.SaveChangesAsync(); } catch (Exception ex) { ex.Log(); // If saving to the DB fails for any reason we need to delete the package we just saved. if (symbolPackage.StatusKey == PackageStatus.Validating) { await _symbolPackageFileService.DeleteValidationPackageFileAsync( package.PackageRegistration.Id, package.Version); } else if (symbolPackage.StatusKey == PackageStatus.Available) { await _symbolPackageFileService.DeletePackageFileAsync( package.PackageRegistration.Id, package.Version); } throw ex; } } catch (FileAlreadyExistsException ex) { ex.Log(); return(PackageCommitResult.Conflict); } _telemetryService.TrackSymbolPackagePushEvent(package.Id, package.NormalizedVersion); return(PackageCommitResult.Success); }
public static Credential CreateSha1Password(string plaintextPassword) { return(new Credential( CredentialTypes.Password.Sha1, CryptographyService.GenerateSaltedHash(plaintextPassword, Constants.Sha1HashAlgorithmId))); }
private async Task <ActionResult> CreatePackageInternal() { var policyResult = await SecurityPolicyService.EvaluateAsync(SecurityPolicyAction.PackagePush, HttpContext); if (!policyResult.Success) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, policyResult.ErrorMessage)); } // Get the user var currentUser = GetCurrentUser(); using (var packageStream = ReadPackageFromRequest()) { try { using (var archive = new ZipArchive(packageStream, ZipArchiveMode.Read, leaveOpen: true)) { var reference = DateTime.UtcNow.AddDays(1); // allow "some" clock skew var entryInTheFuture = archive.Entries.FirstOrDefault( e => e.LastWriteTime.UtcDateTime > reference); if (entryInTheFuture != null) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, Strings.PackageEntryFromTheFuture, entryInTheFuture.Name))); } } using (var packageToPush = new PackageArchiveReader(packageStream, leaveStreamOpen: false)) { try { PackageService.EnsureValid(packageToPush); } catch (Exception ex) { ex.Log(); var message = Strings.FailedToReadUploadFile; if (ex is InvalidPackageException || ex is InvalidDataException || ex is EntityException) { message = ex.Message; } return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, message)); } NuspecReader nuspec; var errors = ManifestValidator.Validate(packageToPush.GetNuspec(), out nuspec).ToArray(); if (errors.Length > 0) { var errorsString = string.Join("', '", errors.Select(error => error.ErrorMessage)); return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, errors.Length > 1 ? Strings.UploadPackage_InvalidNuspecMultiple : Strings.UploadPackage_InvalidNuspec, errorsString))); } if (nuspec.GetMinClientVersion() > Constants.MaxSupportedMinClientVersion) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, Strings.UploadPackage_MinClientVersionOutOfRange, nuspec.GetMinClientVersion()))); } User owner; // Ensure that the user can push packages for this partialId. var id = nuspec.GetId(); var version = nuspec.GetVersion(); var packageRegistration = PackageService.FindPackageRegistrationById(id); if (packageRegistration == null) { // Check if the current user's scopes allow pushing a new package ID var apiScopeEvaluationResult = EvaluateApiScope(ActionsRequiringPermissions.UploadNewPackageId, new ActionOnNewPackageContext(id, ReservedNamespaceService), NuGetScopes.PackagePush); owner = apiScopeEvaluationResult.Owner; if (!apiScopeEvaluationResult.IsSuccessful()) { // User cannot push a new package ID as the current user's scopes does not allow it return(GetHttpResultFromFailedApiScopeEvaluationForPush(apiScopeEvaluationResult, id, version)); } } else { // Check if the current user's scopes allow pushing a new version of an existing package ID var apiScopeEvaluationResult = EvaluateApiScope(ActionsRequiringPermissions.UploadNewPackageVersion, packageRegistration, NuGetScopes.PackagePushVersion, NuGetScopes.PackagePush); owner = apiScopeEvaluationResult.Owner; if (!apiScopeEvaluationResult.IsSuccessful()) { // User cannot push a package as the current user's scopes does not allow it await AuditingService.SaveAuditRecordAsync( new FailedAuthenticatedOperationAuditRecord( currentUser.Username, AuditedAuthenticatedOperationAction.PackagePushAttemptByNonOwner, attemptedPackage : new AuditedPackageIdentifier( id, version.ToNormalizedStringSafe()))); return(GetHttpResultFromFailedApiScopeEvaluationForPush(apiScopeEvaluationResult, id, version)); } if (packageRegistration.IsLocked) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.Forbidden, string.Format(CultureInfo.CurrentCulture, Strings.PackageIsLocked, packageRegistration.Id))); } // Check if a particular Id-Version combination already exists. We eventually need to remove this check. string normalizedVersion = version.ToNormalizedString(); bool packageExists = packageRegistration.Packages.Any( p => string.Equals( p.NormalizedVersion, normalizedVersion, StringComparison.OrdinalIgnoreCase)); if (packageExists) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.Conflict, string.Format(CultureInfo.CurrentCulture, Strings.PackageExistsAndCannotBeModified, id, nuspec.GetVersion().ToNormalizedStringSafe()))); } } var packageStreamMetadata = new PackageStreamMetadata { HashAlgorithm = Constants.Sha512HashAlgorithmId, Hash = CryptographyService.GenerateHash(packageStream.AsSeekableStream()), Size = packageStream.Length }; var package = await PackageUploadService.GeneratePackageAsync( id, packageToPush, packageStreamMetadata, owner, currentUser); await AutoCuratePackage.ExecuteAsync(package, packageToPush, commitChanges : false); PackageCommitResult commitResult; using (Stream uploadStream = packageStream) { uploadStream.Position = 0; commitResult = await PackageUploadService.CommitPackageAsync( package, uploadStream.AsSeekableStream()); } switch (commitResult) { case PackageCommitResult.Success: break; case PackageCommitResult.Conflict: return(new HttpStatusCodeWithBodyResult( HttpStatusCode.Conflict, Strings.UploadPackage_IdVersionConflict)); default: throw new NotImplementedException($"The package commit result {commitResult} is not supported."); } IndexingService.UpdatePackage(package); // Write an audit record await AuditingService.SaveAuditRecordAsync( new PackageAuditRecord(package, AuditedPackageAction.Create, PackageCreatedVia.Api)); if (!(ConfigurationService.Current.AsynchronousPackageValidationEnabled && ConfigurationService.Current.BlockingAsynchronousPackageValidationEnabled)) { // Notify user of push unless async validation in blocking mode is used MessageService.SendPackageAddedNotice(package, Url.Package(package.PackageRegistration.Id, package.NormalizedVersion, relativeUrl: false), Url.ReportPackage(package.PackageRegistration.Id, package.NormalizedVersion, relativeUrl: false), Url.AccountSettings(relativeUrl: false)); } TelemetryService.TrackPackagePushEvent(package, currentUser, User.Identity); if (package.SemVerLevelKey == SemVerLevelKey.SemVer2) { return(new HttpStatusCodeWithServerWarningResult(HttpStatusCode.Created, Strings.WarningSemVer2PackagePushed)); } return(new HttpStatusCodeResult(HttpStatusCode.Created)); } } catch (InvalidPackageException ex) { return(BadRequestForExceptionMessage(ex)); } catch (InvalidDataException ex) { return(BadRequestForExceptionMessage(ex)); } catch (EntityException ex) { return(BadRequestForExceptionMessage(ex)); } catch (FrameworkException ex) { return(BadRequestForExceptionMessage(ex)); } } }
private async Task <ActionResult> CreatePackageInternal() { // Get the user var user = GetCurrentUser(); using (var packageStream = ReadPackageFromRequest()) { try { using (var archive = new ZipArchive(packageStream, ZipArchiveMode.Read, leaveOpen: true)) { var reference = DateTime.UtcNow.AddDays(1); // allow "some" clock skew var entryInTheFuture = archive.Entries.FirstOrDefault( e => e.LastWriteTime.UtcDateTime > reference); if (entryInTheFuture != null) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, Strings.PackageEntryFromTheFuture, entryInTheFuture.Name))); } } using (var packageToPush = new PackageArchiveReader(packageStream, leaveStreamOpen: false)) { NuspecReader nuspec = null; try { nuspec = packageToPush.GetNuspecReader(); } catch (Exception ex) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, Strings.UploadPackage_InvalidNuspec, ex.Message))); } if (nuspec.GetMinClientVersion() > Constants.MaxSupportedMinClientVersion) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, Strings.UploadPackage_MinClientVersionOutOfRange, nuspec.GetMinClientVersion()))); } // Ensure that the user can push packages for this partialId. var packageRegistration = PackageService.FindPackageRegistrationById(nuspec.GetId()); if (packageRegistration != null) { if (!packageRegistration.IsOwner(user)) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Forbidden, Strings.ApiKeyNotAuthorized)); } // Check if a particular Id-Version combination already exists. We eventually need to remove this check. string normalizedVersion = nuspec.GetVersion().ToNormalizedString(); bool packageExists = packageRegistration.Packages.Any( p => String.Equals( p.NormalizedVersion, normalizedVersion, StringComparison.OrdinalIgnoreCase)); if (packageExists) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.Conflict, String.Format(CultureInfo.CurrentCulture, Strings.PackageExistsAndCannotBeModified, nuspec.GetId(), nuspec.GetVersion().ToNormalizedStringSafe()))); } } var packageStreamMetadata = new PackageStreamMetadata { HashAlgorithm = Constants.Sha512HashAlgorithmId, Hash = CryptographyService.GenerateHash(packageStream.AsSeekableStream()), Size = packageStream.Length, }; var package = await PackageService.CreatePackageAsync( packageToPush, packageStreamMetadata, user, commitChanges : false); await AutoCuratePackage.ExecuteAsync(package, packageToPush, commitChanges : false); await EntitiesContext.SaveChangesAsync(); using (Stream uploadStream = packageStream) { uploadStream.Position = 0; await PackageFileService.SavePackageFileAsync(package, uploadStream.AsSeekableStream()); IndexingService.UpdatePackage(package); } MessageService.SendPackageAddedNotice(package, Url.Action("DisplayPackage", "Packages", routeValues: new { id = package.PackageRegistration.Id, version = package.Version }, protocol: Request.Url.Scheme), Url.Action("ReportMyPackage", "Packages", routeValues: new { id = package.PackageRegistration.Id, version = package.Version }, protocol: Request.Url.Scheme), Url.Action("Account", "Users", routeValues: null, protocol: Request.Url.Scheme)); return(new HttpStatusCodeResult(HttpStatusCode.Created)); } } catch (InvalidPackageException ex) { return(BadRequestForExceptionMessage(ex)); } catch (InvalidDataException ex) { return(BadRequestForExceptionMessage(ex)); } catch (EntityException ex) { return(BadRequestForExceptionMessage(ex)); } catch (FrameworkException ex) { return(BadRequestForExceptionMessage(ex)); } } }
private async Task <ActionResult> CreatePackageInternal() { // Get the user var user = GetCurrentUser(); using (var packageStream = ReadPackageFromRequest()) { try { using (var packageToPush = new PackageArchiveReader(packageStream, leaveStreamOpen: false)) { NuspecReader nuspec = null; try { nuspec = packageToPush.GetNuspecReader(); } catch (Exception ex) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, Strings.UploadPackage_InvalidNuspec, ex.Message))); } if (nuspec.GetMinClientVersion() > Constants.MaxSupportedMinClientVersion) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, Strings.UploadPackage_MinClientVersionOutOfRange, nuspec.GetMinClientVersion()))); } // Ensure that the user can push packages for this partialId. var packageRegistration = PackageService.FindPackageRegistrationById(nuspec.GetId()); if (packageRegistration != null) { if (!packageRegistration.IsOwner(user)) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Forbidden, Strings.ApiKeyNotAuthorized)); } // Check if a particular Id-Version combination already exists. We eventually need to remove this check. string normalizedVersion = nuspec.GetVersion().ToNormalizedString(); bool packageExists = packageRegistration.Packages.Any( p => String.Equals( p.NormalizedVersion, normalizedVersion, StringComparison.OrdinalIgnoreCase)); if (packageExists) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.Conflict, String.Format(CultureInfo.CurrentCulture, Strings.PackageExistsAndCannotBeModified, nuspec.GetId(), nuspec.GetVersion().ToNormalizedStringSafe()))); } } var packageStreamMetadata = new PackageStreamMetadata { HashAlgorithm = Constants.Sha512HashAlgorithmId, Hash = CryptographyService.GenerateHash(packageStream.AsSeekableStream()), Size = packageStream.Length, }; var package = await PackageService.CreatePackageAsync(packageToPush, packageStreamMetadata, user, commitChanges : false); await AutoCuratePackage.ExecuteAsync(package, packageToPush, commitChanges : false); await EntitiesContext.SaveChangesAsync(); using (Stream uploadStream = packageStream) { uploadStream.Position = 0; await PackageFileService.SavePackageFileAsync(package, uploadStream.AsSeekableStream()); IndexingService.UpdatePackage(package); } return(new HttpStatusCodeResult(HttpStatusCode.Created)); } } catch (InvalidDataException ex) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.BadRequest, string.Format(CultureInfo.CurrentCulture, Strings.UploadPackage_InvalidPackage, ex.Message))); } } }
private async Task <ActionResult> CreatePackageInternal() { // Get the user var user = GetCurrentUser(); using (var packageStream = ReadPackageFromRequest()) { try { using (var archive = new ZipArchive(packageStream, ZipArchiveMode.Read, leaveOpen: true)) { var reference = DateTime.UtcNow.AddDays(1); // allow "some" clock skew var entryInTheFuture = archive.Entries.FirstOrDefault( e => e.LastWriteTime.UtcDateTime > reference); if (entryInTheFuture != null) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, Strings.PackageEntryFromTheFuture, entryInTheFuture.Name))); } } using (var packageToPush = new PackageArchiveReader(packageStream, leaveStreamOpen: false)) { try { PackageService.EnsureValid(packageToPush); } catch (Exception ex) { ex.Log(); var message = Strings.FailedToReadUploadFile; if (ex is InvalidPackageException || ex is InvalidDataException || ex is EntityException) { message = ex.Message; } return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, message)); } NuspecReader nuspec; var errors = ManifestValidator.Validate(packageToPush.GetNuspec(), out nuspec).ToArray(); if (errors.Length > 0) { var errorsString = string.Join("', '", errors.Select(error => error.ErrorMessage)); return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, errors.Length > 1 ? Strings.UploadPackage_InvalidNuspecMultiple : Strings.UploadPackage_InvalidNuspec, errorsString))); } if (nuspec.GetMinClientVersion() > Constants.MaxSupportedMinClientVersion) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, Strings.UploadPackage_MinClientVersionOutOfRange, nuspec.GetMinClientVersion()))); } // Ensure that the user can push packages for this partialId. var packageRegistration = PackageService.FindPackageRegistrationById(nuspec.GetId()); if (packageRegistration == null) { // Check if API key allows pushing a new package id if (!ApiKeyScopeAllows( subject: nuspec.GetId(), requestedActions: NuGetScopes.PackagePush)) { // User cannot push a new package ID as the API key scope does not allow it return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Unauthorized, Strings.ApiKeyNotAuthorized)); } } else { // Is the user allowed to push this Id? if (!packageRegistration.IsOwner(user)) { // Audit that a non-owner tried to push the package await AuditingService.SaveAuditRecord( new FailedAuthenticatedOperationAuditRecord( user.Username, AuditedAuthenticatedOperationAction.PackagePushAttemptByNonOwner, attemptedPackage : new AuditedPackageIdentifier( nuspec.GetId(), nuspec.GetVersion().ToNormalizedStringSafe()))); // User cannot push a package to an ID owned by another user. return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Conflict, string.Format(CultureInfo.CurrentCulture, Strings.PackageIdNotAvailable, nuspec.GetId()))); } // Check if API key allows pushing the current package id if (!ApiKeyScopeAllows( packageRegistration.Id, NuGetScopes.PackagePushVersion, NuGetScopes.PackagePush)) { // User cannot push a package as the API key scope does not allow it return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Unauthorized, Strings.ApiKeyNotAuthorized)); } // Check if a particular Id-Version combination already exists. We eventually need to remove this check. string normalizedVersion = nuspec.GetVersion().ToNormalizedString(); bool packageExists = packageRegistration.Packages.Any( p => string.Equals( p.NormalizedVersion, normalizedVersion, StringComparison.OrdinalIgnoreCase)); if (packageExists) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.Conflict, string.Format(CultureInfo.CurrentCulture, Strings.PackageExistsAndCannotBeModified, nuspec.GetId(), nuspec.GetVersion().ToNormalizedStringSafe()))); } } var packageStreamMetadata = new PackageStreamMetadata { HashAlgorithm = Constants.Sha512HashAlgorithmId, Hash = CryptographyService.GenerateHash(packageStream.AsSeekableStream()), Size = packageStream.Length }; var package = await PackageService.CreatePackageAsync( packageToPush, packageStreamMetadata, user, commitChanges : false); await AutoCuratePackage.ExecuteAsync(package, packageToPush, commitChanges : false); using (Stream uploadStream = packageStream) { uploadStream.Position = 0; try { await PackageFileService.SavePackageFileAsync(package, uploadStream.AsSeekableStream()); } catch (InvalidOperationException ex) { ex.Log(); return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Conflict, Strings.UploadPackage_IdVersionConflict)); } } try { await EntitiesContext.SaveChangesAsync(); } catch { // If saving to the DB fails for any reason, we need to delete the package we just saved. await PackageFileService.DeletePackageFileAsync(nuspec.GetId(), nuspec.GetVersion().ToNormalizedString()); throw; } IndexingService.UpdatePackage(package); // Write an audit record await AuditingService.SaveAuditRecord( new PackageAuditRecord(package, AuditedPackageAction.Create, PackageCreatedVia.Api)); // Notify user of push MessageService.SendPackageAddedNotice(package, Url.Action("DisplayPackage", "Packages", routeValues: new { id = package.PackageRegistration.Id, version = package.Version }, protocol: Request.Url.Scheme), Url.Action("ReportMyPackage", "Packages", routeValues: new { id = package.PackageRegistration.Id, version = package.Version }, protocol: Request.Url.Scheme), Url.Action("Account", "Users", routeValues: null, protocol: Request.Url.Scheme)); return(new HttpStatusCodeResult(HttpStatusCode.Created)); } } catch (InvalidPackageException ex) { return(BadRequestForExceptionMessage(ex)); } catch (InvalidDataException ex) { return(BadRequestForExceptionMessage(ex)); } catch (EntityException ex) { return(BadRequestForExceptionMessage(ex)); } catch (FrameworkException ex) { return(BadRequestForExceptionMessage(ex)); } } }
[ValidateInput(false)] // Security note: Disabling ASP.Net input validation which does things like disallow angle brackets in submissions. See http://go.microsoft.com/fwlink/?LinkID=212874 public virtual async Task <ActionResult> VerifyPackage(VerifyPackageRequest formData) { var currentUser = GetCurrentUser(); Package package; using (Stream uploadFile = await _uploadFileService.GetUploadFileAsync(currentUser.Key)) { if (uploadFile == null) { TempData["Message"] = "Your attempt to verify the package submission failed, because we could not find the uploaded package file. Please try again."; return(new RedirectResult(Url.UploadPackage())); } var nugetPackage = await SafeCreatePackage(currentUser, uploadFile); if (nugetPackage == null) { // Send the user back return(new RedirectResult(Url.UploadPackage())); } Debug.Assert(nugetPackage != null); var packageMetadata = PackageMetadata.FromNuspecReader( nugetPackage.GetNuspecReader()); // Rule out problem scenario with multiple tabs - verification request (possibly with edits) was submitted by user // viewing a different package to what was actually most recently uploaded if (!(String.IsNullOrEmpty(formData.Id) || String.IsNullOrEmpty(formData.Version))) { if (!(String.Equals(packageMetadata.Id, formData.Id, StringComparison.OrdinalIgnoreCase) && String.Equals(packageMetadata.Version.ToNormalizedString(), formData.Version, StringComparison.OrdinalIgnoreCase))) { TempData["Message"] = "Your attempt to verify the package submission failed, because the package file appears to have changed. Please try again."; return(new RedirectResult(Url.VerifyPackage())); } } bool pendEdit = false; if (formData.Edit != null) { pendEdit = pendEdit || formData.Edit.RequiresLicenseAcceptance != packageMetadata.RequireLicenseAcceptance; pendEdit = pendEdit || IsDifferent(formData.Edit.IconUrl, packageMetadata.IconUrl.ToEncodedUrlStringOrNull()); pendEdit = pendEdit || IsDifferent(formData.Edit.ProjectUrl, packageMetadata.ProjectUrl.ToEncodedUrlStringOrNull()); pendEdit = pendEdit || IsDifferent(formData.Edit.Authors, packageMetadata.Authors.Flatten()); pendEdit = pendEdit || IsDifferent(formData.Edit.Copyright, packageMetadata.Copyright); pendEdit = pendEdit || IsDifferent(formData.Edit.Description, packageMetadata.Description); pendEdit = pendEdit || IsDifferent(formData.Edit.ReleaseNotes, packageMetadata.ReleaseNotes); pendEdit = pendEdit || IsDifferent(formData.Edit.Summary, packageMetadata.Summary); pendEdit = pendEdit || IsDifferent(formData.Edit.Tags, packageMetadata.Tags); pendEdit = pendEdit || IsDifferent(formData.Edit.VersionTitle, packageMetadata.Title); } var packageStreamMetadata = new PackageStreamMetadata { HashAlgorithm = Constants.Sha512HashAlgorithmId, Hash = CryptographyService.GenerateHash(uploadFile.AsSeekableStream()), Size = uploadFile.Length, }; // update relevant database tables try { package = await _packageService.CreatePackageAsync(nugetPackage, packageStreamMetadata, currentUser, commitChanges : false); Debug.Assert(package.PackageRegistration != null); } catch (EntityException ex) { TempData["Message"] = ex.Message; return(Redirect(Url.UploadPackage())); } await _packageService.PublishPackageAsync(package, commitChanges : false); if (pendEdit) { // Add the edit request to a queue where it will be processed in the background. _editPackageService.StartEditPackageRequest(package, formData.Edit, currentUser); } if (!formData.Listed) { await _packageService.MarkPackageUnlistedAsync(package, commitChanges : false); } await _autoCuratedPackageCmd.ExecuteAsync(package, nugetPackage, commitChanges : false); // save package to blob storage uploadFile.Position = 0; await _packageFileService.SavePackageFileAsync(package, uploadFile.AsSeekableStream()); // commit all changes to database as an atomic transaction await _entitiesContext.SaveChangesAsync(); // tell Lucene to update index for the new package _indexingService.UpdateIndex(); _messageService.SendPackageAddedNotice(package, Url.Action("DisplayPackage", "Packages", routeValues: new { id = package.PackageRegistration.Id, version = package.Version }, protocol: Request.Url.Scheme), Url.Action("ReportMyPackage", "Packages", routeValues: new { id = package.PackageRegistration.Id, version = package.Version }, protocol: Request.Url.Scheme), Url.Action("Account", "Users", routeValues: null, protocol: Request.Url.Scheme)); } // delete the uploaded binary in the Uploads container await _uploadFileService.DeleteUploadFileAsync(currentUser.Key); TempData["Message"] = String.Format( CultureInfo.CurrentCulture, Strings.SuccessfullyUploadedPackage, package.PackageRegistration.Id, package.Version); return(RedirectToRoute(RouteName.DisplayPackage, new { package.PackageRegistration.Id, package.Version })); }