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 (CredentialTypes.IsPackageVerificationApiKey(credential.Type)) { // Secure path: verify that verification key matches package scope. if (!HasAnyScopeThatAllows(package.PackageRegistration, NuGetScopes.PackageVerify)) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Forbidden, Strings.ApiKeyNotAuthorized)); } } else { // Insecure path: verify that API key is legacy or matches package scope. if (!HasAnyScopeThatAllows(package.PackageRegistration, NuGetScopes.PackagePush, NuGetScopes.PackagePushVersion)) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Forbidden, Strings.ApiKeyNotAuthorized)); } } return(null); }
private HttpStatusCodeWithBodyResult VerifyPackageKeyInternal(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); 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, 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); }
private async Task <ActionResult> RemoveCredentialInternal(User user, Credential cred, string message) { if (cred == null) { TempData["Message"] = Strings.CredentialNotFound; return(RedirectToAction("Account")); } // Count credentials and make sure the user can always login if (!CredentialTypes.IsApiKey(cred.Type) && CountLoginCredentials(user) <= 1) { TempData["Message"] = Strings.CannotRemoveOnlyLoginCredential; } else { await _authService.RemoveCredential(user, cred); // Notify the user of the change _messageService.SendCredentialRemovedNotice(user, _authService.DescribeCredential(cred)); TempData["Message"] = message; } return(RedirectToAction("Account")); }
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)); string[] requestedActions; if (CredentialTypes.IsPackageVerificationApiKey(credential.Type)) { requestedActions = new[] { NuGetScopes.PackageVerify }; } else { requestedActions = new[] { NuGetScopes.PackagePush, NuGetScopes.PackagePushVersion }; } var apiScopeEvaluationResult = EvaluateApiScope(ActionsRequiringPermissions.VerifyPackage, package.PackageRegistration, requestedActions); if (!apiScopeEvaluationResult.IsSuccessful()) { return(GetHttpResultFromFailedApiScopeEvaluation(apiScopeEvaluationResult, id, version)); } return(null); }
protected virtual Task <ActionResult> InvokeChangeEmail( TAccountsController controller, TUser account, Func <Fakes, User> getCurrentUser, TAccountViewModel model = null, bool newEmailIsConfirmed = false, EntityException exception = null) { // Arrange controller.SetCurrentUser(getCurrentUser(Fakes)); var messageService = GetMock <IMessageService>(); messageService.Setup(m => m.SendMessageAsync(It.IsAny <EmailChangeConfirmationMessage>(), It.IsAny <bool>(), It.IsAny <bool>())) .Returns(Task.CompletedTask) .Verifiable(); var userService = GetMock <IUserService>(); userService.Setup(u => u.FindByUsername(account.Username, false)) .Returns(account as User); var setup = userService.Setup(u => u.ChangeEmailAddress(It.IsAny <User>(), It.IsAny <string>())) .Callback <User, string>((acct, newEmail) => { if (newEmailIsConfirmed) { acct.EmailAddress = newEmail; } else { acct.UnconfirmedEmailAddress = newEmail; } }); if (exception != null) { setup.Throws(exception); } else { setup.Returns(Task.CompletedTask).Verifiable(); } model = model ?? CreateViewModel(account); var password = account.Credentials.FirstOrDefault(c => CredentialTypes.IsPassword(c.Type)); if (password != null) { model.ChangeEmail.Password = Fakes.Password; } // Act return(controller.ChangeEmail(model)); }
public CredentialTypeInfo GetCredentialTypeInfo() { if (CredentialTypes.IsApiKey(Type)) { return(new CredentialTypeInfo(Type, true, Description)); } else { return(AuthUI == null ? new CredentialTypeInfo(Type, false, TypeCaption) : new CredentialTypeInfo(Type, false, AuthUI.AccountNoun)); } }
public virtual async Task <ActionResult> RemoveCredential(string credentialType, int?credentialKey) { var user = GetCurrentUser(); var cred = user.Credentials.SingleOrDefault( c => string.Equals(c.Type, credentialType, StringComparison.OrdinalIgnoreCase) && CredentialKeyMatches(credentialKey, c)); if (CredentialTypes.IsApiKey(credentialType)) { return(await RemoveApiKeyCredential(user, cred)); } return(await RemoveCredentialInternal(user, cred, Strings.CredentialRemoved)); }
public static IReturnsResult <AuthenticationService> SetupAuth(this Mock <AuthenticationService> self, Credential cred, User user) { if (CredentialTypes.IsApiKey(cred.Type)) { return(self.Setup(us => us.Authenticate(It.Is <string>(c => string.Equals(c, cred.Value, StringComparison.Ordinal)))) .Returns(Task.FromResult(user == null ? null : new AuthenticatedUser(user, cred)))); } else { return(self.Setup(us => us.Authenticate(It.Is <Credential>(c => string.Equals(c.Type, cred.Type, StringComparison.OrdinalIgnoreCase) && string.Equals(c.Value, cred.Value, StringComparison.Ordinal)))) .Returns(Task.FromResult(user == null ? null : new AuthenticatedUser(user, cred)))); } }
public async virtual Task <ActionResult> VerifyPackageKeyAsync(string id, string version) { var user = GetCurrentUser(); var credential = GetCurrentCredential(user); var result = VerifyPackageKeyInternal(user, credential, id, version); // Expire and delete verification key after first use to avoid growing the database tables. if (CredentialTypes.IsPackageVerificationApiKey(credential.Type)) { await AuthenticationService.RemoveCredential(user, credential); } TelemetryService.TrackVerifyPackageKeyEvent(id, version, user, User.Identity, result?.StatusCode ?? 200); return((ActionResult)result ?? new EmptyResult()); }
internal bool ShouldEnforceMultiFactorAuthentication(AuthenticateExternalLoginResult result) { if (result?.Authenticator == null || result.Authentication == null) { return(false); } // Enforce multi-factor authentication only if: // 1. The authenticator supports multi-factor authentication, otherwise no use. // 2. The user has enabled multi-factor authentication for their account. // 3. The user authenticated with the personal microsoft account. AAD 2FA policy is controlled by the tenant admins. // 4. The user did not use the multi-factor authentication for the session, obviously. return(result.Authenticator.SupportsMultiFactorAuthentication() && result.Authentication.User.EnableMultiFactorAuthentication && !result.LoginDetails.WasMultiFactorAuthenticated && result.Authentication.CredentialUsed.IsExternal() && (CredentialTypes.IsMicrosoftAccount(result.Authentication.CredentialUsed.Type))); }
public static void AddExternalLoginCredentialTypeClaim(List <Claim> claims, string credentialType) { string claimValue = null; if (CredentialTypes.IsMicrosoftAccount(credentialType)) { claimValue = NuGetClaims.ExternalLoginCredentialValues.MicrosoftAccount; } else if (CredentialTypes.IsAzureActiveDirectoryAccount(credentialType)) { claimValue = NuGetClaims.ExternalLoginCredentialValues.AzureActiveDirectory; } if (!string.IsNullOrEmpty(claimValue)) { claims.Add(new Claim(NuGetClaims.ExternalLoginCredentialType, claimValue)); } }
public void SendCredentialAddedNotice(User user, CredentialViewModel addedCredentialViewModel) { if (CredentialTypes.IsApiKey(addedCredentialViewModel.Type)) { SendApiKeyChangeNotice( user, addedCredentialViewModel, Strings.Emails_ApiKeyAdded_Body, Strings.Emails_CredentialAdded_Subject); } else { SendCredentialChangeNotice( user, addedCredentialViewModel, Strings.Emails_CredentialAdded_Body, Strings.Emails_CredentialAdded_Subject); } }
public Task SendCredentialAddedNoticeAsync(User user, CredentialViewModel addedCredentialViewModel) { if (CredentialTypes.IsApiKey(addedCredentialViewModel.Type)) { return(SendApiKeyChangeNoticeAsync( user, addedCredentialViewModel, Strings.Emails_ApiKeyAdded_Body, Strings.Emails_CredentialAdded_Subject)); } else { return(SendCredentialChangeNoticeAsync( user, addedCredentialViewModel, Strings.Emails_CredentialAdded_Body, Strings.Emails_CredentialAdded_Subject)); } }
public void SendCredentialRemovedNotice(User user, Credential removed) { if (CredentialTypes.IsApiKey(removed.Type)) { SendApiKeyChangeNotice( user, removed, Strings.Emails_ApiKeyRemoved_Body, Strings.Emails_CredentialRemoved_Subject); } else { SendCredentialChangeNotice( user, removed, Strings.Emails_CredentialRemoved_Body, Strings.Emails_CredentialRemoved_Subject); } }
private ActionResult AccountView(AccountViewModel model) { // Load user info var user = GetCurrentUser(); var curatedFeeds = _curatedFeedService.GetFeedsForManager(user.Key); var creds = user.Credentials.Where(c => CredentialTypes.IsSupportedCredential(c)) .Select(c => _authService.DescribeCredential(c)).ToList(); var packageNames = _packageService.FindPackageRegistrationsByOwner(user).Select(p => p.Id).ToList(); packageNames.Sort(); model.Credentials = creds; model.CuratedFeeds = curatedFeeds.Select(f => f.Name); model.Packages = packageNames; model.ExpirationInDaysForApiKeyV1 = _config.ExpirationInDaysForApiKeyV1; return(View("Account", model)); }
public async virtual Task <ActionResult> VerifyPackageKeyAsync(string id, string version) { var policyResult = await SecurityPolicyService.EvaluateAsync(SecurityPolicyAction.PackageVerify, HttpContext); if (!policyResult.Success) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, policyResult.ErrorMessage)); } var user = GetCurrentUser(); var credential = user.GetCurrentApiKeyCredential(User.Identity); var result = await VerifyPackageKeyInternalAsync(user, credential, id, version); // Expire and delete verification key after first use to avoid growing the database tables. if (CredentialTypes.IsPackageVerificationApiKey(credential.Type)) { await AuthenticationService.RemoveCredential(user, credential); } TelemetryService.TrackVerifyPackageKeyEvent(id, version, user, User.Identity, result?.StatusCode ?? 200); return((ActionResult)result ?? new EmptyResult()); }
public virtual async Task <ActionResult> LinkExternalAccount(string returnUrl, string error = null, string errorDescription = null) { // Extract the external login info var result = await _authService.AuthenticateExternalLogin(OwinContext); if (result.ExternalIdentity == null) { // User got here without an external login cookie (or an expired one) // Send them to the logon action string errorMessage = GetAuthenticationFailureMessage(error, errorDescription); return(AuthenticationFailureOrExternalLinkExpired(errorMessage)); } if (result.Authentication != null) { // If we are an administrator and Gallery.EnforcedAuthProviderForAdmin is set // to require a specific authentication provider, challenge that provider if needed. ActionResult challenge; if (ShouldChallengeEnforcedProvider( NuGetContext.Config.Current.EnforcedAuthProviderForAdmin, result.Authentication, returnUrl, out challenge)) { return(challenge); } // If we are an administrator and Gallery.EnforcedTenantIdForAdmin is set // to require a specific tenant Id, check if the user logged in with the specified tenant. if (!SiteAdminHasValidTenant(result.Authentication)) { string errorMessage = string.Format(Strings.SiteAdminNotLoggedInWithRequiredTenant, NuGetContext.Config.Current.EnforcedTenantIdForAdmin); return(AuthenticationFailureOrExternalLinkExpired(errorMessage)); } if (ShouldEnforceMultiFactorAuthentication(result)) { // Invoke the authentication again enforcing multi-factor authentication for the same provider. return(ChallengeAuthentication( Url.LinkExternalAccount(returnUrl), result.Authenticator.Name, new AuthenticationPolicy() { Email = result.LoginDetails.EmailUsed, EnforceMultiFactorAuthentication = true })); } // Remove the password login if the password logins are deprecated and enforced discontinuation. if (NuGetContext.Config.Current.DeprecateNuGetPasswordLogins && _contentObjectService.LoginDiscontinuationConfiguration.IsPasswordLoginDiscontinuedForAll() && result.Authentication.CredentialUsed.IsExternal() && result.Authentication.User.HasPasswordCredential()) { // Remove password logins when a user signs in with an external login. TempData["Message"] = string.Format(Strings.DiscontinuedLogin_PasswordRemoved, NuGetContext.Config.Current.Brand); await RemovePasswordCredential(result.Authentication.User); } // Create session await _authService.CreateSessionAsync(OwinContext, result.Authentication, wasMultiFactorAuthenticated : result?.LoginDetails?.WasMultiFactorAuthenticated ?? false); // Update the 2FA if used during login but user does not have it set on their account. Only for personal microsoft accounts. if (result?.LoginDetails != null && result.LoginDetails.WasMultiFactorAuthenticated && !result.Authentication.User.EnableMultiFactorAuthentication && CredentialTypes.IsMicrosoftAccount(result.Credential.Type)) { await _userService.ChangeMultiFactorAuthentication(result.Authentication.User, enableMultiFactor : true); OwinContext.AddClaim(NuGetClaims.EnabledMultiFactorAuthentication); TempData["Message"] = Strings.MultiFactorAuth_LoginUpdate; } return(SafeRedirect(returnUrl)); } else { // Gather data for view model string name = null; string email = null; var authUI = result.Authenticator.GetUI(); try { var userInfo = result.Authenticator.GetIdentityInformation(result.ExternalIdentity); name = userInfo.Name; email = userInfo.Email; } catch (Exception) { // Consume the exception for now, for backwards compatibility to previous MSA provider. email = result.ExternalIdentity.GetClaimOrDefault(ClaimTypes.Email); name = result.ExternalIdentity.GetClaimOrDefault(ClaimTypes.Name); } // Check for a user with this email address User existingUser = null; if (!string.IsNullOrEmpty(email)) { existingUser = _userService.FindByEmailAddress(email); } var foundExistingUser = existingUser != null; var existingUserLinkingError = AssociateExternalAccountViewModel.ExistingUserLinkingErrorType.None; if (foundExistingUser) { if (existingUser is Organization) { existingUserLinkingError = AssociateExternalAccountViewModel.ExistingUserLinkingErrorType.AccountIsOrganization; } else if (existingUser.Credentials.Any(c => c.IsExternal()) && !existingUser.IsAdministrator) { existingUserLinkingError = AssociateExternalAccountViewModel.ExistingUserLinkingErrorType.AccountIsAlreadyLinked; } } var external = new AssociateExternalAccountViewModel() { ProviderAccountNoun = authUI.AccountNoun, AccountName = name, FoundExistingUser = foundExistingUser, ExistingUserLinkingError = existingUserLinkingError }; var model = new LogOnViewModel { External = external, SignIn = new SignInViewModel { UserNameOrEmail = email }, Register = new RegisterViewModel { EmailAddress = email } }; return(LinkExternalView(model)); } }