1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.app.web.login;
17
18 import org.apache.logging.log4j.LogManager;
19 import org.apache.logging.log4j.Logger;
20 import org.codelibs.core.lang.StringUtil;
21 import org.codelibs.fess.app.service.UserService;
22 import org.codelibs.fess.app.web.base.FessLoginAction;
23 import org.codelibs.fess.app.web.base.login.LocalUserCredential;
24 import org.codelibs.fess.app.web.profile.ProfileAction;
25 import org.codelibs.fess.mylasta.action.FessMessages;
26 import org.codelibs.fess.util.ComponentUtil;
27 import org.codelibs.fess.util.RenderDataUtil;
28 import org.dbflute.optional.OptionalEntity;
29 import org.dbflute.optional.OptionalThing;
30 import org.lastaflute.web.Execute;
31 import org.lastaflute.web.login.exception.LoginFailureException;
32 import org.lastaflute.web.response.HtmlResponse;
33 import org.lastaflute.web.validation.VaErrorHook;
34
35 import jakarta.annotation.Resource;
36 import jakarta.servlet.http.HttpSession;
37
38
39
40
41 public class LoginAction extends FessLoginAction {
42
43
44
45
46 public LoginAction() {
47 super();
48 }
49
50 private static final Logger logger = LogManager.getLogger(LoginAction.class);
51
52 private static final String INVALID_OLD_PASSWORD = "LoginAction.invalidOldPassword";
53
54
55
56
57 @Resource
58 private UserService userService;
59
60
61
62
63
64
65
66
67
68
69 @Execute
70 public HtmlResponse index() {
71 getSession().ifPresent(session -> session.removeAttribute(INVALID_OLD_PASSWORD));
72 return asIndexPage(null).useForm(LoginForm.class);
73 }
74
75 private HtmlResponse asIndexPage(final LoginForm form) {
76 if (form != null) {
77 form.clearSecurityInfo();
78 }
79 return asHtml(virtualHost(path_Login_IndexJsp)).renderWith(data -> {
80 RenderDataUtil.register(data, "notification", fessConfig.getNotificationLogin());
81 saveToken();
82 });
83 }
84
85
86
87
88
89
90
91 @Execute
92 public HtmlResponse login(final LoginForm form) {
93 validate(form, messages -> {}, () -> asIndexPage(form));
94 verifyToken(() -> asIndexPage(form));
95 final String username = form.username;
96 final String password = form.password;
97 form.clearSecurityInfo();
98 try {
99 final HtmlResponse loginRedirect = fessLoginAssist.loginRedirect(new LocalUserCredential(username, password), op -> {}, () -> {
100 activityHelper.login(getUserBean());
101 userInfoHelper.deleteUserCodeFromCookie(request);
102 return getHtmlResponse();
103 });
104 if (ComponentUtil.getFessConfig().isValidAdminPassword(password)) {
105 return loginRedirect;
106 }
107 getSession().ifPresent(session -> session.setAttribute(INVALID_OLD_PASSWORD, password));
108 return asHtml(virtualHost(path_Login_NewpasswordJsp));
109 } catch (final LoginFailureException lfe) {
110 if (logger.isInfoEnabled()) {
111 logger.info("Login failed for user: username={}, reason={}", username, lfe.getMessage());
112 }
113 activityHelper.loginFailure(OptionalThing.of(new LocalUserCredential(username, password)));
114 throwValidationError(messages -> messages.addErrorsLoginError(GLOBAL), () -> asIndexPage(form));
115 }
116 return redirect(getClass());
117 }
118
119
120
121
122
123
124
125 @Execute
126 public HtmlResponse changePassword(final PasswordForm form) {
127 final VaErrorHook toIndexPage = () -> {
128 form.clearSecurityInfo();
129 return getUserBean().map(u -> asHtml(virtualHost(path_Login_NewpasswordJsp)).useForm(PasswordForm.class))
130 .orElseGet(() -> redirect(LoginAction.class));
131 };
132 validatePasswordForm(form, toIndexPage);
133 if (!getUserBean().isPresent()) {
134 logger.warn("User session not found during password change - potential session timeout or security issue");
135 return redirect(LoginAction.class);
136 }
137 final String username = getUserBean().get().getUserId();
138 try {
139 userService.changePassword(username, form.password);
140 saveInfo(messages -> messages.addSuccessChangedPassword(GLOBAL));
141 } catch (final Exception e) {
142 logger.warn("Failed to change password for user: username={}, error={}", username, e.getMessage(), e);
143 throwValidationError(messages -> messages.addErrorsFailedToChangePassword(GLOBAL), toIndexPage);
144 }
145 getSession().ifPresent(session -> session.removeAttribute(INVALID_OLD_PASSWORD));
146 return redirect(ProfileAction.class);
147 }
148
149 private void validatePasswordForm(final PasswordForm form, final VaErrorHook validationErrorLambda) {
150 validate(form, messages -> {}, validationErrorLambda);
151
152 if (!form.password.equals(form.confirmPassword)) {
153 throwValidationError(messages -> {
154 messages.addErrorsInvalidConfirmPassword(GLOBAL);
155 }, validationErrorLambda);
156 }
157
158 final String validationError = ComponentUtil.getSystemHelper().validatePassword(form.password);
159 if (StringUtil.isNotBlank(validationError)) {
160 throwValidationError(messages -> {
161 addPasswordValidationError(messages, validationError);
162 }, validationErrorLambda);
163 }
164
165 final String oldPassword =
166 getSession().map(session -> (String) session.getAttribute(INVALID_OLD_PASSWORD)).orElse(StringUtil.EMPTY);
167 getUserBean().ifPresent(user -> {
168 final String userId = user.getUserId();
169 fessLoginAssist.findLoginUser(new LocalUserCredential(userId, oldPassword)).orElseGet(() -> {
170 throwValidationError(messages -> {
171 messages.addErrorsNoUserForChangingPassword(GLOBAL);
172 }, validationErrorLambda);
173 return null;
174 });
175 }).orElse(() -> {
176 throwValidationError(messages -> {
177 messages.addErrorsLoginError(GLOBAL);
178 }, validationErrorLambda);
179 });
180 }
181
182
183
184
185
186
187
188 protected void addPasswordValidationError(final FessMessages messages, final String errorKey) {
189 switch (errorKey) {
190 case "errors.password_length":
191 messages.addErrorsPasswordLength(GLOBAL, String.valueOf(ComponentUtil.getFessConfig().getPasswordMinLengthAsInteger()));
192 break;
193 case "errors.password_no_uppercase":
194 messages.addErrorsPasswordNoUppercase(GLOBAL);
195 break;
196 case "errors.password_no_lowercase":
197 messages.addErrorsPasswordNoLowercase(GLOBAL);
198 break;
199 case "errors.password_no_digit":
200 messages.addErrorsPasswordNoDigit(GLOBAL);
201 break;
202 case "errors.password_no_special_char":
203 messages.addErrorsPasswordNoSpecialChar(GLOBAL);
204 break;
205 case "errors.password_is_blacklisted":
206 messages.addErrorsPasswordIsBlacklisted(GLOBAL);
207 break;
208 default:
209 messages.addErrorsBlankPassword(GLOBAL);
210 break;
211 }
212 }
213
214 private OptionalThing<HttpSession> getSession() {
215 final HttpSession session = request.getSession(false);
216 if (session != null) {
217 return OptionalEntity.of(session);
218 }
219 return OptionalEntity.empty();
220 }
221 }