Пример #1
0
        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);
        }
Пример #2
0
        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);
        }
Пример #3
0
        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"));
        }
Пример #4
0
        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));
     }
 }
Пример #7
0
        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));
        }
Пример #8
0
 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))));
     }
 }
Пример #9
0
        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)));
        }
Пример #11
0
        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));
            }
        }
Пример #12
0
 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);
     }
 }
Пример #13
0
 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));
     }
 }
Пример #14
0
 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);
     }
 }
Пример #15
0
        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));
        }
Пример #16
0
        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));
            }
        }