|
| 1 | +## [3.5.0] - 2025-10-26 |
| 2 | +### Features |
| 3 | +- Password policy enforcement integrated into registration and password updates |
| 4 | + - New PasswordPolicyService enforces configurable rules: |
| 5 | + - Length limits (min/max), required character classes (uppercase, lowercase, digit, special) |
| 6 | + - Allowed special characters are fully configurable via user.security.password.special-chars |
| 7 | + - Common password prevention using a Passay dictionary built from bundled common_passwords.txt |
| 8 | + - Username/email similarity check using Levenshtein distance with a configurable similarity threshold |
| 9 | + - Password history reuse prevention using stored password hashes and a configurable history-count |
| 10 | + - UserAPI now validates passwords on registration and returns HTTP 400 with aggregated error messages if policy fails |
| 11 | + - Key implementation details: |
| 12 | + - Passay-based validation with a MessageSource-backed PasswordValidator for localized error messages |
| 13 | + - Dictionary initialization uses WordListDictionary and ArrayWordList; optimized sorting via ArraysSort for faster startup |
| 14 | + - Similarity computed with Apache Commons Text LevenshteinDistance; threshold is percentage-based |
| 15 | + - Password history checks query the latest N hashes via PasswordHistoryRepository.findRecentPasswordHashes(Pageable) |
| 16 | + - History is recorded automatically on changeUserPassword and during registration where applicable |
| 17 | + |
| 18 | +- Password history persistence and automatic cleanup |
| 19 | + - New JPA entity PasswordHistoryEntry stores per-user password hashes with timestamps |
| 20 | + - New repository PasswordHistoryRepository offers: |
| 21 | + - findRecentPasswordHashes(user, pageable) for efficient reuse checks |
| 22 | + - findByUserOrderByEntryDateDesc(user) for cleanup |
| 23 | + - UserService now: |
| 24 | + - Saves hashed passwords to history on password changes |
| 25 | + - Cleans up old entries after saves to limit stored history per configured history-count |
| 26 | + |
| 27 | +- Database-level integrity and performance for password history |
| 28 | + - PasswordHistoryEntry now enforces: |
| 29 | + - passwordHash length capped at 255 and not null (safe for bcrypt, etc.) |
| 30 | + - entryDate not null and mapped as entry_date |
| 31 | + - Indexes added: |
| 32 | + - idx_user_id on user_id |
| 33 | + - idx_entry_date on entry_date |
| 34 | + - Improves performance for queries like findByUserOrderByEntryDateDesc and general cleanup operations |
| 35 | + |
| 36 | +- Build and tooling enhancements |
| 37 | + - Gradle wrapper updated to 9.1.0 |
| 38 | + - Apache Commons Text upgraded to 1.14.0 |
| 39 | + |
| 40 | +### Fixes |
| 41 | +- Correct password history cleanup off-by-one error |
| 42 | + - Previously kept only historyCount entries; now keeps historyCount + 1 (current + N previous) to actually prevent reuse of the Nth previous password |
| 43 | + - Implementation: compute maxEntries = historyCount + 1 and delete entries beyond that index |
| 44 | + |
| 45 | +- Robust password policy configuration parsing |
| 46 | + - Fixed escaping for special characters in dsspringuserconfig.properties |
| 47 | + - Removed surrounding quotes from user.security.password.special-chars so all characters (including backslash and quotes) parse correctly |
| 48 | + |
| 49 | +- Faster and more predictable common password dictionary initialization |
| 50 | + - Updated Passay WordLists.createFromReader to use new ArraysSort() for sorting, improving startup performance and consistency |
| 51 | + |
| 52 | +- Stabilized unit tests by properly stubbing PasswordPolicyService in UserAPI tests |
| 53 | + - Prevented NPE by returning Collections.emptyList() from validate() |
| 54 | + |
| 55 | +### Breaking Changes |
| 56 | +- New required configuration properties for password policy |
| 57 | + - PasswordPolicyService uses @Value without defaults for multiple properties (e.g., user.security.password.enabled, min-length, max-length, require-* flags, special-chars, prevent-common-passwords, history-count, similarity-threshold). |
| 58 | + - If you use UserAPI (now depends on PasswordPolicyService), you must supply these properties or import a property source that defines them. Otherwise, application startup may fail. |
| 59 | + - Behavior change: registration and password updates are now rejected (HTTP 400) when password policy is not met. |
| 60 | + |
| 61 | +- Database schema changes |
| 62 | + - New table password_history_entry with not-null constraints and indexes |
| 63 | + - If you manage schema outside of Hibernate auto-DDL, you must add this table, columns, and indexes in your migrations before deploying. |
| 64 | + |
| 65 | +Migration guidance: |
| 66 | +- Add the new properties to your application configuration (example): |
| 67 | + - user.security.password.enabled=true |
| 68 | + - user.security.password.min-length=8 |
| 69 | + - user.security.password.max-length=128 |
| 70 | + - user.security.password.require-uppercase=true |
| 71 | + - user.security.password.require-lowercase=true |
| 72 | + - user.security.password.require-digit=true |
| 73 | + - user.security.password.require-special=true |
| 74 | + - user.security.password.special-chars=~`!@#$%^&*()_-+={}[]|\\:;"'<>,.?/ |
| 75 | + - user.security.password.prevent-common-passwords=true |
| 76 | + - user.security.password.history-count=3 |
| 77 | + - user.security.password.similarity-threshold=80 |
| 78 | + |
| 79 | +- Apply DB migration to create password_history_entry with: |
| 80 | + - Columns: id (PK), user_id (FK), password_hash VARCHAR(255) NOT NULL, entry_date TIMESTAMP NOT NULL |
| 81 | + - Indexes: idx_user_id(user_id), idx_entry_date(entry_date) |
| 82 | + |
| 83 | +### Refactoring |
| 84 | +- PasswordPolicyService structure and error handling improved |
| 85 | + - Extracted validation into cohesive private methods: |
| 86 | + - buildPassayRules(), createSpecialCharacterRule() |
| 87 | + - checkPasswordHistory(), checkPasswordSimilarity() |
| 88 | + - validateWithPassay() |
| 89 | + - Early returns for failed history/similarity checks to avoid unnecessary work |
| 90 | + - More detailed debug logging and clearer severe-error logging for dictionary load failures |
| 91 | + - Optional used for null-safe error signaling from pre-checks |
| 92 | + |
| 93 | +### Documentation |
| 94 | +- Enhanced Spring configuration metadata |
| 95 | + - additional-spring-configuration-metadata.json updated with meaningful descriptions for the new password policy properties, improving IDE assistance and documentation |
| 96 | + |
| 97 | +### Testing |
| 98 | +- New and updated test coverage |
| 99 | + - PasswordPolicyServiceIntegrationTest ensures: |
| 100 | + - Properties load correctly from config file |
| 101 | + - Allowed special characters are correctly parsed and enforced |
| 102 | + - Validation behaves as expected in a real Spring context |
| 103 | + - PasswordPolicyServiceTest fixes |
| 104 | + - Corrected similarity test to pass username/email to validate(), ensuring the check is truly exercised |
| 105 | + - UserAPIUnitTest updates |
| 106 | + - Added a mock PasswordPolicyService and default stubbing to prevent NPEs |
| 107 | + - Broader test additions from the password policy feature: |
| 108 | + - New tests for PasswordPolicyService and expanded UserService tests to cover history and policy integration |
| 109 | + |
| 110 | +### Other Changes |
| 111 | +- Versioning and editor configuration |
| 112 | + - Project version bumped to 3.5.0-SNAPSHOT |
| 113 | + - Added VS Code Java settings to .vscode/settings.json |
| 114 | + |
| 115 | +- Dependency updates |
| 116 | + - Runtime: |
| 117 | + - com.google.guava:guava 33.4.8-jre → 33.5.0-jre |
| 118 | + - org.apache.commons:commons-text 1.13.1 → 1.14.0 |
| 119 | + - org.springframework.boot 3.5.5 → 3.5.6 |
| 120 | + - org.projectlombok:lombok 1.18.38 → 1.18.42 |
| 121 | + - Test: |
| 122 | + - com.h2database:h2 2.3.232 → 2.4.240 |
| 123 | + - com.icegreen:greenmail 2.1.5 → 2.1.7 |
| 124 | + - org.assertj:assertj-core 3.27.4 → 3.27.6 |
| 125 | + - Build: |
| 126 | + - com.github.ben-manes.versions plugin 0.52.0 → 0.53.0 |
| 127 | + - Removed unused dependencyUpdates config and related helper from build.gradle (cleanup) |
| 128 | + |
| 129 | +What this means for you: |
| 130 | +- If you consume this library’s UserAPI endpoints, define the new password policy properties and run DB migrations for password history before upgrading. |
| 131 | +- Expect stricter password handling during registration and updates, with clear, localized error messages reported to clients when policy checks fail. |
| 132 | +- Performance during startup when loading the common password dictionary should improve due to the new ArraysSort usage. |
| 133 | + |
1 | 134 | ## [3.4.1] - 2025-09-04 |
2 | 135 | ### Features |
3 | 136 | - No new feature functionality introduced in these commits. |
|
0 commit comments