fix #2497 update admin password

This commit is contained in:
Shinsuke Sugaya 2020-11-18 22:26:19 +09:00
parent ed46ec4b51
commit ba8848097e
14 changed files with 252 additions and 5 deletions

View file

@ -15,16 +15,38 @@
*/
package org.codelibs.fess.app.web.login;
import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.codelibs.core.lang.StringUtil;
import org.codelibs.fess.app.service.UserService;
import org.codelibs.fess.app.web.base.FessLoginAction;
import org.codelibs.fess.app.web.base.login.LocalUserCredential;
import org.codelibs.fess.app.web.profile.ProfileAction;
import org.codelibs.fess.mylasta.action.FessUserBean;
import org.codelibs.fess.util.ComponentUtil;
import org.codelibs.fess.util.RenderDataUtil;
import org.dbflute.optional.OptionalEntity;
import org.dbflute.optional.OptionalThing;
import org.lastaflute.web.Execute;
import org.lastaflute.web.login.exception.LoginFailureException;
import org.lastaflute.web.response.HtmlResponse;
import org.lastaflute.web.validation.VaErrorHook;
public class LoginAction extends FessLoginAction {
private static final Logger logger = LogManager.getLogger(LoginAction.class);
private static final String INVALID_OLD_PASSWORD = "LoginAction.invalidOldPassword";
// ===================================================================================
// Attribute
//
@Resource
private UserService userService;
// ===================================================================================
// Login Execute
// ==============
@ -55,7 +77,11 @@ public class LoginAction extends FessLoginAction {
return fessLoginAssist.loginRedirect(new LocalUserCredential(username, password), op -> {}, () -> {
activityHelper.login(getUserBean());
userInfoHelper.deleteUserCodeFromCookie(request);
return getHtmlResponse();
if (ComponentUtil.getFessConfig().isValidAdminPassword(password)) {
return getHtmlResponse();
}
getSession().ifPresent(session -> session.setAttribute(INVALID_OLD_PASSWORD, password));
return asHtml(virtualHost(path_Login_NewpasswordJsp));
});
} catch (final LoginFailureException lfe) {
activityHelper.loginFailure(OptionalThing.of(new LocalUserCredential(username, password)));
@ -64,4 +90,50 @@ public class LoginAction extends FessLoginAction {
return redirect(getClass());
}
@Execute
public HtmlResponse changePassword(final PasswordForm form) {
final VaErrorHook toIndexPage = () -> {
form.clearSecurityInfo();
return getUserBean().map(u -> asHtml(virtualHost(path_Login_NewpasswordJsp)).useForm(PasswordForm.class))
.orElseGet(() -> redirect(LoginAction.class));
};
validatePasswordForm(form, toIndexPage);
final String username = getUserBean().map(FessUserBean::getUserId).get();
try {
userService.changePassword(username, form.password);
saveInfo(messages -> messages.addSuccessChangedPassword(GLOBAL));
} catch (final Exception e) {
logger.warn("Failed to change newPassword for " + username, e);
throwValidationError(messages -> messages.addErrorsFailedToChangePassword(GLOBAL), toIndexPage);
}
getSession().ifPresent(session -> session.removeAttribute(INVALID_OLD_PASSWORD));
return redirect(ProfileAction.class);
}
private void validatePasswordForm(final PasswordForm form, final VaErrorHook validationErrorLambda) {
validate(form, messages -> {}, validationErrorLambda);
if (!form.password.equals(form.confirmPassword)) {
throwValidationError(messages -> {
messages.addErrorsInvalidConfirmPassword(GLOBAL);
}, validationErrorLambda);
}
final String oldPassword =
getSession().map(session -> (String) session.getAttribute(INVALID_OLD_PASSWORD)).orElse(StringUtil.EMPTY);
fessLoginAssist.findLoginUser(new LocalUserCredential(getUserBean().get().getUserId(), oldPassword)).orElseGet(() -> {
throwValidationError(messages -> {
messages.addErrorsNoUserForChangingPassword(GLOBAL);
}, validationErrorLambda);
return null;
});
}
private OptionalThing<HttpSession> getSession() {
final HttpSession session = request.getSession(false);
if (session != null) {
return OptionalEntity.of(session);
}
return OptionalEntity.empty();
}
}

View file

@ -25,8 +25,11 @@ public class LoginForm {
@NotBlank
public String password;
public String confirmPassword;
public void clearSecurityInfo() {
password = null;
confirmPassword = null;
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright 2012-2020 CodeLibs Project and the Others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.codelibs.fess.app.web.login;
import javax.validation.constraints.NotBlank;
public class PasswordForm {
public String username;
@NotBlank
public String password;
@NotBlank
public String confirmPassword;
public void clearSecurityInfo() {
password = null;
confirmPassword = null;
}
}

View file

@ -433,6 +433,9 @@ public interface FessHtmlPath {
/** The path of the HTML: /login/index.jsp */
HtmlNext path_Login_IndexJsp = new HtmlNext("/login/index.jsp");
/** The path of the HTML: /login/newpassword.jsp */
HtmlNext path_Login_NewpasswordJsp = new HtmlNext("/login/newpassword.jsp");
/** The path of the HTML: /profile/index.jsp */
HtmlNext path_Profile_IndexJsp = new HtmlNext("/profile/index.jsp");

View file

@ -3159,6 +3159,18 @@ public class FessLabels extends UserMessages {
/** The key of the message: Download */
public static final String LABELS_storage_button_download = "{labels.storage_button_download}";
/** The key of the message: Your password needs to be updated. */
public static final String LABELS_LOGIN_NEWPASSWORD = "{labels.login.newpassword}";
/** The key of the message: New Password */
public static final String LABELS_LOGIN_placeholder_new_password = "{labels.login.placeholder_new_password}";
/** The key of the message: Confirm New Password */
public static final String LABELS_LOGIN_placeholder_confirm_new_password = "{labels.login.placeholder_confirm_new_password}";
/** The key of the message: Update */
public static final String LABELS_LOGIN_UPDATE = "{labels.login.update}";
/**
* Assert the property is not null.
* @param property The value of the property. (NotNull)

View file

@ -1601,6 +1601,9 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
/** The key of the configuration. e.g. 1000 */
String STORAGE_MAX_ITEMS_IN_PAGE = "storage.max.items.in.page";
/** The key of the configuration. e.g. admin */
String PASSWORD_INVALID_ADMIN_PASSWORDS = "password.invalid.admin.passwords";
/**
* Get the value of property as {@link String}.
* @param propertyKey The key of the property. (NotNull)
@ -6601,6 +6604,14 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
*/
Integer getStorageMaxItemsInPageAsInteger();
/**
* Get the value for the key 'password.invalid.admin.passwords'. <br>
* The value is, e.g. admin <br>
* comment: ------
* @return The value of found property. (NotNull: if not found, exception but basically no way)
*/
String getPasswordInvalidAdminPasswords();
/**
* The simple implementation for configuration.
* @author FreeGen
@ -9210,6 +9221,10 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
return getAsInteger(FessConfig.STORAGE_MAX_ITEMS_IN_PAGE);
}
public String getPasswordInvalidAdminPasswords() {
return get(FessConfig.PASSWORD_INVALID_ADMIN_PASSWORDS);
}
@Override
protected java.util.Map<String, String> prepareGeneratedDefaultMap() {
java.util.Map<String, String> defaultMap = super.prepareGeneratedDefaultMap();
@ -9679,6 +9694,7 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
"https://repo.maven.apache.org/maven2/org/codelibs/fess/,https://fess.codelibs.org/plugin/artifacts.yaml");
defaultMap.put(FessConfig.PLUGIN_VERSION_FILTER, "");
defaultMap.put(FessConfig.STORAGE_MAX_ITEMS_IN_PAGE, "1000");
defaultMap.put(FessConfig.PASSWORD_INVALID_ADMIN_PASSWORDS, "admin");
defaultMap.put(FessConfig.lasta_di_SMART_DEPLOY_MODE, "hot");
defaultMap.put(FessConfig.DEVELOPMENT_HERE, "true");
defaultMap.put(FessConfig.ENVIRONMENT_TITLE, "Local Development");

View file

@ -2067,4 +2067,11 @@ public interface FessProp {
final String value = getHttpFileuploadThresholdSize();
return value != null ? DfTypeUtil.toLong(value) : null;
}
String getPasswordInvalidAdminPasswords();
default boolean isValidAdminPassword(final String password) {
return !split(getPasswordInvalidAdminPasswords(), "\n")
.get(stream -> stream.map(String::trim).filter(StringUtil::isNotEmpty).anyMatch(s -> s.equals(password)));
}
}

View file

@ -554,7 +554,7 @@ index.backup.log.targets=click_log.ndjson,favorite_log.ndjson,search_log.ndjson,
# logging
logging.search.docs.enabled=true
logging.search.docs.fields=filetype,created,click_count,title,doc_id,url,score,site,filename,host,digest,boost,mimetype,favorite_count,_id,lang,last_modified,content_length,timestamp
logging.search.use.logfile=false
logging.search.use.logfile=true
logging.app.packages=org.codelibs,org.dbflute,org.lastaflute
# ========================================================================================
@ -837,3 +837,9 @@ plugin.version.filter=
# ------
storage.max.items.in.page=1000
# ----------------------------------------------------------
# Password
# ------
password.invalid.admin.passwords=\
admin

View file

@ -1044,3 +1044,7 @@ labels.storage_bucket_name=Bucket Name
labels.storage_file=File
labels.storage_folder_name=Folder Name
labels.storage_button_download=Download
labels.login.newpassword=Your password needs to be updated.
labels.login.placeholder_new_password=New Password
labels.login.placeholder_confirm_new_password=Confirm New Password
labels.login.update=Update

View file

@ -1044,3 +1044,7 @@ labels.storage_bucket_name=Bucket Name
labels.storage_file=File
labels.storage_folder_name=Folder Name
labels.storage_button_download=Download
labels.login.newpassword=Your password needs to be updated.
labels.login.placeholder_new_password=New Password
labels.login.placeholder_confirm_new_password=Confirm New Password
labels.login.update=Update

View file

@ -1044,3 +1044,7 @@ labels.storage_bucket_name=バケット名
labels.storage_file=ファイル
labels.storage_folder_name=フォルダ名
labels.storage_button_download=ダウンロード
labels.login.newpassword=パスワードを更新する必要があります
labels.login.placeholder_new_password=新しいパスワード
labels.login.placeholder_confirm_new_password=新しいパスワードの確認
labels.login.update=更新

View file

@ -35,7 +35,7 @@
</la:info>
<la:errors />
</div>
<la:form styleId="/login" method="post">
<la:form styleId="login" method="post">
<div class="input-group mb-3">
<c:set var="ph_username">
<la:message key="labels.login.placeholder_username" />

View file

@ -0,0 +1,81 @@
<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%><!DOCTYPE html>
<html>
<head profile="http://a9.com/-/spec/opensearch/1.1/">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title><la:message key="labels.login.title" /></title>
<link href="${fe:url('/css/admin/bootstrap.min.css')}" rel="stylesheet" type="text/css" />
<link href="${fe:url('/css/admin/font-awesome.min.css')}" rel="stylesheet" type="text/css" />
<link href="${fe:url('/css/admin/adminlte.min.css')}" rel="stylesheet" type="text/css" />
<link href="${fe:url('/css/admin/style.css')}" rel="stylesheet" type="text/css" />
<!--[if lt IE 9]>
<script src="${fe:url('/css/admin/html5shiv.min.js')}"></script>
<script src="${fe:url('/css/admin/respond.min.js')}"></script>
<![endif]-->
</head>
<body class="hold-transition login-page">
<div class="login-box">
<div class="login-logo">
<la:link href="/">
<img src="${fe:url('/images/logo-top.png')}"
alt="<la:message key="labels.header_brand_name" />" />
</la:link>
</div>
<div class="card">
<div class="card-body login-card-body">
<p class="login-box-msg">
<la:message key="labels.login.newpassword" />
</p>
<%-- Message --%>
<div>
<la:info id="msg" message="false">
<div class="alert alert-info">${msg}</div>
</la:info>
<la:errors />
</div>
<la:form styleId="newPassword" method="post">
<div class="input-group mb-3">
<c:set var="ph_new_password">
<la:message key="labels.login.placeholder_new_password" />
</c:set>
<la:password property="password" class="form-control"
placeholder="${ph_new_password}" />
<div class="input-group-append">
<span class="input-group-text">
<em class="fa fa-lock fa-fw"></em>
</span>
</div>
</div>
<div class="input-group mb-3">
<c:set var="ph_confirm_password">
<la:message key="labels.login.placeholder_confirm_new_password" />
</c:set>
<la:password property="confirmPassword" class="form-control"
placeholder="${ph_confirm_password}" />
<div class="input-group-append">
<span class="input-group-text">
<em class="fa fa-lock fa-fw"></em>
</span>
</div>
</div>
<div class="text-center">
<button type="submit" name="changePassword"
class="btn btn-primary btn-block"
value="<la:message key="labels.login.update"/>">
<em class="fa fa-pencil-alt"></em>
<la:message key="labels.login.update" />
</button>
</div>
</la:form>
</div>
</div>
</div>
<input type="hidden" id="contextPath" value="${contextPath}" />
<script type="text/javascript" src="${fe:url('/js/admin/popper.min.js')}"></script>
<script type="text/javascript"
src="${fe:url('/js/admin/jquery-3.4.1.min.js')}"></script>
<script type="text/javascript" src="${fe:url('/js/admin/bootstrap.min.js')}"></script>
<script type="text/javascript" src="${fe:url('/js/login.js')}"></script>
</body>
</html>

View file

@ -34,7 +34,7 @@
</la:info>
<la:errors />
</div>
<la:form styleId="/login" method="post">
<la:form styleId="updatePassword" method="post">
<div class="input-group mb-3">
<c:set var="ph_old_password">
<la:message key="labels.profile.placeholder_old_password" />
@ -49,7 +49,7 @@
<c:set var="ph_new_password">
<la:message key="labels.profile.placeholder_new_password" />
</c:set>
<la:password property="newPassword" class="form-control"
<la:password property="oldPassword" class="form-control"
placeholder="${ph_new_password}" />
<div class="input-group-append">
<span class="input-group-text"><em class="fa fa-lock fa-fw"></em></span>