Skip to content

Commit 8182b7d

Browse files
authored
Merge pull request #40 from Edamijueda/issue-#34
impl password policy func in user registration
2 parents 12ed36b + e9e0c67 commit 8182b7d

27 files changed

Lines changed: 955 additions & 488 deletions

.claude/settings.local.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,13 @@
1818
"Bash(./fix-mockbean-deprecation.sh:*)",
1919
"mcp__zen__debug",
2020
"Bash(git commit:*)",
21-
"mcp__zen__codereview"
21+
"mcp__zen__codereview",
22+
"Bash(gh pr view:*)",
23+
"Bash(gh pr diff:*)",
24+
"Bash(gh issue view:*)",
25+
"Bash(gh pr checkout:*)",
26+
"Bash(git checkout:*)"
2227
],
2328
"deny": []
2429
}
25-
}
30+
}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,4 @@ application-local.yml
137137
.vscode/settings.json
138138
/repomix-output.txt
139139
src/main/resources/application-docker-keycloak.yml
140+
.vscode/

.vscode/settings.json

Lines changed: 0 additions & 11 deletions
This file was deleted.

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ repositories {
3939

4040
dependencies {
4141
// DigitalSanctuary Spring User Framework
42-
implementation 'com.digitalsanctuary:ds-spring-user-framework:3.4.1'
42+
implementation 'com.digitalsanctuary:ds-spring-user-framework:3.5.0'
4343

4444
// Spring Boot starters
4545
implementation 'org.springframework.boot:spring-boot-starter-actuator'

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
services:
33
myapp-db:
44
image: mariadb:11.6.2
5-
container_name: springuser-db #
5+
container_name: springuser-db
66
volumes:
77
- userdb:/var/lib/mysql
88
environment:

src/main/resources/META-INF/additional-spring-configuration-metadata.json

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,61 @@
125125
"type": "java.lang.String",
126126
"description": "A description for 'user.security.forgotPasswordChangeURI'"
127127
},
128+
{
129+
"name": "user.security.password.enabled",
130+
"type": "java.lang.String",
131+
"description": "A description for 'user.security.password.enabled'"
132+
},
133+
{
134+
"name": "user.security.password.min-length",
135+
"type": "java.lang.String",
136+
"description": "A description for 'user.security.password.min-length'"
137+
},
138+
{
139+
"name": "user.security.password.max-length",
140+
"type": "java.lang.String",
141+
"description": "A description for 'user.security.password.max-length'"
142+
},
143+
{
144+
"name": "user.security.password.require-uppercase",
145+
"type": "java.lang.String",
146+
"description": "A description for 'user.security.password.require-uppercase'"
147+
},
148+
{
149+
"name": "user.security.password.require-lowercase",
150+
"type": "java.lang.String",
151+
"description": "A description for 'user.security.password.require-lowercase'"
152+
},
153+
{
154+
"name": "user.security.password.require-digit",
155+
"type": "java.lang.String",
156+
"description": "A description for 'user.security.password.require-digit'"
157+
},
158+
{
159+
"name": "user.security.password.require-special",
160+
"type": "java.lang.String",
161+
"description": "A description for 'user.security.password.require-special'"
162+
},
163+
{
164+
"name": "user.security.password.special-chars",
165+
"type": "java.lang.String",
166+
"description": "A description for 'user.security.password.special-chars'"
167+
},
168+
{
169+
"name": "user.security.password.prevent-common-passwords",
170+
"type": "java.lang.String",
171+
"description": "A description for 'user.security.password.prevent-common-passwords'"
172+
},
173+
{
174+
"name": "user.security.password.history-count",
175+
"type": "java.lang.String",
176+
"description": "A description for 'user.security.password.history-count'"
177+
},
178+
{
179+
"name": "user.security.password.similarity-threshold",
180+
"type": "java.lang.String",
181+
"description": "A description for 'user.security.password.similarity-threshold'"
182+
},
128183
{
129184
"name": "hibernate.globally_quoted_identifiers",
130185
"type": "java.lang.String",

src/main/resources/application.yml

Lines changed: 53 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -15,40 +15,40 @@ spring:
1515
protocol: smtp # Mail server protocol
1616

1717
# security:
18-
# oauth2:
19-
# enabled: true
20-
# client:
21-
# registration:
22-
# google:
23-
# client-id:
24-
# client-secret:
25-
# scope:
26-
# - email
27-
# - profile
28-
# client-name: Google
29-
# authorization-grant-type: authorization_code
30-
# client-authentication-method: post
31-
# provider: google
32-
# facebook:
33-
# client-id:
34-
# client-secret:
35-
# scope:
36-
# - email
37-
# - public_profile
38-
# client-name: Facebook
39-
# authorization-grant-type: authorization_code
40-
# client-authentication-method: post
41-
# provider: facebook
42-
# apple:
43-
# client-id:
44-
# client-secret:
45-
# scope:
46-
# - email
47-
# - name
48-
# client-name: Apple
49-
# authorization-grant-type: authorization_code
50-
# client-authentication-method: post
51-
# provider: apple
18+
# oauth2:
19+
# enabled: true
20+
# client:
21+
# registration:
22+
# google:
23+
# client-id:
24+
# client-secret:
25+
# scope:
26+
# - email
27+
# - profile
28+
# client-name: Google
29+
# authorization-grant-type: authorization_code
30+
# client-authentication-method: post
31+
# provider: google
32+
# facebook:
33+
# client-id:
34+
# client-secret:
35+
# scope:
36+
# - email
37+
# - public_profile
38+
# client-name: Facebook
39+
# authorization-grant-type: authorization_code
40+
# client-authentication-method: post
41+
# provider: facebook
42+
# apple:
43+
# client-id:
44+
# client-secret:
45+
# scope:
46+
# - email
47+
# - name
48+
# client-name: Apple
49+
# authorization-grant-type: authorization_code
50+
# client-authentication-method: post
51+
# provider: apple
5252
thymeleaf: # Thymeleaf configuration
5353
cache: false # Enable or disable Thymeleaf cache
5454
template-loader-path: classpath:/templates # Template loader path for Thymeleaf
@@ -59,8 +59,8 @@ spring:
5959
properties: # Hibernate properties
6060
hibernate:
6161
dialect: org.hibernate.dialect.MariaDBDialect # Hibernate dialect
62-
# "[globally_quoted_identifiers]": false # Globally quoted identifiers
63-
show-sql: 'false' # Enable or disable SQL logging
62+
# "[globally_quoted_identifiers]": false # Globally quoted identifiers
63+
show-sql: "false" # Enable or disable SQL logging
6464
application: # Application configuration
6565
name: User Framework Demo # Application name
6666
datasource: # Datasource configuration
@@ -71,17 +71,14 @@ spring:
7171
messages:
7272
basename: messages/messages # Message basename
7373

74-
75-
76-
7774
management:
7875
newrelic:
7976
metrics:
8077
export:
8178
api-key: # New Relic API key
8279
account-id: # New Relic account ID
8380
hibernate:
84-
globally_quoted_identifiers: 'false' # Hibernate Globally quoted identifiers
81+
globally_quoted_identifiers: "false" # Hibernate Globally quoted identifiers
8582
server:
8683
servlet:
8784
session:
@@ -101,8 +98,6 @@ springdoc:
10198
packages-to-scan: com.digitalsanctuary.spring.demo,com.digitalsanctuary.spring.user
10299
show-actuator: false
103100

104-
105-
106101
# User Framework configuration
107102
user:
108103
actuallyDeleteAccount: false # If true, users can delete their own accounts. If false, accounts are disabled instead of deleted.
@@ -115,14 +110,14 @@ user:
115110
flushOnWrite: false # If true, the audit log will be flushed to disk after every write (less performant). If false, the audit log will be flushed to disk every 10 seconds (more performant).
116111
logEvents: true # If true, all events will be logged.
117112

118-
# Centralizing the URIs of common pages to make changing paths easier. You can leave this section alone if you use the default page locations from this project. These URLs do NOT have to be included in the unprotectedURIs list above as they will automatically be handled.
113+
# Centralizing the URIs of common pages to make changing paths easier. You can leave this section alone if you use the default page locations from this project. These URLs do NOT have to be included in the unprotectedURIs list above as they will automatically be handled.
119114
security:
120115
failedLoginAttempts: 10 # The number of failed login attempts before the user account is locked out. Set this to 0 to disable account lockout.
121116
accountLockoutDuration: 30 # The number of minutes to lock the user account after the maximum number of failed login attempts is reached. Set this to 0 to disable account lockout. Set this to -1 to lock the account until an administrator unlocks it.
122117
bcryptStrength: 12 # The bcrypt strength to use for password hashing. The higher the number, the longer it takes to hash the password. The default is 12. The minimum is 4. The maximum is 31.
123118
testHashTime: true # If true, the test hash time will be logged to the console on startup. This is useful for determining the optimal bcryptStrength value.
124119
defaultAction: deny # The default action for all requests. This can be either deny or allow.
125-
unprotectedURIs: /,/index.html,/favicon.ico,/apple-touch-icon-precomposed.png,/css/*,/js/*,/js/user/*,/js/event/*,/img/**,/user/registration,/user/resendRegistrationToken,/user/resetPassword,/user/registrationConfirm,/user/changePassword,/user/savePassword,/oauth2/authorization/*,/login,/user/login,/user/login.html,/swagger-ui.html,/swagger-ui/**,/v3/api-docs/**,/event/,/event/list.html,/event/**,/about.html # A comma delimited list of URIs that should not be protected by Spring Security if the defaultAction is deny.
120+
unprotectedURIs: /,/index.html,/favicon.ico,/apple-touch-icon-precomposed.png,/css/*,/js/*,/js/user/*,/js/event/*,/img/**,/user/registration,/user/resendRegistrationToken,/user/resetPassword,/user/registrationConfirm,/user/changePassword,/user/savePassword,/oauth2/authorization/*,/login,/user/login,/user/login.html,/swagger-ui.html,/swagger-ui/**,/v3/api-docs/**,/event/,/event/list.html,/event/**,/about.html,error.html # A comma delimited list of URIs that should not be protected by Spring Security if the defaultAction is deny.
126121
protectedURIs: /protected.html # A comma delimited list of URIs that should be protected by Spring Security if the defaultAction is allow.
127122
disableCSRFdURIs: /no-csrf-test # A comma delimited list of URIs that should not be protected by CSRF protection. This may include API endpoints that need to be called without a CSRF token.
128123

@@ -141,6 +136,20 @@ user:
141136
registrationNewVerificationURI: /user/request-new-verification-email.html # The URI for the request new verification email page.
142137
updateUserURI: /user/update-user.html # The URI for the update user page.
143138

139+
# Password policy enforcement/rules
140+
password:
141+
enabled: true # Enable/disable password policy enforcement
142+
min-length: 8 # Minimum password length
143+
max-length: 128 # Maximum password length
144+
require-uppercase: true # Require at least one uppercase character
145+
require-lowercase: true # Require at least one lowercase character
146+
require-digit: true # Require at least one digit
147+
require-special: true # Require at least one special character
148+
special-chars: "~`!@#$%^&*()_-+={}[]|\\:;\"'<>,.?/" # Allowed special characters
149+
prevent-common-passwords: true # Prevent use of common passwords (dictionary check)
150+
history-count: 3 # Number of previous passwords to prevent reuse
151+
similarity-threshold: 70 # Percentage of similarity allowed with username/email
152+
144153
mail:
145154
fromAddress: test@test.com # The from address for all emails sent by the application.
146155
purgetokens:
@@ -171,4 +180,3 @@ user:
171180
role-hierarchy: # Role hierarchy configuration section. This defines a hierarchy of roles, where a higher level role inherits all roles from a lower level role. The roles are defined in the roles-and-privileges section above.
172181
- ROLE_ADMIN > ROLE_MANAGER
173182
- ROLE_MANAGER > ROLE_USER
174-

src/main/resources/messages/messages.properties

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,17 @@ label.user.old-password=Old Password
5353
# Page Labels
5454
label.pages.home.title=Home
5555

56+
# Password Validation Error Messages
57+
TOO_SHORT=Password must be {0} or more characters in length.
58+
TOO_LONG=Password must be no more than {0} characters in length.
59+
INSUFFICIENT_UPPERCASE=Password must contain at least {0} uppercase letter(s).
60+
INSUFFICIENT_LOWERCASE=Password must contain at least {0} lowercase letter(s).
61+
INSUFFICIENT_DIGIT=Password must contain at least {0} digit(s).
62+
INSUFFICIENT_SPECIAL=Password must contain at least {0} special character(s).
63+
ILLEGAL_WORD=Password is too common or easy to guess.
64+
password.error.history.reuse=You cannot reuse your last {0} passwords.
65+
password.error.similarity=Password is too similar to your username or email ({0}% similarity).
66+
5667
# Registration Messages
5768
## Success Messages
5869
registration.success.thank-you=Thank you for registering!

src/main/resources/static/js/shared.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ export function showError(container, message) {
44
container.classList.remove("d-none");
55
}
66

7+
export function showHtmlError(container, htmlMessage) {
8+
if (!container) return;
9+
container.innerHTML = htmlMessage;
10+
container.classList.remove("d-none");
11+
}
12+
713
export function hideError(container) {
814
if (!container) return;
915
container.textContent = "";

0 commit comments

Comments
 (0)