Store remember me on server side #PL-3787

This commit is contained in:
Yuriy Artamonov 2014-07-09 06:42:47 +00:00
parent e70920dac7
commit 968d8eecc7
32 changed files with 783 additions and 266 deletions

View File

@ -743,6 +743,21 @@ create table SYS_JMX_INSTANCE (
------------------------------------------------------------------------------------------------------------
create table SEC_REMEMBER_ME (
ID varchar(36) not null,
CREATE_TS timestamp,
CREATED_BY varchar(50),
VERSION integer,
--
USER_ID varchar(36) not null,
TOKEN varchar(32) not null,
--
primary key (ID),
constraint FK_SEC_REMEMBER_ME_USER foreign key (USER_ID) references SEC_USER(ID)
)^
------------------------------------------------------------------------------------------------------------
create function NEWID() returns varchar(36)
return uuid(uuid());

View File

@ -809,6 +809,23 @@ create index IDX_SYS_QUERY_RESULT_SESSION_KEY on SYS_QUERY_RESULT (SESSION_ID, Q
------------------------------------------------------------------------------------------------------------------
create table SEC_REMEMBER_ME (
ID uniqueidentifier not null,
CREATE_TS datetime,
CREATED_BY varchar(50),
VERSION integer,
--
USER_ID uniqueidentifier not null,
TOKEN varchar(32) not null,
--
primary key (ID),
constraint FK_SEC_REMEMBER_ME_USER foreign key (USER_ID) references SEC_USER(ID)
)^
create index IDX_SEC_REMEMBER_ME_USER on SEC_REMEMBER_ME(USER_ID)^
create index IDX_SEC_REMEMBER_ME_TOKEN on SEC_REMEMBER_ME(TOKEN)^
------------------------------------------------------------------------------------------------------------------
insert into SEC_GROUP (ID, CREATE_TS, VERSION, NAME, PARENT_ID)
values ('0fa2b1a5-1d68-4d69-9fbd-dff348347f93', current_timestamp, 0, 'Company', null)^

View File

@ -579,6 +579,20 @@ create table SEC_USER_SUBSTITUTION (
)^
create index IDX_SEC_USER_SUBSTITUTION_USER on SEC_USER_SUBSTITUTION(USER_ID)^
create table SEC_REMEMBER_ME (
ID varchar2(32) not null,
CREATE_TS timestamp,
CREATED_BY varchar2(50),
VERSION integer,
--
USER_ID varchar2(32) not null,
TOKEN varchar2(32) not null,
--
primary key (ID)
)^
create index IDX_SEC_REMEMBER_ME_USER on SEC_REMEMBER_ME(USER_ID)^
create index IDX_SEC_REMEMBER_ME_TOKEN on SEC_REMEMBER_ME(TOKEN)^
alter table SYS_APP_FOLDER add constraint FK_SYS_APP_FOLDER_FOLDER foreign key (FOLDER_ID) references SYS_FOLDER(ID)^
alter table SYS_ATTR_VALUE add constraint SYS_ATTR_VALUE_CATEGORY_ATT_ID foreign key (CATEGORY_ATTR_ID) references SYS_CATEGORY_ATTR(ID)^
@ -635,6 +649,8 @@ alter table SEC_USER_SETTING add constraint SEC_USER_SETTING_USER foreign key (U
alter table SEC_USER_SUBSTITUTION add constraint FK_SEC_USER_SUB_SUB_USE foreign key (SUBSTITUTED_USER_ID) references SEC_USER(ID)^
alter table SEC_USER_SUBSTITUTION add constraint FK_SEC_USER_SUBSTITUTION_USER foreign key (USER_ID) references SEC_USER(ID)^
alter table SEC_REMEMBER_ME add constraint FK_SEC_REMEMBER_ME_USER foreign key (USER_ID) references SEC_USER(ID)^
--------------------------------------------------------------------------------------------------------------
create or replace function NEWID return varchar2

View File

@ -776,6 +776,24 @@ create index IDX_SYS_QUERY_RESULT_ENTITY_SESSION_KEY on SYS_QUERY_RESULT (ENTITY
create index IDX_SYS_QUERY_RESULT_SESSION_KEY on SYS_QUERY_RESULT (SESSION_ID, QUERY_KEY)^
--------------------------------------------------------------------------------------------------------------
create table SEC_REMEMBER_ME (
ID uuid not null,
CREATE_TS timestamp,
CREATED_BY varchar(50),
VERSION integer,
--
USER_ID uuid not null,
TOKEN varchar(32) not null,
--
primary key (ID),
constraint FK_SEC_REMEMBER_ME_USER foreign key (USER_ID) references SEC_USER(ID)
)^
create index IDX_SEC_REMEMBER_ME_USER on SEC_REMEMBER_ME(USER_ID)^
create index IDX_SEC_REMEMBER_ME_TOKEN on SEC_REMEMBER_ME(TOKEN)^
--------------------------------------------------------------------------------------------------------------
create or replace function newid()
returns uuid
as '$libdir/uuid-ossp', 'uuid_generate_v1'

View File

@ -0,0 +1,15 @@
-- $Id$
-- Description: add table for server-side remember me
create table SEC_REMEMBER_ME (
ID varchar(36) not null,
CREATE_TS timestamp,
CREATED_BY varchar(50),
VERSION integer,
--
USER_ID varchar(36) not null,
TOKEN varchar(32) not null,
--
primary key (ID),
constraint FK_SEC_REMEMBER_ME_USER foreign key (USER_ID) references SEC_USER(ID)
)^

View File

@ -0,0 +1,17 @@
-- $Id$
-- Description: add table for server-side remember me
create table SEC_REMEMBER_ME (
ID uniqueidentifier not null,
CREATE_TS datetime,
CREATED_BY varchar(50),
VERSION integer,
--
USER_ID uniqueidentifier not null,
TOKEN varchar(32) not null,
--
primary key (ID),
constraint FK_SEC_REMEMBER_ME_USER foreign key (USER_ID) references SEC_USER(ID)
)^
create index IDX_SEC_REMEMBER_ME_USER on SEC_REMEMBER_ME(USER_ID)^
create index IDX_SEC_REMEMBER_ME_TOKEN on SEC_REMEMBER_ME(TOKEN)^

View File

@ -0,0 +1,18 @@
-- $Id$
-- Description: add table for server-side remember me
create table SEC_REMEMBER_ME (
ID varchar2(32) not null,
CREATE_TS timestamp,
CREATED_BY varchar2(50),
VERSION integer,
--
USER_ID varchar2(32) not null,
TOKEN varchar2(32) not null,
--
primary key (ID)
)^
create index IDX_SEC_REMEMBER_ME_USER on SEC_REMEMBER_ME(USER_ID)^
create index IDX_SEC_REMEMBER_ME_TOKEN on SEC_REMEMBER_ME(TOKEN)^
alter table SEC_REMEMBER_ME add constraint FK_SEC_REMEMBER_ME_USER foreign key (USER_ID) references SEC_USER(ID)^

View File

@ -0,0 +1,17 @@
-- $Id$
-- Description: add table for server-side remember me
create table SEC_REMEMBER_ME (
ID uuid not null,
CREATE_TS timestamp,
CREATED_BY varchar(50),
VERSION integer,
--
USER_ID uuid not null,
TOKEN varchar(32) not null,
--
primary key (ID),
constraint FK_SEC_REMEMBER_ME_USER foreign key (USER_ID) references SEC_USER(ID)
)^
create index IDX_SEC_REMEMBER_ME_USER on SEC_REMEMBER_ME(USER_ID)^
create index IDX_SEC_REMEMBER_ME_TOKEN on SEC_REMEMBER_ME(TOKEN)^

View File

@ -83,6 +83,33 @@ public class LoginServiceBean implements LoginService {
}
}
@Override
public UserSession loginByRememberMe(String login, String rememberMeToken, Locale locale) throws LoginException {
try {
return loginWorker.loginByRememberMe(login, rememberMeToken, locale);
} catch (LoginException e) {
log.info("Login failed: " + e.toString());
throw e;
} catch (Throwable e) {
log.error("Login error", e);
throw wrapInLoginException(e);
}
}
@Override
public UserSession loginByRememberMe(String login, String rememberMeToken, Locale locale, Map<String, Object> params)
throws LoginException {
try {
return loginWorker.loginByRememberMe(login, rememberMeToken, locale, params);
} catch (LoginException e) {
log.info("Login failed: " + e.toString());
throw e;
} catch (Throwable e) {
log.error("Login error", e);
throw wrapInLoginException(e);
}
}
@Override
public void logout() {
try {
@ -103,6 +130,11 @@ public class LoginServiceBean implements LoginService {
return loginWorker.getSession(sessionId);
}
@Override
public boolean checkRememberMe(String login, String rememberMeToken) {
return loginWorker.checkRememberMe(login, rememberMeToken);
}
protected LoginException wrapInLoginException(Throwable throwable) {
//noinspection ThrowableResultOfMethodCallIgnored
Throwable rootCause = ExceptionUtils.getRootCause(throwable);
@ -111,4 +143,4 @@ public class LoginServiceBean implements LoginService {
// send text only to avoid ClassNotFoundException when the client has no dependency to some library
return new LoginException(rootCause.toString());
}
}
}

View File

@ -41,7 +41,19 @@ public interface LoginWorker {
/**
* @see LoginService#loginTrusted(String, String, java.util.Locale, java.util.Map))
*/
UserSession loginTrusted(String login, String password, Locale locale, Map<String, Object> params) throws LoginException;
UserSession loginTrusted(String login, String password, Locale locale, Map<String, Object> params)
throws LoginException;
/**
* @see LoginService#loginByRememberMe(String, String, java.util.Locale))
*/
UserSession loginByRememberMe(String login, String rememberMeToken, Locale locale) throws LoginException;
/**
* @see LoginService#loginTrusted(String, String, java.util.Locale, java.util.Map))
*/
UserSession loginByRememberMe(String login, String rememberMeToken, Locale locale, Map<String, Object> params)
throws LoginException;
/**
* @see LoginService#logout()
@ -67,4 +79,9 @@ public interface LoginWorker {
* @throws LoginException in case of unsuccessful log in
*/
UserSession loginSystem(String login) throws LoginException;
}
/**
* @see com.haulmont.cuba.security.app.LoginService#checkRememberMe(String, String)
*/
boolean checkRememberMe(String login, String rememberMeToken);
}

View File

@ -8,6 +8,7 @@ import com.haulmont.cuba.core.*;
import com.haulmont.cuba.core.app.ServerConfig;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.core.sys.remoting.RemoteClientInfo;
import com.haulmont.cuba.security.entity.RememberMeToken;
import com.haulmont.cuba.security.entity.User;
import com.haulmont.cuba.security.global.LoginException;
import com.haulmont.cuba.security.global.NoUserSessionException;
@ -30,9 +31,10 @@ import java.util.regex.Pattern;
/**
* Class that encapsulates the middleware login/logout functionality.
*
* @see com.haulmont.cuba.security.app.LoginServiceBean
*
* @author krivopustov
* @version $Id$
* @see com.haulmont.cuba.security.app.LoginServiceBean
*/
@ManagedBean(LoginWorker.NAME)
public class LoginWorkerBean implements LoginWorker {
@ -81,11 +83,24 @@ public class LoginWorkerBean implements LoginWorker {
log.warn("Failed to authenticate: " + login);
return null;
} else {
//noinspection UnnecessaryLocalVariable
User user = (User) list.get(0);
return user;
}
}
@Nullable
protected RememberMeToken loadRememberMeToken(String rememberMeToken, User user) {
EntityManager em = persistence.getEntityManager();
TypedQuery<RememberMeToken> query = em.createQuery(
"select rt from sec$RememberMeToken rt where rt.token = :token and rt.user.id = :userId",
RememberMeToken.class);
query.setParameter("token", rememberMeToken);
query.setParameter("userId", user.getId());
return query.getFirstResult();
}
@Override
public UserSession login(String login, String password, Locale locale) throws LoginException {
if (password == null)
@ -204,6 +219,48 @@ public class LoginWorkerBean implements LoginWorker {
return loginTrusted(login, password, locale);
}
@Override
public UserSession loginByRememberMe(String login, String rememberMeToken, Locale locale) throws LoginException {
Transaction tx = persistence.createTransaction();
try {
User user = loadUser(login);
if (user == null) {
throw new LoginException(
messages.formatMessage(getClass(), "LoginException.InvalidActiveDirectoryUser", locale, login));
}
RememberMeToken loginToken = loadRememberMeToken(rememberMeToken, user);
if (loginToken == null) {
throw new LoginException(getInvalidCredentialsMessage(login, locale));
}
Locale userLocale = locale;
if (!StringUtils.isBlank(user.getLanguage())) {
userLocale = new Locale(user.getLanguage());
}
UserSession session = userSessionManager.createSession(user, userLocale, false);
if (user.getDefaultSubstitutedUser() != null) {
session = userSessionManager.createSession(session, user.getDefaultSubstitutedUser());
}
log.info("Logged in: " + session);
tx.commit();
userSessionManager.storeSession(session);
return session;
} finally {
tx.end();
}
}
@Override
public UserSession loginByRememberMe(String login, String rememberMeToken, Locale locale, Map<String, Object> params)
throws LoginException {
return loginByRememberMe(login, rememberMeToken, locale);
}
@Override
public void logout() {
try {
@ -261,6 +318,7 @@ public class LoginWorkerBean implements LoginWorker {
@Override
public UserSession getSession(UUID sessionId) {
try {
//noinspection UnnecessaryLocalVariable
UserSession session = userSessionManager.getSession(sessionId);
return session;
} catch (RuntimeException e) {
@ -270,4 +328,28 @@ public class LoginWorkerBean implements LoginWorker {
throw e;
}
}
}
@Override
public boolean checkRememberMe(String login, String rememberMeToken) {
boolean verified = false;
Transaction tx = persistence.createTransaction();
try {
EntityManager em = persistence.getEntityManager();
TypedQuery<RememberMeToken> query = em.createQuery(
"select rt from sec$RememberMeToken rt where rt.token = :token and rt.user.loginLowerCase = :userLogin",
RememberMeToken.class);
query.setParameter("token", rememberMeToken);
query.setParameter("userLogin", StringUtils.lowerCase(login));
if (query.getFirstResult() != null) {
verified = true;
}
tx.commit();
} finally {
tx.end();
}
return verified;
}
}

View File

@ -14,6 +14,7 @@ import groovy.text.SimpleTemplateEngine;
import groovy.text.Template;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -219,10 +220,57 @@ public class UserManagementServiceBean implements UserManagementService {
return passwordEncryption.checkPassword(user, passwordHash);
}
@Deprecated
@Override
public boolean checkEqualsOfNewAndOldPassword(UUID userId, String newPasswordHash) {
return checkPassword(userId, newPasswordHash);
public void resetRememberMeTokens(List<UUID> userIds) {
Transaction tx = persistence.getTransaction();
try {
EntityManager em = persistence.getEntityManager();
Query query = em.createQuery("delete from sec$RememberMeToken rt where rt.user.id in :userIds");
query.setParameter("userIds", userIds);
query.executeUpdate();
tx.commit();
} finally {
tx.end();
}
}
@Override
public void resetRememberMeTokens() {
Transaction tx = persistence.createTransaction();
try {
EntityManager em = persistence.getEntityManager();
Query query = em.createQuery("delete from sec$RememberMeToken rt");
query.executeUpdate();
tx.commit();
} finally {
tx.end();
}
}
@Override
public String generateRememberMeToken(UUID userId) {
String token = RandomStringUtils.randomAlphanumeric(RememberMeToken.TOKEN_LENGTH);
Transaction tx = persistence.createTransaction();
try {
EntityManager em = persistence.getEntityManager();
RememberMeToken rememberMeToken = new RememberMeToken();
rememberMeToken.setToken(token);
rememberMeToken.setUser(em.getReference(User.class, userId));
em.persist(rememberMeToken);
tx.commit();
} finally {
tx.end();
}
return token;
}
protected EmailTemplate getResetPasswordTemplate(User user,
@ -357,10 +405,13 @@ public class UserManagementServiceBean implements UserManagementService {
modifiedUsers.put(user, password);
}
resetRememberMeTokens(userIds);
tx.commit();
} finally {
tx.end();
}
return modifiedUsers;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 736 B

View File

@ -67,7 +67,31 @@ public interface LoginService {
* @return created user session
* @throws LoginException in case of unsuccessful login
*/
UserSession loginTrusted(String login, String password, Locale locale, Map<String, Object> params) throws LoginException;
UserSession loginTrusted(String login, String password, Locale locale, Map<String, Object> params)
throws LoginException;
/**
* Login using user name and remember me token
*
* @param login login name
* @param rememberMeToken client's remember me token
* @param locale client locale
* @return created user session
* @throws LoginException in case of unsuccessful login
*/
UserSession loginByRememberMe(String login, String rememberMeToken, Locale locale) throws LoginException;
/**
* Login using user name and remember me token
*
* @param login login name
* @param rememberMeToken client's remember me token
* @param locale client locale
* @param params login params
* @return created user session
* @throws LoginException in case of unsuccessful login
*/
UserSession loginByRememberMe(String login, String rememberMeToken, Locale locale, Map<String, Object> params) throws LoginException;
/**
* Log out and destroy an active user session.
@ -79,7 +103,7 @@ public interface LoginService {
* <p/>
* This method replaces an active UserSession with the new one, which is returned.
*
* @param substitutedUser a user to substitute. Must be in the current users's {@link User#substitutions} list.
* @param substitutedUser a user to substitute. Must be in the current users' {@link User#substitutions} list.
* @return new UserSession instance that contains: <ul>
* <li> id - the previously active user session id </li>
* <li> user - the logged in user </li>
@ -97,4 +121,13 @@ public interface LoginService {
*/
@Nullable
UserSession getSession(UUID sessionId);
/**
* Check if remember me token exists in db
*
* @param login user login
* @param rememberMeToken remember me token
* @return true if remember me token exists in db
*/
boolean checkRememberMe(String login, String rememberMeToken);
}

View File

@ -65,11 +65,21 @@ public interface UserManagementService {
boolean checkPassword(UUID userId, String passwordHash);
/**
* @param userId User id
* @param newPasswordHash Plain hash of new password
* @return True if the new and old passwords are equal
* @deprecated Use ${@link com.haulmont.cuba.security.app.UserManagementService#checkPassword(java.util.UUID, String)}
* Remove remember me tokens for users
*
* @param userIds User ids
*/
@Deprecated
boolean checkEqualsOfNewAndOldPassword(UUID userId, String newPasswordHash);
void resetRememberMeTokens(List<UUID> userIds);
/**
* Remove remember me tokens for all users
*/
void resetRememberMeTokens();
/**
* Generate and store to DB {@link com.haulmont.cuba.security.entity.RememberMeToken}
*
* @return token string
*/
String generateRememberMeToken(UUID userId);
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2008-2014 Haulmont. All rights reserved.
* Use is subject to license terms, see http://www.cuba-platform.com/license for details.
*/
package com.haulmont.cuba.security.entity;
import com.haulmont.cuba.core.entity.BaseUuidEntity;
import com.haulmont.cuba.core.entity.annotation.SystemLevel;
import javax.persistence.*;
/**
* @author artamonov
* @version $Id$
*/
@Entity(name = "sec$RememberMeToken")
@Table(name = "SEC_REMEMBER_ME")
@SystemLevel
public class RememberMeToken extends BaseUuidEntity {
private static final long serialVersionUID = -3757776319150532739L;
public static final int TOKEN_LENGTH = 32;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "USER_ID", nullable = false)
protected User user;
@Column(name = "TOKEN", nullable = false, length = TOKEN_LENGTH)
protected String token;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}

View File

@ -52,6 +52,7 @@
<class>com.haulmont.cuba.core.entity.CategoryAttributeValue</class>
<class>com.haulmont.cuba.core.entity.JmxInstance</class>
<class>com.haulmont.cuba.security.entity.RememberMeToken</class>
<properties>
<property name="openjpa.Log" value="log4j"/>

View File

@ -12,10 +12,7 @@ import com.haulmont.cuba.gui.WindowManager;
import com.haulmont.cuba.gui.WindowParams;
import com.haulmont.cuba.gui.app.security.user.edit.UserEditor;
import com.haulmont.cuba.gui.app.security.user.resetpasswords.ResetPasswordsDialog;
import com.haulmont.cuba.gui.components.AbstractLookup;
import com.haulmont.cuba.gui.components.Action;
import com.haulmont.cuba.gui.components.Table;
import com.haulmont.cuba.gui.components.Window;
import com.haulmont.cuba.gui.components.*;
import com.haulmont.cuba.gui.components.actions.RemoveAction;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.data.DataSupplier;
@ -58,6 +55,12 @@ public class UserBrowser extends AbstractLookup {
@Named("usersTable.changePasswAtLogon")
protected Action changePasswAtLogonAction;
@Named("usersTable.resetRememberMe")
protected Action resetRememberMeAction;
@Inject
protected PopupButton additionalActionsBtn;
@Inject
protected UserSession userSession;
@ -116,6 +119,11 @@ public class UserBrowser extends AbstractLookup {
}
});
additionalActionsBtn.addAction(copySettingsAction);
additionalActionsBtn.addAction(changePasswAction);
additionalActionsBtn.addAction(changePasswAtLogonAction);
additionalActionsBtn.addAction(resetRememberMeAction);
if (WindowParams.MULTI_SELECT.getBool(getContext())) {
usersTable.setMultiSelect(true);
}
@ -136,8 +144,9 @@ public class UserBrowser extends AbstractLookup {
List<UserRole> userRoles = new ArrayList<>();
for (UserRole oldUserRole : selectedUser.getUserRoles()) {
Role oldRole = dataSupplier.reload(oldUserRole.getRole(), "_local");
if (BooleanUtils.isTrue(oldRole.getDefaultRole()))
if (BooleanUtils.isTrue(oldRole.getDefaultRole())) {
continue;
}
UserRole role = new UserRole();
role.setUser(newUser);
role.setRole(oldRole);
@ -163,6 +172,7 @@ public class UserBrowser extends AbstractLookup {
public void copySettings() {
Set<User> selected = usersTable.getSelected();
if (!selected.isEmpty()) {
@SuppressWarnings("unchecked")
Window copySettingsWindow = openWindow(
"sec$User.copySettings",
WindowManager.OpenType.DIALOG,
@ -208,7 +218,7 @@ public class UserBrowser extends AbstractLookup {
boolean sendEmails = resetPasswordsDialog.getSendEmails();
boolean generatePasswords = resetPasswordsDialog.getGeneratePasswords();
Set<User> users = usersTable.getSelected();
resetPasswordsForUsers(users, sendEmails, generatePasswords);
resetPasswords(users, sendEmails, generatePasswords);
}
usersTable.requestFocus();
}
@ -216,7 +226,7 @@ public class UserBrowser extends AbstractLookup {
}
}
private void resetPasswordsForUsers(Set<User> users, boolean sendEmails, boolean generatePasswords) {
protected void resetPasswords(Set<User> users, boolean sendEmails, boolean generatePasswords) {
List<UUID> usersForModify = new ArrayList<>();
for (User user : users) {
usersForModify.add(user.getId());
@ -251,4 +261,63 @@ public class UserBrowser extends AbstractLookup {
usersDs.refresh();
}
}
public void resetRememberMe() {
if (usersTable.getSelected().isEmpty()) {
showOptionDialog(getMessage("resetRememberMeTitle"), getMessage("resetRememberMeQuestion"), MessageType.CONFIRMATION,
new Action[]{
new AbstractAction("actions.ResetAll") {
@Override
public void actionPerform(Component component) {
resetRememberMeAll();
}
},
new AbstractAction("actions.Cancel") {
@Override
public void actionPerform(Component component) {
}
}
}
);
} else {
showOptionDialog(getMessage("resetRememberMeTitle"), getMessage("resetRememberMeQuestion"), MessageType.CONFIRMATION,
new Action[]{
new AbstractAction("actions.ResetSelected") {
@Override
public void actionPerform(Component component) {
resetRememberMe(usersTable.<User>getSelected());
}
},
new AbstractAction("actions.ResetAll") {
@Override
public void actionPerform(Component component) {
resetRememberMeAll();
}
},
new AbstractAction("actions.Cancel") {
@Override
public void actionPerform(Component component) {
}
}
}
);
}
}
public void resetRememberMe(Set<User> users) {
List<UUID> usersForModify = new ArrayList<>();
for (User user : users) {
usersForModify.add(user.getId());
}
userManagementService.resetRememberMeTokens(usersForModify);
showNotification(getMessage("resetRememberMeCompleted"), NotificationType.HUMANIZED);
}
public void resetRememberMeAll() {
userManagementService.resetRememberMeTokens();
showNotification(getMessage("resetRememberMeCompleted"), NotificationType.HUMANIZED);
}
}

View File

@ -26,4 +26,13 @@ filter.customSuperRole=Has super role
filter.Language=Language
Language=Language
copySettings=Copy settings
copySettings=Copy settings
additional=Additional
resetRememberMe=Reset 'remember me' tokens
resetRememberMeTitle=Reset 'remember me' tokens for users
resetRememberMeQuestion=Reset tokens for all users?
resetRememberMeCompleted='Remember me' tokens have been removed
actions.Reset=Reset
actions.ResetAll=Reset for all
actions.ResetSelected=Reset for selected

View File

@ -22,4 +22,13 @@ Language=Язык
copySettings=Копировать настройки
changePasswAtLogon=Сброс паролей
resetPasswordCompleted=Сброс пароля запланирован для %s пользователей
changePasswordAtLogonCompleted=Применено к %s пользователям
changePasswordAtLogonCompleted=Применено к %s пользователям
additional=Дополнительно
resetRememberMe=Сброс токенов 'Запомнить меня'
resetRememberMeTitle=Сбросить токены 'Запомнить меня'
resetRememberMeQuestion=Сбросить токены 'Запомнить меня' для всех пользователей?
resetRememberMeCompleted=Токены 'Запомнить меня' удалены
actions.Reset=Сбросить
actions.ResetAll=Сбросить для всех
actions.ResetSelected=Сбросить для выбранных

View File

@ -41,12 +41,10 @@
<action id="edit"/>
<action id="copy" caption="msg://copy" icon="icons/copy.png" invoke="copy" enable="false"/>
<action id="remove"/>
<action id="copySettings" caption="msg://copySettings" icon="icons/copy.png"
invoke="copySettings"/>
<action id="changePassw" caption="msg://changePassw" icon="icons/change-pass.png"
invoke="changePassword"/>
<action id="changePasswAtLogon" caption="msg://changePasswAtLogon" icon="icons/change-pass-at-logon.png"
invoke="changePasswordAtLogon"/>
<action id="copySettings" caption="msg://copySettings" invoke="copySettings"/>
<action id="changePassw" caption="msg://changePassw" invoke="changePassword"/>
<action id="changePasswAtLogon" caption="msg://changePasswAtLogon" invoke="changePasswordAtLogon"/>
<action id="resetRememberMe" caption="msg://resetRememberMe" invoke="resetRememberMe"/>
<action id="excel"/>
</actions>
<buttonsPanel>
@ -54,9 +52,7 @@
<button action="usersTable.edit"/>
<button id="userTableCopyButton" action="usersTable.copy"/>
<button action="usersTable.remove"/>
<button action="usersTable.copySettings"/>
<button id="changePasswordButton" action="usersTable.changePassw"/>
<button action="usersTable.changePasswAtLogon"/>
<popupButton id="additionalActionsBtn" caption="msg://additional" icon="icons/gear.png"/>
<button action="usersTable.excel"/>
</buttonsPanel>
<rowsCount/>

View File

@ -15,6 +15,7 @@ import org.apache.commons.lang.ObjectUtils;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;
@ -128,6 +129,9 @@ public class UserChangePassw extends AbstractEditor {
@Override
protected boolean postCommit(boolean committed, boolean close) {
if (committed) {
UUID userId = userDs.getItem().getId();
userManagementService.resetRememberMeTokens(Collections.singletonList(userId));
showNotification(getMessage("passwordChanged"), NotificationType.HUMANIZED);
}

View File

@ -24,9 +24,22 @@ public class CASProtectedConnection extends AbstractConnection {
update(loginService.login(login, password, locale));
}
@Override
public void loginByRememberMe(String login, String rememberMeToken, Locale locale) throws LoginException {
if (locale == null)
throw new IllegalArgumentException("Locale is null");
update(loginService.loginByRememberMe(login, rememberMeToken, locale));
}
@Override
public String logout() {
super.logout();
return "logout";
}
}
@Override
public boolean checkRememberMe(String login, String rememberMeToken) {
return loginService.checkRememberMe(login, rememberMeToken);
}
}

View File

@ -24,10 +24,19 @@ public interface Connection {
* @param login user login name
* @param password encrypted user password
* @param locale user locale
* @throws LoginException in case of unsuccesful login due to wrong credentials or other issues
* @throws LoginException in case of unsuccessful login due to wrong credentials or other issues
*/
void login(String login, String password, Locale locale) throws LoginException;
/**
* Log in to the system.
* @param login user login name
* @param rememberMeToken remember me token
* @param locale user locale
* @throws LoginException in case of unsuccessful login due to wrong credentials or other issues
*/
void loginByRememberMe(String login, String rememberMeToken, Locale locale) throws LoginException;
/**
* Log out of the system.
* Returns URL to which the user will be redirected after logout.
@ -50,6 +59,15 @@ public interface Connection {
*/
boolean isConnected();
/**
* Check if remember me token exists in db
*
* @param login user login
* @param rememberMeToken remember me token
* @return true if remember me token exists in db
*/
boolean checkRememberMe(String login, String rememberMeToken);
/**
* Get current user session.
* @return user session object or null if not connected
@ -60,7 +78,7 @@ public interface Connection {
/**
* Update internal state with the passed user session object. Also fires connection listeners.
* @param session new UserSession object
* @throws LoginException in case of unsuccesful update
* @throws LoginException in case of unsuccessful update
*/
void update(UserSession session) throws LoginException;
@ -87,4 +105,4 @@ public interface Connection {
* @param listener listener to remove
*/
void removeListener(UserSubstitutionListener listener);
}
}

View File

@ -26,16 +26,27 @@ public class DefaultConnection extends AbstractConnection implements ActiveDirec
@Override
public void login(String login, String password, Locale locale) throws LoginException {
if (locale == null)
if (locale == null) {
throw new IllegalArgumentException("Locale is null");
}
update(loginService.login(login, password, locale));
}
@Override
public void loginActiveDirectory(String login, Locale locale) throws LoginException {
if (locale == null)
public void loginByRememberMe(String login, String rememberMeToken, Locale locale) throws LoginException {
if (locale == null) {
throw new IllegalArgumentException("Locale is null");
}
update(loginService.loginByRememberMe(login, rememberMeToken, locale));
}
@Override
public void loginActiveDirectory(String login, Locale locale) throws LoginException {
if (locale == null) {
throw new IllegalArgumentException("Locale is null");
}
String password = configuration.getConfig(WebAuthConfig.class).getTrustedClientPassword();
update(loginService.loginTrusted(login, password, locale));
@ -44,6 +55,11 @@ public class DefaultConnection extends AbstractConnection implements ActiveDirec
@Override
public String logout() {
super.logout();
return ActiveDirectoryHelper.useActiveDirectory()? "login" : "";
return ActiveDirectoryHelper.useActiveDirectory() ? "login" : "";
}
@Override
public boolean checkRememberMe(String login, String rememberMeToken) {
return loginService.checkRememberMe(login, rememberMeToken);
}
}

View File

@ -8,9 +8,13 @@ import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.gui.AppConfig;
import com.haulmont.cuba.gui.ComponentsHelper;
import com.haulmont.cuba.gui.TestIdManager;
import com.haulmont.cuba.security.app.UserManagementService;
import com.haulmont.cuba.security.entity.User;
import com.haulmont.cuba.security.global.LoginException;
import com.haulmont.cuba.security.global.UserSession;
import com.haulmont.cuba.web.auth.ActiveDirectoryConnection;
import com.haulmont.cuba.web.auth.ActiveDirectoryHelper;
import com.haulmont.cuba.web.auth.CubaAuthProvider;
import com.haulmont.cuba.web.auth.DomainAliasesResolver;
import com.haulmont.cuba.web.sys.Browser;
import com.haulmont.cuba.web.toolkit.VersionedThemeResource;
@ -25,16 +29,12 @@ import com.vaadin.server.VaadinSession;
import com.vaadin.server.WebBrowser;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.ui.*;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.annotation.Nullable;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
@ -59,12 +59,6 @@ public class LoginWindow extends UIView implements Action.Handler {
private static final char[] DOMAIN_SEPARATORS = new char[]{'\\', '@'};
/**
* This key is used to encrypt password in cookie to support "remember me" in AD auth.
* Must be of 8 symbols.
*/
private static final String PASSWORD_KEY = "25tuThUw";
protected Connection connection;
protected final AppUI ui;
@ -95,6 +89,8 @@ public class LoginWindow extends UIView implements Action.Handler {
protected Configuration configuration;
protected PasswordEncryption passwordEncryption;
protected UserManagementService userManagementService;
public LoginWindow(AppUI ui) {
log.trace("Creating " + this);
this.ui = ui;
@ -102,6 +98,7 @@ public class LoginWindow extends UIView implements Action.Handler {
configuration = AppBeans.get(Configuration.NAME);
messages = AppBeans.get(Messages.NAME);
passwordEncryption = AppBeans.get(PasswordEncryption.NAME);
userManagementService = AppBeans.get(UserManagementService.NAME);
globalConfig = configuration.getConfig(GlobalConfig.class);
webConfig = configuration.getConfig(WebConfig.class);
@ -300,11 +297,8 @@ public class LoginWindow extends UIView implements Action.Handler {
}
protected void initRememberMe() {
App app = App.getInstance();
String rememberMeCookie = app.getCookieValue(COOKIE_REMEMBER_ME);
if (Boolean.parseBoolean(rememberMeCookie)) {
rememberMe.setValue(true);
String login;
String encodedLogin = app.getCookieValue(COOKIE_LOGIN) != null ? app.getCookieValue(COOKIE_LOGIN) : "";
try {
@ -313,9 +307,14 @@ public class LoginWindow extends UIView implements Action.Handler {
login = encodedLogin;
}
loginField.setValue(login);
passwordField.setValue(app.getCookieValue(COOKIE_PASSWORD) != null ? app.getCookieValue(COOKIE_PASSWORD) : "");
loginByRememberMe = true;
String rememberMeToken = app.getCookieValue(COOKIE_PASSWORD) != null ? app.getCookieValue(COOKIE_PASSWORD) : "";
if (connection.checkRememberMe(login, rememberMeToken)) {
rememberMe.setValue(true);
loginField.setValue(login);
passwordField.setValue(rememberMeToken);
loginByRememberMe = true;
}
loginChangeListener = new Property.ValueChangeListener() {
@Override
@ -335,37 +334,42 @@ public class LoginWindow extends UIView implements Action.Handler {
protected void initFields() {
String currLocale = messages.getTools().localeToString(resolvedLocale);
String selected = null;
App app = App.getInstance();
for (Map.Entry<String, Locale> entry : locales.entrySet()) {
localesSelect.addItem(entry.getKey());
if (messages.getTools().localeToString(entry.getValue()).equals(currLocale))
if (messages.getTools().localeToString(entry.getValue()).equals(currLocale)) {
selected = entry.getKey();
}
}
if (selected == null)
if (selected == null) {
selected = locales.keySet().iterator().next();
}
localesSelect.setValue(selected);
if (ActiveDirectoryHelper.useActiveDirectory()) {
loginField.setValue(app.getPrincipal() == null ? "" : app.getPrincipal().getName());
passwordField.setValue("");
if (rememberMeAllowed && !ActiveDirectoryHelper.activeDirectorySupportedBySession())
if (rememberMeAllowed && !ActiveDirectoryHelper.activeDirectorySupportedBySession()) {
initRememberMe();
}
} else {
String defaultUser = webConfig.getLoginDialogDefaultUser();
if (!StringUtils.isBlank(defaultUser) && !"<disabled>".equals(defaultUser))
if (!StringUtils.isBlank(defaultUser) && !"<disabled>".equals(defaultUser)) {
loginField.setValue(defaultUser);
else
} else {
loginField.setValue("");
}
String defaultPassw = webConfig.getLoginDialogDefaultPassword();
if (!StringUtils.isBlank(defaultPassw) && !"<disabled>".equals(defaultPassw))
if (!StringUtils.isBlank(defaultPassw) && !"<disabled>".equals(defaultPassw)) {
passwordField.setValue(defaultPassw);
else
} else {
passwordField.setValue("");
}
if (rememberMeAllowed)
if (rememberMeAllowed) {
initRememberMe();
}
}
}
@ -399,27 +403,21 @@ public class LoginWindow extends UIView implements Action.Handler {
protected void login() {
String login = loginField.getValue();
try {
// Login with AD if domain specified
if (ActiveDirectoryHelper.useActiveDirectory() && StringUtils.containsAny(login, DOMAIN_SEPARATORS)) {
Locale locale = getUserLocale();
App.getInstance().setLocale(locale);
Locale locale = getUserLocale();
app.setLocale(locale);
String password = passwordField.getValue();
if (loginByRememberMe && StringUtils.isNotEmpty(password))
password = decryptPassword(password);
String passwordValue = passwordField.getValue() != null ? passwordField.getValue() : "";
ActiveDirectoryHelper.getAuthProvider().authenticate(login, password, resolvedLocale);
if (loginByRememberMe && rememberMeAllowed) {
loginByRememberMe(login, passwordValue, locale);
} else if (ActiveDirectoryHelper.useActiveDirectory() && StringUtils.containsAny(login, DOMAIN_SEPARATORS)) {
CubaAuthProvider authProvider = ActiveDirectoryHelper.getAuthProvider();
authProvider.authenticate(login, passwordValue, resolvedLocale);
login = convertLoginString(login);
((ActiveDirectoryConnection) connection).loginActiveDirectory(login, locale);
} else {
Locale locale = getUserLocale();
App.getInstance().setLocale(locale);
String value = passwordField.getValue() != null ? passwordField.getValue() : "";
String passwd = loginByRememberMe ? value : passwordEncryption.getPlainHash(value);
login(login, passwd, locale);
login(login, passwordEncryption.getPlainHash(passwordValue), locale);
}
} catch (LoginException e) {
log.info("Login failed: " + e.toString());
@ -428,7 +426,7 @@ public class LoginWindow extends UIView implements Action.Handler {
new Notification(
ComponentsHelper.preprocessHtmlMessage(message),
StringUtils.abbreviate(e.getMessage(), 1000), Notification.Type.ERROR_MESSAGE, true)
.show(getUI().getPage());
.show(ui.getPage());
if (loginByRememberMe) {
loginByRememberMe = false;
@ -464,90 +462,58 @@ public class LoginWindow extends UIView implements Action.Handler {
return login;
}
protected void login(String login, String passwd, Locale locale) throws LoginException {
connection.login(login, passwd, locale);
protected void login(String login, String password, Locale locale) throws LoginException {
connection.login(login, password, locale);
}
protected void loginByRememberMe(String login, String rememberMeToken, Locale locale) throws LoginException {
connection.loginByRememberMe(login, rememberMeToken, locale);
}
protected void doLogin() {
login();
if (rememberMeAllowed) {
App app = App.getInstance();
if (Boolean.TRUE.equals(rememberMe.getValue())) {
if (!loginByRememberMe) {
app.addCookie(COOKIE_REMEMBER_ME, String.valueOf(rememberMe.getValue()));
String login = loginField.getValue();
String password = passwordField.getValue() != null ? passwordField.getValue() : "";
if (connection.isConnected()) {
if (rememberMeAllowed) {
if (Boolean.TRUE.equals(rememberMe.getValue())) {
if (!loginByRememberMe) {
app.addCookie(COOKIE_REMEMBER_ME, Boolean.TRUE.toString());
String encodedLogin;
try {
encodedLogin = URLEncoder.encode(login, "UTF-8");
} catch (UnsupportedEncodingException e) {
encodedLogin = login;
}
app.addCookie(COOKIE_LOGIN, StringEscapeUtils.escapeJava(encodedLogin));
if (!ActiveDirectoryHelper.useActiveDirectory())
app.addCookie(COOKIE_PASSWORD, passwordEncryption.getPlainHash(password));
else {
if (StringUtils.isNotEmpty(password))
app.addCookie(COOKIE_PASSWORD, encryptPassword(password));
String login = loginField.getValue();
String encodedLogin;
try {
encodedLogin = URLEncoder.encode(login, "UTF-8");
} catch (UnsupportedEncodingException e) {
encodedLogin = login;
}
app.addCookie(COOKIE_LOGIN, StringEscapeUtils.escapeJava(encodedLogin));
UserSession session = connection.getSession();
if (session == null) {
throw new IllegalStateException("Unable to get session after login");
}
User user = session.getUser();
String rememberMeToken = userManagementService.generateRememberMeToken(user.getId());
app.addCookie(COOKIE_PASSWORD, rememberMeToken);
}
} else {
app.removeCookie(COOKIE_REMEMBER_ME);
app.removeCookie(COOKIE_LOGIN);
app.removeCookie(COOKIE_PASSWORD);
}
} else {
app.removeCookie(COOKIE_REMEMBER_ME);
app.removeCookie(COOKIE_LOGIN);
app.removeCookie(COOKIE_PASSWORD);
}
if (webConfig.getUseSessionFixationProtection()) {
VaadinService.reinitializeSession(VaadinService.getCurrentRequest());
VaadinSession.getCurrent().getSession().setMaxInactiveInterval(webConfig.getHttpSessionExpirationTimeoutSec());
}
}
if (webConfig.getUseSessionFixationProtection()) {
VaadinService.reinitializeSession(VaadinService.getCurrentRequest());
VaadinSession.getCurrent().getSession().setMaxInactiveInterval(webConfig.getHttpSessionExpirationTimeoutSec());
}
}
/**
* Encrypt password to store in cookie for "remember me". <br/>
* Used only for AD auth.
*
* @param password plain password
* @return encrypted password
*/
protected String encryptPassword(String password) {
SecretKeySpec key = new SecretKeySpec(PASSWORD_KEY.getBytes(), "DES");
IvParameterSpec ivSpec = new IvParameterSpec(PASSWORD_KEY.getBytes());
String result;
try {
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
result = new String(Hex.encodeHex(cipher.doFinal(password.getBytes())));
} catch (Exception e) {
throw new RuntimeException();
}
return result;
}
/**
* Decrypt the password stored in cookie. <br/>
* Used only for AD auth.
*
* @param password encrypted password
* @return plain password, or input string if decryption fails
*/
protected String decryptPassword(String password) {
SecretKeySpec key = new SecretKeySpec(PASSWORD_KEY.getBytes(), "DES");
IvParameterSpec ivSpec = new IvParameterSpec(PASSWORD_KEY.getBytes());
String result;
try {
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
result = new String(cipher.doFinal(Hex.decodeHex(password.toCharArray())));
} catch (Exception e) {
return password;
}
return result;
}
protected Locale getUserLocale() {

Binary file not shown.

After

Width:  |  Height:  |  Size: 736 B

View File

@ -24,9 +24,22 @@ public class CASProtectedConnection extends AbstractConnection {
update(loginService.login(login, password, locale));
}
@Override
public void loginByRememberMe(String login, String rememberMeToken, Locale locale) throws LoginException {
if (locale == null)
throw new IllegalArgumentException("Locale is null");
update(loginService.loginByRememberMe(login, rememberMeToken, locale));
}
@Override
public String logout() {
super.logout();
return "logout";
}
}
@Override
public boolean checkRememberMe(String login, String rememberMeToken) {
return loginService.checkRememberMe(login, rememberMeToken);
}
}

View File

@ -14,9 +14,8 @@ import java.util.Locale;
/**
* Interface to be implemented by middleware connection objects on web-client.
*
* <p>$Id$</p>
*
* @author krivopustov
* @version $Id$
*/
public interface Connection {
@ -25,10 +24,19 @@ public interface Connection {
* @param login user login name
* @param password encrypted user password
* @param locale user locale
* @throws LoginException in case of unsuccesful login due to wrong credentials or other issues
* @throws LoginException in case of unsuccessful login due to wrong credentials or other issues
*/
void login(String login, String password, Locale locale) throws LoginException;
/**
* Log in to the system.
* @param login user login name
* @param rememberMeToken remember me token
* @param locale user locale
* @throws LoginException in case of unsuccessful login due to wrong credentials or other issues
*/
void loginByRememberMe(String login, String rememberMeToken, Locale locale) throws LoginException;
/**
* Log out of the system.
* Returns URL to which the user will be redirected after logout.
@ -51,6 +59,15 @@ public interface Connection {
*/
boolean isConnected();
/**
* Check if remember me token exists in db
*
* @param login user login
* @param rememberMeToken remember me token
* @return true if remember me token exists in db
*/
boolean checkRememberMe(String login, String rememberMeToken);
/**
* Get current user session.
* @return user session object or null if not connected
@ -61,7 +78,7 @@ public interface Connection {
/**
* Update internal state with the passed user session object. Also fires connection listeners.
* @param session new UserSession object
* @throws LoginException in case of unsuccesful update
* @throws LoginException in case of unsuccessful update
*/
void update(UserSession session) throws LoginException;
@ -88,4 +105,4 @@ public interface Connection {
* @param listener listener to remove
*/
void removeListener(UserSubstitutionListener listener);
}
}

View File

@ -32,6 +32,15 @@ public class DefaultConnection extends AbstractConnection implements ActiveDirec
update(loginService.login(login, password, locale));
}
@Override
public void loginByRememberMe(String login, String rememberMeToken, Locale locale) throws LoginException {
if (locale == null) {
throw new IllegalArgumentException("Locale is null");
}
update(loginService.loginByRememberMe(login, rememberMeToken, locale));
}
@Override
public void loginActiveDirectory(String login, Locale locale) throws LoginException {
if (locale == null)
@ -46,4 +55,9 @@ public class DefaultConnection extends AbstractConnection implements ActiveDirec
super.logout();
return ActiveDirectoryHelper.useActiveDirectory()? "login" : "";
}
@Override
public boolean checkRememberMe(String login, String rememberMeToken) {
return loginService.checkRememberMe(login, rememberMeToken);
}
}

View File

@ -7,9 +7,13 @@ package com.haulmont.cuba.web;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.gui.AppConfig;
import com.haulmont.cuba.gui.ComponentsHelper;
import com.haulmont.cuba.security.app.UserManagementService;
import com.haulmont.cuba.security.entity.User;
import com.haulmont.cuba.security.global.LoginException;
import com.haulmont.cuba.security.global.UserSession;
import com.haulmont.cuba.web.auth.ActiveDirectoryConnection;
import com.haulmont.cuba.web.auth.ActiveDirectoryHelper;
import com.haulmont.cuba.web.auth.CubaAuthProvider;
import com.haulmont.cuba.web.auth.DomainAliasesResolver;
import com.haulmont.cuba.web.sys.Browser;
import com.haulmont.cuba.web.sys.CubaApplicationContext;
@ -20,16 +24,12 @@ import com.vaadin.event.ShortcutAction;
import com.vaadin.terminal.gwt.server.WebApplicationContext;
import com.vaadin.terminal.gwt.server.WebBrowser;
import com.vaadin.ui.*;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.annotation.Nullable;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
@ -56,12 +56,6 @@ public class LoginWindow extends Window implements Action.Handler {
private static final char[] DOMAIN_SEPARATORS = new char[]{'\\', '@'};
/**
* This key is used to encrypt password in cookie to support "remember me" in AD auth.
* Must be of 8 symbols.
*/
private static final String PASSWORD_KEY = "25tuThUw";
protected Connection connection;
protected TextField loginField;
@ -84,12 +78,15 @@ public class LoginWindow extends Window implements Action.Handler {
protected Configuration configuration;
protected PasswordEncryption passwordEncryption;
protected UserManagementService userManagementService;
public LoginWindow(App app, Connection connection) {
log.trace("Creating " + this);
configuration = AppBeans.get(Configuration.NAME);
messages = AppBeans.get(Messages.NAME);
passwordEncryption = AppBeans.get(PasswordEncryption.NAME);
userManagementService = AppBeans.get(UserManagementService.NAME);
globalConfig = configuration.getConfig(GlobalConfig.class);
webConfig = configuration.getConfig(WebConfig.class);
@ -285,8 +282,6 @@ public class LoginWindow extends Window implements Action.Handler {
String rememberMeCookie = app.getCookieValue(COOKIE_REMEMBER_ME);
if (Boolean.parseBoolean(rememberMeCookie)) {
rememberMe.setValue(true);
String login;
String encodedLogin = app.getCookieValue(COOKIE_LOGIN) != null ? app.getCookieValue(COOKIE_LOGIN) : "";
try {
@ -295,9 +290,14 @@ public class LoginWindow extends Window implements Action.Handler {
login = encodedLogin;
}
loginField.setValue(login);
passwordField.setValue(app.getCookieValue(COOKIE_PASSWORD) != null ? app.getCookieValue(COOKIE_PASSWORD) : "");
loginByRememberMe = true;
String rememberMeToken = app.getCookieValue(COOKIE_PASSWORD) != null ? app.getCookieValue(COOKIE_PASSWORD) : "";
if (connection.checkRememberMe(login, rememberMeToken)) {
rememberMe.setValue(true);
loginField.setValue(login);
passwordField.setValue(rememberMeToken);
loginByRememberMe = true;
}
loginChangeListener = new Property.ValueChangeListener() {
@Override
@ -374,24 +374,21 @@ public class LoginWindow extends Window implements Action.Handler {
protected void login() {
String login = (String) loginField.getValue();
try {
// Login with AD if domain specified
if (ActiveDirectoryHelper.useActiveDirectory() && StringUtils.containsAny(login, DOMAIN_SEPARATORS)) {
Locale locale = getUserLocale();
App.getInstance().setLocale(locale);
String password = (String) passwordField.getValue();
if (loginByRememberMe && StringUtils.isNotEmpty(password))
password = decryptPassword(password);
Locale locale = getUserLocale();
App.getInstance().setLocale(locale);
ActiveDirectoryHelper.getAuthProvider().authenticate(login, password, resolvedLocale);
String passwordValue = passwordField.getValue() != null ? (String) passwordField.getValue() : "";
if (loginByRememberMe && rememberMe != null) {
loginByRememberMe(login, passwordValue, locale);
} else if (ActiveDirectoryHelper.useActiveDirectory() && StringUtils.containsAny(login, DOMAIN_SEPARATORS)) {
CubaAuthProvider authProvider = ActiveDirectoryHelper.getAuthProvider();
authProvider.authenticate(login, passwordValue, resolvedLocale);
login = convertLoginString(login);
((ActiveDirectoryConnection) connection).loginActiveDirectory(login, locale);
} else {
String value = passwordField.getValue() != null ? (String) passwordField.getValue() : "";
String passwd = loginByRememberMe ? value : passwordEncryption.getPlainHash(value);
Locale locale = getUserLocale();
App.getInstance().setLocale(locale);
login(login, passwd, locale);
login(login, passwordEncryption.getPlainHash(passwordValue), locale);
}
} catch (LoginException e) {
log.info("Login failed: " + e.toString());
@ -435,91 +432,60 @@ public class LoginWindow extends Window implements Action.Handler {
return login;
}
protected void login(String login, String passwd, Locale locale) throws LoginException {
connection.login(login, passwd, locale);
protected void login(String login, String password, Locale locale) throws LoginException {
connection.login(login, password, locale);
}
protected void loginByRememberMe(String login, String rememberMeToken, Locale locale) throws LoginException {
connection.loginByRememberMe(login, rememberMeToken, locale);
}
protected void doLogin() {
login();
if (rememberMe != null) {
App app = App.getInstance();
if (Boolean.TRUE.equals(rememberMe.getValue())) {
if (!loginByRememberMe) {
app.addCookie(COOKIE_REMEMBER_ME, String.valueOf(rememberMe));
String login = (String) loginField.getValue();
String password = passwordField.getValue() != null ? (String) passwordField.getValue() : "";
if (connection.isConnected()) {
if (rememberMe != null) {
App app = App.getInstance();
if (Boolean.TRUE.equals(rememberMe.getValue())) {
if (!loginByRememberMe) {
app.addCookie(COOKIE_REMEMBER_ME, Boolean.TRUE.toString());
String encodedLogin;
try {
encodedLogin = URLEncoder.encode(login, "UTF-8");
} catch (UnsupportedEncodingException e) {
encodedLogin = login;
}
app.addCookie(COOKIE_LOGIN, StringEscapeUtils.escapeJava(encodedLogin));
if (!ActiveDirectoryHelper.useActiveDirectory())
app.addCookie(COOKIE_PASSWORD, passwordEncryption.getPlainHash(password));
else {
if (StringUtils.isNotEmpty(password))
app.addCookie(COOKIE_PASSWORD, encryptPassword(password));
String login = (String) loginField.getValue();
String encodedLogin;
try {
encodedLogin = URLEncoder.encode(login, "UTF-8");
} catch (UnsupportedEncodingException e) {
encodedLogin = login;
}
app.addCookie(COOKIE_LOGIN, StringEscapeUtils.escapeJava(encodedLogin));
UserSession session = connection.getSession();
if (session == null) {
throw new IllegalStateException("Unable to get session after login");
}
User user = session.getUser();
String rememberMeToken = userManagementService.generateRememberMeToken(user.getId());
app.addCookie(COOKIE_PASSWORD, rememberMeToken);
}
} else {
app.removeCookie(COOKIE_REMEMBER_ME);
app.removeCookie(COOKIE_LOGIN);
app.removeCookie(COOKIE_PASSWORD);
}
} else {
app.removeCookie(COOKIE_REMEMBER_ME);
app.removeCookie(COOKIE_LOGIN);
app.removeCookie(COOKIE_PASSWORD);
}
if (webConfig.getUseSessionFixationProtection()) {
CubaApplicationContext context = (CubaApplicationContext) App.getInstance().getContext();
context.reinitializeSession();
context.getHttpSession().setMaxInactiveInterval(webConfig.getHttpSessionExpirationTimeoutSec());
}
}
if (webConfig.getUseSessionFixationProtection()) {
CubaApplicationContext context = (CubaApplicationContext) App.getInstance().getContext();
context.reinitializeSession();
context.getHttpSession().setMaxInactiveInterval(webConfig.getHttpSessionExpirationTimeoutSec());
}
}
/**
* Encrypt password to store in cookie for "remember me". <br/>
* Used only for AD auth.
*
* @param password plain password
* @return encrypted password
*/
protected String encryptPassword(String password) {
SecretKeySpec key = new SecretKeySpec(PASSWORD_KEY.getBytes(), "DES");
IvParameterSpec ivSpec = new IvParameterSpec(PASSWORD_KEY.getBytes());
String result;
try {
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
result = new String(Hex.encodeHex(cipher.doFinal(password.getBytes())));
} catch (Exception e) {
throw new RuntimeException();
}
return result;
}
/**
* Decrypt the password stored in cookie. <br/>
* Used only for AD auth.
*
* @param password encrypted password
* @return plain password, or input string if decryption fails
*/
protected String decryptPassword(String password) {
SecretKeySpec key = new SecretKeySpec(PASSWORD_KEY.getBytes(), "DES");
IvParameterSpec ivSpec = new IvParameterSpec(PASSWORD_KEY.getBytes());
String result;
try {
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
result = new String(cipher.doFinal(Hex.decodeHex(password.toCharArray())));
} catch (Exception e) {
return password;
}
return result;
}
protected Locale getUserLocale() {

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 B