public virtual ActionResult PublishPackage(string id, string version) { var package = PackageService.FindPackageByIdAndVersion(id, version); if (package == null) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.NotFound, String.Format(CultureInfo.CurrentCulture, Strings.PackageWithIdAndVersionNotFound, id, version))); } User user = GetCurrentUser(); if (!package.IsOwner(user)) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Forbidden, String.Format(CultureInfo.CurrentCulture, Strings.ApiKeyNotAuthorized, "publish"))); } PackageService.MarkPackageListed(package); IndexingService.UpdatePackage(package); return(new EmptyResult()); }
public virtual ActionResult VerifyPackageKey(string id, string version) { if (!String.IsNullOrEmpty(id)) { // If the partialId is present, then verify that the user has permission to push for the specific Id \ version combination. var package = PackageService.FindPackageByIdAndVersion(id, version); if (package == null) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.NotFound, String.Format(CultureInfo.CurrentCulture, Strings.PackageWithIdAndVersionNotFound, id, version))); } var user = GetCurrentUser(); if (!package.IsOwner(user)) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Forbidden, Strings.ApiKeyNotAuthorized)); } } return(new EmptyResult()); }
public virtual ActionResult Profiles(string username, int page = 1) { var currentUser = GetCurrentUser(); var user = UserService.FindByUsername(username); if (user == null || user.IsDeleted) { return(HttpNotFound()); } var packages = PackageService.FindPackagesByOwner(user, includeUnlisted: false) .OrderByDescending(p => p.PackageRegistration.DownloadCount) .Select(p => new ListPackageItemViewModel(p, currentUser) { DownloadCount = p.PackageRegistration.DownloadCount }).ToList(); var model = new UserProfileModel(user, currentUser, packages, page - 1, Constants.DefaultPackageListPageSize, Url); return(View(model)); }
private async Task <List <PackageRegistration> > DeleteOwnerFromReservedNamespaceImplAsync(string prefix, string username, ReservedNamespace namespaceToModify) { var userToRemove = UserService.FindByUsername(username) ?? throw new InvalidOperationException(string.Format( CultureInfo.CurrentCulture, Strings.ReservedNamespace_UserNotFound, username)); if (!namespaceToModify.Owners.Contains(userToRemove)) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Strings.ReservedNamespace_UserNotAnOwner, username)); } var packagesOwnedByUserMatchingPrefix = namespaceToModify .PackageRegistrations .Where(pr => pr .Owners .Any(pro => pro.Username == userToRemove.Username)) .ToList(); namespaceToModify.Owners.Remove(userToRemove); // Remove verified mark for package registrations if the user to be removed is the only prefix owner // for the given package registration. var packageRegistrationsToMarkUnverified = packagesOwnedByUserMatchingPrefix .Where(pr => !pr.Owners.Any(o => ActionsRequiringPermissions.AddPackageToReservedNamespace.CheckPermissionsOnBehalfOfAnyAccount( o, new[] { namespaceToModify }) == PermissionsCheckResult.Allowed)) .ToList(); if (packageRegistrationsToMarkUnverified.Any()) { packageRegistrationsToMarkUnverified .ForEach(pr => namespaceToModify.PackageRegistrations.Remove(pr)); await PackageService.UpdatePackageVerifiedStatusAsync(packageRegistrationsToMarkUnverified, isVerified : false); } await ReservedNamespaceRepository.CommitChangesAsync(); return(packageRegistrationsToMarkUnverified); }
public virtual ActionResult Profiles(string username, int page = 1, bool showAllPackages = false) { var user = UserService.FindByUsername(username); if (user == null) { return(HttpNotFound()); } var packages = PackageService.FindPackagesByOwner(user, includeUnlisted: false) .OrderByDescending(p => p.PackageRegistration.DownloadCount) .Select(p => new PackageViewModel(p) { DownloadCount = p.PackageRegistration.DownloadCount }).ToList(); var model = new UserProfileModel(user, packages, page - 1, Constants.DefaultPackageListPageSize, Url); model.ShowAllPackages = showAllPackages; return(View(model)); }
private async Task <HttpStatusCodeWithBodyResult> VerifyPackageKeyInternalAsync(User user, Credential credential, string id, string version) { // Verify that the user has permission to push for the specific Id \ version combination. var package = PackageService.FindPackageByIdAndVersion(id, version, semVerLevelKey: SemVerLevelKey.SemVer2); if (package == null) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.NotFound, String.Format(CultureInfo.CurrentCulture, Strings.PackageWithIdAndVersionNotFound, id, version))); } // Write an audit record await AuditingService.SaveAuditRecordAsync( new PackageAuditRecord(package, AuditedPackageAction.Verify)); if (!package.IsOwner(user)) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Forbidden, Strings.ApiKeyNotAuthorized)); } if (CredentialTypes.IsPackageVerificationApiKey(credential.Type)) { // Secure path: verify that verification key matches package scope. if (!ApiKeyScopeAllows(id, NuGetScopes.PackageVerify)) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Forbidden, Strings.ApiKeyNotAuthorized)); } } else { // Insecure path: verify that API key is legacy or matches package scope. if (!ApiKeyScopeAllows(id, NuGetScopes.PackagePush, NuGetScopes.PackagePushVersion)) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Forbidden, Strings.ApiKeyNotAuthorized)); } } return(null); }
public virtual async Task <ActionResult> PublishPackage(string id, string version) { var package = PackageService.FindPackageByIdAndVersionStrict(id, version); if (package == null) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.NotFound, String.Format(CultureInfo.CurrentCulture, Strings.PackageWithIdAndVersionNotFound, id, version))); } // Check if API key allows listing/unlisting the current package id User user = GetCurrentUser(); if (!HasAnyScopeThatAllows(package.PackageRegistration, NuGetScopes.PackageUnlist)) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Forbidden, Strings.ApiKeyNotAuthorized)); } await PackageService.MarkPackageListedAsync(package); IndexingService.UpdatePackage(package); return(new EmptyResult()); }
public virtual ActionResult Profiles(string username) { var user = UserService.FindByUsername(username); if (user == null) { return(HttpNotFound()); } var packages = PackageService.FindPackagesByOwner(user, includeUnlisted: false) .Select(p => new PackageViewModel(p) { DownloadCount = p.PackageRegistration.DownloadCount, Version = null }).ToList(); var model = new UserProfileModel(user) { Packages = packages, TotalPackageDownloadCount = packages.Sum(p => p.TotalDownloadCount), }; return(View(model)); }
public virtual ActionResult PublishPackage(string apiKey, string id, string version) { Guid parsedApiKey; if (!Guid.TryParse(apiKey, out parsedApiKey)) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.BadRequest, String.Format(CultureInfo.CurrentCulture, Strings.InvalidApiKey, apiKey))); } var user = UserService.FindByApiKey(parsedApiKey); if (user == null) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.Forbidden, String.Format(CultureInfo.CurrentCulture, Strings.ApiKeyNotAuthorized, "publish"))); } var package = PackageService.FindPackageByIdAndVersion(id, version); if (package == null) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.NotFound, String.Format(CultureInfo.CurrentCulture, Strings.PackageWithIdAndVersionNotFound, id, version))); } if (!package.IsOwner(user)) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.Forbidden, String.Format(CultureInfo.CurrentCulture, Strings.ApiKeyNotAuthorized, "publish"))); } PackageService.MarkPackageListed(package); IndexingService.UpdatePackage(package); return(new EmptyResult()); }
public virtual async Task <ActionResult> DeletePackage(string id, string version) { var package = PackageService.FindPackageByIdAndVersion(id, version); if (package == null) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.NotFound, String.Format(CultureInfo.CurrentCulture, Strings.PackageWithIdAndVersionNotFound, id, version))); } var user = GetCurrentUser(); if (!package.IsOwner(user)) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Forbidden, Strings.ApiKeyNotAuthorized)); } // Check if API key allows listing/unlisting the current package id if (!ApiKeyScopeAllows( subject: id, requestedActions: NuGetScopes.PackageUnlist)) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Forbidden, Strings.ApiKeyNotAuthorized)); } await PackageService.MarkPackageUnlistedAsync(package); // Handle in separate transaction because of concurrency check with retry. Due to using // separate transactions, we must always call UpdateIsLatest on delete/unlist. This is // because a concurrent thread could be marking the package as latest before this thread // is able to commit the delete /unlist. await PackageService.UpdateIsLatestAsync(package.PackageRegistration); IndexingService.UpdatePackage(package); return(new EmptyResult()); }
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)); } } }
public async Task AddOwnerToReservedNamespaceAsync(string prefix, string username) { if (string.IsNullOrWhiteSpace(prefix)) { throw new ArgumentException(ServicesStrings.ReservedNamespace_InvalidNamespace); } if (string.IsNullOrWhiteSpace(username)) { throw new ArgumentException(ServicesStrings.ReservedNamespace_InvalidUsername); } using (var strategy = new SuspendDbExecutionStrategy()) using (var transaction = EntitiesContext.GetDatabase().BeginTransaction()) { var namespaceToModify = FindReservedNamespaceForPrefix(prefix) ?? throw new InvalidOperationException(string.Format( CultureInfo.CurrentCulture, ServicesStrings.ReservedNamespace_NamespaceNotFound, prefix)); var userToAdd = UserService.FindByUsername(username) ?? throw new InvalidOperationException(string.Format( CultureInfo.CurrentCulture, ServicesStrings.ReservedNamespace_UserNotFound, username)); if (namespaceToModify.Owners.Contains(userToAdd)) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, ServicesStrings.ReservedNamespace_UserAlreadyOwner, username)); } Expression <Func <PackageRegistration, bool> > predicate; if (namespaceToModify.IsPrefix) { predicate = registration => registration.Id.StartsWith(namespaceToModify.Value); } else { predicate = registration => registration.Id.Equals(namespaceToModify.Value); } // Mark all packages owned by this user that start with the given namespace as verified. var allPackageRegistrationsForUser = PackageService.FindPackageRegistrationsByOwner(userToAdd); // We need 'AsQueryable' here because FindPackageRegistrationsByOwner returns an IEnumerable // and to evaluate the predicate server side, the casting is essential. var packageRegistrationsMatchingNamespace = allPackageRegistrationsForUser .AsQueryable() .Where(predicate) .ToList(); if (packageRegistrationsMatchingNamespace.Any()) { packageRegistrationsMatchingNamespace .ForEach(pr => namespaceToModify.PackageRegistrations.Add(pr)); await PackageService.UpdatePackageVerifiedStatusAsync(packageRegistrationsMatchingNamespace.AsReadOnly(), isVerified : true); } namespaceToModify.Owners.Add(userToAdd); await ReservedNamespaceRepository.CommitChangesAsync(); transaction.Commit(); await AuditingService.SaveAuditRecordAsync( new ReservedNamespaceAuditRecord(namespaceToModify, AuditedReservedNamespaceAction.AddOwner, username, packageRegistrationsMatchingNamespace)); } }
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)); } } }
public virtual async Task <ActionResult> GetPackage(string id, string version) { // some security paranoia about URL hacking somehow creating e.g. open redirects // validate user input: explicit calls to the same validators used during Package Registrations // Ideally shouldn't be necessary? if (!PackageIdValidator.IsValidPackageId(id ?? string.Empty)) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, "The format of the package id is invalid")); } // if version is non-null, check if it's semantically correct and normalize it. if (!String.IsNullOrEmpty(version)) { NuGetVersion dummy; if (!NuGetVersion.TryParse(version, out dummy)) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, "The package version is not a valid semantic version")); } // Normalize the version version = NuGetVersionFormatter.Normalize(version); } else { // If version is null, get the latest version from the database. // This ensures that on package restore scenario where version will be non null, we don't hit the database. try { var package = PackageService.FindPackageByIdAndVersion( id, version, SemVerLevelKey.SemVer2, allowPrerelease: false); if (package == null) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.NotFound, String.Format(CultureInfo.CurrentCulture, Strings.PackageWithIdAndVersionNotFound, id, version))); } version = package.NormalizedVersion; } catch (SqlException e) { QuietLog.LogHandledException(e); // Database was unavailable and we don't have a version, return a 503 return(new HttpStatusCodeWithBodyResult(HttpStatusCode.ServiceUnavailable, Strings.DatabaseUnavailable_TrySpecificVersion)); } catch (DataException e) { QuietLog.LogHandledException(e); // Database was unavailable and we don't have a version, return a 503 return(new HttpStatusCodeWithBodyResult(HttpStatusCode.ServiceUnavailable, Strings.DatabaseUnavailable_TrySpecificVersion)); } } if (ConfigurationService.Features.TrackPackageDownloadCountInLocalDatabase) { await PackageService.IncrementDownloadCountAsync(id, version); } return(await PackageFileService.CreateDownloadPackageActionResultAsync( HttpContext.Request.Url, id, version)); }
public virtual ActionResult Packages() { var currentUser = GetCurrentUser(); var owners = new List <ListPackageOwnerViewModel> { new ListPackageOwnerViewModel { Username = "******" }, new ListPackageOwnerViewModel(currentUser) }.Concat(currentUser.Organizations.Select(o => new ListPackageOwnerViewModel(o.Organization))); var wasAADLoginOrMultiFactorAuthenticated = User.WasMultiFactorAuthenticated() || User.WasAzureActiveDirectoryAccountUsedForSignin(); var packages = PackageService.FindPackagesByAnyMatchingOwner(currentUser, includeUnlisted: true); var listedPackages = packages .Where(p => p.Listed) .Select(p => new ListPackageItemRequiredSignerViewModel(p, currentUser, SecurityPolicyService, wasAADLoginOrMultiFactorAuthenticated)) .OrderBy(p => p.Id) .ToList(); var unlistedPackages = packages .Where(p => !p.Listed) .Select(p => new ListPackageItemRequiredSignerViewModel(p, currentUser, SecurityPolicyService, wasAADLoginOrMultiFactorAuthenticated)) .OrderBy(p => p.Id) .ToList(); // find all received ownership requests var userReceived = _packageOwnerRequestService.GetPackageOwnershipRequests(newOwner: currentUser); var orgReceived = currentUser.Organizations .Where(m => ActionsRequiringPermissions.HandlePackageOwnershipRequest.CheckPermissions(currentUser, m.Organization) == PermissionsCheckResult.Allowed) .SelectMany(m => _packageOwnerRequestService.GetPackageOwnershipRequests(newOwner: m.Organization)); var received = userReceived.Union(orgReceived); // find all sent ownership requests var userSent = _packageOwnerRequestService.GetPackageOwnershipRequests(requestingOwner: currentUser); var orgSent = currentUser.Organizations .Where(m => ActionsRequiringPermissions.HandlePackageOwnershipRequest.CheckPermissions(currentUser, m.Organization) == PermissionsCheckResult.Allowed) .SelectMany(m => _packageOwnerRequestService.GetPackageOwnershipRequests(requestingOwner: m.Organization)); var sent = userSent.Union(orgSent); var ownerRequests = new OwnerRequestsViewModel(received, sent, currentUser, PackageService); var userReservedNamespaces = currentUser.ReservedNamespaces; var organizationsReservedNamespaces = currentUser.Organizations.SelectMany(m => m.Organization.ReservedNamespaces); var reservedPrefixes = new ReservedNamespaceListViewModel(userReservedNamespaces.Union(organizationsReservedNamespaces).ToArray()); var model = new ManagePackagesViewModel { User = currentUser, Owners = owners, ListedPackages = listedPackages, UnlistedPackages = unlistedPackages, OwnerRequests = ownerRequests, ReservedNamespaces = reservedPrefixes, WasMultiFactorAuthenticated = User.WasMultiFactorAuthenticated(), IsCertificatesUIEnabled = ContentObjectService.CertificatesConfiguration?.IsUIEnabledForUser(currentUser) ?? false }; return(View(model)); }
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)); } } }
public virtual async Task <ActionResult> GetPackage(string id, string version) { // some security paranoia about URL hacking somehow creating e.g. open redirects // validate user input: explicit calls to the same validators used during Package Registrations // Ideally shouldn't be necessary? if (!PackageIdValidator.IsValidPackageId(id ?? "")) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, "The format of the package id is invalid")); } if (!String.IsNullOrEmpty(version)) { SemanticVersion dummy; if (!SemanticVersion.TryParse(version, out dummy)) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, "The package version is not a valid semantic version")); } } // Normalize the version version = SemanticVersionExtensions.Normalize(version); // if the version is null, the user is asking for the latest version. Presumably they don't want includePrerelease release versions. // The allow prerelease flag is ignored if both partialId and version are specified. // In general we want to try to add download statistics for any package regardless of whether a version was specified. Package package = null; try { package = PackageService.FindPackageByIdAndVersion(id, version, allowPrerelease: false); if (package == null) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.NotFound, String.Format(CultureInfo.CurrentCulture, Strings.PackageWithIdAndVersionNotFound, id, version))); } try { var stats = new PackageStatistics { // IMPORTANT: Timestamp is managed by the database. IPAddress = Request.UserHostAddress, UserAgent = Request.UserAgent, Package = package, Operation = Request.Headers["NuGet-Operation"], DependentPackage = Request.Headers["NuGet-DependentPackage"], ProjectGuids = Request.Headers["NuGet-ProjectGuids"], }; PackageService.AddDownloadStatistics(stats); } catch (ReadOnlyModeException) { // *gulp* Swallowed. It's OK not to add statistics and ok to not log errors in read only mode. } catch (SqlException e) { // Log the error and continue QuietLog.LogHandledException(e); } catch (DataException e) { // Log the error and continue QuietLog.LogHandledException(e); } } catch (SqlException e) { QuietLog.LogHandledException(e); } catch (DataException e) { QuietLog.LogHandledException(e); } // Fall back to constructing the URL based on the package version and ID. if (String.IsNullOrEmpty(version) && package == null) { // Database was unavailable and we don't have a version, return a 503 return(new HttpStatusCodeWithBodyResult(HttpStatusCode.ServiceUnavailable, Strings.DatabaseUnavailable_TrySpecificVersion)); } return(await PackageFileService.CreateDownloadPackageActionResultAsync( HttpContext.Request.Url, id, String.IsNullOrEmpty(version)?package.NormalizedVersion : version)); }
private async Task <ActionResult> CreatePackageInternal() { // Get the user var user = GetCurrentUser(); using (var packageToPush = ReadPackageFromRequest()) { if (packageToPush.Metadata.MinClientVersion > typeof(Manifest).Assembly.GetName().Version) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, String.Format( CultureInfo.CurrentCulture, Strings.UploadPackage_MinClientVersionOutOfRange, packageToPush.Metadata.MinClientVersion))); } // Ensure that the user can push packages for this partialId. var packageRegistration = PackageService.FindPackageRegistrationById(packageToPush.Metadata.Id); 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 = packageToPush.Metadata.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, packageToPush.Metadata.Id, packageToPush.Metadata.Version.ToNormalizedStringSafe()))); } } var package = PackageService.CreatePackage(packageToPush, user, commitChanges: false); AutoCuratePackage.Execute(package, packageToPush, commitChanges: false); EntitiesContext.SaveChanges(); using (Stream uploadStream = packageToPush.GetStream()) { await PackageFileService.SavePackageFileAsync(package, uploadStream); } if ( packageToPush.Metadata.Id.Equals(Constants.NuGetCommandLinePackageId, StringComparison.OrdinalIgnoreCase) && package.IsLatestStable) { // If we're pushing a new stable version of NuGet.CommandLine, update the extracted executable. await NugetExeDownloaderService.UpdateExecutableAsync(packageToPush); } } return(new HttpStatusCodeResult(HttpStatusCode.Created)); }
private async Task <ActionResult> CreatePackageInternal(string apiKey) { Guid parsedApiKey; if (!Guid.TryParse(apiKey, out parsedApiKey)) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.BadRequest, String.Format(CultureInfo.CurrentCulture, Strings.InvalidApiKey, apiKey))); } var user = UserService.FindByApiKey(parsedApiKey); if (user == null) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.Forbidden, String.Format(CultureInfo.CurrentCulture, Strings.ApiKeyNotAuthorized, "push"))); } using (var packageToPush = ReadPackageFromRequest()) { // Ensure that the user can push packages for this partialId. var packageRegistration = PackageService.FindPackageRegistrationById(packageToPush.Metadata.Id); if (packageRegistration != null) { if (!packageRegistration.IsOwner(user)) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.Forbidden, String.Format(CultureInfo.CurrentCulture, Strings.ApiKeyNotAuthorized, "push"))); } // Check if a particular Id-Version combination already exists. We eventually need to remove this check. bool packageExists = packageRegistration.Packages.Any( p => p.Version.Equals(packageToPush.Metadata.Version.ToString(), StringComparison.OrdinalIgnoreCase)); if (packageExists) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.Conflict, String.Format(CultureInfo.CurrentCulture, Strings.PackageExistsAndCannotBeModified, packageToPush.Metadata.Id, packageToPush.Metadata.Version))); } } var package = PackageService.CreatePackage(packageToPush, user, commitChanges: true); using (Stream uploadStream = packageToPush.GetStream()) { await PackageFileService.SavePackageFileAsync(package, uploadStream); } if ( packageToPush.Metadata.Id.Equals(Constants.NuGetCommandLinePackageId, StringComparison.OrdinalIgnoreCase) && package.IsLatestStable) { // If we're pushing a new stable version of NuGet.CommandLine, update the extracted executable. await NugetExeDownloaderService.UpdateExecutableAsync(packageToPush); } } return(new HttpStatusCodeResult(201)); }
public virtual async Task <ActionResult> GetPackage(string id, string version) { // some security paranoia about URL hacking somehow creating e.g. open redirects // validate user input: explicit calls to the same validators used during Package Registrations // Ideally shouldn't be necessary? if (!PackageIdValidator.IsValidPackageId(id ?? "")) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, "The format of the package id is invalid")); } // if version is non-null, check if it's semantically correct and normalize it. if (!String.IsNullOrEmpty(version)) { NuGetVersion dummy; if (!NuGetVersion.TryParse(version, out dummy)) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, "The package version is not a valid semantic version")); } // Normalize the version version = NuGetVersionNormalizer.Normalize(version); } else { // if version is null, get the latest version from the database. // This ensures that on package restore scenario where version will be non null, we don't hit the database. try { var package = PackageService.FindPackageByIdAndVersion(id, version, allowPrerelease: false); if (package == null) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.NotFound, String.Format(CultureInfo.CurrentCulture, Strings.PackageWithIdAndVersionNotFound, id, version))); } version = package.NormalizedVersion; } catch (SqlException e) { QuietLog.LogHandledException(e); // Database was unavailable and we don't have a version, return a 503 return(new HttpStatusCodeWithBodyResult(HttpStatusCode.ServiceUnavailable, Strings.DatabaseUnavailable_TrySpecificVersion)); } catch (DataException e) { QuietLog.LogHandledException(e); // Database was unavailable and we don't have a version, return a 503 return(new HttpStatusCodeWithBodyResult(HttpStatusCode.ServiceUnavailable, Strings.DatabaseUnavailable_TrySpecificVersion)); } } // If metrics service is specified we post the data to it asynchronously. Else we skip stats. if (_config != null && _config.MetricsServiceUri != null) { try { var userHostAddress = Request.UserHostAddress; var userAgent = Request.UserAgent; var operation = Request.Headers["NuGet-Operation"]; var dependentPackage = Request.Headers["NuGet-DependentPackage"]; var projectGuids = Request.Headers["NuGet-ProjectGuids"]; HostingEnvironment.QueueBackgroundWorkItem(cancellationToken => PostDownloadStatistics(id, version, userHostAddress, userAgent, operation, dependentPackage, projectGuids, cancellationToken)); } catch (Exception ex) { QuietLog.LogHandledException(ex); } } return(await PackageFileService.CreateDownloadPackageActionResultAsync( HttpContext.Request.Url, id, version)); }