public virtual async Task <AuthenticatedUser> Authenticate(string userNameOrEmail, string password)
        {
            using (_trace.Activity("Authenticate:" + userNameOrEmail))
            {
                var user = FindByUserNameOrEmail(userNameOrEmail);

                // Check if the user exists
                if (user == null)
                {
                    _trace.Information("No such user: "******"Password validation failed: " + userNameOrEmail);
                    return(null);
                }

                var passwordCredentials = user
                                          .Credentials
                                          .Where(c => c.Type.StartsWith(CredentialTypes.Password.Prefix, StringComparison.OrdinalIgnoreCase))
                                          .ToList();
                if (passwordCredentials.Count > 1 || !passwordCredentials.Any(c => String.Equals(c.Type, CredentialTypes.Password.Pbkdf2, StringComparison.OrdinalIgnoreCase)))
                {
                    await MigrateCredentials(user, passwordCredentials, password);
                }

                // Return the result
                _trace.Verbose("Successfully authenticated '" + user.Username + "' with '" + matched.Type + "' credential");
                return(new AuthenticatedUser(user, matched));
            }
        }
예제 #2
0
        /// <summary>
        /// On subscribe, set API keys with push capability to expire in <see cref="PushKeysExpirationInDays" /> days.
        /// </summary>
        /// <param name="context"></param>
        public async Task OnSubscribeAsync(UserSecurityPolicySubscriptionContext context)
        {
            var pushKeys = context.User.Credentials.Where(c =>
                                                          CredentialTypes.IsApiKey(c.Type) &&
                                                          (
                                                              c.Scopes.Count == 0 ||
                                                              c.Scopes.Any(s =>
                                                                           s.AllowedAction.Equals(NuGetScopes.PackagePush, StringComparison.OrdinalIgnoreCase) ||
                                                                           s.AllowedAction.Equals(NuGetScopes.PackagePushVersion, StringComparison.OrdinalIgnoreCase)
                                                                           ))
                                                          );

            var expires     = DateTime.UtcNow.AddDays(PushKeysExpirationInDays);
            var expireTasks = new List <Task>();

            foreach (var key in pushKeys)
            {
                if (!key.Expires.HasValue || key.Expires > expires)
                {
                    expireTasks.Add(_auditing.SaveAuditRecordAsync(
                                        new UserAuditRecord(context.User, AuditedUserAction.ExpireCredential, key)));

                    key.Expires = expires;
                }
            }
            await Task.WhenAll(expireTasks);

            _diagnostics.Information($"Expiring {pushKeys.Count()} keys with push capability for user '{context.User.Username}'.");
        }
        public async Task <string> GetSecretAsync(string secretName)
        {
            if (!_cache.ContainsKey(secretName))
            {
                _trace.Information("Cache miss for setting " + secretName);
                var secretValue = await _internalReader.GetSecretAsync(secretName);

                _cache[secretName] = secretValue;
            }

            return(_cache[secretName]);
        }
예제 #4
0
        public async Task UpdateIsLatestAsync(PackageRegistration packageRegistration)
        {
            // Must suspend the retry execution strategy in order to use transactions.
            using (EntitiesConfiguration.SuspendRetriableExecutionStrategy())
            {
                if (await TryUpdateIsLatestAsync(_entitiesContext, packageRegistration))
                {
                    return;
                }

                // Retry the update in case a concurrency conflict was detected on the first attempt.
                int retryCount = 1;
                do
                {
                    await Task.Delay(_randomGenerator.Value.Next(0, 1000));

                    _trace.Information(String.Format("Retrying {0} for package '{1}' ({2}/{3})",
                                                     nameof(UpdateIsLatestAsync), packageRegistration.Id, retryCount, UpdateIsLatestMaxRetries));

                    // Since EF contexts are short-lived and do not really support refresh, we will use a
                    // different context than the request on retry to avoid putting the request context in
                    // a bad state. More than likely the retry will detect that the concurrent update has
                    // already made the right updates and no changes will be necessary.
                    using (var detachedRetryContext = CreateNewEntitiesContext())
                    {
                        var detachedPackageRegistration = detachedRetryContext.PackageRegistrations.SingleOrDefault(
                            pr => pr.Id == packageRegistration.Id);

                        if (await TryUpdateIsLatestAsync(detachedRetryContext, detachedPackageRegistration))
                        {
                            return;
                        }
                    }
                    retryCount++;
                }while (retryCount <= UpdateIsLatestMaxRetries);
            }
        }
예제 #5
0
        public virtual async Task <PasswordAuthenticationResult> Authenticate(string userNameOrEmail, string password)
        {
            using (_trace.Activity("Authenticate"))
            {
                var user = FindByUserNameOrEmail(userNameOrEmail);

                // Check if the user exists
                if (user == null)
                {
                    _trace.Information("No such user.");

                    await Auditing.SaveAuditRecordAsync(
                        new FailedAuthenticatedOperationAuditRecord(
                            userNameOrEmail, AuditedAuthenticatedOperationAction.FailedLoginNoSuchUser));

                    return(new PasswordAuthenticationResult(PasswordAuthenticationResult.AuthenticationResult.BadCredentials));
                }

                if (user is Organization)
                {
                    _trace.Information("Cannot authenticate organization account.");

                    await Auditing.SaveAuditRecordAsync(
                        new FailedAuthenticatedOperationAuditRecord(
                            userNameOrEmail, AuditedAuthenticatedOperationAction.FailedLoginUserIsOrganization));

                    return(new PasswordAuthenticationResult(PasswordAuthenticationResult.AuthenticationResult.BadCredentials));
                }

                int remainingMinutes;

                if (IsAccountLocked(user, out remainingMinutes))
                {
                    _trace.Information($"Login failed. User account is locked for the next {remainingMinutes} minutes.");

                    return(new PasswordAuthenticationResult(PasswordAuthenticationResult.AuthenticationResult.AccountLocked,
                                                            authenticatedUser: null, lockTimeRemainingMinutes: remainingMinutes));
                }

                // Validate the password
                Credential matched;
                if (!ValidatePasswordCredential(user.Credentials, password, out matched))
                {
                    _trace.Information("Password validation failed.");

                    await UpdateFailedLoginAttempt(user);

                    await Auditing.SaveAuditRecordAsync(
                        new FailedAuthenticatedOperationAuditRecord(
                            userNameOrEmail, AuditedAuthenticatedOperationAction.FailedLoginInvalidPassword));

                    return(new PasswordAuthenticationResult(PasswordAuthenticationResult.AuthenticationResult.BadCredentials));
                }

                var passwordCredentials = user
                                          .Credentials
                                          .Where(c => c.IsPassword())
                                          .ToList();

                if (passwordCredentials.Count > 1 ||
                    !passwordCredentials.Any(c => string.Equals(c.Type, CredentialBuilder.LatestPasswordType, StringComparison.OrdinalIgnoreCase)))
                {
                    await MigrateCredentials(user, passwordCredentials, password);
                }

                // Reset failed login count upon successful login
                await UpdateSuccessfulLoginAttempt(user);

                // Return the result
                _trace.Verbose("User successfully authenticated with '" + matched.Type + "' credential");
                return(new PasswordAuthenticationResult(PasswordAuthenticationResult.AuthenticationResult.Success, new AuthenticatedUser(user, matched)));
            }
        }
예제 #6
0
        private async Task <PackageValidationResult> CheckLicenseMetadataAsync(PackageArchiveReader nuGetPackage, List <IValidationMessage> warnings, User user)
        {
            var nuspecReader = GetNuspecReader(nuGetPackage);

            var licenseElement = nuspecReader.LicenseElement;

            if (licenseElement != null)
            {
                if (licenseElement.Value.Length > MaxAllowedLicenseNodeValueLength)
                {
                    return(PackageValidationResult.Invalid(Strings.UploadPackage_LicenseNodeValueTooLong));
                }

                if (HasChildElements(licenseElement))
                {
                    return(PackageValidationResult.Invalid(string.Format(Strings.UploadPackage_NodeContainsChildren, LicenseNodeName)));
                }

                var typeText = GetLicenseType(licenseElement);

                if (!AllowedLicenseTypes.Contains(typeText, StringComparer.OrdinalIgnoreCase))
                {
                    return(PackageValidationResult.Invalid(string.Format(Strings.UploadPackage_UnsupportedLicenseType, typeText)));
                }

                var versionText = GetLicenseVersion(licenseElement);

                if (versionText != null && AllowedLicenseVersion != versionText)
                {
                    return(PackageValidationResult.Invalid(
                               string.Format(
                                   Strings.UploadPackage_UnsupportedLicenseVersion,
                                   versionText)));
                }
            }

            var licenseUrl            = nuspecReader.GetLicenseUrl();
            var licenseMetadata       = nuspecReader.GetLicenseMetadata();
            var licenseDeprecationUrl = GetExpectedLicenseUrl(licenseMetadata);

            if (licenseMetadata == null)
            {
                if (string.IsNullOrWhiteSpace(licenseUrl))
                {
                    if (!_config.AllowLicenselessPackages)
                    {
                        return(PackageValidationResult.Invalid(new LicenseUrlDeprecationValidationMessage(Strings.UploadPackage_MissingLicenseInformation)));
                    }
                    else
                    {
                        warnings.Add(new LicenseUrlDeprecationValidationMessage(Strings.UploadPackage_LicenseShouldBeSpecified));
                    }
                }

                if (licenseDeprecationUrl == licenseUrl)
                {
                    return(PackageValidationResult.Invalid(Strings.UploadPackage_DeprecationUrlUsage));
                }

                if (!string.IsNullOrWhiteSpace(licenseUrl))
                {
                    if (_config.BlockLegacyLicenseUrl)
                    {
                        return(PackageValidationResult.Invalid(new LicenseUrlDeprecationValidationMessage(Strings.UploadPackage_LegacyLicenseUrlNotAllowed)));
                    }
                    else
                    {
                        warnings.Add(new LicenseUrlDeprecationValidationMessage(Strings.UploadPackage_DeprecatingLicenseUrl));
                    }
                }

                // we will return here, so the code below would not need to check for licenseMetadata to be non-null over and over.
                return(null);
            }

            if (licenseMetadata.WarningsAndErrors != null && licenseMetadata.WarningsAndErrors.Any())
            {
                _telemetryService.TrackInvalidLicenseMetadata(licenseMetadata.License);
                return(PackageValidationResult.Invalid(
                           string.Format(
                               Strings.UploadPackage_InvalidLicenseMetadata,
                               string.Join(" ", licenseMetadata.WarningsAndErrors))));
            }

            if (licenseDeprecationUrl != licenseUrl)
            {
                if (IsMalformedDeprecationUrl(licenseUrl))
                {
                    return(PackageValidationResult.Invalid(new InvalidUrlEncodingForLicenseUrlValidationMessage()));
                }

                if (licenseMetadata.Type == LicenseType.File)
                {
                    return(PackageValidationResult.Invalid(
                               new InvalidLicenseUrlValidationMessage(
                                   string.Format(Strings.UploadPackage_DeprecationUrlRequiredForLicenseFiles, licenseDeprecationUrl))));
                }
                else if (licenseMetadata.Type == LicenseType.Expression)
                {
                    return(PackageValidationResult.Invalid(
                               new InvalidLicenseUrlValidationMessage(
                                   string.Format(Strings.UploadPackage_DeprecationUrlRequiredForLicenseExpressions, licenseDeprecationUrl))));
                }
            }

            if (licenseMetadata.Type == LicenseType.File)
            {
                // fix the path separator. Client enforces forward slashes in all file paths when packing
                var licenseFilename = FileNameHelper.GetZipEntryPath(licenseMetadata.License);
                if (licenseFilename != licenseMetadata.License)
                {
                    var packageIdentity = nuspecReader.GetIdentity();
                    _trace.Information($"Transformed license file name from `{licenseMetadata.License}` to `{licenseFilename}` for package {packageIdentity.Id} {packageIdentity.Version}");
                }

                // check if specified file is present in the package
                if (!FileExists(nuGetPackage, licenseFilename))
                {
                    return(PackageValidationResult.Invalid(
                               string.Format(
                                   Strings.UploadPackage_FileDoesNotExist,
                                   Strings.UploadPackage_LicenseFileType,
                                   licenseFilename)));
                }

                // check if specified file has allowed extension
                var licenseFileExtension = Path.GetExtension(licenseFilename);
                if (!AllowedLicenseFileExtensions.Contains(licenseFileExtension, StringComparer.OrdinalIgnoreCase))
                {
                    return(PackageValidationResult.Invalid(
                               string.Format(
                                   Strings.UploadPackage_InvalidLicenseFileExtension,
                                   licenseFileExtension,
                                   string.Join(", ", AllowedLicenseFileExtensions.Where(x => x != string.Empty).Select(extension => $"'{extension}'")))));
                }

                var licenseFileEntry = nuGetPackage.GetEntry(licenseFilename);
                if (licenseFileEntry.Length > MaxAllowedLicenseLengthForUploading)
                {
                    return(PackageValidationResult.Invalid(
                               string.Format(
                                   Strings.UploadPackage_FileTooLong,
                                   Strings.UploadPackage_LicenseFileType,
                                   MaxAllowedLicenseLengthForUploading.ToUserFriendlyBytesLabel())));
                }

                if (!await IsStreamLengthMatchesReportedAsync(nuGetPackage, licenseFilename, licenseFileEntry.Length))
                {
                    return(PackageValidationResult.Invalid(Strings.UploadPackage_CorruptNupkg));
                }

                // zip streams do not support seeking, so we'll have to reopen them
                using (var licenseFileStream = nuGetPackage.GetStream(licenseFilename))
                {
                    // check if specified file is a text file
                    if (!await TextHelper.LooksLikeUtf8TextStreamAsync(licenseFileStream))
                    {
                        return(PackageValidationResult.Invalid(Strings.UploadPackage_LicenseMustBePlainText));
                    }
                }
            }

            if (licenseMetadata.Type == LicenseType.Expression)
            {
                if (licenseMetadata.LicenseExpression == null)
                {
                    throw new InvalidOperationException($"Unexpected value of {nameof(licenseMetadata.LicenseExpression)} property");
                }

                var licenseList        = GetLicenseList(licenseMetadata.LicenseExpression);
                var unapprovedLicenses = licenseList.Where(license => !license.IsOsiApproved && !license.IsFsfLibre).ToList();
                if (unapprovedLicenses.Any())
                {
                    _telemetryService.TrackNonFsfOsiLicenseUse(licenseMetadata.License);
                    return(PackageValidationResult.Invalid(
                               string.Format(
                                   Strings.UploadPackage_NonFsfOrOsiLicense, string.Join(", ", unapprovedLicenses.Select(l => l.LicenseID)))));
                }
            }

            return(null);
        }
예제 #7
0
        public virtual async Task <PasswordAuthenticationResult> Authenticate(string userNameOrEmail, string password)
        {
            using (_trace.Activity("Authenticate:" + userNameOrEmail))
            {
                var user = FindByUserNameOrEmail(userNameOrEmail);

                // Check if the user exists
                if (user == null)
                {
                    var ldapUser = this.Ldap.ValidateUsernameAndPassword(userNameOrEmail, password);

                    if (ldapUser != null)
                    {
                        _trace.Information($"Creating user from LDAP credentials: {userNameOrEmail}");
                        var ldapCredential = _credentialBuilder.CreateExternalCredential(AuthenticationTypes.LdapUser, ldapUser.Username, ldapUser.Identity);

                        var authUser = await this.Register(ldapUser.Username, ldapUser.Email, ldapCredential);

                        return(new PasswordAuthenticationResult(PasswordAuthenticationResult.AuthenticationResult.Success, authUser));
                    }

                    _trace.Information("No such user: "******"Login failed. User account {userNameOrEmail} is locked for the next {remainingMinutes} minutes.");

                    return(new PasswordAuthenticationResult(PasswordAuthenticationResult.AuthenticationResult.AccountLocked,
                                                            authenticatedUser: null, lockTimeRemainingMinutes: remainingMinutes));
                }

                // Validate the password
                Credential matched;
                if (!ValidatePasswordCredential(user.Credentials, password, out matched))
                {
                    var isValid = this.Ldap.ValidateCredentials(user.Credentials, password, out matched);
                    if (isValid)
                    {
                        _trace.Verbose($"Successfully authenticated '{user.Username}' with '{matched.Type}' credential");
                        return(new PasswordAuthenticationResult(PasswordAuthenticationResult.AuthenticationResult.Success, new AuthenticatedUser(user, matched)));
                    }

                    _trace.Information($"Password validation failed: {userNameOrEmail}");

                    await UpdateFailedLoginAttempt(user);

                    await Auditing.SaveAuditRecordAsync(
                        new FailedAuthenticatedOperationAuditRecord(
                            userNameOrEmail, AuditedAuthenticatedOperationAction.FailedLoginInvalidPassword));

                    return(new PasswordAuthenticationResult(PasswordAuthenticationResult.AuthenticationResult.BadCredentials));
                }

                var passwordCredentials = user
                                          .Credentials
                                          .Where(c => CredentialTypes.IsPassword(c.Type))
                                          .ToList();

                if (passwordCredentials.Count > 1 ||
                    !passwordCredentials.Any(c => string.Equals(c.Type, CredentialBuilder.LatestPasswordType, StringComparison.OrdinalIgnoreCase)))
                {
                    await MigrateCredentials(user, passwordCredentials, password);
                }

                // Reset failed login count upon successful login
                await UpdateSuccessfulLoginAttempt(user);

                // Return the result
                _trace.Verbose("Successfully authenticated '" + user.Username + "' with '" + matched.Type + "' credential");
                return(new PasswordAuthenticationResult(PasswordAuthenticationResult.AuthenticationResult.Success, new AuthenticatedUser(user, matched)));
            }
        }