public async Task <Organization> AddOrganizationAsync(string username, string emailAddress, User adminUser) { if (!ContentObjectService.LoginDiscontinuationConfiguration.AreOrganizationsSupportedForUser(adminUser)) { throw new EntityException(String.Format(CultureInfo.CurrentCulture, Strings.Organizations_NotInDomainWhitelist, adminUser.Username)); } var existingUserWithIdentity = EntitiesContext.Users .FirstOrDefault(u => u.Username == username || u.EmailAddress == emailAddress); if (existingUserWithIdentity != null) { if (existingUserWithIdentity.Username.Equals(username, StringComparison.OrdinalIgnoreCase)) { throw new EntityException(Strings.UsernameNotAvailable, username); } if (string.Equals(existingUserWithIdentity.EmailAddress, emailAddress, StringComparison.OrdinalIgnoreCase)) { throw new EntityException(Strings.EmailAddressBeingUsed, emailAddress); } } var organization = new Organization(username) { EmailAllowed = true, UnconfirmedEmailAddress = emailAddress, EmailConfirmationToken = Crypto.GenerateToken(), NotifyPackagePushed = true, CreatedUtc = DateTimeProvider.UtcNow, Members = new List <Membership>() }; var membership = new Membership { Organization = organization, Member = adminUser, IsAdmin = true }; organization.Members.Add(membership); adminUser.Organizations.Add(membership); OrganizationRepository.InsertOnCommit(organization); if (string.IsNullOrEmpty(GetAzureActiveDirectoryCredentialTenant(adminUser))) { throw new EntityException(String.Format(CultureInfo.CurrentCulture, Strings.Organizations_AdminAccountDoesNotHaveTenant, adminUser.Username)); } if (!await SubscribeOrganizationToTenantPolicy(organization, adminUser, commitChanges: false)) { throw new EntityException(Strings.DefaultUserSafeExceptionMessage); } await EntitiesContext.SaveChangesAsync(); return(organization); }
public async Task <Organization> AddOrganizationAsync(string username, string emailAddress, User adminUser) { var existingUserWithIdentity = EntitiesContext.Users .FirstOrDefault(u => u.Username == username || u.EmailAddress == emailAddress); if (existingUserWithIdentity != null) { if (existingUserWithIdentity.Username.Equals(username, StringComparison.OrdinalIgnoreCase)) { throw new EntityException(Strings.UsernameNotAvailable, username); } if (string.Equals(existingUserWithIdentity.EmailAddress, emailAddress, StringComparison.OrdinalIgnoreCase)) { throw new EntityException(Strings.EmailAddressBeingUsed, emailAddress); } } var organization = new Organization(username) { EmailAllowed = true, UnconfirmedEmailAddress = emailAddress, EmailConfirmationToken = Crypto.GenerateToken(), NotifyPackagePushed = true, CreatedUtc = DateTimeProvider.UtcNow, Members = new List <Membership>() }; if (!Config.ConfirmEmailAddresses) { organization.ConfirmEmailAddress(); } var membership = new Membership { Organization = organization, Member = adminUser, IsAdmin = true }; organization.Members.Add(membership); adminUser.Organizations.Add(membership); OrganizationRepository.InsertOnCommit(organization); await SubscribeOrganizationToTenantPolicyIfTenantIdIsSupported(organization, adminUser, commitChanges : false); await Auditing.SaveAuditRecordAsync(new UserAuditRecord(organization, AuditedUserAction.AddOrganization, membership)); await EntitiesContext.SaveChangesAsync(); return(organization); }
public async Task <Membership> AddMemberAsync(Organization organization, string memberName, bool isAdmin) { organization = organization ?? throw new ArgumentNullException(nameof(organization)); var membership = FindMembershipByUsername(organization, memberName); if (membership != null) { throw new EntityException(string.Format(CultureInfo.CurrentCulture, Strings.AddMember_AlreadyAMember, memberName)); } var member = FindByUsername(memberName); if (member == null) { throw new EntityException(string.Format(CultureInfo.CurrentCulture, Strings.AddMember_UserNotFound, memberName)); } if (!member.Confirmed) { throw new EntityException(string.Format(CultureInfo.CurrentCulture, Strings.AddMember_UserNotConfirmed, memberName)); } // Ensure that the new member meets the AAD tenant policy for this organization. var policyResult = await SecurityPolicyService.EvaluateOrganizationPoliciesAsync( SecurityPolicyAction.JoinOrganization, organization, member); if (policyResult != SecurityPolicyResult.SuccessResult) { throw new EntityException(policyResult.ErrorMessage); } membership = new Membership() { Member = member, IsAdmin = isAdmin }; organization.Members.Add(membership); await EntitiesContext.SaveChangesAsync(); return(membership); }
private async Task <User> DeleteMembershipRequestHelperAsync(Organization organization, string memberName, string confirmationToken = null) { organization = organization ?? throw new ArgumentNullException(nameof(organization)); var request = FindMembershipRequestByUsername(organization, memberName); if (request == null || (confirmationToken != null && request.ConfirmationToken != confirmationToken)) { throw new InvalidOperationException("No such membership request exists!"); } var pendingMember = request.NewMember; organization.MemberRequests.Remove(request); await EntitiesContext.SaveChangesAsync(); return(pendingMember); }
public async Task DeleteMemberAsync(Organization organization, string memberName) { organization = organization ?? throw new ArgumentNullException(nameof(organization)); var membership = FindMembershipByUsername(organization, memberName); if (membership == null) { throw new EntityException(string.Format(CultureInfo.CurrentCulture, Strings.UpdateOrDeleteMember_MemberNotFound, memberName)); } // block removal of last admin if (membership.IsAdmin && organization.Administrators.Count() == 1) { throw new EntityException(Strings.DeleteMember_CannotRemoveLastAdmin); } organization.Members.Remove(membership); await EntitiesContext.SaveChangesAsync(); }
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)); } } }
public async Task <MembershipRequest> AddMembershipRequestAsync(Organization organization, string memberName, bool isAdmin) { organization = organization ?? throw new ArgumentNullException(nameof(organization)); var membership = FindMembershipByUsername(organization, memberName); if (membership != null) { throw new EntityException(string.Format(CultureInfo.CurrentCulture, Strings.AddMember_AlreadyAMember, memberName)); } var request = FindMembershipRequestByUsername(organization, memberName); if (request != null) { // If there is already an existing request, return it. // If the existing request grants collaborator but we are trying to create a request that grants admin, update the request to grant admin. request.IsAdmin = isAdmin || request.IsAdmin; await EntitiesContext.SaveChangesAsync(); return(request); } if (Regex.IsMatch(memberName, GalleryConstants.EmailValidationRegex, RegexOptions.None, GalleryConstants.EmailValidationRegexTimeout)) { throw new EntityException(Strings.AddMember_NameIsEmail); } var member = FindByUsername(memberName); if (member == null) { throw new EntityException(string.Format(CultureInfo.CurrentCulture, Strings.AddMember_UserNotFound, memberName)); } if (!member.Confirmed) { throw new EntityException(string.Format(CultureInfo.CurrentCulture, Strings.AddMember_UserNotConfirmed, memberName)); } if (member is Organization) { throw new EntityException(string.Format(CultureInfo.CurrentCulture, Strings.AddMember_UserIsOrganization, memberName)); } // Ensure that the new member meets the AAD tenant policy for this organization. var policyResult = await SecurityPolicyService.EvaluateOrganizationPoliciesAsync( SecurityPolicyAction.JoinOrganization, organization, member); if (policyResult != SecurityPolicyResult.SuccessResult) { throw new EntityException(policyResult.ErrorMessage); } request = new MembershipRequest() { Organization = organization, NewMember = member, IsAdmin = isAdmin, ConfirmationToken = Crypto.GenerateToken(), RequestDate = DateTime.UtcNow, }; organization.MemberRequests.Add(request); await EntitiesContext.SaveChangesAsync(); return(request); }
public async Task <Membership> AddMemberAsync(Organization organization, string memberName, string confirmationToken) { organization = organization ?? throw new ArgumentNullException(nameof(organization)); var request = FindMembershipRequestByUsername(organization, memberName); if (request == null || request.ConfirmationToken != confirmationToken) { throw new EntityException(string.Format(CultureInfo.CurrentCulture, Strings.AddMember_MissingRequest, memberName)); } var member = request.NewMember; organization.MemberRequests.Remove(request); if (!member.Confirmed) { throw new EntityException(string.Format(CultureInfo.CurrentCulture, Strings.AddMember_UserNotConfirmed, memberName)); } if (member is Organization) { throw new EntityException(string.Format(CultureInfo.CurrentCulture, Strings.AddMember_UserIsOrganization, memberName)); } var membership = FindMembershipByUsername(organization, memberName); if (membership == null) { // Ensure that the new member meets the AAD tenant policy for this organization. var policyResult = await SecurityPolicyService.EvaluateOrganizationPoliciesAsync( SecurityPolicyAction.JoinOrganization, organization, member); if (policyResult != SecurityPolicyResult.SuccessResult) { throw new EntityException(string.Format(CultureInfo.CurrentCulture, Strings.AddMember_PolicyFailure, policyResult.ErrorMessage)); } membership = new Membership() { Member = member, IsAdmin = request.IsAdmin }; organization.Members.Add(membership); await Auditing.SaveAuditRecordAsync(new UserAuditRecord(organization, AuditedUserAction.AddOrganizationMember, membership)); } else { // If the user is already a member, update the existing membership. // If the request grants admin but this member is not an admin, grant admin to the member. membership.IsAdmin = membership.IsAdmin || request.IsAdmin; await Auditing.SaveAuditRecordAsync(new UserAuditRecord(organization, AuditedUserAction.UpdateOrganizationMember, membership)); } await EntitiesContext.SaveChangesAsync(); return(membership); }