#571 : sso support

This commit is contained in:
Shinsuke Sugaya 2016-07-07 22:40:37 +09:00
parent b2f3093ab9
commit 4b945297c8
12 changed files with 315 additions and 40 deletions

View file

@ -0,0 +1,33 @@
/*
* Copyright 2012-2016 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.base;
import org.codelibs.fess.app.web.admin.dashboard.AdminDashboardAction;
import org.codelibs.fess.mylasta.action.FessUserBean;
import org.lastaflute.web.response.HtmlResponse;
public abstract class FessLoginAction extends FessSearchAction {
protected HtmlResponse getHtmlResponse() {
return getUserBean().map(user -> redirectByUser(user)).orElse(asHtml(path_Login_IndexJsp));
}
protected HtmlResponse redirectByUser(final FessUserBean user) {
if (!user.hasRoles(fessConfig.getAuthenticationAdminRolesAsArray())) {
return redirectToRoot();
}
return redirect(AdminDashboardAction.class);
}
}

View file

@ -33,8 +33,12 @@ import org.lastaflute.core.time.TimeManager;
import org.lastaflute.web.login.LoginHandlingResource;
import org.lastaflute.web.login.PrimaryLoginManager;
import org.lastaflute.web.login.TypicalLoginAssist;
import org.lastaflute.web.login.exception.LoginFailureException;
import org.lastaflute.web.login.exception.LoginRequiredException;
import org.lastaflute.web.login.option.LoginOpCall;
import org.lastaflute.web.login.option.LoginSpecifiedOption;
import org.lastaflute.web.login.redirect.LoginRedirectSuccessCall;
import org.lastaflute.web.response.HtmlResponse;
/**
* @author jflute
@ -66,17 +70,6 @@ public class FessLoginAssist extends TypicalLoginAssist<String, FessUserBean, Fe
}) > 0;
}
@Override
public OptionalEntity<FessUser> findLoginUser(final String username, final String password) {
if (!fessConfig.isAdminUser(username)) {
final OptionalEntity<FessUser> ldapUser = ComponentUtil.getLdapManager().login(username, password);
if (ldapUser.isPresent()) {
return ldapUser;
}
}
return doFindLoginUser(username, encryptPassword(password));
}
@Override
protected OptionalEntity<FessUser> doFindLoginUser(final String username, final String cipheredPassword) {
return userBhv.selectEntity(cb -> {
@ -145,4 +138,43 @@ public class FessLoginAssist extends TypicalLoginAssist<String, FessUserBean, Fe
protected String toTypedUserId(final String userKey) {
return userKey;
}
// ===================================================================================
// Login Extention
// ==============
public HtmlResponse loginRedirect(final LoginCredential credential, final LoginOpCall opLambda,
final LoginRedirectSuccessCall oneArgLambda) throws LoginFailureException {
doLogin(credential, createLoginOption(opLambda)); // exception if login failure
return switchToRequestedActionIfExists(oneArgLambda.success()); // so success only here
}
protected void doLogin(final LoginCredential credential, final LoginSpecifiedOption option) throws LoginFailureException {
credential.validate();
handleLoginSuccess(findLoginUser(credential).orElseThrow(() -> {
final String msg = "Not found the user by the account and password: " + credential.getId() + ", " + option;
return handleLoginFailure(msg, credential.getResource(), OptionalThing.of(option));
}), option);
}
public OptionalEntity<FessUser> findLoginUser(final LoginCredential credential) {
if (credential instanceof UserPasswordLoginCredential) {
final UserPasswordLoginCredential userCredential = (UserPasswordLoginCredential) credential;
final String username = userCredential.getUsername();
final String password = userCredential.getPassword();
if (!fessConfig.isAdminUser(username)) {
final OptionalEntity<FessUser> ldapUser = ComponentUtil.getLdapManager().login(username, password);
if (ldapUser.isPresent()) {
return ldapUser;
}
}
return doFindLoginUser(username, encryptPassword(password));
} else if (credential instanceof SSOLoginCredential) {
final String username = ((SSOLoginCredential) credential).getUsername();
if (!fessConfig.isAdminUser(username)) {
return ComponentUtil.getLdapManager().login(username);
}
}
return OptionalEntity.empty();
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright 2012-2016 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.base.login;
public interface LoginCredential {
void validate();
String getId();
Object getResource();
public default void assertLoginAccountRequired(String account) {
if (account == null || account.length() == 0) {
String msg = "The argument 'account' should not be null for login.";
throw new IllegalArgumentException(msg);
}
}
public default void assertLoginPasswordRequired(String password) {
if (password == null || password.length() == 0) {
String msg = "The argument 'password' should not be null for login.";
throw new IllegalArgumentException(msg);
}
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright 2012-2016 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.base.login;
import org.dbflute.util.DfCollectionUtil;
public class SSOLoginCredential implements LoginCredential {
private String username;
// private Principal principal;
public SSOLoginCredential(final String username) {
this.username = username;
}
@Override
public void validate() {
assertLoginAccountRequired(username);
}
@Override
public Object getResource() {
return DfCollectionUtil.newHashMap("account", username);
}
@Override
public String getId() {
return username;
}
public String getUsername() {
return username;
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright 2012-2016 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.base.login;
import org.dbflute.util.DfCollectionUtil;
public class UserPasswordLoginCredential implements LoginCredential {
private String username;
private String password;
public UserPasswordLoginCredential(final String username, final String password) {
this.username = username;
this.password = password;
}
@Override
public void validate() {
assertLoginAccountRequired(username);
assertLoginPasswordRequired(password);
}
@Override
public Object getResource() {
return DfCollectionUtil.newHashMap("account", username, "password", password);
}
@Override
public String getId() {
return username;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}

View file

@ -15,30 +15,17 @@
*/
package org.codelibs.fess.app.web.login;
import org.codelibs.fess.app.web.admin.dashboard.AdminDashboardAction;
import org.codelibs.fess.app.web.base.FessSearchAction;
import org.codelibs.fess.mylasta.action.FessUserBean;
import org.codelibs.fess.app.web.base.FessLoginAction;
import org.codelibs.fess.app.web.base.login.UserPasswordLoginCredential;
import org.codelibs.fess.util.RenderDataUtil;
import org.lastaflute.web.Execute;
import org.lastaflute.web.login.exception.LoginFailureException;
import org.lastaflute.web.response.HtmlResponse;
public class LoginAction extends FessSearchAction {
public class LoginAction extends FessLoginAction {
// ===================================================================================
// Constant
//
// ===================================================================================
// Attribute
//
// ===================================================================================
// Hook
// ======
// ===================================================================================
// Search Execute
// Login Execute
// ==============
@Execute
@ -58,7 +45,7 @@ public class LoginAction extends FessSearchAction {
final String password = form.password;
form.clearSecurityInfo();
try {
return fessLoginAssist.loginRedirect(username, password, op -> {}, () -> {
return fessLoginAssist.loginRedirect(new UserPasswordLoginCredential(username, password), op -> {}, () -> {
activityHelper.login(getUserBean());
return getHtmlResponse();
});
@ -70,15 +57,4 @@ public class LoginAction extends FessSearchAction {
return redirect(getClass());
}
private HtmlResponse getHtmlResponse() {
return getUserBean().map(user -> redirectByUser(user)).orElse(asHtml(path_Login_IndexJsp));
}
private HtmlResponse redirectByUser(final FessUserBean user) {
if (!user.hasRoles(fessConfig.getAuthenticationAdminRolesAsArray())) {
return redirectToRoot();
}
return redirect(AdminDashboardAction.class);
}
}

View file

@ -0,0 +1,62 @@
/*
* Copyright 2012-2016 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.sso;
import org.codelibs.fess.app.web.base.FessLoginAction;
import org.codelibs.fess.app.web.base.login.SSOLoginCredential;
import org.codelibs.fess.app.web.login.LoginAction;
import org.jsoup.helper.StringUtil;
import org.lastaflute.web.Execute;
import org.lastaflute.web.login.exception.LoginFailureException;
import org.lastaflute.web.response.HtmlResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SsoAction extends FessLoginAction {
// ===================================================================================
// Constant
//
private static final Logger logger = LoggerFactory.getLogger(SsoAction.class);
// ===================================================================================
// Login Execute
// ==============
@Execute
public HtmlResponse index() {
final String user = request.getRemoteUser();
if (StringUtil.isBlank(user)) {
if (logger.isDebugEnabled()) {
logger.debug("No remote user in SSO request.");
}
saveError(messages -> messages.addErrorsSsoLoginError(GLOBAL));
return redirect(LoginAction.class);
}
try {
return fessLoginAssist.loginRedirect(new SSOLoginCredential(user), op -> {}, () -> {
activityHelper.login(getUserBean());
return getHtmlResponse();
});
} catch (final LoginFailureException lfe) {
if (logger.isDebugEnabled()) {
logger.debug("SSO login failure.", lfe);
}
saveError(messages -> messages.addErrorsSsoLoginError(GLOBAL));
return redirect(LoginAction.class);
}
}
}

View file

@ -105,6 +105,20 @@ public class LdapManager {
return OptionalEntity.empty();
}
public OptionalEntity<FessUser> login(final String username) {
final Hashtable<String, String> env = createAdminEnv();
try (DirContextHolder holder = getDirContext(() -> env)) {
final DirContext context = holder.get();
if (logger.isDebugEnabled()) {
logger.debug("Logged in.", context);
}
return OptionalEntity.of(createLdapUser(username, env));
} catch (final Exception e) {
logger.debug("Login failed.", e);
}
return OptionalEntity.empty();
}
protected LdapUser createLdapUser(final String username, final Hashtable<String, String> env) {
return new LdapUser(env, username);
}

View file

@ -167,6 +167,9 @@ public class FessMessages extends FessLabels {
/** The key of the message: Username or Password is not correct. */
public static final String ERRORS_login_error = "{errors.login_error}";
/** The key of the message: Failed to process SSO login. */
public static final String ERRORS_sso_login_error = "{errors.sso_login_error}";
/** The key of the message: Could not find {0}. */
public static final String ERRORS_could_not_find_log_file = "{errors.could_not_find_log_file}";
@ -1124,6 +1127,20 @@ public class FessMessages extends FessLabels {
return this;
}
/**
* Add the created action message for the key 'errors.sso_login_error' with parameters.
* <pre>
* message: Failed to process SSO login.
* </pre>
* @param property The property name for the message. (NotNull)
* @return this. (NotNull)
*/
public FessMessages addErrorsSsoLoginError(String property) {
assertPropertyNotNull(property);
add(property, new ActionMessage(ERRORS_sso_login_error));
return this;
}
/**
* Add the created action message for the key 'errors.could_not_find_log_file' with parameters.
* <pre>

View file

@ -77,6 +77,7 @@ errors.app.double.submit.request=Your request might have been processed before t
# Fess
# ======
errors.login_error=Username or Password is not correct.
errors.sso_login_error=Failed to process SSO login.
errors.could_not_find_log_file=Could not find {0}.
errors.failed_to_start_crawl_process=Failed to start a crawl process.
errors.invalid_design_jsp_file_name=Invalid JSP file.

View file

@ -77,6 +77,7 @@ errors.app.double.submit.request=Your request might have been processed before t
# Fess
# ======
errors.login_error=Username or Password is not correct.
errors.sso_login_error=Failed to process SSO login.
errors.could_not_find_log_file=Could not find {0}.
errors.failed_to_start_crawl_process=Failed to start a crawl process.
errors.invalid_design_jsp_file_name=Invalid JSP file.

View file

@ -73,6 +73,7 @@ errors.app.double.submit.request = \u3053\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u3
# Fess
# ======
errors.login_error = \u30e6\u30fc\u30b6\u30fc\u540d\u307e\u305f\u306f\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u6b63\u3057\u304f\u3042\u308a\u307e\u305b\u3093\u3002
errors.sso_login_error=SSO\u30ed\u30b0\u30a4\u30f3\u51e6\u7406\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002
errors.could_not_find_log_file = {0} \u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002
errors.failed_to_start_crawl_process = \u30af\u30ed\u30fc\u30eb\u30d7\u30ed\u30bb\u30b9\u306e\u8d77\u52d5\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002
errors.invalid_design_jsp_file_name = \u7121\u52b9\u306aJSP\u30d5\u30a1\u30a4\u30eb\u3067\u3059\u3002