PL-6104 Allow to login as anonymous

This commit is contained in:
Yuriy Artamonov 2016-07-24 19:03:40 +04:00
parent dbfcadbc28
commit c27f7a85a6
98 changed files with 2087 additions and 1744 deletions

View File

@ -19,6 +19,7 @@ package com.haulmont.cuba.client;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.security.app.UserSessionService;
import com.haulmont.cuba.security.global.ClientBasedSession;
import com.haulmont.cuba.security.global.UserSession;
import java.io.Serializable;
@ -28,13 +29,14 @@ import java.util.TimeZone;
/**
* Client-side extension of {@link UserSession}.
*
* <p>Sends updates of the user session properties to the middleware.</p>
*
* <p>Sends updates of the user session properties to the middleware if authenticated.</p>
*/
public class ClientUserSession extends UserSession {
public class ClientUserSession extends UserSession implements ClientBasedSession {
private static final long serialVersionUID = -5358664165808633540L;
protected volatile boolean authenticated = false; // indicates whether user passed authentication
public ClientUserSession(UserSession src) {
super(src);
}
@ -42,35 +44,63 @@ public class ClientUserSession extends UserSession {
@Override
public void setAttribute(String name, Serializable value) {
super.setAttribute(name, value);
UserSessionService uss = AppBeans.get(UserSessionService.NAME);
uss.setSessionAttribute(id, name, value);
if (authenticated) {
UserSessionService uss = AppBeans.get(UserSessionService.NAME);
uss.setSessionAttribute(id, name, value);
}
}
@Override
public void setLocale(Locale locale) {
super.setLocale(locale);
UserSessionService uss = AppBeans.get(UserSessionService.NAME);
uss.setSessionLocale(id, locale);
if (authenticated) {
UserSessionService uss = AppBeans.get(UserSessionService.NAME);
uss.setSessionLocale(id, locale);
}
}
@Override
public void setTimeZone(TimeZone timeZone) {
super.setTimeZone(timeZone);
UserSessionService uss = AppBeans.get(UserSessionService.NAME);
uss.setSessionTimeZone(id, timeZone);
if (authenticated) {
UserSessionService uss = AppBeans.get(UserSessionService.NAME);
uss.setSessionTimeZone(id, timeZone);
}
}
@Override
public void setAddress(String address) {
super.setAddress(address);
UserSessionService uss = AppBeans.get(UserSessionService.NAME);
uss.setSessionAddress(id, address);
if (authenticated) {
UserSessionService uss = AppBeans.get(UserSessionService.NAME);
uss.setSessionAddress(id, address);
}
}
@Override
public void setClientInfo(String clientInfo) {
super.setClientInfo(clientInfo);
UserSessionService uss = AppBeans.get(UserSessionService.NAME);
uss.setSessionClientInfo(id, clientInfo);
if (authenticated) {
UserSessionService uss = AppBeans.get(UserSessionService.NAME);
uss.setSessionClientInfo(id, clientInfo);
}
}
}
public boolean isAuthenticated() {
return authenticated;
}
public void setAuthenticated(boolean authenticated) {
this.authenticated = authenticated;
}
@Override
public boolean isLocaleRequestScoped() {
return !authenticated;
}
}

View File

@ -806,6 +806,10 @@ values ('60885987-1b61-4247-94c7-dff348347f93', current_timestamp, 0, 'admin', '
'cc2229d1b8a052423d9e1c9ef0113b850086586a',
'Administrator', '0fa2b1a5-1d68-4d69-9fbd-dff348347f93', true)^
insert into SEC_USER (ID, CREATE_TS, VERSION, LOGIN, LOGIN_LC, PASSWORD, NAME, GROUP_ID, ACTIVE)
values ('a405db59-e674-4f63-8afe-269dda788fe8', now(), 0, 'anonymous', 'anonymous', null,
'Anonymous', '0fa2b1a5-1d68-4d69-9fbd-dff348347f93', true)^
insert into SEC_ROLE (ID, CREATE_TS, VERSION, NAME, ROLE_TYPE)
values ('0c018061-b26f-4de2-a5be-dff348347f93', current_timestamp, 0, 'Administrators', 10)^

View File

@ -854,6 +854,10 @@ values ('60885987-1b61-4247-94c7-dff348347f93', current_timestamp, 0, 'admin', '
'cc2229d1b8a052423d9e1c9ef0113b850086586a',
'Administrator', '0fa2b1a5-1d68-4d69-9fbd-dff348347f93', 1)^
insert into SEC_USER (ID, CREATE_TS, VERSION, LOGIN, LOGIN_LC, PASSWORD, NAME, GROUP_ID, ACTIVE)
values ('a405db59-e674-4f63-8afe-269dda788fe8', current_timestamp, 0, 'anonymous', 'anonymous', null,
'Anonymous', '0fa2b1a5-1d68-4d69-9fbd-dff348347f93', 1)^
insert into SEC_ROLE (ID, CREATE_TS, VERSION, NAME, ROLE_TYPE)
values ('0c018061-b26f-4de2-a5be-dff348347f93', current_timestamp, 0, 'Administrators', 10)^

View File

@ -861,6 +861,10 @@ values ('608859871b61424794c7dff348347f93', current_timestamp, 0, 'admin', 'admi
'cc2229d1b8a052423d9e1c9ef0113b850086586a',
'Administrator', '0fa2b1a51d684d699fbddff348347f93', 1)^
insert into SEC_USER (ID, CREATE_TS, VERSION, LOGIN, LOGIN_LC, PASSWORD, NAME, GROUP_ID, ACTIVE)
values ('a405db59e6744f638afe269dda788fe8', now(), 0, 'anonymous', 'anonymous', null,
'Anonymous', '0fa2b1a51d684d699fbddff348347f93', true)^
insert into SEC_ROLE (ID, CREATE_TS, VERSION, NAME, ROLE_TYPE)
values ('0c018061b26f4de2a5bedff348347f93', current_timestamp, 0, 'Administrators', 10)^

View File

@ -740,6 +740,10 @@ values ('608859871b61424794c7dff348347f93', current_timestamp, 0, 'admin', 'admi
'cc2229d1b8a052423d9e1c9ef0113b850086586a',
'Administrator', '0fa2b1a51d684d699fbddff348347f93', 1)^
insert into SEC_USER (ID, CREATE_TS, VERSION, LOGIN, LOGIN_LC, PASSWORD, NAME, GROUP_ID, ACTIVE)
values ('a405db59e6744f638afe269dda788fe8', current_timestamp, 0, 'anonymous', 'anonymous', null,
'Anonymous', '0fa2b1a51d684d699fbddff348347f93', 1)^
insert into SEC_ROLE (ID, CREATE_TS, VERSION, NAME, ROLE_TYPE)
values ('0c018061b26f4de2a5bedff348347f93', current_timestamp, 0, 'Administrators', 10)^

View File

@ -833,6 +833,10 @@ values ('60885987-1b61-4247-94c7-dff348347f93', now(), 0, 'admin', 'admin',
'cc2229d1b8a052423d9e1c9ef0113b850086586a',
'Administrator', '0fa2b1a5-1d68-4d69-9fbd-dff348347f93', true)^
insert into SEC_USER (ID, CREATE_TS, VERSION, LOGIN, LOGIN_LC, PASSWORD, NAME, GROUP_ID, ACTIVE)
values ('a405db59-e674-4f63-8afe-269dda788fe8', now(), 0, 'anonymous', 'anonymous', null,
'Anonymous', '0fa2b1a5-1d68-4d69-9fbd-dff348347f93', true)^
insert into SEC_ROLE (ID, CREATE_TS, VERSION, NAME, ROLE_TYPE)
values ('0c018061-b26f-4de2-a5be-dff348347f93', now(), 0, 'Administrators', 10)^

View File

@ -0,0 +1,5 @@
-- add anonymous user
insert into SEC_USER (ID, CREATE_TS, VERSION, LOGIN, LOGIN_LC, PASSWORD, NAME, GROUP_ID, ACTIVE)
values ('a405db59-e674-4f63-8afe-269dda788fe8', now(), 0, 'anonymous', 'anonymous', null,
'Anonymous', '0fa2b1a5-1d68-4d69-9fbd-dff348347f93', true)^

View File

@ -0,0 +1,5 @@
-- add anonymous user
insert into SEC_USER (ID, CREATE_TS, VERSION, LOGIN, LOGIN_LC, PASSWORD, NAME, GROUP_ID, ACTIVE)
values ('a405db59-e674-4f63-8afe-269dda788fe8', current_timestamp, 0, 'anonymous', 'anonymous', null,
'Anonymous', '0fa2b1a5-1d68-4d69-9fbd-dff348347f93', 1)^

View File

@ -0,0 +1,5 @@
-- add anonymous user
insert into SEC_USER (ID, CREATE_TS, VERSION, LOGIN, LOGIN_LC, PASSWORD, NAME, GROUP_ID, ACTIVE)
values ('a405db59e6744f638afe269dda788fe8', now(), 0, 'anonymous', 'anonymous', null,
'Anonymous', '0fa2b1a51d684d699fbddff348347f93', true)^

View File

@ -0,0 +1,5 @@
-- add anonymous user
insert into SEC_USER (ID, CREATE_TS, VERSION, LOGIN, LOGIN_LC, PASSWORD, NAME, GROUP_ID, ACTIVE)
values ('a405db59e6744f638afe269dda788fe8', current_timestamp, 0, 'anonymous', 'anonymous', null,
'Anonymous', '0fa2b1a51d684d699fbddff348347f93', 1)^

View File

@ -0,0 +1,5 @@
-- add anonymous user
insert into SEC_USER (ID, CREATE_TS, VERSION, LOGIN, LOGIN_LC, PASSWORD, NAME, GROUP_ID, ACTIVE)
values ('a405db59-e674-4f63-8afe-269dda788fe8', now(), 0, 'anonymous', 'anonymous', null,
'Anonymous', '0fa2b1a5-1d68-4d69-9fbd-dff348347f93', true);

View File

@ -24,7 +24,6 @@ import com.haulmont.cuba.core.config.defaults.*;
/**
* Configuration parameters interface used by the CORE layer.
*
*/
@Source(type = SourceType.APP)
public interface ServerConfig extends Config {
@ -193,4 +192,9 @@ public interface ServerConfig extends Config {
@Source(type = SourceType.DATABASE)
@DefaultInt(60)
int getBruteForceBlockIntervalSec();
}
@Property("cuba.anonymousLogin")
@Source(type = SourceType.DATABASE)
@Default("anonymous")
String getAnonymousLogin();
}

View File

@ -19,8 +19,10 @@ package com.haulmont.cuba.core.sys.remoting;
import com.haulmont.cuba.core.app.ServerConfig;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.Configuration;
import com.haulmont.cuba.core.global.GlobalConfig;
import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.core.sys.SecurityContext;
import com.haulmont.cuba.core.sys.UserInvocationContext;
import com.haulmont.cuba.security.app.LoginService;
import com.haulmont.cuba.security.global.UserSession;
import com.haulmont.cuba.security.sys.UserSessionManager;
@ -31,39 +33,43 @@ import org.springframework.remoting.support.RemoteInvocation;
import org.springframework.remoting.support.RemoteInvocationExecutor;
import java.lang.reflect.InvocationTargetException;
import java.util.Locale;
import java.util.UUID;
/**
* Executes {@link CubaRemoteInvocation} on middleware, setting and clearing a {@link SecurityContext} in the request
* handling thread.
*
*/
public class CubaRemoteInvocationExecutor implements RemoteInvocationExecutor {
private Logger log = LoggerFactory.getLogger(CubaRemoteInvocationExecutor.class);
private final Logger log = LoggerFactory.getLogger(CubaRemoteInvocationExecutor.class);
private UserSessionManager userSessionManager;
private volatile ClusterInvocationSupport clusterInvocationSupport;
private Configuration configuration;
private GlobalConfig globalConfig;
public CubaRemoteInvocationExecutor() {
userSessionManager = AppBeans.get("cuba_UserSessionManager");
configuration = AppBeans.get(Configuration.NAME);
globalConfig = configuration.getConfig(GlobalConfig.class);
}
@Override
public Object invoke(RemoteInvocation invocation, Object targetObject)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
if (invocation instanceof CubaRemoteInvocation) {
UUID sessionId = ((CubaRemoteInvocation) invocation).getSessionId();
CubaRemoteInvocation cubaInvocation = (CubaRemoteInvocation) invocation;
UUID sessionId = cubaInvocation.getSessionId();
if (sessionId != null) {
UserSession session = userSessionManager.findSession(sessionId);
if (session == null) {
String sessionProviderUrl = configuration.getConfig(ServerConfig.class).getUserSessionProviderUrl();
if (StringUtils.isNotBlank(sessionProviderUrl)) {
log.debug("User session " + sessionId + " not found, trying to get it from " + sessionProviderUrl);
log.debug("User session {} not found, trying to get it from {}", sessionId, sessionProviderUrl);
try {
HttpServiceProxy proxyFactory = new HttpServiceProxy(
getClusterInvocationSupport(sessionProviderUrl));
@ -76,19 +82,27 @@ public class CubaRemoteInvocationExecutor implements RemoteInvocationExecutor {
if (userSession != null) {
userSessionManager.storeSession(userSession);
} else {
log.debug("User session " + sessionId + " not found on " + sessionProviderUrl);
log.debug("User session {} not found on {}", sessionId, sessionProviderUrl);
}
}
} catch (Exception e) {
log.error("Error getting user session from " + sessionProviderUrl, e);
log.error("Error getting user session from {}", sessionProviderUrl, e);
}
}
}
AppContext.setSecurityContext(new SecurityContext(sessionId));
}
if (cubaInvocation.getLocale() != null) {
Locale locale = Locale.forLanguageTag(cubaInvocation.getLocale());
if (globalConfig.getAvailableLocales().containsValue(locale)) {
UserInvocationContext.setRequestScopeLocale(sessionId, locale);
}
}
}
Object result = invocation.invoke(targetObject);
AppContext.setSecurityContext(null);
UserInvocationContext.clearRequestScopeLocale();
return result;
}

View File

@ -19,12 +19,14 @@ package com.haulmont.cuba.core.sys.remoting;
import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.core.sys.SecurityContext;
import com.haulmont.cuba.core.sys.UserInvocationContext;
import com.haulmont.cuba.core.sys.serialization.SerializationSupport;
import org.apache.commons.lang.ClassUtils;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Locale;
public class LocalServiceInvokerImpl implements LocalServiceInvoker {
@ -80,6 +82,11 @@ public class LocalServiceInvokerImpl implements LocalServiceInvoker {
else
AppContext.setSecurityContext(null);
if (invocation.getLocale() != null) {
Locale locale = Locale.forLanguageTag(invocation.getLocale());
UserInvocationContext.setRequestScopeLocale(invocation.getSessionId(), locale);
}
Method method = target.getClass().getMethod(invocation.getMethodName(), parameterTypes);
Object data = method.invoke(target, arguments);
@ -97,6 +104,7 @@ public class LocalServiceInvokerImpl implements LocalServiceInvoker {
} finally {
Thread.currentThread().setContextClassLoader(clientClassLoader);
AppContext.setSecurityContext(null);
UserInvocationContext.clearRequestScopeLocale();
}
}
}

View File

@ -94,6 +94,14 @@ public interface LoginWorker {
*/
UserSession loginSystem(String login) throws LoginException;
/**
* Login anonymous session for trusted clients
*
* @return anonymous user session that is not replicated in cluster
* @throws LoginException
*/
UserSession loginAnonymous() throws LoginException;
/**
* @see com.haulmont.cuba.security.app.LoginService#checkRememberMe(String, String)
*/

View File

@ -36,6 +36,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import java.util.*;
@ -144,8 +145,6 @@ public class LoginWorkerBean implements LoginWorker {
UserSession session = userSessionManager.createSession(user, userLocale, false);
checkPermissions(login, params, userLocale, session);
log.info("Logged in: " + session);
tx.commit();
userSessionManager.clearPermissionsOnUser(session);
@ -162,6 +161,8 @@ public class LoginWorkerBean implements LoginWorker {
userSessionManager.storeSession(session);
}
log.info("Logged in: {}", session);
return session;
} finally {
tx.end();
@ -179,17 +180,51 @@ public class LoginWorkerBean implements LoginWorker {
Transaction tx = persistence.createTransaction();
try {
User user = loadUser(login);
if (user == null)
if (user == null) {
throw new LoginException(getInvalidCredentialsMessage(login, locale));
}
UserSession session = userSessionManager.createSession(user, locale, true);
log.info("Logged in: " + session);
tx.commit();
userSessionManager.clearPermissionsOnUser(session);
userSessionManager.storeSession(session);
log.info("Logged in: {}", session);
return session;
} finally {
tx.end();
}
}
@Override
public UserSession loginAnonymous() throws LoginException {
GlobalConfig globalConfig = configuration.getConfig(GlobalConfig.class);
UUID anonymousSessionId = globalConfig.getAnonymousSessionId();
ServerConfig serverConfig = configuration.getConfig(ServerConfig.class);
String anonymousLogin = serverConfig.getAnonymousLogin();
Locale locale = messages.getTools().trimLocale(messages.getTools().getDefaultLocale());
Transaction tx = persistence.createTransaction();
try {
User user = loadUser(anonymousLogin);
if (user == null) {
throw new LoginException(getInvalidCredentialsMessage(anonymousLogin, locale));
}
UserSession session = userSessionManager.createSession(anonymousSessionId, user, locale, true);
session.setClientInfo("System anonymous session");
tx.commit();
userSessionManager.clearPermissionsOnUser(session);
userSessionManager.storeSession(session);
log.info("Logged in: {}", session);
return session;
} finally {
tx.end();
@ -239,8 +274,9 @@ public class LoginWorkerBean implements LoginWorker {
log.debug("Unable to check trusted client IP when obtaining system session");
}
if (!trustedLoginHandler.checkPassword(password))
if (!trustedLoginHandler.checkPassword(password)) {
throw new LoginException(getInvalidCredentialsMessage(login, locale));
}
Transaction tx = persistence.createTransaction();
try {
@ -258,7 +294,7 @@ public class LoginWorkerBean implements LoginWorker {
UserSession session = userSessionManager.createSession(user, userLocale, false);
checkPermissions(login, params, userLocale, session);
log.info("Logged in: " + session);
log.info("Logged in: {}", session);
tx.commit();
@ -300,13 +336,13 @@ public class LoginWorkerBean implements LoginWorker {
UserSession session = userSessionManager.createSession(user, userLocale, false);
checkPermissions(login, params, userLocale, session);
log.info("Logged in: " + session);
tx.commit();
userSessionManager.clearPermissionsOnUser(session);
userSessionManager.storeSession(session);
log.info("Logged in: {}", session);
return session;
} finally {
tx.end();
@ -318,9 +354,9 @@ public class LoginWorkerBean implements LoginWorker {
try {
UserSession session = userSessionSource.getUserSession();
userSessionManager.removeSession(session);
log.info("Logged out: " + session);
log.info("Logged out: {}", session);
} catch (SecurityException e) {
log.warn("Couldn't logout: " + e);
log.warn("Couldn't logout: {}", e);
} catch (NoUserSessionException e) {
log.warn("NoUserSessionException thrown on logout");
}
@ -408,15 +444,40 @@ public class LoginWorkerBean implements LoginWorker {
protected void checkPermissions(String login, Map<String, Object> params, Locale userLocale, UserSession session)
throws LoginException {
String clientTypeParam = (String) params.get(ClientType.class.getName());
if (ClientType.DESKTOP.name().equals(clientTypeParam) || ClientType.WEB.name().equals(clientTypeParam)) {
if (!session.isSpecificPermitted("cuba.gui.loginToClient")) {
log.warn(String.format("Attempt of login to %s for user '%s' without cuba.gui.loginToClient permission",
clientTypeParam, login));
log.warn("Attempt of login to {} for user '{}' without cuba.gui.loginToClient permission",
clientTypeParam, login);
throw new LoginException(getInvalidCredentialsMessage(login, userLocale));
}
}
}
@PostConstruct
public void init() {
AppContext.addListener(new AppContext.Listener() {
@Override
public void applicationStarted() {
initializeAnonymousSession();
}
@Override
public void applicationStopped() {
}
});
}
protected void initializeAnonymousSession() {
log.debug("Initialize anonymous session");
try {
loginAnonymous();
log.debug("Anonymous session initialized");
} catch (LoginException e) {
log.error("Unable to login anonymous session", e);
}
}
}

View File

@ -344,10 +344,12 @@ public class UserManagementServiceBean implements UserManagementService {
@Override
public void saveOwnLocale(String locale) {
log.debug("Saving user's language settings: " + locale);
UUID userId = userSessionSource.getUserSession().getUser().getId();
log.debug("Saving user's {} language settings: {}", userId, locale);
try (Transaction tx = persistence.createTransaction()) {
EntityManager em = persistence.getEntityManager();
User user = em.find(User.class, userSessionSource.getUserSession().getUser().getId(), "user.locale");
User user = em.find(User.class, userId, "user.locale");
if (user == null)
throw new EntityAccessException();

View File

@ -44,7 +44,6 @@ import java.util.UUID;
/**
* System-level class managing {@link UserSession}s.
*
*/
@Component(UserSessionManager.NAME)
public class UserSessionManager implements UserSessionFinder {
@ -79,13 +78,25 @@ public class UserSessionManager implements UserSessionFinder {
* @return new session instance
*/
public UserSession createSession(User user, Locale locale, boolean system) {
return createSession(uuidSource.createUuid(), user, locale, system);
}
/**
* Create a new session and fill it with security data. Must be called inside a transaction.
* @param sessionId target session id
* @param user user instance
* @param locale user locale
* @param system create system session
* @return new session instance
*/
public UserSession createSession(UUID sessionId, User user, Locale locale, boolean system) {
List<Role> roles = new ArrayList<>();
for (UserRole userRole : user.getUserRoles()) {
if (userRole.getRole() != null) {
roles.add(userRole.getRole());
}
}
UserSession session = new UserSession(uuidSource.createUuid(), user, roles, locale, system);
UserSession session = new UserSession(sessionId, user, roles, locale, system);
compilePermissions(session, roles);
if (user.getGroup() == null)
throw new IllegalStateException("User is not in a Group");
@ -179,8 +190,8 @@ public class UserSessionManager implements UserSessionFinder {
List<SessionAttribute> list = new ArrayList<>(group.getSessionAttributes());
EntityManager em = persistence.getEntityManager();
Query q = em.createQuery("select a from sec$GroupHierarchy h join h.parent.sessionAttributes a " +
"where h.group.id = ?1 order by h.level desc");
TypedQuery<SessionAttribute> q = em.createQuery("select a from sec$GroupHierarchy h join h.parent.sessionAttributes a " +
"where h.group.id = ?1 order by h.level desc", SessionAttribute.class);
q.setParameter(1, group);
List<SessionAttribute> attributes = q.getResultList();
list.addAll(attributes);

View File

@ -85,6 +85,8 @@ cuba.passwordPolicyEnabled=false
cuba.passwordPolicyRegExp=((?=.*\\d)(?=.*\\p{javaLowerCase})(?=.*\\p{javaUpperCase}).{6,20})
cuba.passwordEncryptionModule=cuba_Sha1EncryptionModule
cuba.anonymousSessionId=9c91dbdf-3e73-428e-9088-d586da2434c5
###############################################################################
# Presentation #
###############################################################################

View File

@ -60,7 +60,7 @@ public abstract class DesktopAbstractOptionsField<C extends JComponent>
protected CollectionDatasource<Entity<Object>, Object> optionsDatasource;
protected List optionsList;
protected Map<String, Object> optionsMap;
protected Map<String, ?> optionsMap;
protected Class<? extends EnumClass> optionsEnum;
protected Datasource datasource;
@ -104,12 +104,12 @@ public abstract class DesktopAbstractOptionsField<C extends JComponent>
}
@Override
public Map<String, Object> getOptionsMap() {
public Map<String, ?> getOptionsMap() {
return optionsMap;
}
@Override
public void setOptionsMap(Map<String, Object> map) {
public void setOptionsMap(Map<String, ?> map) {
optionsMap = map;
}
@ -262,7 +262,7 @@ public abstract class DesktopAbstractOptionsField<C extends JComponent>
Object selectedItem;
if (optionsMap != null) {
for (Map.Entry<String, Object> entry : optionsMap.entrySet()) {
for (Map.Entry<String, ?> entry : optionsMap.entrySet()) {
if (value.equals(entry.getValue())) {
setSelectedItem(new MapKeyWrapper(entry.getKey()));
return;

View File

@ -478,7 +478,7 @@ public class DesktopLookupField extends DesktopAbstractOptionsField<JComponent>
}
@Override
public void setOptionsMap(Map<String, Object> map) {
public void setOptionsMap(Map<String, ?> map) {
super.setOptionsMap(map);
if (optionsInitialized) {
optionsInitialized = false;

View File

@ -158,7 +158,7 @@ public class DesktopOptionsGroup extends DesktopAbstractOptionsField<JPanel> imp
}
@Override
public void setOptionsMap(Map<String, Object> map) {
public void setOptionsMap(Map<String, ?> map) {
if (optionsInitialized)
return;

View File

@ -501,7 +501,7 @@ public class DesktopSearchField extends DesktopAbstractOptionsField<JComponent>
}
@Override
public void setOptionsMap(Map<String, Object> map) {
public void setOptionsMap(Map<String, ?> map) {
throw new UnsupportedOperationException();
}

View File

@ -582,7 +582,7 @@ public class DesktopSuggestionField extends DesktopAbstractOptionsField<JCompone
}
@Override
public void setOptionsMap(Map<String, Object> map) {
public void setOptionsMap(Map<String, ?> map) {
throw new UnsupportedOperationException();
}

View File

@ -255,12 +255,12 @@ public class DesktopTokenList extends DesktopAbstractField<JPanel> implements To
}
@Override
public Map<String, Object> getOptionsMap() {
public Map<String, ?> getOptionsMap() {
return lookupPickerField.getOptionsMap();
}
@Override
public void setOptionsMap(Map<String, Object> map) {
public void setOptionsMap(Map<String, ?> map) {
lookupPickerField.setOptionsMap(map);
}

View File

@ -26,11 +26,13 @@ import com.haulmont.cuba.core.config.defaults.DefaultBoolean;
import com.haulmont.cuba.core.config.defaults.DefaultInt;
import com.haulmont.cuba.core.config.defaults.DefaultString;
import com.haulmont.cuba.core.config.type.Factory;
import com.haulmont.cuba.core.config.type.UuidTypeFactory;
import com.haulmont.cuba.core.sys.AvailableLocalesFactory;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
/**
* Configuration parameters interface used by all layers: CORE, WEB, DESKTOP.
@ -181,5 +183,8 @@ public interface GlobalConfig extends Config {
@Property("cuba.numberIdCacheSize")
@DefaultInt(100)
int getNumberIdCacheSize();
}
@Property("cuba.anonymousSessionId")
@Factory(factory = UuidTypeFactory.class)
UUID getAnonymousSessionId();
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2008-2016 Haulmont.
*
* 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 com.haulmont.cuba.core.sys;
import javax.annotation.Nullable;
import java.util.Locale;
import java.util.Objects;
import java.util.UUID;
/**
* Parameters of user invocation, may be passed by client tier
*/
public final class UserInvocationContext {
private static final ThreadLocal<RequestScopeLocale> userRequestScopeLocale = new ThreadLocal<>();
public static void setRequestScopeLocale(UUID sessionId, Locale locale) {
userRequestScopeLocale.set(new RequestScopeLocale(sessionId, locale));
}
@Nullable
public static Locale getRequestScopeLocale(UUID sessionId) {
RequestScopeLocale requestScopeLocale = userRequestScopeLocale.get();
if (requestScopeLocale != null && Objects.equals(sessionId, requestScopeLocale.getSessionId())) {
return requestScopeLocale.getLocale();
}
return null;
}
public static void clearRequestScopeLocale() {
userRequestScopeLocale.set(null);
}
protected static final class RequestScopeLocale {
private final UUID sessionId;
private final Locale locale;
public RequestScopeLocale(UUID sessionId, Locale locale) {
this.sessionId = sessionId;
this.locale = locale;
}
public UUID getSessionId() {
return sessionId;
}
public Locale getLocale() {
return locale;
}
}
}

View File

@ -32,7 +32,6 @@ import java.util.List;
/**
* HttpInvokerRequestExecutor that executes a request on a server which is selected according to the current cluster
* topology, provided by {@link ClusterInvocationSupport}.
*
*/
public class ClusteredHttpInvokerRequestExecutor extends SimpleHttpInvokerRequestExecutor {
@ -45,7 +44,8 @@ public class ClusteredHttpInvokerRequestExecutor extends SimpleHttpInvokerReques
}
@Override
protected RemoteInvocationResult doExecuteRequest(HttpInvokerClientConfiguration config, ByteArrayOutputStream baos) throws IOException, ClassNotFoundException {
protected RemoteInvocationResult doExecuteRequest(HttpInvokerClientConfiguration config, ByteArrayOutputStream baos)
throws IOException, ClassNotFoundException {
List<String> urlList = support.getUrlList(config.getServiceUrl());
if (urlList.isEmpty())
throw new IllegalStateException("URL list is empty");
@ -65,7 +65,8 @@ public class ClusteredHttpInvokerRequestExecutor extends SimpleHttpInvokerReques
result = readRemoteInvocationResult(responseBody, config.getCodebaseUrl());
break;
} catch (IOException e) {
logger.info("Invocation of " + url + " failed: " + e);
logger.info(String.format("Invocation of %s failed: %s", url, e));
if (i < urlList.size() - 1) {
logger.info("Trying to invoke the next available URL: " + urlList.get(i + 1));
continue;
@ -80,7 +81,7 @@ public class ClusteredHttpInvokerRequestExecutor extends SimpleHttpInvokerReques
protected HttpURLConnection openConnection(String serviceUrl) throws IOException {
URLConnection con = new URL(serviceUrl).openConnection();
if (!(con instanceof HttpURLConnection)) {
throw new IOException("Service URL [" + serviceUrl + "] is not an HTTP URL");
throw new IOException(String.format("Service URL [%s] is not an HTTP URL", serviceUrl));
}
return (HttpURLConnection) con;
}

View File

@ -23,21 +23,31 @@ import java.util.UUID;
/**
* Encapsulates a remote invocation of a middleware service.
* Additionally transfers the current user session identifier.
*
* Additionally transfers the current user session identifier and request scope locale (for anonymous sessions).
*/
public class CubaRemoteInvocation extends RemoteInvocation {
private static final long serialVersionUID = 5460262566597755733L;
private UUID sessionId;
private String locale;
public CubaRemoteInvocation(MethodInvocation methodInvocation, UUID sessionId) {
super(methodInvocation);
this.sessionId = sessionId;
}
public CubaRemoteInvocation(MethodInvocation methodInvocation, UUID sessionId, String locale) {
super(methodInvocation);
this.sessionId = sessionId;
this.locale = locale;
}
public UUID getSessionId() {
return sessionId;
}
}
public String getLocale() {
return locale;
}
}

View File

@ -18,6 +18,8 @@ package com.haulmont.cuba.core.sys.remoting;
import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.core.sys.SecurityContext;
import com.haulmont.cuba.security.global.ClientBasedSession;
import com.haulmont.cuba.security.global.UserSession;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.remoting.support.RemoteInvocation;
import org.springframework.remoting.support.RemoteInvocationFactory;
@ -27,6 +29,19 @@ public class CubaRemoteInvocationFactory implements RemoteInvocationFactory {
@Override
public RemoteInvocation createRemoteInvocation(MethodInvocation methodInvocation) {
SecurityContext securityContext = AppContext.getSecurityContext();
return new CubaRemoteInvocation(methodInvocation, securityContext == null ? null : securityContext.getSessionId());
String requestScopeLocale = null;
if (securityContext != null) {
UserSession session = securityContext.getSession();
if (session instanceof ClientBasedSession) {
if (((ClientBasedSession) session).isLocaleRequestScoped()) {
requestScopeLocale = session.getLocale() != null ? session.getLocale().toLanguageTag() : null;
}
}
}
return new CubaRemoteInvocation(methodInvocation,
securityContext == null ? null : securityContext.getSessionId(),
requestScopeLocale);
}
}

View File

@ -1,35 +1,24 @@
/*
* Copyright (c) 2008-2016 Haulmont.
*
* 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 com.haulmont.cuba.web;
import com.vaadin.ui.VerticalLayout;
/**
* Base class for application's UI content.
*
* @see LoginWindow
* @see AppWindow
*
*/
public abstract class UIView extends VerticalLayout {
public abstract String getTitle();
public void show() {
}
/*
* Copyright (c) 2008-2016 Haulmont.
*
* 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 com.haulmont.cuba.security.global;
/**
* Interface for client sessions that allow anonymous requests
*/
public interface ClientBasedSession {
boolean isLocaleRequestScoped();
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2008-2016 Haulmont.
*
* 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 com.haulmont.cuba.security.global;
import com.haulmont.cuba.core.global.Logging;
import com.haulmont.cuba.core.global.SupportedByClient;
@SupportedByClient
@Logging(Logging.Type.BRIEF)
public class LoginFailedException extends LoginException {
public LoginFailedException(String message) {
super(message);
}
public LoginFailedException(String template, Object... params) {
super(template, params);
}
}

View File

@ -18,6 +18,7 @@ package com.haulmont.cuba.security.global;
import com.google.common.collect.ArrayListMultimap;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.core.sys.UserInvocationContext;
import com.haulmont.cuba.security.entity.*;
import javax.annotation.Nullable;
@ -42,8 +43,8 @@ public class UserSession implements Serializable {
protected UUID id;
protected User user;
protected User substitutedUser;
private List<String> roles = new ArrayList<>();
private EnumSet<RoleType> roleTypes = EnumSet.noneOf(RoleType.class);
protected List<String> roles = new ArrayList<>();
protected EnumSet<RoleType> roleTypes = EnumSet.noneOf(RoleType.class);
protected Locale locale;
protected TimeZone timeZone;
protected String address;
@ -162,6 +163,11 @@ public class UserSession implements Serializable {
* User locale
*/
public Locale getLocale() {
Locale requestScopeLocale = UserInvocationContext.getRequestScopeLocale(id);
if (requestScopeLocale != null) {
return requestScopeLocale;
}
return locale;
}

View File

@ -95,6 +95,8 @@ public class UserBrowser extends AbstractLookup {
@Override
public void init(Map<String, Object> params) {
super.init(params);
MetaClass userMetaClass = metadata.getClassNN(User.class);
final boolean hasPermissionsToCreateUsers =

View File

@ -112,6 +112,8 @@ public class UserEditor extends AbstractEditor<User> {
@Override
public void init(Map<String, Object> params) {
super.init(params);
userDs.addItemPropertyChangeListener(new NameBuilderListener<>(userDs));
userDs.addItemPropertyChangeListener(e -> {
if ("timeZoneAuto".equals(e.getProperty())) {

View File

@ -525,14 +525,24 @@ public class AbstractFrame implements Frame, Frame.Wrapper, Component.Wrapper, C
this.styleName = styleName;
}
@SuppressWarnings("unchecked")
@Override
public <X> X unwrap(Class<X> internalComponentClass) {
return (X) getComponent();
if (getComponent() instanceof Component.Wrapper) {
return (X) ((Component.Wrapper) frame).getComponent();
}
return (X) frame;
}
@SuppressWarnings("unchecked")
@Override
public <X> X unwrapComposition(Class<X> internalCompositionClass) {
return (X) getComponent();
if (getComposition() instanceof Component.Wrapper) {
return (X) ((Component.Wrapper) frame).getComposition();
}
return (X) frame;
}
@Override

View File

@ -25,9 +25,8 @@ import javax.annotation.Nullable;
/**
* Base class for controller of application Main window
*
*/
public class AbstractMainWindow extends AbstractWindow implements Window.MainWindow {
public class AbstractMainWindow extends AbstractTopLevelWindow implements Window.MainWindow {
private AppWorkArea workArea;
private UserIndicator userIndicator;
@ -62,19 +61,4 @@ public class AbstractMainWindow extends AbstractWindow implements Window.MainWin
public void setFoldersPane(FoldersPane foldersPane) {
this.foldersPane = foldersPane;
}
@Override
public boolean close(String actionId) {
throw new UnsupportedOperationException("Close operation for Main window is unsupported");
}
@Override
public boolean close(String actionId, boolean force) {
throw new UnsupportedOperationException("Close operation for Main window is unsupported");
}
@Override
public void closeAndRun(String actionId, Runnable runnable) {
throw new UnsupportedOperationException("Close operation for Main window is unsupported");
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2008-2016 Haulmont.
*
* 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 com.haulmont.cuba.gui.components;
public class AbstractTopLevelWindow extends AbstractWindow implements Window.TopLevelWindow {
@Override
public boolean close(String actionId) {
throw new UnsupportedOperationException("Close operation for TopLevelWindow is unsupported");
}
@Override
public boolean close(String actionId, boolean force) {
throw new UnsupportedOperationException("Close operation for TopLevelWindow is unsupported");
}
@Override
public void closeAndRun(String actionId, Runnable runnable) {
throw new UnsupportedOperationException("Close operation for TopLevelWindow is unsupported");
}
}

View File

@ -41,8 +41,8 @@ public interface OptionsField extends Field {
List getOptionsList();
void setOptionsList(List optionsList);
Map<String, Object> getOptionsMap();
void setOptionsMap(Map<String, Object> map);
Map<String, ?> getOptionsMap();
void setOptionsMap(Map<String, ?> map);
Class<? extends EnumClass> getOptionsEnum();
void setOptionsEnum(Class<? extends EnumClass> optionsEnum);

View File

@ -59,8 +59,8 @@ public interface TokenList extends Field, Component.BelongToFrame, Component.Has
java.util.List getOptionsList();
void setOptionsList(java.util.List optionsList);
Map<String, Object> getOptionsMap();
void setOptionsMap(Map<String, Object> map);
Map<String, ?> getOptionsMap();
void setOptionsMap(Map<String, ?> map);
boolean isLookup();
void setLookup(boolean lookup);

View File

@ -361,17 +361,27 @@ public interface Window extends Frame, Component.HasCaption {
}
}
interface MainWindow extends Window {
interface TopLevelWindow extends Window {
}
interface HasWorkArea {
@Nullable
AppWorkArea getWorkArea();
}
interface HasUserIndicator {
@Nullable
UserIndicator getUserIndicator();
}
interface HasFoldersPane {
@Nullable
FoldersPane getFoldersPane();
}
interface MainWindow extends TopLevelWindow, HasWorkArea, HasUserIndicator, HasFoldersPane {
}
/**
* Listener to be notified when a screen is closed.
*/

View File

@ -22,23 +22,11 @@ import com.haulmont.cuba.security.global.UserSession;
/**
* User session that holds middleware session.
*
*/
public class PortalSession extends ClientUserSession {
private static final long serialVersionUID = 64089583666599524L;
private volatile boolean authenticated; // indicates whether user passed authentication
public PortalSession(UserSession src) {
super(src);
authenticated = false;
}
public boolean isAuthenticated() {
return authenticated;
}
public void setAuthenticated(boolean authenticated) {
this.authenticated = authenticated;
}
}
}

View File

@ -19,11 +19,14 @@ package com.haulmont.cuba.portal.sys.remoting;
import com.haulmont.cuba.core.global.RemoteException;
import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.core.sys.SecurityContext;
import com.haulmont.cuba.core.sys.serialization.SerializationSupport;
import com.haulmont.cuba.core.sys.remoting.LocalServiceDirectory;
import com.haulmont.cuba.core.sys.remoting.LocalServiceInvocation;
import com.haulmont.cuba.core.sys.remoting.LocalServiceInvocationResult;
import com.haulmont.cuba.core.sys.remoting.LocalServiceInvoker;
import com.haulmont.cuba.security.global.ClientBasedSession;
import com.haulmont.cuba.security.global.UserSession;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.remoting.support.RemoteAccessor;
@ -123,9 +126,21 @@ public class LocalServiceProxy extends RemoteAccessor implements FactoryBean<Obj
}
}
UUID sessionId = AppContext.getSecurityContext() == null ? null : AppContext.getSecurityContext().getSessionId();
SecurityContext securityContext = AppContext.getSecurityContext();
UUID sessionId = securityContext == null ? null : securityContext.getSessionId();
String requestScopeLocale = null;
if (securityContext != null) {
UserSession session = securityContext.getSession();
if (session instanceof ClientBasedSession) {
if (((ClientBasedSession) session).isLocaleRequestScoped()) {
requestScopeLocale = session.getLocale() != null ? session.getLocale().toLanguageTag() : null;
}
}
}
LocalServiceInvocation invocation = new LocalServiceInvocation(
method.getName(), parameterTypeNames, argumentsData, notSerializableArguments, sessionId);
method.getName(), parameterTypeNames, argumentsData, notSerializableArguments, sessionId, requestScopeLocale);
LocalServiceInvocationResult result = invoker.invoke(invocation);
AppContext.setSecurityContext(AppContext.getSecurityContext());//need reset application name in LogMDC for the current thread

View File

@ -77,4 +77,6 @@ cuba.rest.client.secret=secret
cuba.rest.client.tokenExpirationTimeSec=43200
# A comma-separated list of allowed origins for cross-domain requests
cuba.rest.allowedOrigins=*
cuba.rest.allowedOrigins=*
cuba.anonymousSessionId=9c91dbdf-3e73-428e-9088-d586da2434c5

View File

@ -17,23 +17,26 @@
package com.haulmont.cuba.core.sys.remoting;
import javax.annotation.Nullable;
import java.util.UUID;
public class LocalServiceInvocation {
private String methodName;
private String[] parameterTypeNames;
private byte[][] argumentsData;
private Object[] notSerializableArguments;
private UUID sessionId;
private String locale;
public LocalServiceInvocation(String methodName, String[] parameterTypeNames,
byte[][] argumentsData, Object[] notSerializableArguments, UUID sessionId) {
byte[][] argumentsData, Object[] notSerializableArguments, UUID sessionId,
@Nullable String locale) {
this.methodName = methodName;
this.parameterTypeNames = parameterTypeNames;
this.argumentsData = argumentsData;
this.notSerializableArguments = notSerializableArguments;
this.sessionId = sessionId;
this.locale = locale;
}
public byte[][] getArgumentsData() {
@ -55,4 +58,8 @@ public class LocalServiceInvocation {
public UUID getSessionId() {
return sessionId;
}
public String getLocale() {
return locale;
}
}

View File

@ -17,8 +17,6 @@
package com.haulmont.cuba.web;
import com.haulmont.cuba.client.ClientUserSession;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.Configuration;
import com.haulmont.cuba.core.global.GlobalConfig;
import com.haulmont.cuba.core.global.Messages;
import com.haulmont.cuba.core.sys.AppContext;
@ -38,32 +36,48 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.TimeZone;
/**
* Abstract class that encapsulates common connection behaviour for web-client.
*
*/
public abstract class AbstractConnection implements Connection {
protected Logger log = LoggerFactory.getLogger(getClass());
private static final Logger log = LoggerFactory.getLogger(AbstractConnection.class);
protected Map<ConnectionListener, Object> connListeners = new HashMap<>();
protected Map<UserSubstitutionListener, Object> usListeners = new HashMap<>();
protected List<ConnectionListener> connectionListeners = new ArrayList<>();
protected List<UserSubstitutionListener> userSubstitutionListeners = new ArrayList<>();
protected boolean connected;
protected LoginService loginService = AppBeans.get(LoginService.NAME);
protected UserSessionService userSessionService = AppBeans.get(UserSessionService.NAME);
protected Messages messages = AppBeans.get(Messages.NAME);
@Inject
protected LoginService loginService;
@Inject
protected UserSessionService userSessionService;
@Inject
protected Messages messages;
@Inject
protected GlobalConfig globalConfig;
@Override
public boolean isConnected() {
return connected;
}
@Override
public boolean isAuthenticated() {
if (!connected) {
return false;
}
UserSession session = getSession();
return session instanceof ClientUserSession
&& ((ClientUserSession) session).isAuthenticated();
}
@Override
@Nullable
public UserSession getSession() {
@ -95,8 +109,9 @@ public abstract class AbstractConnection implements Connection {
}
@Override
public void update(UserSession session) throws LoginException {
public void update(UserSession session, SessionMode sessionMode) throws LoginException {
ClientUserSession clientUserSession = new ClientUserSession(session);
clientUserSession.setAuthenticated(sessionMode == SessionMode.AUTHENTICATED);
setSession(clientUserSession);
@ -118,11 +133,17 @@ public abstract class AbstractConnection implements Connection {
App app = App.getInstance();
if (!StringUtils.isBlank(session.getUser().getIpMask())) {
boolean sessionIsAuthenticated = true;
if (session instanceof ClientUserSession) {
sessionIsAuthenticated = ((ClientUserSession) session).isAuthenticated();
}
if (sessionIsAuthenticated && !StringUtils.isBlank(session.getUser().getIpMask())) {
IpMatcher ipMatcher = new IpMatcher(session.getUser().getIpMask());
if (!ipMatcher.match(app.getClientAddress())) {
log.info(String.format("IP address %s is not permitted for user %s", app.getClientAddress(), session.getUser().toString()));
throw new LoginException(messages.getMessage(getClass(), "login.invalidIP"));
log.info("IP address {} is not permitted for user {}", app.getClientAddress(), session.getUser());
throw new LoginException(messages.getMainMessage("login.invalidIP"));
}
}
@ -130,29 +151,28 @@ public abstract class AbstractConnection implements Connection {
String clientInfo = makeClientInfo();
session.setClientInfo(clientInfo);
if (Boolean.TRUE.equals(session.getUser().getTimeZoneAuto()))
if (Boolean.TRUE.equals(session.getUser().getTimeZoneAuto())) {
session.setTimeZone(detectTimeZone());
}
fireConnectionListeners();
if (log.isDebugEnabled()) {
log.debug(String.format("Logged in: user=%s, ip=%s, clientInfo=%s",
session.getUser().getLogin(), app.getClientAddress(), clientInfo));
}
log.debug("Logged in: user={}, ip={}, clientInfo={}",
session.getUser().getLogin(), app.getClientAddress(), clientInfo);
}
protected String makeClientInfo() {
Page page = AppUI.getCurrent().getPage();
WebBrowser webBrowser = page.getWebBrowser();
Configuration configuration = AppBeans.get(Configuration.NAME);
GlobalConfig globalConfig = configuration.getConfig(GlobalConfig.class);
String serverInfo = "Web (" +
globalConfig.getWebHostName() + ":" +
globalConfig.getWebPort() + "/" +
globalConfig.getWebContextName() + ") ";
//noinspection UnnecessaryLocalVariable
String serverInfo = String.format("Web (%s:%s/%s) %s",
globalConfig.getWebHostName(),
globalConfig.getWebPort(),
globalConfig.getWebContextName(),
webBrowser.getBrowserApplication());
return serverInfo + webBrowser.getBrowserApplication();
return serverInfo;
}
protected TimeZone detectTimeZone() {
@ -161,9 +181,9 @@ public abstract class AbstractConnection implements Connection {
int offset = webBrowser.getTimezoneOffset() / 1000 / 60;
String hours = StringUtils.leftPad(String.valueOf(offset / 60), 2, '0');
String mins = StringUtils.leftPad(String.valueOf(offset % 60), 2, '0');
String minutes = StringUtils.leftPad(String.valueOf(offset % 60), 2, '0');
char sign = offset >= 0 ? '+' : '-';
return TimeZone.getTimeZone("GMT" + sign + hours + mins);
return TimeZone.getTimeZone("GMT" + sign + hours + minutes);
}
@Override
@ -173,55 +193,59 @@ public abstract class AbstractConnection implements Connection {
}
@Override
public String logout() {
if (!connected)
return null;
public void logout() {
internalLogout();
try {
fireConnectionListeners();
} catch (LoginException e) {
log.warn("Exception on logout:", e);
}
return null;
}
protected void internalLogout() {
loginService.logout();
if (getSession() instanceof ClientUserSession
&& ((ClientUserSession) getSession()).isAuthenticated()) {
loginService.logout();
}
AppContext.setSecurityContext(null);
usListeners.clear();
userSubstitutionListeners.clear();
connected = false;
setSession(null);
}
@Override
public void addConnectionListener(ConnectionListener listener) {
connListeners.put(listener, null);
if (!connectionListeners.contains(listener)) {
connectionListeners.add(listener);
}
}
@Override
public void removeConnectionListener(ConnectionListener listener) {
connListeners.remove(listener);
connectionListeners.remove(listener);
}
@Override
public void addSubstitutionListener(UserSubstitutionListener listener) {
usListeners.put(listener, null);
if (!userSubstitutionListeners.contains(listener)) {
userSubstitutionListeners.add(listener);
}
}
@Override
public void removeSubstitutionListener(UserSubstitutionListener listener) {
usListeners.remove(listener);
userSubstitutionListeners.remove(listener);
}
protected void fireConnectionListeners() throws LoginException {
for (ConnectionListener listener : connListeners.keySet()) {
for (ConnectionListener listener : new ArrayList<>(connectionListeners)) {
listener.connectionStateChanged(this);
}
}
protected void fireSubstitutionListeners() {
for (UserSubstitutionListener listener : usListeners.keySet()) {
for (UserSubstitutionListener listener : new ArrayList<>(userSubstitutionListeners)) {
listener.userSubstituted(this);
}
}

View File

@ -17,17 +17,20 @@
package com.haulmont.cuba.web;
import com.haulmont.cuba.client.sys.cache.ClientCacheManager;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.Configuration;
import com.haulmont.cuba.core.global.GlobalConfig;
import com.haulmont.cuba.core.global.MessageTools;
import com.haulmont.cuba.gui.components.Frame;
import com.haulmont.cuba.gui.components.Window;
import com.haulmont.cuba.gui.config.WindowConfig;
import com.haulmont.cuba.gui.executors.IllegalConcurrentAccessException;
import com.haulmont.cuba.gui.settings.SettingsClient;
import com.haulmont.cuba.gui.theme.ThemeConstants;
import com.haulmont.cuba.gui.theme.ThemeConstantsRepository;
import com.haulmont.cuba.security.app.UserSessionService;
import com.haulmont.cuba.security.global.NoUserSessionException;
import com.haulmont.cuba.security.global.UserSession;
import com.haulmont.cuba.web.auth.RequestContext;
import com.haulmont.cuba.web.auth.WebAuthConfig;
import com.haulmont.cuba.web.exception.ExceptionHandlers;
@ -37,20 +40,21 @@ import com.haulmont.cuba.web.sys.AppCookies;
import com.haulmont.cuba.web.sys.BackgroundTaskManager;
import com.haulmont.cuba.web.sys.LinkHandler;
import com.vaadin.server.AbstractClientConnector;
import com.vaadin.server.ErrorHandler;
import com.vaadin.server.VaadinServlet;
import com.vaadin.server.VaadinSession;
import com.vaadin.ui.UI;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.*;
import java.util.concurrent.Future;
/**
@ -60,11 +64,16 @@ import java.util.concurrent.Future;
* Use {@link #getInstance()} static method to obtain the reference to the current App instance.
*/
public abstract class App {
public static final String NAME = "cuba_App";
public static final String USER_SESSION_ATTR = "userSessionId";
public static final String APP_THEME_COOKIE_PREFIX = "APP_THEME_NAME_";
private static Logger log = LoggerFactory.getLogger(App.class);
public static final String COOKIE_LOCALE = "LAST_LOCALE";
private static final Logger log = LoggerFactory.getLogger(App.class);
static {
AbstractClientConnector.setIncorrectConcurrentAccessHandler(() -> {
@ -78,22 +87,41 @@ public abstract class App {
protected ExceptionHandlers exceptionHandlers;
protected final GlobalConfig globalConfig;
@Inject
protected GlobalConfig globalConfig;
protected final WebConfig webConfig;
@Inject
protected WebConfig webConfig;
protected final WebAuthConfig webAuthConfig;
@Inject
protected WebAuthConfig webAuthConfig;
@Inject
protected WindowConfig windowConfig;
@Inject
protected ThemeConstantsRepository themeConstantsRepository;
@Inject
protected ClientCacheManager clientCacheManager;
@Inject
protected UserSessionService userSessionService;
@Inject
protected MessageTools messageTools;
@Inject
protected SettingsClient settingsClient;
protected AppCookies cookies;
protected LinkHandler linkHandler;
protected final BackgroundTaskManager backgroundTaskManager;
protected BackgroundTaskManager backgroundTaskManager = new BackgroundTaskManager();
protected Principal principal;
protected Locale locale = Locale.getDefault();
protected String webResourceTimestamp = "DEBUG";
protected String clientAddress;
@ -101,51 +129,23 @@ public abstract class App {
protected ThemeConstants themeConstants;
public App() {
log.trace("Creating application " + this);
try {
Configuration configuration = AppBeans.get(Configuration.NAME);
webConfig = configuration.getConfig(WebConfig.class);
webAuthConfig = configuration.getConfig(WebAuthConfig.class);
globalConfig = configuration.getConfig(GlobalConfig.class);
appLog = new AppLog();
connection = createConnection();
exceptionHandlers = new ExceptionHandlers(this);
cookies = new AppCookies();
backgroundTaskManager = new BackgroundTaskManager();
themeConstants = loadTheme();
VaadinServlet vaadinServlet = VaadinServlet.getCurrent();
ServletContext sc = vaadinServlet.getServletContext();
String resourcesTimestamp = sc.getInitParameter("webResourcesTs");
if (StringUtils.isNotEmpty(resourcesTimestamp)) {
this.webResourceTimestamp = resourcesTimestamp;
}
} catch (Exception e) {
log.error("Error initializing application", e);
throw new Error("Error initializing application. See log for details.");
}
log.trace("Creating application {}", this);
}
protected ThemeConstants loadTheme() {
ThemeConstantsRepository themeRepository = AppBeans.get(ThemeConstantsRepository.NAME);
String appWindowTheme = webConfig.getAppWindowTheme();
String userAppTheme = cookies.getCookieValue(APP_THEME_COOKIE_PREFIX + globalConfig.getWebContextName());
if (userAppTheme != null) {
if (!StringUtils.equals(userAppTheme, appWindowTheme)) {
// check theme support
Set<String> supportedThemes = themeRepository.getAvailableThemes();
Set<String> supportedThemes = themeConstantsRepository.getAvailableThemes();
if (supportedThemes.contains(userAppTheme)) {
appWindowTheme = userAppTheme;
}
}
}
ThemeConstants theme = themeRepository.getConstants(appWindowTheme);
ThemeConstants theme = themeConstantsRepository.getConstants(appWindowTheme);
if (theme == null) {
throw new IllegalStateException("Unable to use theme constants '" + appWindowTheme + "'");
}
@ -154,11 +154,10 @@ public abstract class App {
}
protected void applyTheme(String appWindowTheme) {
ThemeConstantsRepository themeRepository = AppBeans.get(ThemeConstantsRepository.NAME);
ThemeConstants theme = themeRepository.getConstants(appWindowTheme);
ThemeConstants theme = themeConstantsRepository.getConstants(appWindowTheme);
if (theme == null) {
log.warn("Unable to use theme constants '" + appWindowTheme + "'");
log.warn("Unable to use theme constants '{}'", appWindowTheme);
} else {
this.themeConstants = theme;
setUserAppTheme(appWindowTheme);
@ -179,11 +178,8 @@ public abstract class App {
}
}
/**
* @return AppWindow displayed in the current UI. Can be null if not logged in.
*/
public AppWindow getAppWindow() {
return AppUI.getCurrent().getAppWindow();
public Window.TopLevelWindow getTopLevelWindow() {
return getAppUI().getTopLevelWindow();
}
/**
@ -203,36 +199,127 @@ public abstract class App {
if (ui instanceof AppUI)
list.add((AppUI) ui);
else
log.warn("Invalid UI in the session: " + ui);
log.warn("Invalid UI in the session: {}", ui);
}
return list;
}
protected abstract boolean loginOnStart();
protected abstract Connection createConnection();
protected Connection createConnection() {
return AppBeans.getPrototype(Connection.NAME);
}
/**
* Called when <em>the first</em> UI of the session is initialized.
*/
protected void init() {
protected void init(Locale requestLocale) {
VaadinSession vSession = VaadinSession.getCurrent();
vSession.setAttribute(App.class, this);
vSession.setLocale(messageTools.getDefaultLocale());
// set root error handler for all session
vSession.setErrorHandler((ErrorHandler) event -> {
try {
getExceptionHandlers().handle(event);
getAppLog().log(event);
} catch (Throwable e) {
//noinspection ThrowableResultOfMethodCallIgnored
log.error("Error handling exception\nOriginal exception:\n{}\nException in handlers:\n{}",
ExceptionUtils.getStackTrace(event.getThrowable()), ExceptionUtils.getStackTrace(e)
);
}
});
appLog = new AppLog();
connection = createConnection();
exceptionHandlers = new ExceptionHandlers(this);
cookies = new AppCookies();
themeConstants = loadTheme();
VaadinServlet vaadinServlet = VaadinServlet.getCurrent();
ServletContext sc = vaadinServlet.getServletContext();
String resourcesTimestamp = sc.getInitParameter("webResourcesTs");
if (StringUtils.isNotEmpty(resourcesTimestamp)) {
this.webResourceTimestamp = resourcesTimestamp;
}
log.debug("Initializing application");
// get default locale from config
MessageTools messageTools = AppBeans.get(MessageTools.NAME);
locale = messageTools.getDefaultLocale();
Locale targetLocale = resolveLocale(requestLocale);
setLocale(targetLocale);
clientCacheManager.initialize();
if (webAuthConfig.getExternalAuthentication()) {
principal = RequestContext.get().getRequest().getUserPrincipal();
}
}
protected Locale resolveLocale(@Nullable Locale requestLocale) {
Map<String, Locale> locales = globalConfig.getAvailableLocales();
if (globalConfig.getLocaleSelectVisible()) {
String lastLocale = getCookieValue(COOKIE_LOCALE);
if (lastLocale != null) {
for (Locale locale : locales.values()) {
if (locale.toLanguageTag().equals(lastLocale)) {
return locale;
}
}
}
}
if (requestLocale != null) {
Locale requestTrimmedLocale = messageTools.trimLocale(requestLocale);
if (locales.containsValue(requestTrimmedLocale)) {
return requestTrimmedLocale;
}
// if not found and application locale contains country, try to match by language only
if (!StringUtils.isEmpty(requestLocale.getCountry())) {
Locale appLocale = Locale.forLanguageTag(requestLocale.getLanguage());
for (Locale locale : locales.values()) {
if (Locale.forLanguageTag(locale.getLanguage()).equals(appLocale)) {
return locale;
}
}
}
}
// return default locale
return messageTools.getDefaultLocale();
}
/**
* Called on each UI initialization.
*
* @param ui initialized UI
* Called on each browser tab initialization.
*/
protected void initView(AppUI ui) {
public void createTopLevelWindow(AppUI ui) {
WebWindowManager wm = new WebWindowManager(ui);
String topLevelWindowId = routeTopLevelWindowId();
wm.createTopLevelWindow(windowConfig.getWindowInfo(topLevelWindowId));
}
protected abstract String routeTopLevelWindowId();
public void createTopLevelWindow() {
createTopLevelWindow(AppUI.getCurrent());
}
/**
* Initialize new TopLevelWindow and replace current
*
* @param topLevelWindowId target top level window id
*/
public void navigateTo(String topLevelWindowId) {
WebWindowManager wm = new WebWindowManager(AppUI.getCurrent());
wm.createTopLevelWindow(windowConfig.getWindowInfo(topLevelWindowId));
}
/**
@ -240,13 +327,14 @@ public abstract class App {
* Used for ping middleware session and show session messages
*/
public void onHeartbeat() {
if (getConnection().isConnected()) {
Connection connection = getConnection();
if (getConnection().isConnected() && connection.isAuthenticated()) {
// Ping middleware session if connected and show messages
log.debug("Ping session");
try {
UserSessionService service = AppBeans.get(UserSessionService.NAME);
String message = service.getMessages();
String message = userSessionService.getMessages();
if (message != null) {
message = message.replace("\n", "<br/>");
getWindowManager().showNotification(message, Frame.NotificationType.ERROR_HTML);
@ -291,15 +379,16 @@ public abstract class App {
}
/**
* @return WindowManager instance or null if the current UI has no AppWindow
* @return WindowManager instance or null if the current UI has no MainWindow
*/
public WebWindowManager getWindowManager() {
if (getAppUI() == null) {
return null;
}
AppWindow appWindow = getAppUI().getAppWindow();
return appWindow != null ? appWindow.getWindowManager() : null;
Window.TopLevelWindow topLevelWindow = getTopLevelWindow();
return topLevelWindow != null ? (WebWindowManager) topLevelWindow.getWindowManager() : null;
}
public AppLog getAppLog() {
@ -310,26 +399,6 @@ public abstract class App {
return exceptionHandlers;
}
/**
* Create the login window instance.
*
* @param ui current UI
* @return login window
*/
protected UIView createLoginWindow(AppUI ui) {
return new LoginWindow(ui);
}
/**
* Create the main window instance.
*
* @param ui current UI
* @return main window
*/
protected AppWindow createAppWindow(AppUI ui) {
return new AppWindow(ui);
}
public String getCookieValue(String name) {
return cookies.getCookieValue(name);
}
@ -355,14 +424,27 @@ public abstract class App {
}
public Locale getLocale() {
return locale;
return VaadinSession.getCurrent().getLocale();
}
public void setLocale(Locale locale) {
this.locale = locale;
UserSession session = getConnection().getSession();
if (session != null) {
session.setLocale(locale);
}
UI.getCurrent().setLocale(locale);
VaadinSession.getCurrent().setLocale(locale);
AppUI currentUi = AppUI.getCurrent();
currentUi.getSession().setLocale(locale);
currentUi.updateClientSystemMessages(locale);
for (AppUI ui : getAppUIs()) {
if (ui != currentUi) {
ui.accessSynchronously(() ->
ui.updateClientSystemMessages(locale)
);
}
}
}
public String getClientAddress() {
@ -371,9 +453,10 @@ public abstract class App {
String xForwardedFor = request.getHeader("X_FORWARDED_FOR");
if (!StringUtils.isBlank(xForwardedFor)) {
String[] strings = xForwardedFor.split(",");
clientAddress = strings[strings.length - 1].trim();
} else
clientAddress = StringUtils.trimToEmpty(strings[strings.length - 1]);
} else {
clientAddress = request.getRemoteAddr();
}
}
return clientAddress;
@ -387,10 +470,6 @@ public abstract class App {
return webResourceTimestamp;
}
public BackgroundTaskManager getTaskManager() {
return backgroundTaskManager;
}
public void addBackgroundTask(Future task) {
backgroundTaskManager.addTask(task);
}
@ -406,15 +485,16 @@ public abstract class App {
public void closeAllWindows() {
log.debug("Closing all windows");
try {
for (final AppUI ui : getAppUIs()) {
for (AppUI ui : getAppUIs()) {
ui.accessSynchronously(() -> {
AppWindow appWindow = ui.getAppWindow();
if (appWindow != null) {
WebWindowManager webWindowManager = appWindow.getWindowManager();
Window.TopLevelWindow topLevelWindow = getTopLevelWindow();
if (topLevelWindow != null) {
WebWindowManager webWindowManager = (WebWindowManager) topLevelWindow.getWindowManager();
webWindowManager.disableSavingScreenHistory = true;
webWindowManager.closeAll();
}
// also remove all native Vaadin windows, that is not under CUBA control
for (com.vaadin.ui.Window win : new ArrayList<>(ui.getWindows())) {
ui.removeWindow(win);
}
@ -426,7 +506,6 @@ public abstract class App {
}
protected void clearSettingsCache() {
WebSettingsClient webSettingsClient = AppBeans.get(SettingsClient.NAME);
webSettingsClient.clearCache();
((WebSettingsClient) settingsClient).clearCache();
}
}

View File

@ -17,46 +17,74 @@
package com.haulmont.cuba.web;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.client.ClientUserSession;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.GlobalConfig;
import com.haulmont.cuba.core.global.Messages;
import com.haulmont.cuba.core.global.ScreenProfilerConfig;
import com.haulmont.cuba.gui.TestIdManager;
import com.haulmont.cuba.gui.components.Window.TopLevelWindow;
import com.haulmont.cuba.gui.theme.ThemeConstantsRepository;
import com.haulmont.cuba.security.app.UserSessionService;
import com.haulmont.cuba.security.global.LoginException;
import com.haulmont.cuba.security.global.UserSession;
import com.haulmont.cuba.web.app.UserSettingsTools;
import com.haulmont.cuba.web.controllers.ControllerUtils;
import com.haulmont.cuba.web.sys.LinkHandler;
import com.haulmont.cuba.web.toolkit.ui.CubaClientManager;
import com.haulmont.cuba.web.toolkit.ui.*;
import com.haulmont.cuba.web.toolkit.ui.client.appui.AppUIClientRpc;
import com.vaadin.annotations.PreserveOnRefresh;
import com.vaadin.annotations.Push;
import com.vaadin.server.*;
import com.vaadin.server.ErrorHandler;
import com.vaadin.server.Extension;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.WrappedSession;
import com.vaadin.shared.ui.ui.Transport;
import com.vaadin.ui.Component;
import com.vaadin.ui.UI;
import com.vaadin.ui.*;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.inject.Inject;
import java.util.*;
/**
* Single window / page of web application. Root component of Vaadin layout.
*/
@org.springframework.stereotype.Component(AppUI.NAME)
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
@Push(transport = Transport.WEBSOCKET_XHR)
@PreserveOnRefresh
public class AppUI extends UI implements ErrorHandler {
public class AppUI extends UI implements ErrorHandler, CubaHistoryControl.HistoryBackHandler {
public static final String APPLICATION_CLASS_CONFIG_KEY = "Application";
public static final String NAME = "cuba_AppUI";
public static final String LAST_REQUEST_ACTION_ATTR = "lastRequestAction";
public static final String LAST_REQUEST_PARAMS_ATTR = "lastRequestParams";
private final static Logger log = LoggerFactory.getLogger(AppUI.class);
private static final Logger log = LoggerFactory.getLogger(AppUI.class);
protected final App app;
protected App app;
protected boolean applicationInitRequired = false;
@Inject
protected Messages messages;
@Inject
protected GlobalConfig globalConfig;
@Inject
protected WebConfig webConfig;
@Inject
protected ScreenProfilerConfig screenProfilerConfig;
@Inject
protected UserSettingsTools userSettingsTools;
@Inject
protected ThemeConstantsRepository themeConstantsRepository;
protected TestIdManager testIdManager = new TestIdManager();
@ -68,47 +96,21 @@ public class AppUI extends UI implements ErrorHandler {
protected CubaClientManager clientManager;
protected ScreenClientProfilerAgent clientProfiler;
protected CubaFileDownloader fileDownloader;
protected CubaHistoryControl historyControl;
protected TopLevelWindow topLevelWindow;
public AppUI() {
log.trace("Creating UI {}", this);
if (!App.isBound()) {
app = createApplication();
VaadinSession vSession = VaadinSession.getCurrent();
vSession.setAttribute(App.class, app);
// set root error handler for all session
vSession.setErrorHandler((ErrorHandler) event -> {
try {
app.getExceptionHandlers().handle(event);
app.getAppLog().log(event);
} catch (Throwable e) {
//noinspection ThrowableResultOfMethodCallIgnored
log.error("Error handling exception\nOriginal exception:\n{}\nException in handlers:\n{}",
ExceptionUtils.getStackTrace(event.getThrowable()), ExceptionUtils.getStackTrace(e)
);
}
});
applicationInitRequired = true;
} else {
app = App.getInstance();
}
Configuration configuration = AppBeans.get(Configuration.NAME);
testMode = configuration.getConfig(GlobalConfig.class).getTestMode();
// do not grab focus
setTabIndex(-1);
initJsLibraries();
initInternalComponents();
}
/**
* Dynamically init external JS libraries.
* You should create JavaScriptExtension class and extend UI object here. <br/>
*
* <p>
* Example: <br/>
* <pre><code>
* JavaScriptExtension:
@ -132,7 +134,7 @@ public class AppUI extends UI implements ErrorHandler {
* protected void initJsLibraries() {
* new JQueryIntegration().extend(this);
* }</code></pre>
*
* <p>
* If you want to include scripts to generated page statically see {@link com.haulmont.cuba.web.sys.CubaBootstrapListener}.
*/
protected void initJsLibraries() {
@ -141,60 +143,128 @@ public class AppUI extends UI implements ErrorHandler {
protected void initInternalComponents() {
clientManager = new CubaClientManager();
clientManager.extend(this);
fileDownloader = new CubaFileDownloader();
fileDownloader.extend(this);
clientProfiler = new ScreenClientProfilerAgent();
clientProfiler.extend(this);
if (webConfig.getAllowHandleBrowserHistoryBack()) {
historyControl = new CubaHistoryControl();
historyControl.extend(this, this);
}
}
protected App createApplication() {
String applicationClass = getApplicationClass();
App application;
try {
Class<?> aClass = getClass().getClassLoader().loadClass(applicationClass);
application = (App) aClass.newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
throw new Error(String.format("Unable to create application '%s'", applicationClass), e);
}
return application;
}
protected String getApplicationClass() {
DeploymentConfiguration vConf = VaadinService.getCurrent().getDeploymentConfiguration();
return vConf.getApplicationOrSystemProperty(APPLICATION_CLASS_CONFIG_KEY,
DefaultApp.class.getCanonicalName());
return AppBeans.getPrototype(App.NAME);
}
@Override
protected void init(VaadinRequest request) {
log.debug("Initializing AppUI");
if (applicationInitRequired) {
app.init();
log.trace("Initializing UI {}", this);
MessageTools messageTools = AppBeans.get(MessageTools.NAME);
Locale locale = messageTools.trimLocale(request.getLocale());
app.setLocale(locale);
try {
this.testMode = globalConfig.getTestMode();
applicationInitRequired = false;
// init error handlers
setErrorHandler(this);
// do not grab focus
setTabIndex(-1);
initJsLibraries();
initInternalComponents();
if (!App.isBound()) {
App app = createApplication();
app.init(request.getLocale());
this.app = app;
} else {
this.app = App.getInstance();
}
setupUI();
} catch (Exception e) {
log.error("Unable to init ui", e);
// unable to connect to middle ware
showCriticalExceptionMessage(e);
return;
}
// init error handlers
setErrorHandler(this);
// open login or main window
app.initView(this);
processExternalLink(request);
}
protected void showCriticalExceptionMessage(Exception e) {
String initErrorCaption = messages.getMainMessage("app.initErrorCaption");
String initErrorMessage = messages.getMainMessage("app.initErrorMessage");
VerticalLayout content = new VerticalLayout();
content.setStyleName("cuba-init-error-view");
content.setSizeFull();
VerticalLayout errorPanel = new VerticalLayout();
errorPanel.setStyleName("cuba-init-error-panel");
errorPanel.setWidthUndefined();
errorPanel.setSpacing(true);
Label captionLabel = new Label(initErrorCaption);
captionLabel.setWidthUndefined();
captionLabel.setStyleName("cuba-init-error-caption");
captionLabel.addStyleName("h2");
captionLabel.setValue(initErrorCaption);
errorPanel.addComponent(captionLabel);
Label messageLabel = new Label(initErrorCaption);
messageLabel.setWidthUndefined();
messageLabel.setStyleName("cuba-init-error-message");
messageLabel.setValue(initErrorMessage);
errorPanel.addComponent(messageLabel);
Button retryButton = new Button(messages.getMainMessage("app.initRetry"));
retryButton.setStyleName("cuba-init-error-retry");
retryButton.addClickListener((Button.ClickListener) event -> {
// always restart UI
String url = ControllerUtils.getLocationWithoutParams() + "?restartApp";
getPage().open(url, "_self");
});
errorPanel.addComponent(retryButton);
errorPanel.setComponentAlignment(retryButton, Alignment.MIDDLE_CENTER);
content.addComponent(errorPanel);
content.setComponentAlignment(errorPanel, Alignment.MIDDLE_CENTER);
setContent(content);
}
protected void setupUI() throws LoginException {
if (!app.getConnection().isConnected() && !app.loginOnStart()) {
app.getConnection().loginAnonymous(app.getLocale());
} else {
app.createTopLevelWindow(this);
}
}
@Override
protected void refresh(VaadinRequest request) {
super.refresh(request);
// handle page refresh
if (app.getConnection().isConnected()) {
if (app.getConnection().isAuthenticated()) {
// Ping middleware session if connected
log.debug("Check middleware session");
log.debug("Ping middleware session");
try {
UserSessionService service = AppBeans.get(UserSessionService.NAME);
UserSession session = app.getConnection().getSession();
if (session != null) {
if (session instanceof ClientUserSession
&& ((ClientUserSession) session).isAuthenticated()) {
service.getUserSession(session.getId());
}
} catch (Exception e) {
@ -209,17 +279,6 @@ public class AppUI extends UI implements ErrorHandler {
processExternalLink(request);
}
public void showView(UIView view) {
try {
setContent(view);
getPage().setTitle(view.getTitle());
view.show();
} catch (Exception e) {
error(new com.vaadin.server.ErrorEvent(e));
}
}
/**
* @return current AppUI
*/
@ -234,15 +293,18 @@ public class AppUI extends UI implements ErrorHandler {
return app;
}
/**
* @return AppWindow instance or null if not logged in
*/
public AppWindow getAppWindow() {
Component currentUIView = getContent();
if (currentUIView instanceof AppWindow) {
return (AppWindow) currentUIView;
} else {
return null;
public TopLevelWindow getTopLevelWindow() {
return topLevelWindow;
}
public void setTopLevelWindow(TopLevelWindow window) {
if (this.topLevelWindow != window) {
this.topLevelWindow = window;
// unregister previous components
setContent(null);
setContent(topLevelWindow.unwrapComposition(Component.class));
}
}
@ -268,14 +330,14 @@ public class AppUI extends UI implements ErrorHandler {
}
public void processExternalLink(VaadinRequest request) {
String action = (String) request.getWrappedSession().getAttribute(LAST_REQUEST_ACTION_ATTR);
WrappedSession wrappedSession = request.getWrappedSession();
String action = (String) wrappedSession.getAttribute(LAST_REQUEST_ACTION_ATTR);
Configuration configuration = AppBeans.get(Configuration.NAME);
WebConfig webConfig = configuration.getConfig(WebConfig.class);
if (webConfig.getLinkHandlerActions().contains(action)) {
//noinspection unchecked
Map<String, String> params =
(Map<String, String>) request.getWrappedSession().getAttribute(LAST_REQUEST_PARAMS_ATTR);
(Map<String, String>) wrappedSession.getAttribute(LAST_REQUEST_PARAMS_ATTR);
if (params == null) {
log.warn("Unable to process the external link: lastRequestParams not found in session");
return;
@ -327,14 +389,15 @@ public class AppUI extends UI implements ErrorHandler {
}
public void clearProfiledScreens(List<String> profilerMarkers) {
for (String profilerMarker : profilerMarkers) {
profiledScreens.remove(profilerMarker);
if (profiledScreens != null) {
for (String profilerMarker : profilerMarkers) {
profiledScreens.remove(profilerMarker);
}
}
}
protected void updateClientSystemMessages(Locale locale) {
CubaClientManager.SystemMessages msgs = new CubaClientManager.SystemMessages();
Messages messages = AppBeans.get(Messages.NAME);
msgs.communicationErrorCaption = messages.getMainMessage("communicationErrorCaption", locale);
msgs.communicationErrorMessage = messages.getMainMessage("communicationErrorMessage", locale);
@ -350,4 +413,81 @@ public class AppUI extends UI implements ErrorHandler {
getReconnectDialogConfiguration().setDialogText(messages.getMainMessage("reconnectDialogText", locale));
getReconnectDialogConfiguration().setDialogTextGaveUp(messages.getMainMessage("reconnectDialogTextGaveUp", locale));
}
@Override
public void onHistoryBackPerformed() {
TopLevelWindow topLevelWindow = getTopLevelWindow();
if (topLevelWindow instanceof CubaHistoryControl.HistoryBackHandler) {
((CubaHistoryControl.HistoryBackHandler) topLevelWindow).onHistoryBackPerformed();
}
}
protected AbstractComponent getTopLevelWindowComposition() {
if (topLevelWindow == null) {
throw new IllegalStateException("UI does not have top level window");
}
return topLevelWindow.unwrapComposition(AbstractComponent.class);
}
public List<CubaTimer> getTimers() {
AbstractComponent timersHolder = getTopLevelWindowComposition();
List<CubaTimer> timers = new ArrayList<>();
for (Extension extension : timersHolder.getExtensions()) {
if (extension instanceof CubaTimer) {
timers.add((CubaTimer) extension);
}
}
return timers;
}
public void addTimer(CubaTimer timer) {
AbstractComponent timersHolder = getTopLevelWindowComposition();
if (!timersHolder.getExtensions().contains(timer)) {
timer.extend(timersHolder);
}
}
public void removeTimer(CubaTimer timer) {
AbstractComponent timersHolder = getTopLevelWindowComposition();
timersHolder.removeExtension(timer);
}
public void beforeTopLevelWindowInit() {
updateUiTheme();
setProfilerParameters();
updateClientSystemMessages(app.getLocale());
getTestIdManager().reset();
}
protected void setProfilerParameters() {
clientProfiler.setFlushEventsCount(screenProfilerConfig.getFlushEventsCount());
clientProfiler.setFlushTimeout(screenProfilerConfig.getFlushTimeout());
}
protected void updateUiTheme() {
// load theme from user settings
String themeName = webConfig.getAppWindowTheme();
themeName = userSettingsTools.loadAppWindowTheme() == null ? themeName : userSettingsTools.loadAppWindowTheme();
if (!Objects.equals(themeName, getTheme())) {
// check theme support
Set<String> supportedThemes = themeConstantsRepository.getAvailableThemes();
if (supportedThemes.contains(themeName)) {
app.applyTheme(themeName);
setTheme(themeName);
}
}
}
public CubaFileDownloader getFileDownloader() {
return fileDownloader;
}
}

View File

@ -1,210 +0,0 @@
/*
* Copyright (c) 2008-2016 Haulmont.
*
* 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 com.haulmont.cuba.web;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.Configuration;
import com.haulmont.cuba.core.global.Messages;
import com.haulmont.cuba.core.global.UserSessionSource;
import com.haulmont.cuba.gui.components.Window;
import com.haulmont.cuba.gui.components.mainwindow.UserIndicator;
import com.haulmont.cuba.gui.config.WindowConfig;
import com.haulmont.cuba.gui.config.WindowInfo;
import com.haulmont.cuba.gui.theme.ThemeConstantsRepository;
import com.haulmont.cuba.web.app.UserSettingsTools;
import com.haulmont.cuba.web.toolkit.ui.*;
import com.vaadin.server.Extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
/**
* Standard main application window.
* <p/>
* To use a specific implementation override {@link App#createAppWindow(AppUI)} method.
*/
public class AppWindow extends UIView implements CubaHistoryControl.HistoryBackHandler {
private static final Logger log = LoggerFactory.getLogger(AppWindow.class);
protected final AppUI ui;
protected final App app;
protected final Connection connection;
protected final WebWindowManager windowManager;
protected CubaFileDownloader fileDownloader;
protected CubaHistoryControl historyControl;
protected ScreenClientProfilerAgent clientProfiler;
protected WebConfig webConfig;
protected Window.MainWindow mainWindow;
protected Messages messages = AppBeans.get(Messages.NAME);
public AppWindow(AppUI ui) {
log.trace("Creating " + this);
this.ui = ui;
this.app = ui.getApp();
this.connection = app.getConnection();
this.windowManager = createWindowManager();
Configuration configuration = AppBeans.get(Configuration.NAME);
webConfig = configuration.getConfig(WebConfig.class);
setSizeFull();
initInternalComponents();
}
@Override
public void show() {
updateClientSystemMessages();
beforeInitLayout();
initAppMainWindow();
}
protected void initAppMainWindow() {
WindowConfig windowConfig = AppBeans.get(WindowConfig.NAME);
WindowInfo mainWindowInfo = windowConfig.getWindowInfo("mainWindow");
windowManager.initMainWindow(mainWindowInfo);
}
public AppUI getAppUI() {
return ui;
}
public WebWindowManager getWindowManager() {
return windowManager;
}
/**
* @return a new instance of {@link WebWindowManager}
*/
protected WebWindowManager createWindowManager() {
return new WebWindowManager(app, this);
}
/**
* init system components
*/
protected void initInternalComponents() {
fileDownloader = new CubaFileDownloader();
fileDownloader.extend(this);
clientProfiler = new ScreenClientProfilerAgent();
clientProfiler.extend(this);
if (webConfig.getAllowHandleBrowserHistoryBack()) {
historyControl = new CubaHistoryControl();
historyControl.extend(this, this);
}
}
protected void updateClientSystemMessages() {
UserSessionSource sessionSource = AppBeans.get(UserSessionSource.NAME);
Locale locale = sessionSource.getLocale();
ui.updateClientSystemMessages(locale);
}
/**
* Called when the user presses browser "Back" button and {@code cuba.web.allowHandleBrowserHistoryBack}
* application property is true.
* <p>Override this method and implement your logic to handle the "Back" button.
*/
@Override
public void onHistoryBackPerformed() {
}
@Override
public String getTitle() {
return getAppCaption();
}
public Window.MainWindow getMainWindow() {
return mainWindow;
}
protected void setMainWindow(Window.MainWindow mainWindow) {
this.mainWindow = mainWindow;
}
/**
* @return Application caption to be shown in browser page title
*/
protected String getAppCaption() {
return messages.getMainMessage("application.caption");
}
public CubaFileDownloader getFileDownloader() {
return fileDownloader;
}
public List<CubaTimer> getTimers() {
List<CubaTimer> timers = new LinkedList<>();
for (Extension extension : this.getExtensions()) {
if (extension instanceof CubaTimer) {
timers.add((CubaTimer) extension);
}
}
return timers;
}
public void addTimer(CubaTimer timer) {
if (!getExtensions().contains(timer)) {
timer.extend(this);
}
}
public void removeTimer(CubaTimer timer) {
removeExtension(timer);
}
protected void beforeInitLayout() {
// load theme from user settings
String themeName = webConfig.getAppWindowTheme();
UserSettingsTools userSettingsTools = AppBeans.get(UserSettingsTools.NAME);
themeName = userSettingsTools.loadAppWindowTheme() == null ? themeName : userSettingsTools.loadAppWindowTheme();
if (!Objects.equals(themeName, ui.getTheme())) {
// check theme support
ThemeConstantsRepository themeRepository = AppBeans.get(ThemeConstantsRepository.NAME);
Set<String> supportedThemes = themeRepository.getAvailableThemes();
if (supportedThemes.contains(themeName)) {
app.applyTheme(themeName);
ui.setTheme(themeName);
}
}
}
public void refreshUserSubstitutions() {
UserIndicator userIndicator = getMainWindow().getUserIndicator();
if (userIndicator != null) {
userIndicator.refreshUserSubstitutions();
}
}
}

View File

@ -25,10 +25,16 @@ import java.util.Locale;
/**
* Interface to be implemented by objects that connect web-client to the middleware.
*
*/
public interface Connection {
String NAME = "cuba_Connection";
enum SessionMode {
AUTHENTICATED,
ANONYMOUS
}
/**
* Log in to the system.
* @param login user login name
@ -38,6 +44,13 @@ public interface Connection {
*/
void login(String login, String password, Locale locale) throws LoginException;
/**
* Log in to the system.
* @param locale user locale
* @throws LoginException in case of unsuccessful login due to wrong credentials or other issues
*/
void loginAnonymous(Locale locale) throws LoginException;
/**
* Log in to the system.
* @param login user login name
@ -52,7 +65,7 @@ public interface Connection {
* Returns URL to which the user will be redirected after logout.
* @return redirection URL
*/
String logout();
void logout();
/**
* Substitute a user in the current session with another user. This method creates a new UserSession instance,
@ -69,6 +82,12 @@ public interface Connection {
*/
boolean isConnected();
/**
* Check if the client was authenticated.
* @return true if authenticated
*/
boolean isAuthenticated();
/**
* Check if session is alive on middleware
*
@ -97,7 +116,7 @@ public interface Connection {
* @param session new UserSession object
* @throws LoginException in case of unsuccessful update
*/
void update(UserSession session) throws LoginException;
void update(UserSession session, SessionMode sessionMode) throws LoginException;
/**
* Add a connection listener.

View File

@ -21,7 +21,6 @@ import com.haulmont.cuba.security.global.LoginException;
/**
* Listener of connection events. See {@link com.haulmont.cuba.web.Connection}.
*/
public interface ConnectionListener
{
public interface ConnectionListener {
void connectionStateChanged(Connection connection) throws LoginException;
}
}

View File

@ -16,54 +16,74 @@
*/
package com.haulmont.cuba.web;
import com.haulmont.cuba.client.sys.cache.ClientCacheManager;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.bali.util.ParamsMap;
import com.haulmont.cuba.core.global.UserSessionSource;
import com.haulmont.cuba.gui.WindowManager;
import com.haulmont.cuba.gui.config.WindowConfig;
import com.haulmont.cuba.gui.WindowManager.OpenType;
import com.haulmont.cuba.gui.components.Window;
import com.haulmont.cuba.gui.config.WindowInfo;
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.app.loginwindow.AppLoginWindow;
import com.haulmont.cuba.web.auth.ExternallyAuthenticatedConnection;
import com.vaadin.server.*;
import com.vaadin.server.VaadinService;
import com.vaadin.server.VaadinSession;
import com.vaadin.server.WrappedHttpSession;
import com.vaadin.server.WrappedSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.inject.Inject;
import javax.servlet.http.HttpSession;
import java.util.Collections;
import java.util.Map;
/**
* Default {@link App} implementation that shows {@link LoginWindow} on start.
* Default {@link App} implementation that shows {@link AppLoginWindow} on start.
* Supports SSO through external authentication.
*
*/
@Component(App.NAME)
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class DefaultApp extends App implements ConnectionListener, UserSubstitutionListener {
private static Logger log = LoggerFactory.getLogger(DefaultApp.class);
private static final Logger log = LoggerFactory.getLogger(DefaultApp.class);
// Login on start only on first request from user
protected boolean tryLoginOnStart = true;
@Inject
protected UserSessionSource userSessionSource;
public DefaultApp() {
}
@Override
protected Connection createConnection() {
Connection connection = new DefaultConnection();
Connection connection = super.createConnection();
connection.addConnectionListener(this);
return connection;
}
@Override
public void connectionStateChanged(Connection connection) throws LoginException {
log.debug("connectionStateChanged connected: {}, authenticated: {}",
connection.isConnected(), connection.isAuthenticated());
cleanupBackgroundTasks();
closeAllWindows();
clearSettingsCache();
if (connection.isConnected()) {
UserSession userSession = connection.getSession();
if (userSession == null) {
throw new IllegalStateException("Unable to obtain session from connected Connection");
}
setLocale(userSession.getLocale());
// substitution listeners are cleared by connection on logout
connection.addSubstitutionListener(this);
ClientCacheManager clientCacheManager = AppBeans.get(ClientCacheManager.NAME);
clientCacheManager.initialize();
if (webConfig.getUseSessionFixationProtection()) {
VaadinService.reinitializeSession(VaadinService.getCurrentRequest());
@ -71,18 +91,23 @@ public class DefaultApp extends App implements ConnectionListener, UserSubstitut
int timeout = webConfig.getHttpSessionExpirationTimeoutSec();
session.setMaxInactiveInterval(timeout);
HttpSession httpSession = session instanceof WrappedHttpSession ? ((WrappedHttpSession) session).getHttpSession() : null;
log.debug("connectionStateChanged: HttpSession={}, timeout={}sec, UserSession={}", httpSession, timeout, connection.getSession());
} else {
log.debug("connectionStateChanged");
HttpSession httpSession = session instanceof WrappedHttpSession ?
((WrappedHttpSession) session).getHttpSession() : null;
log.debug("Session reinitialized: HttpSession={}, timeout={}sec, UserSession={}",
httpSession, timeout, connection.getSession());
}
initExceptionHandlers(true);
for (final AppUI ui : getAppUIs()) {
ui.accessSynchronously(() -> {
AppWindow appWindow = createAppWindow(ui);
ui.showView(appWindow);
});
AppUI currentUi = AppUI.getCurrent();
createTopLevelWindow(currentUi);
for (AppUI ui : getAppUIs()) {
if (currentUi != ui) {
ui.accessSynchronously(() ->
createTopLevelWindow(ui)
);
}
}
if (linkHandler != null) {
@ -92,30 +117,20 @@ public class DefaultApp extends App implements ConnectionListener, UserSubstitut
afterLoggedIn();
} else {
cleanupBackgroundTasks();
closeAllWindows();
clearSettingsCache();
for (final AppUI ui : getAppUIs()) {
ui.accessSynchronously(() -> {
if (ui.isTestMode()) {
ui.getTestIdManager().reset();
}
UIView window = createLoginWindow(ui);
ui.showView(window);
});
}
initExceptionHandlers(false);
setLocale(resolveLocale(null));
getConnection().loginAnonymous(getLocale());
}
}
@Override
protected void initView(AppUI ui) {
if (connection.isAlive() || loginOnStart()) {
ui.showView(createAppWindow(ui));
protected String routeTopLevelWindowId() {
if (connection.isConnected() && connection.isAuthenticated()) {
return "mainWindow";
} else {
ui.showView(createLoginWindow(ui));
return "loginWindow";
}
}
@ -123,24 +138,23 @@ public class DefaultApp extends App implements ConnectionListener, UserSubstitut
* Perform actions after successful login
*/
protected void afterLoggedIn() {
if (!webAuthConfig.getExternalAuthentication()) {
UserSessionSource sessionSource = AppBeans.get(UserSessionSource.NAME);
final User user = sessionSource.getUserSession().getUser();
if (connection.isAuthenticated() && !webAuthConfig.getExternalAuthentication()) {
User user = userSessionSource.getUserSession().getUser();
// Change password on logon
if (Boolean.TRUE.equals(user.getChangePasswordAtNextLogon())) {
final WebWindowManager wm = getWindowManager();
for (com.haulmont.cuba.gui.components.Window window : wm.getOpenWindows())
WebWindowManager wm = getWindowManager();
for (Window window : wm.getOpenWindows()) {
window.setEnabled(false);
}
WindowConfig windowConfig = AppBeans.get(WindowConfig.NAME);
WindowInfo changePasswordDialog = windowConfig.getWindowInfo("sec$User.changePassword");
wm.getDialogParams().setCloseable(false);
Map<String, Object> params = Collections.singletonMap("cancelEnabled", (Object) Boolean.FALSE);
com.haulmont.cuba.gui.components.Window changePasswordWindow = wm.openWindow(changePasswordDialog,
WindowManager.OpenType.DIALOG, params);
Window changePasswordWindow = wm.openWindow(changePasswordDialog,
OpenType.DIALOG.closeable(false),
ParamsMap.of("cancelEnabled", Boolean.FALSE));
changePasswordWindow.addCloseListener(actionId -> {
for (com.haulmont.cuba.gui.components.Window window : wm.getOpenWindows()) {
for (Window window : wm.getOpenWindows()) {
window.setEnabled(true);
}
});
@ -150,14 +164,14 @@ public class DefaultApp extends App implements ConnectionListener, UserSubstitut
@Override
protected boolean loginOnStart() {
if (tryLoginOnStart &&
principal != null
if (tryLoginOnStart
&& principal != null
&& webAuthConfig.getExternalAuthentication()) {
String userName = principal.getName();
log.debug("Trying to login after external authentication as " + userName);
log.debug("Trying to login after external authentication as {}", userName);
try {
((ExternallyAuthenticatedConnection) connection).loginAfterExternalAuthentication(userName, locale);
((ExternallyAuthenticatedConnection) connection).loginAfterExternalAuthentication(userName, getLocale());
return true;
} catch (LoginException e) {
@ -175,15 +189,17 @@ public class DefaultApp extends App implements ConnectionListener, UserSubstitut
public void userSubstituted(Connection connection) {
cleanupBackgroundTasks();
clearSettingsCache();
closeAllWindows();
for (final AppUI ui : getAppUIs()) {
ui.accessSynchronously(() -> {
if (ui.isTestMode()) {
ui.getTestIdManager().reset();
}
AppUI currentUi = AppUI.getCurrent();
createTopLevelWindow(currentUi);
ui.showView(createAppWindow(ui));
});
for (AppUI ui : getAppUIs()) {
if (currentUi != ui) {
ui.accessSynchronously(() ->
createTopLevelWindow(ui)
);
}
}
}
}

View File

@ -18,24 +18,34 @@
package com.haulmont.cuba.web;
import com.haulmont.bali.util.ParamsMap;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.ClientType;
import com.haulmont.cuba.core.global.Configuration;
import com.haulmont.cuba.core.global.GlobalConfig;
import com.haulmont.cuba.security.global.LoginException;
import com.haulmont.cuba.security.global.LoginFailedException;
import com.haulmont.cuba.security.global.UserSession;
import com.haulmont.cuba.web.auth.ExternallyAuthenticatedConnection;
import com.haulmont.cuba.web.auth.WebAuthConfig;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.inject.Inject;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
/**
* Default {@link Connection} implementation for web-client.
*
*/
@Component(Connection.NAME)
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class DefaultConnection extends AbstractConnection implements ExternallyAuthenticatedConnection {
protected Configuration configuration = AppBeans.get(Configuration.NAME);
@Inject
protected GlobalConfig globalConfig;
@Inject
protected WebAuthConfig webAuthConfig;
@Override
public void login(String login, String password, Locale locale) throws LoginException {
@ -43,45 +53,72 @@ public class DefaultConnection extends AbstractConnection implements ExternallyA
throw new IllegalArgumentException("Locale is null");
}
update(doLogin(login, password, locale, getLoginParams()));
update(doLogin(login, password, locale, getLoginParams()), SessionMode.AUTHENTICATED);
}
@Override
public void loginAnonymous(Locale locale) throws LoginException {
UUID anonymousSessionId = globalConfig.getAnonymousSessionId();
UserSession session = doLoginAnonymous(anonymousSessionId, locale);
if (session == null) {
throw new LoginFailedException("Unable to obtain anonymous session");
}
session.setLocale(locale);
update(session, SessionMode.ANONYMOUS);
}
/**
* Forward login logic to {@link com.haulmont.cuba.security.app.LoginService}.
* Can be overridden to change login logic.
*
* @param login login name
* @param password encrypted password
* @param locale client locale
* @param loginParams login params
* @param login login name
* @param password encrypted password
* @param locale client locale
* @param loginParams login params
* @return created user session
* @throws LoginException in case of unsuccessful login
*/
protected UserSession doLogin(String login, String password, Locale locale, Map<String, Object> loginParams) throws LoginException {
protected UserSession doLogin(String login, String password, Locale locale, Map<String, Object> loginParams)
throws LoginException {
return loginService.login(login, password, locale, loginParams);
}
/**
* Forward login logic to {@link com.haulmont.cuba.security.app.LoginService}.
* Can be overridden to change login logic.
*
* @param locale client locale
* @return obtained user session
* @throws LoginException in case of unsuccessful login
*/
protected UserSession doLoginAnonymous(UUID anonymousSessionId, Locale locale) throws LoginException {
return loginService.getSession(anonymousSessionId);
}
@Override
public void loginByRememberMe(String login, String rememberMeToken, Locale locale) throws LoginException {
if (locale == null) {
throw new IllegalArgumentException("Locale is null");
}
update(doLoginByRememberMe(login, rememberMeToken, locale, getLoginParams()));
update(doLoginByRememberMe(login, rememberMeToken, locale, getLoginParams()), SessionMode.AUTHENTICATED);
}
/**
* Forward login logic to {@link com.haulmont.cuba.security.app.LoginService}.
* Can be overridden to change login logic.
*
* @param login login name
* @param password encrypted password
* @param locale client locale
* @param loginParams login params
* @param login login name
* @param password encrypted password
* @param locale client locale
* @param loginParams login params
* @return created user session
* @throws LoginException in case of unsuccessful login
*/
protected UserSession doLoginByRememberMe(String login, String password, Locale locale, Map<String, Object> loginParams) throws LoginException {
protected UserSession doLoginByRememberMe(String login, String password, Locale locale, Map<String, Object> loginParams)
throws LoginException {
return loginService.loginByRememberMe(login, password, locale, loginParams);
}
@ -91,8 +128,8 @@ public class DefaultConnection extends AbstractConnection implements ExternallyA
throw new IllegalArgumentException("Locale is null");
}
String password = configuration.getConfig(WebAuthConfig.class).getTrustedClientPassword();
update(doLoginTrusted(login, password, locale, getLoginParams()));
String password = webAuthConfig.getTrustedClientPassword();
update(doLoginTrusted(login, password, locale, getLoginParams()), SessionMode.AUTHENTICATED);
UserSession session = getSession();
if (session == null) {
@ -105,14 +142,15 @@ public class DefaultConnection extends AbstractConnection implements ExternallyA
* Forward login logic to {@link com.haulmont.cuba.security.app.LoginService}.
* Can be overridden to change login logic.
*
* @param login login name
* @param password encrypted password
* @param locale client locale
* @param loginParams login params
* @param login login name
* @param password encrypted password
* @param locale client locale
* @param loginParams login params
* @return created user session
* @throws LoginException in case of unsuccessful login
*/
protected UserSession doLoginTrusted(String login, String password, Locale locale, Map<String, Object> loginParams) throws LoginException {
protected UserSession doLoginTrusted(String login, String password, Locale locale, Map<String, Object> loginParams)
throws LoginException {
return loginService.loginTrusted(login, password, locale, loginParams);
}
@ -120,12 +158,6 @@ public class DefaultConnection extends AbstractConnection implements ExternallyA
return ParamsMap.of(ClientType.class.getName(), ClientType.WEB.name());
}
@Override
public String logout() {
super.logout();
return configuration.getConfig(WebAuthConfig.class).getExternalAuthentication() ? "login" : "";
}
@Override
public boolean checkRememberMe(String login, String rememberMeToken) {
return loginService.checkRememberMe(login, rememberMeToken);

View File

@ -1,658 +0,0 @@
/*
* Copyright (c) 2008-2016 Haulmont.
*
* 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 com.haulmont.cuba.web;
import com.google.common.base.Strings;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.gui.AppConfig;
import com.haulmont.cuba.gui.TestIdManager;
import com.haulmont.cuba.gui.theme.ThemeConstants;
import com.haulmont.cuba.gui.theme.ThemeConstantsRepository;
import com.haulmont.cuba.security.app.LoginService;
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.CubaAuthProvider;
import com.haulmont.cuba.web.auth.DomainAliasesResolver;
import com.haulmont.cuba.web.auth.ExternallyAuthenticatedConnection;
import com.haulmont.cuba.web.auth.WebAuthConfig;
import com.haulmont.cuba.web.gui.components.WebComponentsHelper;
import com.haulmont.cuba.web.toolkit.VersionedThemeResource;
import com.haulmont.cuba.web.toolkit.ui.CubaButton;
import com.haulmont.cuba.web.toolkit.ui.CubaCheckBox;
import com.haulmont.cuba.web.toolkit.ui.CubaPasswordField;
import com.vaadin.data.Property;
import com.vaadin.event.ShortcutAction;
import com.vaadin.event.ShortcutListener;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.ui.*;
import com.vaadin.ui.Notification.Type;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
/**
* Standard login window.
* <p/>
* To use a specific implementation override {@link App#createLoginWindow(AppUI)} method.
*/
public class LoginWindow extends UIView {
public static final String COOKIE_LOGIN = "rememberMe.Login";
public static final String COOKIE_PASSWORD = "rememberMe.Password";
public static final String COOKIE_REMEMBER_ME = "rememberMe";
public static final String COOKIE_LOCALE = "LAST_LOCALE";
protected static final Logger log = LoggerFactory.getLogger(LoginWindow.class);
protected final App app;
protected final AppUI ui;
protected final Connection connection;
protected TextField loginField;
protected CubaPasswordField passwordField;
protected ComboBox localesSelect;
protected Locale resolvedLocale;
protected Map<String, Locale> locales;
protected GlobalConfig globalConfig;
protected WebConfig webConfig;
protected CheckBox rememberMeCheckBox;
protected boolean rememberMeAllowed = false;
protected boolean loginByRememberMe = false;
protected Property.ValueChangeListener loginChangeListener;
protected Button okButton;
protected SubmitListener submitListener;
protected Messages messages = AppBeans.get(Messages.NAME);
protected Configuration configuration = AppBeans.get(Configuration.NAME);
protected LoginService loginService = AppBeans.get(LoginService.class);
protected Boolean bruteForceProtectionEnabled;
public LoginWindow(AppUI ui) {
log.trace("Creating {}", this);
this.ui = ui;
globalConfig = configuration.getConfig(GlobalConfig.class);
webConfig = configuration.getConfig(WebConfig.class);
locales = globalConfig.getAvailableLocales();
app = ui.getApp();
resolvedLocale = resolveLocale(app);
connection = app.getConnection();
loginField = new TextField();
passwordField = new CubaPasswordField();
localesSelect = new ComboBox();
localesSelect.setTextInputAllowed(false);
// make fields immediate to resync fast in case of login is already performed from another UI (i.e. browser tab)
loginField.setImmediate(true);
passwordField.setImmediate(true);
localesSelect.setImmediate(true);
okButton = new CubaButton();
submitListener = new SubmitListener();
rememberMeAllowed = webConfig.getRememberMeEnabled();
if (rememberMeAllowed) {
rememberMeCheckBox = new CubaCheckBox();
}
setSizeFull();
// load theme from cookies if it is changed by user in settings dialog
applyUserTheme();
initUI();
localesSelect.addValueChangeListener(event -> {
if (!resolvedLocale.equals(getUserLocale())) {
resolvedLocale = getUserLocale();
clearUI();
initUI();
}
});
if (ui.isTestMode()) {
loginField.setCubaId("loginField");
passwordField.setCubaId("passwordField");
localesSelect.setCubaId("localesField");
okButton.setCubaId("loginSubmitButton");
if (rememberMeCheckBox != null) {
rememberMeCheckBox.setCubaId("rememberMeCheckBox");
}
TestIdManager testIdManager = ui.getTestIdManager();
loginField.setId(testIdManager.reserveId("loginField"));
passwordField.setId(testIdManager.reserveId("passwordField"));
localesSelect.setId(testIdManager.reserveId("localesField"));
okButton.setId(testIdManager.reserveId("loginSubmitButton"));
if (rememberMeCheckBox != null) {
rememberMeCheckBox.setId(testIdManager.reserveId("rememberMeCheckBox"));
}
}
addShortcutListener(new ShortcutListener("Default key", ShortcutAction.KeyCode.ENTER, null) {
@Override
public void handleAction(Object sender, Object target) {
doLogin();
}
});
}
protected void applyUserTheme() {
String uiTheme = ui.getTheme();
String userAppTheme = app.getCookieValue(App.APP_THEME_COOKIE_PREFIX + globalConfig.getWebContextName());
if (userAppTheme != null) {
if (!StringUtils.equals(userAppTheme, uiTheme)) {
// check theme support
ThemeConstantsRepository themeRepository = AppBeans.get(ThemeConstantsRepository.NAME);
Set<String> supportedThemes = themeRepository.getAvailableThemes();
if (supportedThemes.contains(userAppTheme)) {
app.applyTheme(userAppTheme);
ui.setTheme(userAppTheme);
}
}
}
}
protected Locale resolveLocale(App app) {
if (globalConfig.getLocaleSelectVisible()) {
String lastLocale = app.getCookieValue(COOKIE_LOCALE);
if (lastLocale != null) {
for (Locale locale : locales.values()) {
if (locale.toLanguageTag().equals(lastLocale)) {
return locale;
}
}
}
}
for (Locale locale : locales.values()) {
if (locale.equals(app.getLocale())) {
return locale;
}
}
// if not found and application locale contains country, try to match by language only
if (!StringUtils.isEmpty(app.getLocale().getCountry())) {
Locale appLocale = Locale.forLanguageTag(app.getLocale().getLanguage());
for (Locale locale : locales.values()) {
if (Locale.forLanguageTag(locale.getLanguage()).equals(appLocale)) {
return locale;
}
}
}
// return first locale set in the cuba.availableLocales app property
return locales.values().iterator().next();
}
protected void initStandartUI(int formWidth, int formHeight, int fieldWidth, boolean localesSelectVisible) {
setStyleName("cuba-login-main-layout");
VerticalLayout centerLayout = createCenterLayout(formWidth, formHeight, fieldWidth, localesSelectVisible);
addComponent(centerLayout);
setSizeFull();
setComponentAlignment(centerLayout, Alignment.MIDDLE_CENTER);
initFields();
loginField.focus();
}
protected VerticalLayout createCenterLayout(int formWidth, int formHeight, int fieldWidth, boolean localesSelectVisible) {
VerticalLayout centerLayout = new VerticalLayout();
centerLayout.setStyleName("cuba-login-bottom");
centerLayout.setWidth(formWidth + "px");
centerLayout.setHeight(formHeight + "px");
HorizontalLayout titleLayout = createTitleLayout();
centerLayout.addComponent(titleLayout);
centerLayout.setComponentAlignment(titleLayout, Alignment.MIDDLE_CENTER);
FormLayout loginFormLayout = createLoginFormLayout(fieldWidth, localesSelectVisible);
centerLayout.addComponent(loginFormLayout);
centerLayout.setComponentAlignment(loginFormLayout, Alignment.MIDDLE_CENTER);
return centerLayout;
}
protected HorizontalLayout createTitleLayout() {
HorizontalLayout titleLayout = new HorizontalLayout();
titleLayout.setStyleName("cuba-login-title");
titleLayout.setSpacing(true);
Image logoImage = getLogoImage();
if (logoImage != null) {
logoImage.setStyleName("cuba-login-icon");
titleLayout.addComponent(logoImage);
titleLayout.setComponentAlignment(logoImage, Alignment.MIDDLE_LEFT);
}
String welcomeMsg = messages.getMainMessage("loginWindow.welcomeLabel", resolvedLocale);
Label label = new Label(welcomeMsg.replace("\n", "<br/>"));
label.setContentMode(ContentMode.HTML);
label.setWidthUndefined();
label.setStyleName("cuba-login-caption");
if (!StringUtils.isBlank(label.getValue())) {
titleLayout.addComponent(label);
titleLayout.setComponentAlignment(label, Alignment.MIDDLE_LEFT);
}
return titleLayout;
}
protected FormLayout createLoginFormLayout(int fieldWidth, boolean localesSelectVisible) {
FormLayout loginFormLayout = new FormLayout();
loginFormLayout.setStyleName("cuba-login-form");
loginFormLayout.setSpacing(true);
loginFormLayout.setSizeUndefined();
loginField.setCaption(messages.getMainMessage("loginWindow.loginField", resolvedLocale));
loginFormLayout.addComponent(loginField);
loginField.setWidth(fieldWidth + "px");
loginField.setStyleName("username-field");
loginFormLayout.setComponentAlignment(loginField, Alignment.MIDDLE_CENTER);
passwordField.setCaption(messages.getMainMessage("loginWindow.passwordField", resolvedLocale));
passwordField.setWidth(fieldWidth + "px");
passwordField.setAutocomplete(true);
passwordField.setStyleName("password-field");
loginFormLayout.addComponent(passwordField);
loginFormLayout.setComponentAlignment(passwordField, Alignment.MIDDLE_CENTER);
if (localesSelectVisible) {
localesSelect.setCaption(messages.getMainMessage("loginWindow.localesSelect", resolvedLocale));
localesSelect.setWidth(fieldWidth + "px");
localesSelect.setNullSelectionAllowed(false);
loginFormLayout.addComponent(localesSelect);
loginFormLayout.setComponentAlignment(localesSelect, Alignment.MIDDLE_CENTER);
}
if (rememberMeAllowed) {
rememberMeCheckBox.setCaption(messages.getMainMessage("loginWindow.rememberMe", resolvedLocale));
rememberMeCheckBox.setStyleName("remember-me");
loginFormLayout.addComponent(rememberMeCheckBox);
loginFormLayout.setComponentAlignment(rememberMeCheckBox, Alignment.MIDDLE_CENTER);
}
okButton.setCaption(messages.getMainMessage("loginWindow.okButton", resolvedLocale));
okButton.addClickListener(submitListener);
okButton.setStyleName("cuba-login-submit");
okButton.setIcon(WebComponentsHelper.getIcon("app/images/login-button.png"));
loginFormLayout.addComponent(okButton);
loginFormLayout.setComponentAlignment(okButton, Alignment.MIDDLE_CENTER);
return loginFormLayout;
}
@Nullable
protected Image getLogoImage() {
final String loginLogoImagePath = messages.getMainMessage("loginWindow.logoImage", resolvedLocale);
if (StringUtils.isBlank(loginLogoImagePath) || "loginWindow.logoImage".equals(loginLogoImagePath))
return null;
return new Image(null, new VersionedThemeResource(loginLogoImagePath));
}
protected void initUI() {
ui.updateClientSystemMessages(resolvedLocale);
boolean localeSelectVisible = globalConfig.getLocaleSelectVisible();
ThemeConstants theme = app.getThemeConstants();
int formWidth = theme.getInt("cuba.web.LoginWindow.form.width");
int formHeight = theme.getInt("cuba.web.LoginWindow.form.height");
int fieldWidth = theme.getInt("cuba.web.LoginWindow.field.width");
initStandartUI(formWidth, formHeight, fieldWidth, localeSelectVisible);
}
protected void clearUI() {
okButton.removeClickListener(submitListener);
removeAllComponents();
}
protected void initRememberMe() {
String rememberMeCookie = app.getCookieValue(COOKIE_REMEMBER_ME);
if (Boolean.parseBoolean(rememberMeCookie)) {
String login;
String encodedLogin = app.getCookieValue(COOKIE_LOGIN) != null ? app.getCookieValue(COOKIE_LOGIN) : "";
try {
login = URLDecoder.decode(encodedLogin, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
login = encodedLogin;
}
String rememberMeToken = app.getCookieValue(COOKIE_PASSWORD) != null ? app.getCookieValue(COOKIE_PASSWORD) : "";
if (connection.checkRememberMe(login, rememberMeToken)) {
rememberMeCheckBox.setValue(true);
loginField.setValue(login);
passwordField.setValue(rememberMeToken);
loginByRememberMe = true;
}
loginChangeListener = new Property.ValueChangeListener() {
@Override
public void valueChange(Property.ValueChangeEvent event) {
loginByRememberMe = false;
}
};
loginField.addValueChangeListener(loginChangeListener);
passwordField.addValueChangeListener(loginChangeListener);
} else {
rememberMeCheckBox.setValue(false);
loginChangeListener = null;
}
}
protected void initFields() {
String currLocale = messages.getTools().localeToString(resolvedLocale);
String selected = null;
for (Map.Entry<String, Locale> entry : locales.entrySet()) {
localesSelect.addItem(entry.getKey());
if (messages.getTools().localeToString(entry.getValue()).equals(currLocale)) {
selected = entry.getKey();
}
}
if (selected == null) {
selected = locales.keySet().iterator().next();
}
localesSelect.setValue(selected);
if (configuration.getConfig(WebAuthConfig.class).getExternalAuthentication()) {
loginField.setValue(app.getPrincipal() == null ? "" : app.getPrincipal().getName());
passwordField.setValue("");
} else {
String defaultUser = webConfig.getLoginDialogDefaultUser();
if (!StringUtils.isBlank(defaultUser) && !"<disabled>".equals(defaultUser)) {
loginField.setValue(defaultUser);
} else {
loginField.setValue("");
}
String defaultPassw = webConfig.getLoginDialogDefaultPassword();
if (!StringUtils.isBlank(defaultPassw) && !"<disabled>".equals(defaultPassw)) {
passwordField.setValue(defaultPassw);
} else {
passwordField.setValue("");
}
}
if (rememberMeAllowed) {
initRememberMe();
}
}
@Override
public String getTitle() {
return messages.getMainMessage("loginWindow.caption", resolvedLocale);
}
public class SubmitListener implements Button.ClickListener {
@Override
public void buttonClick(Button.ClickEvent event) {
doLogin();
}
}
protected void login() {
String login = loginField.getValue();
String password = passwordField.getValue() != null ? passwordField.getValue() : "";
if (StringUtils.isEmpty(login) || StringUtils.isEmpty(password)) {
String message = messages.getMainMessage("loginWindow.emptyLoginOrPassword", resolvedLocale);
Notification notification = new Notification(message, Type.WARNING_MESSAGE);
notification.setDelayMsec(WebWindowManager.WARNING_NOTIFICATION_DELAY_MSEC);
notification.show(ui.getPage());
return;
}
if (!bruteForceProtectionCheck(login, app.getClientAddress())) {
return;
}
try {
Locale locale = getUserLocale();
app.setLocale(locale);
PasswordEncryption passwordEncryption = AppBeans.get(PasswordEncryption.NAME);
if (loginByRememberMe && rememberMeAllowed) {
loginByRememberMe(login, password, locale);
} else if (configuration.getConfig(WebAuthConfig.class).getExternalAuthentication()) {
// try to login as externally authenticated user, fallback to regular authentication
// we use resolved locale for error messages
if (authenticateExternally(login, password, resolvedLocale)) {
login = convertLoginString(login);
((ExternallyAuthenticatedConnection) connection).loginAfterExternalAuthentication(login, locale);
} else {
login(login, passwordEncryption.getPlainHash(password), locale);
}
} else {
login(login, passwordEncryption.getPlainHash(password), locale);
}
// locale could be set on the server
if (connection.getSession() != null) {
app.setLocale(connection.getSession().getLocale());
if (globalConfig.getLocaleSelectVisible()) {
app.addCookie(COOKIE_LOCALE, locale.toLanguageTag());
}
}
} catch (LoginException e) {
log.info("Login failed: {}", e.toString());
String message = StringUtils.abbreviate(e.getMessage(), 1000);
String bruteForceMsg = registerUnsuccessfulLoginAttempt(login, app.getClientAddress());
if (!Strings.isNullOrEmpty(bruteForceMsg)) {
message = bruteForceMsg;
}
showLoginException(message);
} catch (Exception e) {
log.warn("Unable to login", e);
showException(e);
}
}
protected boolean isBruteForceProtectionEnabled() {
if (bruteForceProtectionEnabled == null) {
bruteForceProtectionEnabled = loginService.isBruteForceProtectionEnabled();
}
return bruteForceProtectionEnabled;
}
protected boolean bruteForceProtectionCheck(String login, String ipAddress) {
if (isBruteForceProtectionEnabled()) {
if (loginService.loginAttemptsLeft(login, ipAddress) <= 0) {
String title = messages.getMainMessage("loginWindow.loginFailed", resolvedLocale);
String message = messages.formatMessage(messages.getMainMessagePack(),
"loginWindow.loginAttemptsNumberExceeded",
resolvedLocale,
loginService.getBruteForceBlockIntervalSec());
new Notification(title, message, Type.ERROR_MESSAGE, true).show(ui.getPage());
log.info("Blocked user login attempt: login={}, ip={}", login, ipAddress);
return false;
}
}
return true;
}
@Nullable
protected String registerUnsuccessfulLoginAttempt(String login, String ipAddress) {
String message = null;
if (isBruteForceProtectionEnabled()) {
int loginAttemptsLeft = loginService.registerUnsuccessfulLogin(login, ipAddress);
if (loginAttemptsLeft > 0) {
message = messages.formatMessage(messages.getMainMessagePack(),
"loginWindow.loginFailedAttemptsLeft",
resolvedLocale,
loginAttemptsLeft);
} else {
message = messages.formatMessage(messages.getMainMessagePack(),
"loginWindow.loginAttemptsNumberExceeded",
resolvedLocale,
loginService.getBruteForceBlockIntervalSec());
}
}
return message;
}
protected void showLoginException(String message){
String title = messages.getMainMessage("loginWindow.loginFailed", resolvedLocale);
new Notification(title, message, Type.ERROR_MESSAGE, true).show(ui.getPage());
if (loginByRememberMe) {
loginByRememberMe = false;
loginField.removeValueChangeListener(loginChangeListener);
passwordField.removeValueChangeListener(loginChangeListener);
loginChangeListener = null;
}
}
protected void showException(Exception e){
String title = messages.getMainMessage("loginWindow.loginFailed", resolvedLocale);
String message = messages.getMainMessage("loginWindow.pleaseContactAdministrator", resolvedLocale);
new Notification(title, message, Type.ERROR_MESSAGE, true).show(ui.getPage());
}
protected boolean authenticateExternally(String login, String passwordValue, Locale locale) {
CubaAuthProvider authProvider = AppBeans.get(CubaAuthProvider.NAME);
try {
authProvider.authenticate(login, passwordValue, locale);
} catch (Exception e) {
log.debug("External authentication failed", e);
return false;
}
return true;
}
/**
* Convert userName to db form
* In database users stores in form DOMAIN&#92;userName
*
* @param login Login string
* @return login in form DOMAIN&#92;userName
*/
private String convertLoginString(String login) {
DomainAliasesResolver aliasesResolver = AppBeans.get(DomainAliasesResolver.NAME);
int slashPos = login.indexOf("\\");
if (slashPos >= 0) {
String domainAlias = login.substring(0, slashPos);
String domain = aliasesResolver.getDomainName(domainAlias).toUpperCase();
String userName = login.substring(slashPos + 1);
login = domain + "\\" + userName;
} else {
int atSignPos = login.indexOf("@");
if (atSignPos >= 0) {
String domainAlias = login.substring(atSignPos + 1);
String domain = aliasesResolver.getDomainName(domainAlias).toUpperCase();
String userName = login.substring(0, atSignPos);
login = domain + "\\" + userName;
}
}
return login;
}
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 (connection.isConnected()) {
if (rememberMeAllowed) {
if (Boolean.TRUE.equals(rememberMeCheckBox.getValue())) {
if (!loginByRememberMe) {
app.addCookie(COOKIE_REMEMBER_ME, Boolean.TRUE.toString());
String login = loginField.getValue();
String encodedLogin;
try {
encodedLogin = URLEncoder.encode(login, StandardCharsets.UTF_8.name());
} 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();
UserManagementService userManagementService = AppBeans.get(UserManagementService.NAME);
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);
}
}
}
}
protected Locale getUserLocale() {
String lang = (String) localesSelect.getValue();
return locales.get(lang);
}
protected String getMessagesPack() {
return AppConfig.getMessagesPack();
}
}

View File

@ -272,4 +272,18 @@ public interface WebConfig extends Config {
@Property("cuba.web.pushEnabled")
@DefaultBoolean(value = true)
boolean getPushEnabled();
/**
* @return true if production mode is enabled
*/
@Property("cuba.web.productionMode")
@DefaultBoolean(value = true)
boolean getProductionMode();
/**
* @return GWT widgetset class
*/
@Property("cuba.web.widgetSet")
@Default("com.haulmont.cuba.web.toolkit.ui.WidgetSet")
String getWidgetSet();
}

View File

@ -17,15 +17,22 @@
package com.haulmont.cuba.web;
import com.google.common.collect.Lists;
import com.haulmont.bali.datastruct.Pair;
import com.haulmont.bali.util.ParamsMap;
import com.haulmont.cuba.client.ClientConfig;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.gui.*;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.DevelopmentException;
import com.haulmont.cuba.core.global.SilentException;
import com.haulmont.cuba.gui.ComponentsHelper;
import com.haulmont.cuba.gui.DialogParams;
import com.haulmont.cuba.gui.ScreenHistorySupport;
import com.haulmont.cuba.gui.WindowManager;
import com.haulmont.cuba.gui.app.core.dev.LayoutAnalyzer;
import com.haulmont.cuba.gui.app.core.dev.LayoutTip;
import com.haulmont.cuba.gui.components.*;
import com.haulmont.cuba.gui.components.Action.Status;
import com.haulmont.cuba.gui.components.Component.BelongToFrame;
import com.haulmont.cuba.gui.components.DialogAction.Type;
import com.haulmont.cuba.gui.components.Window;
import com.haulmont.cuba.gui.components.mainwindow.AppMenu;
@ -44,6 +51,7 @@ import com.haulmont.cuba.web.gui.components.WebComponentsHelper;
import com.haulmont.cuba.web.gui.components.mainwindow.WebAppWorkArea;
import com.haulmont.cuba.web.sys.WindowBreadCrumbs;
import com.haulmont.cuba.web.toolkit.ui.CubaLabel;
import com.haulmont.cuba.web.toolkit.ui.CubaOrderedActionsLayout;
import com.haulmont.cuba.web.toolkit.ui.CubaTabSheet;
import com.haulmont.cuba.web.toolkit.ui.CubaWindow;
import com.vaadin.event.ShortcutAction;
@ -80,7 +88,6 @@ public class WebWindowManager extends WindowManager {
protected App app;
protected AppUI ui;
protected AppWindow appWindow;
protected final WebConfig webConfig;
protected final ClientConfig clientConfig;
@ -88,17 +95,16 @@ public class WebWindowManager extends WindowManager {
protected ScreenProfiler screenProfiler;
protected final Map<ComponentContainer, WindowBreadCrumbs> tabs = new HashMap<>();
protected final Map<WindowBreadCrumbs, Stack<Map.Entry<Window, Integer>>> stacks = new HashMap<>();
protected final Map<WindowBreadCrumbs, Stack<Pair<Window, Integer>>> stacks = new HashMap<>();
protected final Map<Window, WindowOpenInfo> windowOpenMode = new LinkedHashMap<>();
protected final Map<Window, Integer> windows = new HashMap<>();
protected boolean disableSavingScreenHistory;
protected ScreenHistorySupport screenHistorySupport;
public WebWindowManager(final App app, AppWindow appWindow) {
this.ui = app.getAppUI();
this.app = app;
this.appWindow = appWindow;
public WebWindowManager(AppUI ui) {
this.ui = ui;
this.app = ui.getApp();
webConfig = configuration.getConfig(WebConfig.class);
clientConfig = configuration.getConfig(ClientConfig.class);
@ -252,7 +258,7 @@ public class WebWindowManager extends WindowManager {
return null;
}
protected Stack<Map.Entry<Window, Integer>> getStack(WindowBreadCrumbs breadCrumbs) {
protected Stack<Pair<Window, Integer>> getStack(WindowBreadCrumbs breadCrumbs) {
return stacks.get(breadCrumbs);
}
@ -310,12 +316,9 @@ public class WebWindowManager extends WindowManager {
WindowBreadCrumbs oldBreadCrumbs = tabs.get(oldLayout);
if (oldBreadCrumbs != null) {
Window oldWindow = oldBreadCrumbs.getCurrentWindow();
oldWindow.closeAndRun(MAIN_MENU_ACTION_ID, new Runnable() {
@Override
public void run() {
showWindow(window, caption, description, OpenType.NEW_TAB, false);
}
});
oldWindow.closeAndRun(MAIN_MENU_ACTION_ID, () ->
showWindow(window, caption, description, OpenType.NEW_TAB, false)
);
return;
}
}
@ -329,7 +332,7 @@ public class WebWindowManager extends WindowManager {
final WindowBreadCrumbs oldBreadCrumbs = tabs.get(oldLayout);
if (oldBreadCrumbs != null
&& windowOpenMode.containsKey(oldBreadCrumbs.getCurrentWindow().<Window>getFrame())
&& windowOpenMode.containsKey(oldBreadCrumbs.getCurrentWindow().getFrame())
&& !multipleOpen) {
Window oldWindow = oldBreadCrumbs.getCurrentWindow();
selectWindowTab(((Window.Wrapper) oldBreadCrumbs.getCurrentWindow()).getWrappedWindow());
@ -342,19 +345,16 @@ public class WebWindowManager extends WindowManager {
}
final int finalTabPosition = tabPosition;
oldWindow.closeAndRun(MAIN_MENU_ACTION_ID, new Runnable() {
@Override
public void run() {
showWindow(window, caption, description, OpenType.NEW_TAB, false);
oldWindow.closeAndRun(MAIN_MENU_ACTION_ID, () -> {
showWindow(window, caption, description, OpenType.NEW_TAB, false);
Window wrappedWindow = window;
if (window instanceof Window.Wrapper) {
wrappedWindow = ((Window.Wrapper) window).getWrappedWindow();
}
Window wrappedWindow = window;
if (window instanceof Window.Wrapper) {
wrappedWindow = ((Window.Wrapper) window).getWrappedWindow();
}
if (finalTabPosition >= 0 && finalTabPosition < tabSheet.getComponentCount() - 1) {
moveWindowTab(workArea, wrappedWindow, finalTabPosition);
}
if (finalTabPosition >= 0 && finalTabPosition < tabSheet.getComponentCount() - 1) {
moveWindowTab(workArea, wrappedWindow, finalTabPosition);
}
});
return;
@ -509,8 +509,10 @@ public class WebWindowManager extends WindowManager {
newTab = tabSheet.addTab(layout);
if (ui.isTestMode()) {
tabSheet.setTestId(newTab, ui.getTestIdManager().getTestId("tab_" + window.getId()));
tabSheet.setCubaId(newTab, "tab_" + window.getId());
String id = "tab_" + window.getId();
tabSheet.setTestId(newTab, ui.getTestIdManager().getTestId(id));
tabSheet.setCubaId(newTab, id);
}
}
String formattedCaption = formatTabCaption(window.getCaption(), window.getDescription());
@ -523,19 +525,15 @@ public class WebWindowManager extends WindowManager {
}
newTab.setClosable(true);
tabSheet.setTabCloseHandler(layout,
new CubaTabSheet.TabCloseHandler() {
@Override
public void onClose(TabSheet tabSheet, Component tabContent) {
//noinspection SuspiciousMethodCalls
WindowBreadCrumbs breadCrumbs = tabs.get(tabContent);
Runnable closeTask = new TabCloseTask(breadCrumbs);
closeTask.run();
tabSheet.setTabCloseHandler(layout, (targetTabSheet, tabContent) -> {
//noinspection SuspiciousMethodCalls
WindowBreadCrumbs breadCrumbs1 = tabs.get(tabContent);
Runnable closeTask = new TabCloseTask(breadCrumbs1);
closeTask.run();
// it is needed to force redraw tabsheet if it has a lot of tabs and part of them are hidden
tabSheet.markAsDirty();
}
});
// it is needed to force redraw tabsheet if it has a lot of tabs and part of them are hidden
targetTabSheet.markAsDirty();
});
tabSheet.setSelectedTab(layout);
} else {
tabs.put(layout, breadCrumbs);
@ -609,13 +607,13 @@ public class WebWindowManager extends WindowManager {
for (Map.Entry<Window, Integer> entry : set) {
if (entry.getKey().equals(currentWindow)) {
windows.remove(currentWindow);
getStack(breadCrumbs).push(entry);
getStack(breadCrumbs).push(new Pair<>(entry.getKey(), entry.getValue()));
pushed = true;
break;
}
}
if (!pushed) {
getStack(breadCrumbs).push(new AbstractMap.SimpleEntry<Window, Integer>(currentWindow, null));
getStack(breadCrumbs).push(new Pair<>(currentWindow, null));
}
removeFromWindowMap(currentWindow);
@ -648,11 +646,16 @@ public class WebWindowManager extends WindowManager {
}
protected WebAppWorkArea getConfiguredWorkArea() {
AppWorkArea workArea = appWindow.getMainWindow().getWorkArea();
if (workArea == null) {
throw new IllegalStateException("Application does not have any configured work area");
Window.TopLevelWindow topLevelWindow = ui.getTopLevelWindow();
if (topLevelWindow instanceof Window.MainWindow) {
AppWorkArea workArea = ((Window.MainWindow) topLevelWindow).getWorkArea();
if (workArea != null) {
return (WebAppWorkArea) workArea;
}
}
return (WebAppWorkArea) workArea;
throw new IllegalStateException("Application does not have any configured work area");
}
protected Component showWindowDialog(final Window window, OpenType openType, boolean forciblyDialog) {
@ -744,7 +747,7 @@ public class WebWindowManager extends WindowManager {
protected WindowBreadCrumbs createWindowBreadCrumbs() {
WindowBreadCrumbs windowBreadCrumbs = new WindowBreadCrumbs(getConfiguredWorkArea());
windowBreadCrumbs.setVisible(webConfig.getShowBreadCrumbs());
stacks.put(windowBreadCrumbs, new Stack<Map.Entry<Window, Integer>>());
stacks.put(windowBreadCrumbs, new Stack<>());
return windowBreadCrumbs;
}
@ -872,7 +875,6 @@ public class WebWindowManager extends WindowManager {
MessageType.WARNING,
new Action[]{
new AbstractAction(messages.getMessage(WebWindow.class, "closeTabs")) {
{
ThemeConstantsManager thCM = AppBeans.get(ThemeConstantsManager.NAME);
icon = thCM.getThemeValue("actions.dialog.Ok.icon");
@ -936,7 +938,7 @@ public class WebWindowManager extends WindowManager {
switch (openInfo.getOpenMode()) {
case DIALOG: {
final CubaWindow cubaDialogWindow = (CubaWindow) openInfo.getData();
cubaDialogWindow.dispose();
cubaDialogWindow.forceClose();
fireListeners(window, tabs.size() != 0);
break;
}
@ -980,8 +982,8 @@ public class WebWindowManager extends WindowManager {
breadCrumbs.removeWindow();
Window currentWindow = breadCrumbs.getCurrentWindow();
if (!getStack(breadCrumbs).empty()) {
Map.Entry<Window, Integer> entry = getStack(breadCrumbs).pop();
putToWindowMap(entry.getKey(), entry.getValue());
Pair<Window, Integer> entry = getStack(breadCrumbs).pop();
putToWindowMap(entry.getFirst(), entry.getSecond());
}
final Component component = WebComponentsHelper.getComposition(currentWindow);
component.setSizeFull();
@ -1129,12 +1131,9 @@ public class WebWindowManager extends WindowManager {
button.setCaption(action.getCaption());
button.setIcon(WebComponentsHelper.getIcon(action.getIcon()));
button.addStyleName(WebButton.ICON_STYLE);
button.addClickListener(new Button.ClickListener() {
@Override
public void buttonClick(Button.ClickEvent event) {
vWindow.close();
}
});
button.addClickListener((Button.ClickListener) event ->
vWindow.close()
);
buttonsContainer.addComponent(button);
button.focus();
@ -1350,21 +1349,18 @@ public class WebWindowManager extends WindowManager {
@Override
public void initDebugIds(final Frame frame) {
if (ui.isTestMode()) {
com.haulmont.cuba.gui.ComponentsHelper.walkComponents(frame, new ComponentVisitor() {
@Override
public void visit(com.haulmont.cuba.gui.components.Component component, String name) {
if (component.getDebugId() == null) {
Frame componentFrame = null;
if (component instanceof com.haulmont.cuba.gui.components.Component.BelongToFrame) {
componentFrame = ((com.haulmont.cuba.gui.components.Component.BelongToFrame) component).getFrame();
}
if (componentFrame == null) {
log.warn("Frame for component " + component.getClass() + " is not assigned");
} else {
if (component instanceof WebAbstractComponent) {
WebAbstractComponent webComponent = (WebAbstractComponent) component;
webComponent.assignAutoDebugId();
}
com.haulmont.cuba.gui.ComponentsHelper.walkComponents(frame, (component, name) -> {
if (component.getDebugId() == null) {
Frame componentFrame = null;
if (component instanceof BelongToFrame) {
componentFrame = ((BelongToFrame) component).getFrame();
}
if (componentFrame == null) {
log.warn("Frame for component " + component.getClass() + " is not assigned");
} else {
if (component instanceof WebAbstractComponent) {
WebAbstractComponent webComponent = (WebAbstractComponent) component;
webComponent.assignAutoDebugId();
}
}
}
@ -1389,7 +1385,7 @@ public class WebWindowManager extends WindowManager {
@Override
protected Window getWindow(Integer hashCode) {
AppWorkArea workArea = appWindow.getMainWindow().getWorkArea();
AppWorkArea workArea = getConfiguredWorkArea();
if (workArea == null || workArea.getMode() == AppWorkArea.Mode.SINGLE) {
return null;
}
@ -1426,46 +1422,49 @@ public class WebWindowManager extends WindowManager {
}
}
protected void initMainWindow(WindowInfo windowInfo) {
public void createTopLevelWindow(WindowInfo windowInfo) {
ui.beforeTopLevelWindowInit();
String template = windowInfo.getTemplate();
Window mainWindow;
Window.TopLevelWindow topLevelWindow;
Map<String, Object> params = Collections.emptyMap();
if (template != null) {
//noinspection unchecked
mainWindow = createWindow(windowInfo, OpenType.NEW_TAB, params, LayoutLoaderConfig.getWindowLoaders());
topLevelWindow = (Window.TopLevelWindow) createWindow(windowInfo, OpenType.NEW_TAB, params,
LayoutLoaderConfig.getWindowLoaders());
} else {
Class screenClass = windowInfo.getScreenClass();
if (screenClass != null) {
//noinspection unchecked
mainWindow = createWindowByScreenClass(windowInfo, params);
topLevelWindow = (Window.TopLevelWindow) createWindowByScreenClass(windowInfo, params);
} else {
throw new DevelopmentException("Unable to load app window");
throw new DevelopmentException("Unable to load top level window");
}
}
// detect work area
WebWindow windowImpl = (WebWindow) ((Window.Wrapper) mainWindow).getWrappedWindow();
Window windowImpl = ((Window.Wrapper) topLevelWindow).getWrappedWindow();
// bind system UI components to AbstractMainWindow
ComponentsHelper.walkComponents(windowImpl, component -> {
if (component instanceof AppWorkArea) {
((AbstractMainWindow) mainWindow).setWorkArea((AppWorkArea) component);
} else if (component instanceof UserIndicator) {
((AbstractMainWindow) mainWindow).setUserIndicator((UserIndicator) component);
} else if (component instanceof FoldersPane) {
((AbstractMainWindow) mainWindow).setFoldersPane((FoldersPane) component);
}
if (topLevelWindow instanceof AbstractMainWindow) {
AbstractMainWindow mainWindow = (AbstractMainWindow) topLevelWindow;
return false;
});
// bind system UI components to AbstractMainWindow
ComponentsHelper.walkComponents(windowImpl, component -> {
if (component instanceof AppWorkArea) {
mainWindow.setWorkArea((AppWorkArea) component);
} else if (component instanceof UserIndicator) {
mainWindow.setUserIndicator((UserIndicator) component);
} else if (component instanceof FoldersPane) {
mainWindow.setFoldersPane((FoldersPane) component);
}
// attach main window to UI
Component windowLayout = windowImpl.getComposition();
appWindow.addComponent(windowLayout, 0);
appWindow.setExpandRatio(windowLayout, 1);
return false;
});
}
appWindow.setMainWindow((Window.MainWindow) mainWindow);
ui.setTopLevelWindow(topLevelWindow);
// load menu
ComponentsHelper.walkComponents(windowImpl, component -> {
@ -1477,30 +1476,35 @@ public class WebWindowManager extends WindowManager {
return false;
});
AppWorkArea workArea = appWindow.getMainWindow().getWorkArea();
if (workArea != null) {
workArea.addStateChangeListener(new AppWorkArea.StateChangeListener() {
@Override
public void stateChanged(AppWorkArea.State newState) {
if (newState == AppWorkArea.State.WINDOW_CONTAINER) {
initTabShortcuts();
if (topLevelWindow instanceof Window.HasWorkArea) {
AppWorkArea workArea = ((Window.HasWorkArea) topLevelWindow).getWorkArea();
if (workArea != null) {
workArea.addStateChangeListener(new AppWorkArea.StateChangeListener() {
@Override
public void stateChanged(AppWorkArea.State newState) {
if (newState == AppWorkArea.State.WINDOW_CONTAINER) {
initTabShortcuts();
// listener used only once
getConfiguredWorkArea().removeStateChangeListener(this);
// listener used only once
getConfiguredWorkArea().removeStateChangeListener(this);
}
}
}
});
});
}
}
afterShowWindow(mainWindow);
afterShowWindow(topLevelWindow);
}
protected void initTabShortcuts() {
Window.TopLevelWindow topLevelWindow = ui.getTopLevelWindow();
CubaOrderedActionsLayout actionsLayout = topLevelWindow.unwrap(CubaOrderedActionsLayout.class);
if (getConfiguredWorkArea().getMode() == AppWorkArea.Mode.TABBED) {
appWindow.addShortcutListener(createNextWindowTabShortcut());
appWindow.addShortcutListener(createPreviousWindowTabShortcut());
actionsLayout.addShortcutListener(createNextWindowTabShortcut());
actionsLayout.addShortcutListener(createPreviousWindowTabShortcut());
}
appWindow.addShortcutListener(createCloseShortcut());
actionsLayout.addShortcutListener(createCloseShortcut());
}
protected boolean hasDialogWindows() {
@ -1531,12 +1535,9 @@ public class WebWindowManager extends WindowManager {
if (stacks.get(breadCrumbs).empty()) {
final Component previousTab = tabSheet.getPreviousTab(layout);
if (previousTab != null) {
breadCrumbs.getCurrentWindow().closeAndRun(Window.CLOSE_ACTION_ID, new Runnable() {
@Override
public void run() {
tabSheet.setSelectedTab(previousTab);
}
});
breadCrumbs.getCurrentWindow().closeAndRun(Window.CLOSE_ACTION_ID, () ->
tabSheet.setSelectedTab(previousTab)
);
} else {
breadCrumbs.getCurrentWindow().close(Window.CLOSE_ACTION_ID);
}

View File

@ -40,7 +40,6 @@ import com.haulmont.cuba.security.app.UserSettingService;
import com.haulmont.cuba.security.entity.SearchFolder;
import com.haulmont.cuba.web.App;
import com.haulmont.cuba.web.AppUI;
import com.haulmont.cuba.web.AppWindow;
import com.haulmont.cuba.web.WebConfig;
import com.haulmont.cuba.web.app.UserSettingsTools;
import com.haulmont.cuba.web.filestorage.WebExportDisplay;
@ -64,7 +63,6 @@ import static com.haulmont.cuba.gui.components.Window.COMMIT_ACTION_ID;
/**
* Left panel containing application and search folders.
*
*/
public class CubaFoldersPane extends VerticalLayout {
@ -227,7 +225,7 @@ public class CubaFoldersPane extends VerticalLayout {
protected void setupUpdateTimer() {
int period = webConfig.getAppFoldersRefreshPeriodSec() * 1000;
AppWindow appWindow = App.getInstance().getAppWindow();
AppUI appWindow = AppUI.getCurrent();
for (CubaTimer t : appWindow.getTimers()) {
if (t instanceof FoldersPaneTimer) {
t.stop();

View File

@ -0,0 +1,461 @@
/*
* Copyright (c) 2008-2016 Haulmont.
*
* 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 com.haulmont.cuba.web.app.loginwindow;
import com.google.common.base.Strings;
import com.haulmont.cuba.core.global.GlobalConfig;
import com.haulmont.cuba.core.global.PasswordEncryption;
import com.haulmont.cuba.core.global.UserSessionSource;
import com.haulmont.cuba.gui.components.*;
import com.haulmont.cuba.security.app.LoginService;
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.App;
import com.haulmont.cuba.web.Connection;
import com.haulmont.cuba.web.WebConfig;
import com.haulmont.cuba.web.auth.CubaAuthProvider;
import com.haulmont.cuba.web.auth.DomainAliasesResolver;
import com.haulmont.cuba.web.auth.ExternallyAuthenticatedConnection;
import com.haulmont.cuba.web.auth.WebAuthConfig;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import javax.inject.Inject;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Map;
public class AppLoginWindow extends AbstractWindow implements Window.TopLevelWindow {
private static final Logger log = LoggerFactory.getLogger(AppLoginWindow.class);
protected static final ThreadLocal<AuthInfo> authInfoThreadLocal = new ThreadLocal<>();
public static final String COOKIE_REMEMBER_ME = "rememberMe";
public static final String COOKIE_LOGIN = "rememberMe.Login";
public static final String COOKIE_PASSWORD = "rememberMe.Password";
public static class AuthInfo {
private String login;
private String password;
private Boolean rememberMe;
public AuthInfo(String login, String password, Boolean rememberMe) {
this.login = login;
this.password = password;
this.rememberMe = rememberMe;
}
public String getLogin() {
return login;
}
public String getPassword() {
return password;
}
public Boolean getRememberMe() {
return rememberMe;
}
}
@Inject
protected GlobalConfig globalConfig;
@Inject
protected WebConfig webConfig;
@Inject
protected WebAuthConfig webAuthConfig;
@Inject
protected LoginService loginService;
@Inject
protected UserSessionSource userSessionSource;
@Inject
protected PasswordEncryption passwordEncryption;
@Inject
protected DomainAliasesResolver domainAliasesResolver;
@Inject
protected CubaAuthProvider cubaAuthProvider;
@Inject
protected UserManagementService userManagementService;
@Inject
protected Embedded logoImage;
@Inject
protected TextField loginField;
@Inject
protected CheckBox rememberMeCheckBox;
@Inject
protected PasswordField passwordField;
@Inject
protected LookupField localesSelect;
protected boolean loginByRememberMe = false;
protected ValueChangeListener loginChangeListener;
protected Boolean bruteForceProtectionEnabled;
@Override
public void init(Map<String, Object> params) {
super.init(params);
loginField.requestFocus();
initLogoImage();
initDefaultCredentials();
initLocales();
initRememberMe();
}
protected void initLocales() {
Map<String, Locale> locales = globalConfig.getAvailableLocales();
localesSelect.setOptionsMap(locales);
localesSelect.setValue(App.getInstance().getLocale());
localesSelect.setVisible(globalConfig.getLocaleSelectVisible());
localesSelect.addValueChangeListener(e -> {
Locale selectedLocale = (Locale) e.getValue();
App app = App.getInstance();
app.setLocale(selectedLocale);
authInfoThreadLocal.set(new AuthInfo(loginField.getValue(), passwordField.getValue(),
rememberMeCheckBox.getValue()));
try {
app.createTopLevelWindow();
} finally {
authInfoThreadLocal.set(null);
}
});
}
protected void initLogoImage() {
String loginLogoImagePath = messages.getMainMessage("loginWindow.logoImage", userSessionSource.getLocale());
if (StringUtils.isBlank(loginLogoImagePath) || "loginWindow.logoImage".equals(loginLogoImagePath)) {
logoImage.setVisible(false);
} else {
logoImage.setSource("theme://" + loginLogoImagePath);
}
}
protected void initRememberMe() {
loginChangeListener = e -> loginByRememberMe = false;
if (!webConfig.getRememberMeEnabled()) {
rememberMeCheckBox.setValue(false);
rememberMeCheckBox.setVisible(false);
return;
}
App app = App.getInstance();
String rememberMeCookie = app.getCookieValue(COOKIE_REMEMBER_ME);
if (Boolean.parseBoolean(rememberMeCookie)) {
String login;
String encodedLogin = app.getCookieValue(COOKIE_LOGIN) != null ? app.getCookieValue(COOKIE_LOGIN) : "";
try {
login = URLDecoder.decode(encodedLogin, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
login = encodedLogin;
}
String rememberMeToken = app.getCookieValue(COOKIE_PASSWORD) != null ? app.getCookieValue(COOKIE_PASSWORD) : "";
if (app.getConnection().checkRememberMe(login, rememberMeToken)) {
rememberMeCheckBox.setValue(true);
loginField.setValue(login);
passwordField.setValue(rememberMeToken);
loginByRememberMe = true;
}
loginField.addValueChangeListener(loginChangeListener);
passwordField.addValueChangeListener(loginChangeListener);
}
}
protected void initDefaultCredentials() {
AuthInfo authInfo = authInfoThreadLocal.get();
if (authInfo != null) {
loginField.setValue(authInfo.getLogin());
passwordField.setValue(authInfo.getPassword());
rememberMeCheckBox.setValue(authInfo.getRememberMe());
localesSelect.requestFocus();
authInfoThreadLocal.set(null);
return;
}
App app = App.getInstance();
if (webAuthConfig.getExternalAuthentication()) {
loginField.setValue(app.getPrincipal() == null ? "" : app.getPrincipal().getName());
passwordField.setValue("");
} else {
String defaultUser = webConfig.getLoginDialogDefaultUser();
if (!StringUtils.isBlank(defaultUser) && !"<disabled>".equals(defaultUser)) {
loginField.setValue(defaultUser);
} else {
loginField.setValue("");
}
String defaultPassw = webConfig.getLoginDialogDefaultPassword();
if (!StringUtils.isBlank(defaultPassw) && !"<disabled>".equals(defaultPassw)) {
passwordField.setValue(defaultPassw);
} else {
passwordField.setValue("");
}
}
}
/**
* Convert userName to db form
* In database users stores in form DOMAIN&#92;userName
*
* @param login Login string
* @return login in form DOMAIN&#92;userName
*/
protected String convertLoginString(String login) {
int slashPos = login.indexOf("\\");
if (slashPos >= 0) {
String domainAlias = login.substring(0, slashPos);
String domain = domainAliasesResolver.getDomainName(domainAlias).toUpperCase();
String userName = login.substring(slashPos + 1);
login = domain + "\\" + userName;
} else {
int atSignPos = login.indexOf("@");
if (atSignPos >= 0) {
String domainAlias = login.substring(atSignPos + 1);
String domain = domainAliasesResolver.getDomainName(domainAlias).toUpperCase();
String userName = login.substring(0, atSignPos);
login = domain + "\\" + userName;
}
}
return login;
}
protected void showUnhandledExceptionOnLogin(Exception e) {
String title = messages.getMainMessage("loginWindow.loginFailed", userSessionSource.getLocale());
String message = messages.getMainMessage("loginWindow.pleaseContactAdministrator", userSessionSource.getLocale());
showNotification(title, message, NotificationType.ERROR);
}
protected void showLoginException(String message){
String title = messages.getMainMessage("loginWindow.loginFailed", userSessionSource.getLocale());
showNotification(title, message, NotificationType.ERROR);
if (loginByRememberMe) {
loginByRememberMe = false;
loginField.removeValueChangeListener(loginChangeListener);
passwordField.removeValueChangeListener(loginChangeListener);
}
}
public void login() {
doLogin();
App app = App.getInstance();
Connection connection = app.getConnection();
if (connection.isConnected()) {
if (webConfig.getRememberMeEnabled()) {
if (Boolean.TRUE.equals(rememberMeCheckBox.getValue())) {
if (!loginByRememberMe) {
app.addCookie(COOKIE_REMEMBER_ME, Boolean.TRUE.toString());
String login = loginField.getValue();
String encodedLogin;
try {
encodedLogin = URLEncoder.encode(login, StandardCharsets.UTF_8.name());
} 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);
}
}
}
}
protected void doLogin() {
String login = loginField.getValue();
String password = passwordField.getValue() != null ? passwordField.getValue() : "";
if (StringUtils.isEmpty(login) || StringUtils.isEmpty(password)) {
showNotification(messages.getMainMessage("loginWindow.emptyLoginOrPassword"), NotificationType.WARNING);
}
App app = App.getInstance();
if (!bruteForceProtectionCheck(login, app.getClientAddress())) {
return;
}
try {
Connection connection = app.getConnection();
Locale selectedLocale = localesSelect.getValue();
app.setLocale(selectedLocale);
if (loginByRememberMe && webConfig.getRememberMeEnabled()) {
doLoginByRememberMe(login, password, selectedLocale);
} else if (webAuthConfig.getExternalAuthentication()) {
// try to login as externally authenticated user, fallback to regular authentication
// we use resolved locale for error messages
if (authenticateExternally(login, password, selectedLocale)) {
login = convertLoginString(login);
((ExternallyAuthenticatedConnection) connection).loginAfterExternalAuthentication(login, selectedLocale);
} else {
doLogin(login, passwordEncryption.getPlainHash(password), selectedLocale);
}
} else {
doLogin(login, passwordEncryption.getPlainHash(password), selectedLocale);
}
// locale could be set on the server
if (connection.getSession() != null) {
Locale loggedInLocale = userSessionSource.getLocale();
if (globalConfig.getLocaleSelectVisible()) {
app.addCookie(App.COOKIE_LOCALE, loggedInLocale.toLanguageTag());
}
}
} catch (LoginException e) {
log.info("Login failed: {}", e.toString());
String message = StringUtils.abbreviate(e.getMessage(), 1000);
String bruteForceMsg = registerUnsuccessfulLoginAttempt(login, app.getClientAddress());
if (!Strings.isNullOrEmpty(bruteForceMsg)) {
message = bruteForceMsg;
}
showLoginException(message);
} catch (Exception e) {
log.warn("Unable to login", e);
showUnhandledExceptionOnLogin(e);
}
}
protected void doLogin(String login, String password, Locale locale) throws LoginException {
App app = App.getInstance();
app.getConnection().login(login, password, locale);
}
protected void doLoginByRememberMe(String login, String rememberMeToken, Locale locale) throws LoginException {
App app = App.getInstance();
app.getConnection().loginByRememberMe(login, rememberMeToken, locale);
}
protected boolean authenticateExternally(String login, String passwordValue, Locale locale) {
try {
cubaAuthProvider.authenticate(login, passwordValue, locale);
} catch (Exception e) {
log.debug("External authentication failed", e);
return false;
}
return true;
}
protected boolean isBruteForceProtectionEnabled() {
if (bruteForceProtectionEnabled == null) {
bruteForceProtectionEnabled = loginService.isBruteForceProtectionEnabled();
}
return bruteForceProtectionEnabled;
}
protected boolean bruteForceProtectionCheck(String login, String ipAddress) {
if (isBruteForceProtectionEnabled()) {
if (loginService.loginAttemptsLeft(login, ipAddress) <= 0) {
String title = messages.getMainMessage("loginWindow.loginFailed");
String message = messages.formatMainMessage(
"loginWindow.loginAttemptsNumberExceeded",
loginService.getBruteForceBlockIntervalSec());
showNotification(title, message, NotificationType.ERROR_HTML);
log.info("Blocked user login attempt: login={}, ip={}", login, ipAddress);
return false;
}
}
return true;
}
@Nullable
protected String registerUnsuccessfulLoginAttempt(String login, String ipAddress) {
String message = null;
if (isBruteForceProtectionEnabled()) {
int loginAttemptsLeft = loginService.registerUnsuccessfulLogin(login, ipAddress);
if (loginAttemptsLeft > 0) {
message = messages.formatMainMessage(
"loginWindow.loginFailedAttemptsLeft",
loginAttemptsLeft);
} else {
message = messages.formatMainMessage(
"loginWindow.loginAttemptsNumberExceeded",
loginService.getBruteForceBlockIntervalSec());
}
}
return message;
}
}

View File

@ -0,0 +1,82 @@
<!--
~ Copyright (c) 2008-2016 Haulmont.
~
~ 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.
-->
<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
class="com.haulmont.cuba.web.app.loginwindow.AppLoginWindow"
caption="mainMsg://loginWindow.caption">
<actions>
<action id="submit" shortcut="ENTER" invoke="login"
caption="mainMsg://loginWindow.okButton"
icon="app/images/login-button.png"/>
</actions>
<layout stylename="cuba-login-main-layout">
<vbox stylename="cuba-login-panel" width="AUTO"
margin="true" spacing="true" align="MIDDLE_CENTER">
<hbox stylename="cuba-login-title" spacing="true" align="MIDDLE_CENTER">
<embedded id="logoImage"
width="AUTO"
height="AUTO"
type="IMAGE"
align="MIDDLE_LEFT"
stylename="cuba-login-icon"/>
<label id="welcomeLabel"
align="MIDDLE_LEFT"
stylename="cuba-login-caption"
value="mainMsg://loginWindow.welcomeLabel"/>
</hbox>
<grid stylename="cuba-login-form" spacing="true">
<columns count="2"/>
<rows>
<row>
<label id="loginFieldLabel" value="mainMsg://loginWindow.loginField" align="MIDDLE_LEFT"
stylename="login-form-label"/>
<textField id="loginField" stylename="cuba-login-username"
width="theme://cuba.web.LoginWindow.field.width"/>
</row>
<row>
<label id="passwordFieldLabel" value="mainMsg://loginWindow.passwordField" align="MIDDLE_LEFT"
stylename="login-form-label"/>
<passwordField id="passwordField" stylename="cuba-login-password" autocomplete="true"
width="theme://cuba.web.LoginWindow.field.width"/>
</row>
<row>
<label id="localesSelectLabel" value="mainMsg://loginWindow.localesSelect" align="MIDDLE_LEFT"
stylename="login-form-label"/>
<lookupField id="localesSelect" width="theme://cuba.web.LoginWindow.field.width"
nullOptionVisible="false" textInputAllowed="false"/>
</row>
<row>
<label id="rememberMeSpacer"/>
<checkBox id="rememberMeCheckBox" caption="mainMsg://loginWindow.rememberMe"/>
</row>
<row>
<label id="submitSpacer"/>
<button id="loginButton" action="submit"/>
</row>
</rows>
</grid>
</vbox>
</layout>
</window>

View File

@ -21,7 +21,7 @@ import com.haulmont.bali.util.ParamsMap;
import com.haulmont.cuba.client.ClientConfig;
import com.haulmont.cuba.core.global.Configuration;
import com.haulmont.cuba.core.global.FtsConfigHelper;
import com.haulmont.cuba.gui.WindowManager;
import com.haulmont.cuba.gui.WindowManager.OpenType;
import com.haulmont.cuba.gui.app.core.dev.LayoutAnalyzer;
import com.haulmont.cuba.gui.app.core.dev.LayoutTip;
import com.haulmont.cuba.gui.components.*;
@ -32,7 +32,7 @@ import com.haulmont.cuba.gui.components.mainwindow.FtsField;
import com.haulmont.cuba.web.WebConfig;
import com.haulmont.cuba.web.gui.components.WebComponentsHelper;
import com.haulmont.cuba.web.toolkit.ui.CubaHorizontalSplitPanel;
import com.vaadin.server.Sizeable;
import com.vaadin.server.Sizeable.Unit;
import org.apache.commons.lang.StringUtils;
import org.vaadin.peter.contextmenu.ContextMenu;
@ -84,20 +84,16 @@ public class AppMainWindow extends AbstractMainWindow {
if (clientConfig.getLayoutAnalyzerEnabled()) {
ContextMenu contextMenu = new ContextMenu();
contextMenu.setOpenAutomatically(true);
contextMenu.setAsContextMenuOf(logoImage.unwrap(com.vaadin.ui.Embedded.class));
contextMenu.setAsContextMenuOf(logoImage.unwrap(com.vaadin.ui.AbstractComponent.class));
ContextMenu.ContextMenuItem analyzeLayout = contextMenu.addItem(messages.getMainMessage("actions.analyzeLayout"));
analyzeLayout.addItemClickListener(new ContextMenu.ContextMenuItemClickListener() {
@Override
public void contextMenuItemClicked(ContextMenu.ContextMenuItemClickEvent event) {
Window window = AppMainWindow.this;
LayoutAnalyzer analyzer = new LayoutAnalyzer();
List<LayoutTip> tipsList = analyzer.analyze(window);
analyzeLayout.addItemClickListener(event -> {
LayoutAnalyzer analyzer = new LayoutAnalyzer();
List<LayoutTip> tipsList = analyzer.analyze(this);
if (tipsList.isEmpty()) {
window.showNotification("No layout problems found", Frame.NotificationType.HUMANIZED);
} else {
window.openWindow("layoutAnalyzer", WindowManager.OpenType.DIALOG, ParamsMap.of("tipsList", tipsList));
}
if (tipsList.isEmpty()) {
showNotification("No layout problems found", NotificationType.HUMANIZED);
} else {
openWindow("layoutAnalyzer", OpenType.DIALOG, ParamsMap.of("tipsList", tipsList));
}
});
}
@ -119,7 +115,7 @@ public class AppMainWindow extends AbstractMainWindow {
CubaHorizontalSplitPanel vSplitPanel = (CubaHorizontalSplitPanel) WebComponentsHelper.unwrap(foldersSplit);
vSplitPanel.setDefaultPosition(webConfig.getFoldersPaneDefaultWidth() + "px");
vSplitPanel.setMaxSplitPosition(50, Sizeable.Unit.PERCENTAGE);
vSplitPanel.setMaxSplitPosition(50, Unit.PERCENTAGE);
vSplitPanel.setDockable(true);
} else {
foldersPane.setEnabled(false);

View File

@ -17,7 +17,8 @@
<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
xmlns:main="http://schemas.haulmont.com/cuba/mainwindow.xsd"
class="com.haulmont.cuba.web.app.mainwindow.AppMainWindow">
class="com.haulmont.cuba.web.app.mainwindow.AppMainWindow"
caption="mainMsg://application.caption">
<layout expand="foldersSplit">
<hbox id="titleBar" stylename="cuba-app-menubar"

View File

@ -17,12 +17,20 @@
package com.haulmont.cuba.web.app.ui.security.user;
import com.haulmont.cuba.gui.app.security.user.browse.UserBrowser;
import com.haulmont.cuba.gui.components.Window;
import com.haulmont.cuba.gui.components.mainwindow.UserIndicator;
import com.haulmont.cuba.web.App;
public class UserBrowserCompanion implements UserBrowser.Companion {
@Override
public void refreshUserSubstitutions() {
App.getInstance().getAppWindow().refreshUserSubstitutions();
Window.TopLevelWindow topLevelWindow = App.getInstance().getTopLevelWindow();
if (topLevelWindow instanceof Window.HasUserIndicator) {
UserIndicator userIndicator = ((Window.HasUserIndicator) topLevelWindow).getUserIndicator();
if (userIndicator != null) {
userIndicator.refreshUserSubstitutions();
}
}
}
}

View File

@ -19,13 +19,14 @@ package com.haulmont.cuba.web.app.ui.security.user;
import com.haulmont.cuba.gui.app.security.user.edit.UserEditor;
import com.haulmont.cuba.gui.components.PasswordField;
import com.haulmont.cuba.gui.components.Window;
import com.haulmont.cuba.gui.components.mainwindow.UserIndicator;
import com.haulmont.cuba.web.App;
import com.haulmont.cuba.web.auth.WebAuthConfig;
import javax.inject.Inject;
public class UserEditorCompanion implements UserEditor.Companion {
@Inject
protected WebAuthConfig config;
@ -36,6 +37,13 @@ public class UserEditorCompanion implements UserEditor.Companion {
@Override
public void refreshUserSubstitutions() {
App.getInstance().getAppWindow().refreshUserSubstitutions();
Window.TopLevelWindow topLevelWindow = App.getInstance().getTopLevelWindow();
if (topLevelWindow instanceof Window.HasUserIndicator) {
UserIndicator userIndicator = ((Window.HasUserIndicator) topLevelWindow).getUserIndicator();
if (userIndicator != null) {
userIndicator.refreshUserSubstitutions();
}
}
}
}

View File

@ -23,6 +23,7 @@ import com.haulmont.cuba.gui.components.Frame;
import com.haulmont.cuba.gui.executors.BackgroundWorker;
import com.haulmont.cuba.gui.export.*;
import com.haulmont.cuba.web.App;
import com.haulmont.cuba.web.AppUI;
import com.haulmont.cuba.web.WebConfig;
import com.haulmont.cuba.web.toolkit.ui.CubaFileDownloader;
import com.vaadin.server.StreamResource;
@ -100,7 +101,7 @@ public class WebExportDisplay implements ExportDisplay {
}
}
CubaFileDownloader fileDownloader = App.getInstance().getAppWindow().getFileDownloader();
CubaFileDownloader fileDownloader = AppUI.getCurrent().getFileDownloader();
StreamResource resource = new StreamResource(new StreamResource.StreamSource() {
@Override

View File

@ -35,7 +35,6 @@ import com.haulmont.cuba.gui.data.DsContext;
import com.haulmont.cuba.gui.settings.Settings;
import com.haulmont.cuba.gui.theme.ThemeConstantsManager;
import com.haulmont.cuba.web.AppUI;
import com.haulmont.cuba.web.AppWindow;
import com.haulmont.cuba.web.WebWindowManager;
import com.haulmont.cuba.web.gui.components.WebAbstractComponent;
import com.haulmont.cuba.web.gui.components.WebComponentsHelper;
@ -44,6 +43,7 @@ import com.haulmont.cuba.web.toolkit.ui.CubaGroupBox;
import com.haulmont.cuba.web.toolkit.ui.CubaTree;
import com.haulmont.cuba.web.toolkit.ui.CubaVerticalActionsLayout;
import com.vaadin.event.ItemClickEvent;
import com.vaadin.server.Page;
import com.vaadin.server.Sizeable.Unit;
import com.vaadin.shared.ui.MarginInfo;
import com.vaadin.ui.*;
@ -668,8 +668,7 @@ public class WebWindow implements Window, Component.Wrapper,
@Override
public void addTimer(Timer timer) {
AppWindow appWindow = AppUI.getCurrent().getAppWindow();
appWindow.addTimer(((WebTimer) timer).getTimerImpl());
AppUI.getCurrent().addTimer(((WebTimer) timer).getTimerImpl());
if (timers == null) {
timers = new LinkedList<>();
@ -687,12 +686,12 @@ public class WebWindow implements Window, Component.Wrapper,
}
public void stopTimers() {
AppWindow appWindow = AppUI.getCurrent().getAppWindow();
AppUI appUI = AppUI.getCurrent();
if (timers != null) {
for (Timer timer : timers) {
timer.stop();
WebTimer webTimer = (WebTimer) timer;
appWindow.removeTimer(webTimer.getTimerImpl());
appUI.removeTimer(webTimer.getTimerImpl());
}
}
}
@ -1150,6 +1149,10 @@ public class WebWindow implements Window, Component.Wrapper,
dialogWindow.setCaption(caption);
}
}
if (getWrapper() instanceof TopLevelWindow) {
Page.getCurrent().setTitle(caption);
}
}
@Override

View File

@ -46,7 +46,7 @@ public abstract class WebAbstractOptionsField<T extends com.vaadin.ui.AbstractSe
implements OptionsField {
protected List optionsList;
protected Map<String, Object> optionsMap;
protected Map<String, ?> optionsMap;
protected Class<? extends EnumClass> optionsEnum;
protected CollectionDatasource optionsDatasource;
@ -117,12 +117,12 @@ public abstract class WebAbstractOptionsField<T extends com.vaadin.ui.AbstractSe
}
@Override
public void setOptionsMap(Map<String, Object> options) {
public void setOptionsMap(Map<String, ?> options) {
if (metaProperty != null && metaProperty.getRange().isEnum()) {
List constants = Arrays.asList(metaProperty.getRange().asEnumeration().getJavaClass().getEnumConstants());
List opts = new ArrayList();
for (Map.Entry<String, Object> entry : options.entrySet()) {
for (Map.Entry<String, ?> entry : options.entrySet()) {
String key = entry.getKey();
Object itemId = entry.getValue();
@ -138,7 +138,7 @@ public abstract class WebAbstractOptionsField<T extends com.vaadin.ui.AbstractSe
setCaptionMode(CaptionMode.ITEM);
} else {
List opts = new ArrayList();
for (Map.Entry<String, Object> entry : options.entrySet()) {
for (Map.Entry<String, ?> entry : options.entrySet()) {
String key = entry.getKey();
Object itemId = entry.getValue();
@ -271,7 +271,7 @@ public abstract class WebAbstractOptionsField<T extends com.vaadin.ui.AbstractSe
}
@Override
public Map<String, Object> getOptionsMap() {
public Map<String, ?> getOptionsMap() {
return optionsMap;
}

View File

@ -27,7 +27,7 @@ import com.haulmont.cuba.gui.data.impl.DsContextImplementation;
import com.haulmont.cuba.gui.settings.Settings;
import com.haulmont.cuba.gui.xml.layout.ComponentLoader;
import com.haulmont.cuba.web.AppUI;
import com.haulmont.cuba.web.AppWindow;
import com.haulmont.cuba.web.WebWindowManager;
import com.haulmont.cuba.web.toolkit.ui.CubaAccordion;
import com.vaadin.ui.Layout;
import org.apache.commons.lang.StringUtils;
@ -482,8 +482,8 @@ public class WebAccordion extends WebAbstractComponent<CubaAccordion> implements
// init debug ids after all
if (AppUI.getCurrent().isTestMode()) {
context.addPostInitTask((context1, window1) -> {
AppWindow appWindow = AppUI.getCurrent().getAppWindow();
appWindow.getWindowManager().initDebugIds(window1);
Window.TopLevelWindow appWindow = AppUI.getCurrent().getTopLevelWindow();
((WebWindowManager) appWindow.getWindowManager()).initDebugIds(window1);
});
}
}

View File

@ -31,15 +31,12 @@ public class WebButton extends WebAbstractComponent<CubaButton> implements Butto
public WebButton() {
component = new CubaButton();
component.addClickListener(new com.vaadin.ui.Button.ClickListener() {
@Override
public void buttonClick(com.vaadin.ui.Button.ClickEvent event) {
beforeActionPerformed();
if (action != null) {
performAction(action);
}
afterActionPerformed();
component.addClickListener((com.vaadin.ui.Button.ClickListener) event -> {
beforeActionPerformed();
if (action != null) {
performAction(action);
}
afterActionPerformed();
});
component.setDescription(null);
}
@ -114,20 +111,17 @@ public class WebButton extends WebAbstractComponent<CubaButton> implements Butto
action.addOwner(this);
actionPropertyChangeListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (Action.PROP_ICON.equals(evt.getPropertyName())) {
setIcon(WebButton.this.action.getIcon());
} else if (Action.PROP_CAPTION.equals(evt.getPropertyName())) {
setCaption(WebButton.this.action.getCaption());
} else if (Action.PROP_DESCRIPTION.equals(evt.getPropertyName())) {
setDescription(WebButton.this.action.getDescription());
} else if (Action.PROP_ENABLED.equals(evt.getPropertyName())) {
setEnabled(WebButton.this.action.isEnabled());
} else if (Action.PROP_VISIBLE.equals(evt.getPropertyName())) {
setVisible(WebButton.this.action.isVisible());
}
actionPropertyChangeListener = evt -> {
if (Action.PROP_ICON.equals(evt.getPropertyName())) {
setIcon(WebButton.this.action.getIcon());
} else if (Action.PROP_CAPTION.equals(evt.getPropertyName())) {
setCaption(WebButton.this.action.getCaption());
} else if (Action.PROP_DESCRIPTION.equals(evt.getPropertyName())) {
setDescription(WebButton.this.action.getDescription());
} else if (Action.PROP_ENABLED.equals(evt.getPropertyName())) {
setEnabled(WebButton.this.action.isEnabled());
} else if (Action.PROP_VISIBLE.equals(evt.getPropertyName())) {
setVisible(WebButton.this.action.isVisible());
}
};
action.addPropertyChangeListener(actionPropertyChangeListener);

View File

@ -21,7 +21,6 @@ import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.Configuration;
import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.core.sys.SecurityContext;
import com.haulmont.cuba.gui.components.Component;
import com.haulmont.cuba.gui.components.Embedded;
import com.haulmont.cuba.gui.export.ExportDataProvider;
import com.haulmont.cuba.security.global.UserSession;
@ -44,12 +43,11 @@ import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class WebEmbedded extends WebAbstractComponent<com.vaadin.ui.Embedded> implements Embedded, Component.Disposable {
public class WebEmbedded extends WebAbstractComponent<com.vaadin.ui.Embedded> implements Embedded {
protected Map<String, String> parameters = null;
protected Type type = Type.IMAGE;
protected Resource resource;
protected boolean disposed;
public WebEmbedded() {
component = new com.vaadin.ui.Embedded();
@ -222,18 +220,7 @@ public class WebEmbedded extends WebAbstractComponent<com.vaadin.ui.Embedded> im
}
}
@Override
public void dispose() {
disposed = true;
}
@Override
public boolean isDisposed() {
return disposed;
}
protected static class EmptyStreamSource implements StreamResource.StreamSource {
public static final String EMPTY_IMAGE_PATH = "/com/haulmont/cuba/web/gui/components/resources/empty.png";
protected byte[] emptyImage;

View File

@ -24,9 +24,6 @@ import com.haulmont.cuba.core.global.Configuration;
import com.haulmont.cuba.core.global.Messages;
import com.haulmont.cuba.core.global.Metadata;
import com.haulmont.cuba.gui.components.*;
import com.haulmont.cuba.gui.components.Table;
import com.haulmont.cuba.gui.components.TextField;
import com.haulmont.cuba.gui.components.Tree;
import com.haulmont.cuba.gui.components.filter.ConditionsTree;
import com.haulmont.cuba.gui.components.filter.FilterHelper;
import com.haulmont.cuba.gui.components.filter.condition.AbstractCondition;
@ -35,7 +32,6 @@ import com.haulmont.cuba.gui.components.mainwindow.FoldersPane;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.presentations.Presentations;
import com.haulmont.cuba.web.AppUI;
import com.haulmont.cuba.web.AppWindow;
import com.haulmont.cuba.web.WebConfig;
import com.haulmont.cuba.web.app.folders.AppFolderEditWindow;
import com.haulmont.cuba.web.app.folders.CubaFoldersPane;
@ -85,8 +81,12 @@ public class WebFilterHelper implements FilterHelper {
@Override
@Nullable
public AbstractSearchFolder saveFolder(AbstractSearchFolder folder) {
AppWindow appWindow = AppUI.getCurrent().getAppWindow();
FoldersPane foldersPane = appWindow.getMainWindow().getFoldersPane();
Window.TopLevelWindow topLevelWindow = AppUI.getCurrent().getTopLevelWindow();
FoldersPane foldersPane = null;
if (topLevelWindow instanceof Window.HasFoldersPane) {
foldersPane = ((Window.HasFoldersPane) topLevelWindow).getFoldersPane();
}
if (foldersPane == null)
return null;
@ -214,8 +214,12 @@ public class WebFilterHelper implements FilterHelper {
@Override
public Object getFoldersPane() {
AppWindow appWindow = AppUI.getCurrent().getAppWindow();
FoldersPane foldersPane = appWindow.getMainWindow().getFoldersPane();
Window.TopLevelWindow topLevelWindow = AppUI.getCurrent().getTopLevelWindow();
FoldersPane foldersPane = null;
if (topLevelWindow instanceof Window.HasFoldersPane) {
foldersPane = ((Window.HasFoldersPane) topLevelWindow).getFoldersPane();
}
if (foldersPane == null) {
return null;
}
@ -225,8 +229,12 @@ public class WebFilterHelper implements FilterHelper {
@Override
public void removeFolderFromFoldersPane(Folder folder) {
AppWindow appWindow = AppUI.getCurrent().getAppWindow();
FoldersPane foldersPane = appWindow.getMainWindow().getFoldersPane();
Window.TopLevelWindow topLevelWindow = AppUI.getCurrent().getTopLevelWindow();
FoldersPane foldersPane = null;
if (topLevelWindow instanceof Window.HasFoldersPane) {
foldersPane = ((Window.HasFoldersPane) topLevelWindow).getFoldersPane();
}
if (foldersPane == null) {
return;
}

View File

@ -287,7 +287,7 @@ public class WebLookupField extends WebAbstractOptionsField<CubaComboBox> implem
}
@Override
public void setOptionsMap(Map<String, Object> options) {
public void setOptionsMap(Map<String, ?> options) {
super.setOptionsMap(options);
checkMissingValue();

View File

@ -198,7 +198,7 @@ public class WebSearchField extends WebLookupField implements SearchField {
}
@Override
public void setOptionsMap(Map<String, Object> options) {
public void setOptionsMap(Map<String, ?> options) {
throw new UnsupportedOperationException();
}

View File

@ -25,7 +25,7 @@ import com.haulmont.cuba.gui.settings.Settings;
import com.haulmont.cuba.gui.xml.layout.ComponentLoader;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
import com.haulmont.cuba.web.AppUI;
import com.haulmont.cuba.web.AppWindow;
import com.haulmont.cuba.web.WebWindowManager;
import com.haulmont.cuba.web.toolkit.ui.CubaTabSheet;
import com.vaadin.ui.Layout;
import org.apache.commons.lang.StringUtils;
@ -515,8 +515,8 @@ public class WebTabSheet extends WebAbstractComponent<CubaTabSheet> implements T
// init debug ids after all
if (AppUI.getCurrent().isTestMode()) {
context.addPostInitTask((context1, window1) -> {
AppWindow appWindow = AppUI.getCurrent().getAppWindow();
appWindow.getWindowManager().initDebugIds(window1);
Window.TopLevelWindow appWindow = AppUI.getCurrent().getTopLevelWindow();
((WebWindowManager) appWindow.getWindowManager()).initDebugIds(window1);
});
}
}

View File

@ -289,12 +289,12 @@ public class WebTokenList extends WebAbstractField<WebTokenList.CubaTokenList> i
}
@Override
public Map<String, Object> getOptionsMap() {
public Map<String, ?> getOptionsMap() {
return lookupPickerField.getOptionsMap();
}
@Override
public void setOptionsMap(Map<String, Object> map) {
public void setOptionsMap(Map<String, ?> map) {
lookupPickerField.setOptionsMap(map);
}

View File

@ -23,11 +23,8 @@ import com.haulmont.cuba.gui.components.mainwindow.LogoutButton;
import com.haulmont.cuba.web.Connection;
import com.haulmont.cuba.web.WebWindowManager;
import com.haulmont.cuba.web.gui.components.WebAbstractComponent;
import com.haulmont.cuba.web.gui.components.WebButton;
import com.haulmont.cuba.web.gui.components.WebComponentsHelper;
import com.haulmont.cuba.web.toolkit.ui.CubaButton;
import com.vaadin.ui.Button;
import org.apache.commons.lang.StringUtils;
public class WebLogoutButton extends WebAbstractComponent<CubaButton> implements LogoutButton {
@ -36,12 +33,9 @@ public class WebLogoutButton extends WebAbstractComponent<CubaButton> implements
public WebLogoutButton() {
component = new CubaButton();
component.addStyleName(LOGOUT_BUTTON_STYLENAME);
component.addClickListener(new Button.ClickListener() {
@Override
public void buttonClick(Button.ClickEvent event) {
logout();
}
});
component.addClickListener((Button.ClickListener) event ->
logout()
);
component.setDescription(null);
}
@ -54,12 +48,9 @@ public class WebLogoutButton extends WebAbstractComponent<CubaButton> implements
window.saveSettings();
final WebWindowManager wm = (WebWindowManager) window.getWindowManager();
wm.checkModificationsAndCloseAll(new Runnable() {
@Override
public void run() {
Connection connection = wm.getApp().getConnection();
connection.logout();
}
wm.checkModificationsAndCloseAll(() -> {
Connection connection = wm.getApp().getConnection();
connection.logout();
});
}

View File

@ -27,7 +27,6 @@ import com.haulmont.cuba.gui.executors.impl.TaskHandlerImpl;
import com.haulmont.cuba.security.global.UserSession;
import com.haulmont.cuba.web.App;
import com.haulmont.cuba.web.AppUI;
import com.haulmont.cuba.web.AppWindow;
import com.haulmont.cuba.web.WebConfig;
import com.vaadin.server.VaadinSession;
import com.vaadin.ui.UI;
@ -115,8 +114,7 @@ public class WebBackgroundWorker implements BackgroundWorker {
}
// create task executor
AppWindow appWindow = appInstance.getAppWindow();
final WebTaskExecutor<T, V> taskExecutor = new WebTaskExecutor<>(appWindow, task);
final WebTaskExecutor<T, V> taskExecutor = new WebTaskExecutor<>(appInstance.getAppUI(), task);
// add thread to taskSet
appInstance.addBackgroundTask(taskExecutor.getFuture());
@ -145,41 +143,34 @@ public class WebBackgroundWorker implements BackgroundWorker {
}
protected static void withUserSessionAsync(UI ui, Runnable handler) {
ui.access(() -> {
SecurityContext oldSecurityContext = AppContext.getSecurityContext();
try {
UserSession userSession = ui.getSession().getAttribute(UserSession.class);
if (userSession != null) {
AppContext.setSecurityContext(new SecurityContext(userSession));
}
handler.run();
} finally {
AppContext.setSecurityContext(oldSecurityContext);
}
});
ui.access(() ->
executeOnUiThread(ui, handler)
);
}
protected static void withUserSessionInvoke(UI ui, Runnable handler) {
ui.accessSynchronously(() -> {
SecurityContext oldSecurityContext = AppContext.getSecurityContext();
try {
UserSession userSession = ui.getSession().getAttribute(UserSession.class);
if (userSession != null) {
AppContext.setSecurityContext(new SecurityContext(userSession));
}
ui.accessSynchronously(() ->
executeOnUiThread(ui, handler)
);
}
handler.run();
} finally {
AppContext.setSecurityContext(oldSecurityContext);
protected static void executeOnUiThread(UI ui, Runnable handler) {
SecurityContext oldSecurityContext = AppContext.getSecurityContext();
try {
UserSession userSession = ui.getSession().getAttribute(UserSession.class);
if (userSession != null) {
AppContext.setSecurityContext(new SecurityContext(userSession));
}
});
handler.run();
} finally {
AppContext.setSecurityContext(oldSecurityContext);
}
}
private class WebTaskExecutor<T, V> implements TaskExecutor<T, V>, Callable<V> {
private App app;
private AppWindow appWindow;
private AppUI ui;
private FutureTask<V> future;
@ -195,10 +186,9 @@ public class WebBackgroundWorker implements BackgroundWorker {
private Map<String, Object> params;
private TaskHandlerImpl<T, V> taskHandler;
private WebTaskExecutor(AppWindow appWindow, BackgroundTask<T, V> runnableTask) {
private WebTaskExecutor(AppUI ui, BackgroundTask<T, V> runnableTask) {
this.runnableTask = runnableTask;
this.appWindow = appWindow;
this.app = appWindow.getAppUI().getApp();
this.ui = ui;
this.params = runnableTask.getParams() != null ?
Collections.unmodifiableMap(runnableTask.getParams()) :
@ -294,7 +284,7 @@ public class WebBackgroundWorker implements BackgroundWorker {
// do not allow to cancel task from done listeners and exception handler
isClosed = true;
app.removeBackgroundTask(future);
ui.getApp().removeBackgroundTask(future);
watchDog.removeTask(taskHandler);
try {
@ -422,8 +412,6 @@ public class WebBackgroundWorker implements BackgroundWorker {
}
protected final void withUserSessionAsync(Runnable handler) {
AppUI ui = appWindow.getAppUI();
WebBackgroundWorker.withUserSessionAsync(ui, handler);
}
}

View File

@ -37,7 +37,7 @@ import java.util.List;
public class AppLog {
private static Logger log = LoggerFactory.getLogger(AppLog.class);
private static final Logger log = LoggerFactory.getLogger(AppLog.class);
private transient LinkedList<LogItem> items = new LinkedList<>();

View File

@ -17,6 +17,10 @@
@include=com.haulmont.cuba.gui
app.initErrorCaption = Unexpected error
app.initErrorMessage = Please contact system administrator
app.initRetry = Retry
menu-config.sys$Category.browse=Dynamic Attributes
menu-config.serverLog=Server Log
menu-config.printDomain=Data Model

View File

@ -17,6 +17,10 @@
@include=com.haulmont.cuba.gui
app.initErrorCaption = Непредвиденная ошибка
app.initErrorMessage = Пожалуйста свяжитесь с администратором системы
app.initRetry = Попробовать ещё раз
menu-config.sys$Category.browse=Динамические атрибуты
menu-config.serverLog=Журнал сервера
menu-config.printDomain=Модель данных

View File

@ -31,7 +31,6 @@ import java.util.concurrent.ConcurrentHashMap;
/**
* User settings provider for web application. Caches settings in HTTP session.
*
*/
@Component(SettingsClient.NAME)
public class WebSettingsClient implements SettingsClient {
@ -42,6 +41,7 @@ public class WebSettingsClient implements SettingsClient {
@Override
public String getSetting(String name) {
Map<String, Optional<String>> settings = getCache();
//noinspection Guava
Optional<String> cached = settings.get(name);
if (cached != null) {
return cached.orNull();
@ -61,7 +61,7 @@ public class WebSettingsClient implements SettingsClient {
@Override
public void deleteSettings(String name) {
getCache().put(name, Optional.<String>absent());
getCache().put(name, Optional.absent());
userSettingService.deleteSettings(ClientType.WEB, name);
}

View File

@ -68,7 +68,7 @@ public class BackgroundTaskManager {
// Clean task set
taskSet.clear();
if (count > 0) {
log.debug(String.format("Interrupted %s background tasks", count));
log.debug("Interrupted {} background tasks", count);
}
}
}

View File

@ -124,6 +124,19 @@ public class CubaApplicationServlet extends VaadinServlet {
initParameters.setProperty(Constants.SERVLET_PARAMETER_HEARTBEAT_INTERVAL, String.valueOf(sessionPingPeriod));
}
String widgetSet = webConfig.getWidgetSet();
initParameters.setProperty(Constants.PARAMETER_WIDGETSET, widgetSet);
boolean productionMode = webConfig.getProductionMode();
if (!productionMode) {
initParameters.setProperty(Constants.SERVLET_PARAMETER_PRODUCTION_MODE, String.valueOf(false));
}
initParameters.setProperty(Constants.SERVLET_PARAMETER_UI_PROVIDER, CubaUIProvider.class.getCanonicalName());
// not actually used by CubaUIProvider
initParameters.setProperty(VaadinSession.UI_PARAMETER, AppUI.class.getCanonicalName());
return super.createDeploymentConfiguration(initParameters);
}

View File

@ -22,11 +22,13 @@ import com.haulmont.cuba.core.global.Configuration;
import com.haulmont.cuba.core.global.GlobalConfig;
import com.haulmont.cuba.gui.theme.ThemeConstantsRepository;
import com.haulmont.cuba.web.App;
import com.haulmont.cuba.web.AppUI;
import com.haulmont.cuba.web.WebConfig;
import com.vaadin.server.DefaultUIProvider;
import com.vaadin.server.UICreateEvent;
import com.vaadin.shared.communication.PushMode;
import com.vaadin.shared.ui.ui.Transport;
import com.vaadin.ui.UI;
import org.apache.commons.lang.StringUtils;
import javax.servlet.http.Cookie;
@ -39,6 +41,9 @@ import java.util.Set;
public class CubaUIProvider extends DefaultUIProvider {
protected Configuration configuration = AppBeans.get(Configuration.NAME);
public CubaUIProvider() {
}
@Override
public String getTheme(UICreateEvent event) {
// get theme from cookies before app ui initialized for smooth theme enabling
@ -96,4 +101,9 @@ public class CubaUIProvider extends DefaultUIProvider {
return super.getPushTransport(event);
}
@Override
public UI createInstance(UICreateEvent event) {
return AppBeans.getPrototype(AppUI.NAME);
}
}

View File

@ -358,8 +358,27 @@ public class CubaVaadinServletService extends VaadinServletService {
Component component = (Component) connector;
String id = component.getId() == null ? super.createConnectorId(connector) : component.getId();
UserSession session = getAttribute(UserSession.class);
String login = session != null ? session.getCurrentOrSubstitutedUser().getLogin() : null;
return login != null ? login + "-" + id : id;
String login = null;
String locale = null;
if (session != null) {
login = session.getCurrentOrSubstitutedUser().getLogin();
if (session.getLocale() != null) {
locale = session.getLocale().toLanguageTag();
}
}
List<String> idParts = new ArrayList<>(3);
if (login != null) {
idParts.add(login);
}
if (locale != null) {
idParts.add(locale);
}
idParts.add(id);
return StringUtils.join(idParts, "-");
}
return super.createConnectorId(connector);
}

View File

@ -22,14 +22,15 @@ import com.haulmont.cuba.core.global.DevelopmentException;
import com.haulmont.cuba.core.global.UserSessionSource;
import com.haulmont.cuba.gui.NoSuchScreenException;
import com.haulmont.cuba.gui.TestIdManager;
import com.haulmont.cuba.gui.components.Window;
import com.haulmont.cuba.gui.components.mainwindow.AppMenu;
import com.haulmont.cuba.gui.config.*;
import com.haulmont.cuba.security.global.UserSession;
import com.haulmont.cuba.web.AppUI;
import com.haulmont.cuba.web.AppWindow;
import com.haulmont.cuba.web.gui.components.WebComponentsHelper;
import com.haulmont.cuba.web.toolkit.MenuShortcutAction;
import com.haulmont.cuba.web.toolkit.ui.CubaMenuBar;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.MenuBar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -49,7 +50,7 @@ public class MenuBuilder {
protected CubaMenuBar menuBar;
protected AppWindow appWindow;
protected Window.TopLevelWindow topLevelWindow;
protected MenuConfig menuConfig = AppBeans.get(MenuConfig.NAME);
@ -59,7 +60,7 @@ public class MenuBuilder {
public MenuBuilder(AppMenu menu) {
this.session = uss.getUserSession();
this.menuBar = (CubaMenuBar) WebComponentsHelper.unwrap(menu);
this.appWindow = ((AppUI) menuBar.getUI()).getAppWindow();
this.topLevelWindow = ((AppUI) menuBar.getUI()).getTopLevelWindow();
}
public void build() {
@ -186,14 +187,14 @@ public class MenuBuilder {
protected void assignShortcut(MenuBar.MenuItem menuItem, MenuItem item) {
if (item.getShortcut() != null && menuItem.getCommand() != null) {
MenuShortcutAction shortcut = new MenuShortcutAction(menuItem, "shortcut_" + item.getId(), item.getShortcut());
appWindow.addShortcutListener(shortcut);
topLevelWindow.unwrap(AbstractComponent.class).addShortcutListener(shortcut);
menuBar.setShortcut(menuItem, item.getShortcut());
}
}
protected void assignTestId(MenuBar.MenuItem menuItem, MenuItem conf) {
if (menuBar.getId() != null && menuBar.getCubaId() != null && !conf.isSeparator()) {
TestIdManager testIdManager = appWindow.getAppUI().getTestIdManager();
TestIdManager testIdManager = AppUI.getCurrent().getTestIdManager();
String id = testIdManager.normalize(conf.getId());
String testId = menuBar.getId() + "_" + id;

View File

@ -19,11 +19,14 @@ package com.haulmont.cuba.web.sys.remoting;
import com.haulmont.cuba.core.global.RemoteException;
import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.core.sys.SecurityContext;
import com.haulmont.cuba.core.sys.serialization.SerializationSupport;
import com.haulmont.cuba.core.sys.remoting.LocalServiceDirectory;
import com.haulmont.cuba.core.sys.remoting.LocalServiceInvocation;
import com.haulmont.cuba.core.sys.remoting.LocalServiceInvocationResult;
import com.haulmont.cuba.core.sys.remoting.LocalServiceInvoker;
import com.haulmont.cuba.security.global.ClientBasedSession;
import com.haulmont.cuba.security.global.UserSession;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.remoting.support.RemoteAccessor;
@ -96,7 +99,7 @@ public class LocalServiceProxy extends RemoteAccessor implements FactoryBean<Obj
String entryName = connectionUrlList.substring(connectionUrlList.lastIndexOf('/') + 1) + serviceName;
LocalServiceInvoker invoker = LocalServiceDirectory.getInvoker(entryName);
if (invoker == null)
throw new IllegalArgumentException("Service " + entryName + " is not registered in LocalServiceDirectory");
throw new IllegalArgumentException(String.format("Service %s is not registered in LocalServiceDirectory", entryName));
Class<?>[] parameterTypes = method.getParameterTypes();
String[] parameterTypeNames = new String[parameterTypes.length];
@ -123,9 +126,21 @@ public class LocalServiceProxy extends RemoteAccessor implements FactoryBean<Obj
}
}
UUID sessionId = AppContext.getSecurityContext() == null ? null : AppContext.getSecurityContext().getSessionId();
SecurityContext securityContext = AppContext.getSecurityContext();
UUID sessionId = securityContext == null ? null : securityContext.getSessionId();
String requestScopeLocale = null;
if (securityContext != null) {
UserSession session = securityContext.getSession();
if (session instanceof ClientBasedSession) {
if (((ClientBasedSession) session).isLocaleRequestScoped()) {
requestScopeLocale = session.getLocale() != null ? session.getLocale().toLanguageTag() : null;
}
}
}
LocalServiceInvocation invocation = new LocalServiceInvocation(
method.getName(), parameterTypeNames, argumentsData, notSerializableArguments, sessionId);
method.getName(), parameterTypeNames, argumentsData, notSerializableArguments, sessionId, requestScopeLocale);
LocalServiceInvocationResult result = invoker.invoke(invocation);
AppContext.setSecurityContext(AppContext.getSecurityContext());//need reset application name in LogMDC for the current thread

View File

@ -24,7 +24,6 @@ import com.haulmont.cuba.web.sys.CubaApplicationServlet;
import com.haulmont.cuba.web.sys.CubaDispatcherServlet;
import com.haulmont.cuba.web.sys.CubaHttpFilter;
import com.haulmont.cuba.web.sys.WebAppContextLoader;
import org.apache.commons.lang.StringUtils;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
@ -38,7 +37,6 @@ import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* {@link AppContext} loader of the web application block packed in a WAR together with the middleware block.

View File

@ -18,13 +18,10 @@
package com.haulmont.cuba.web.toolkit.ui;
import com.haulmont.cuba.web.toolkit.ui.client.historycontrol.CubaHistoryControlServerRpc;
import com.vaadin.server.AbstractClientConnector;
import com.vaadin.server.AbstractExtension;
import com.vaadin.server.ClientConnector;
import com.vaadin.ui.Layout;
import com.vaadin.ui.AbstractComponent;
public class CubaHistoryControl extends AbstractExtension {
protected HistoryBackHandler handler;
public CubaHistoryControl() {
@ -35,17 +32,12 @@ public class CubaHistoryControl extends AbstractExtension {
});
}
public void extend(AbstractClientConnector target, HistoryBackHandler handler) {
public void extend(AbstractComponent target, HistoryBackHandler handler) {
super.extend(target);
this.handler = handler;
}
@Override
protected Class<? extends ClientConnector> getSupportedParentType() {
return Layout.class;
}
public interface HistoryBackHandler {
void onHistoryBackPerformed();

View File

@ -19,11 +19,11 @@ package com.haulmont.cuba.web.toolkit.ui;
import com.haulmont.cuba.core.global.RemoteException;
import com.haulmont.cuba.security.global.NoUserSessionException;
import com.haulmont.cuba.web.UIView;
import com.haulmont.cuba.web.toolkit.ui.client.timer.CubaTimerClientRpc;
import com.haulmont.cuba.web.toolkit.ui.client.timer.CubaTimerServerRpc;
import com.haulmont.cuba.web.toolkit.ui.client.timer.CubaTimerState;
import com.vaadin.server.AbstractExtension;
import com.vaadin.ui.AbstractComponent;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -42,8 +42,8 @@ public class CubaTimer extends AbstractExtension implements CubaTimerServerRpc {
registerRpc(this);
}
public void extend(UIView view) {
super.extend(view);
public void extend(AbstractComponent component) {
super.extend(component);
}
@Override
@ -108,7 +108,8 @@ public class CubaTimer extends AbstractExtension implements CubaTimerServerRpc {
long endTime = System.currentTimeMillis();
if (System.currentTimeMillis() - startTime > 2000) {
log.warn("Too long timer '" + getLoggingTimerId() + "' processing: " + (endTime - startTime) + " ms ");
long duration = endTime - startTime;
log.warn("Too long timer {} processing: {} ms ", getLoggingTimerId(), duration);
}
} catch (RuntimeException e) {
handleOnTimerException(e);
@ -124,13 +125,13 @@ public class CubaTimer extends AbstractExtension implements CubaTimerServerRpc {
for (RemoteException.Cause cause : re.getCauses()) {
//noinspection ThrowableResultOfMethodCallIgnored
if (cause.getThrowable() instanceof NoUserSessionException) {
log.warn("NoUserSessionException in timer '" + getLoggingTimerId() + "', timer will be stopped");
log.warn("NoUserSessionException in timer {}, timer will be stopped", getLoggingTimerId());
stop();
break;
}
}
} else if (ExceptionUtils.indexOfThrowable(e, NoUserSessionException.class) > -1) {
log.warn("NoUserSessionException in timer '" + getLoggingTimerId() + "', timer will be stopped");
log.warn("NoUserSessionException in timer {}, timer will be stopped", getLoggingTimerId());
stop();
}

View File

@ -177,7 +177,7 @@ public class CubaWindow extends Window {
}
}
public void dispose() {
public void forceClose() {
super.close();
}
}

View File

@ -20,26 +20,19 @@ import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.Configuration;
import com.haulmont.cuba.core.global.ScreenProfilerConfig;
import com.haulmont.cuba.web.ScreenProfiler;
import com.haulmont.cuba.web.toolkit.ui.client.profiler.ScreenClientProfilerState;
import com.haulmont.cuba.web.toolkit.ui.client.profiler.ScreenClientProfilerServerRpc;
import com.haulmont.cuba.web.toolkit.ui.client.profiler.ScreenProfilerClientEvent;
import com.haulmont.cuba.web.toolkit.ui.client.profiler.ScreenClientProfilerState;
import com.vaadin.server.AbstractClientConnector;
import com.vaadin.server.AbstractExtension;
public class ScreenClientProfilerAgent extends AbstractExtension {
protected ScreenProfiler screenProfiler = AppBeans.get(ScreenProfiler.NAME);
public ScreenClientProfilerAgent() {
registerRpc(new ScreenClientProfilerServerRpc() {
@Override
public void flushEvents(ScreenProfilerClientEvent[] clientEvents) {
ScreenProfiler screenProfiler = AppBeans.get(ScreenProfiler.NAME);
screenProfiler.flush(clientEvents);
}
});
Configuration configuration = AppBeans.get(Configuration.class);
ScreenProfilerConfig screenProfilerConfig = configuration.getConfig(ScreenProfilerConfig.class);
getState().flushEventsCount = screenProfilerConfig.getFlushEventsCount();
getState().flushTimeout = screenProfilerConfig.getFlushTimeout();
registerRpc((ScreenClientProfilerServerRpc) clientEvents ->
screenProfiler.flush(clientEvents)
);
}
@Override
@ -56,4 +49,20 @@ public class ScreenClientProfilerAgent extends AbstractExtension {
protected ScreenClientProfilerState getState(boolean markAsDirty) {
return (ScreenClientProfilerState) super.getState(markAsDirty);
}
}
public void setFlushTimeout(int flushTimeout) {
getState().flushTimeout = flushTimeout;
}
public int getFlushTimeout() {
return getState(false).flushTimeout;
}
public void setFlushEventsCount(int flushEventsCount) {
getState().flushEventsCount = flushEventsCount;
}
public int getFlushEventsCount() {
return getState(false).flushEventsCount;
}
}

View File

@ -77,6 +77,8 @@ cuba.httpSessionExpirationTimeoutSec=1800
cuba.trustedClientPassword=MLdWm1Ik4NmM
cuba.passwordEncryptionModule=cuba_Sha1EncryptionModule
cuba.anonymousSessionId=9c91dbdf-3e73-428e-9088-d586da2434c5
# Default user accout to show in login dialog. Comment out for production mode.
cuba.web.loginDialogDefaultUser=admin
cuba.web.loginDialogDefaultPassword=admin
@ -85,9 +87,6 @@ cuba.web.loginDialogDefaultPassword=admin
# Presentation #
###############################################################################
# Client-side debug mode. Set to true for production mode.
com.vaadin.terminal.gwt.server.productionMode=false
# Enable Testing mode: true or false (by default)
cuba.testMode=false

View File

@ -74,4 +74,7 @@
<screen id="mainWindow"
template="/com/haulmont/cuba/web/app/mainwindow/mainwindow.xml"/>
<screen id="loginWindow"
template="/com/haulmont/cuba/web/app/loginwindow/loginwindow.xml"/>
</screen-config>

View File

@ -21,7 +21,7 @@ cuba.web.mainWindow.header.height=40px
# Overrides
cuba.gui.AboutWindow.width=520
cuba.gui.AboutWindow.width=560
cuba.gui.scheduled-task-edit.smallField.width=205
cuba.gui.entity-log-browse.button.width=100px
@ -105,8 +105,6 @@ cuba.web.FolderEditWindow.field.width=250px
cuba.web.JmxInstanceEditor.width=480
cuba.web.LoginWindow.form.width=310
cuba.web.LoginWindow.form.height=-1
cuba.web.LoginWindow.field.width=150
cuba.web.ScreenHistoryBrowse.width=500

View File

@ -91,9 +91,7 @@ cuba.web.FolderEditWindow.field.width=250px
cuba.web.JmxInstanceEditor.width=480
cuba.web.LoginWindow.form.width=310
cuba.web.LoginWindow.form.height=-1
cuba.web.LoginWindow.field.width=125
cuba.web.LoginWindow.field.width=150
cuba.web.ScreenHistoryBrowse.width=500
cuba.web.ScreenHistoryBrowse.height=480

View File

@ -16,32 +16,18 @@
*/
@mixin cuba-login-window {
.cuba-login-bottom {
.cuba-login-panel {
background: $v-panel-background-color;
border: valo-border();
border-radius: $v-border-radius;
padding: $v-layout-margin-top $v-layout-margin-right $v-layout-margin-bottom $v-layout-margin-left;
padding: $v-layout-margin-top $v-layout-margin-right*4 $v-layout-margin-bottom $v-layout-margin-left*4;
}
.cuba-login-main-layout {
background-color: $v-app-background-color;
}
.cuba-login-title {
padding-bottom: $v-layout-spacing-horizontal;
}
.cuba-login-form {
font-weight: normal;
.v-formlayout > table,
.v-formlayout > table > tbody {
border: 0;
border-image: none;
}
}
.cuba-login-icon {
.cuba-login-icon > img {
height: 32px;
}
}

View File

@ -16,51 +16,26 @@
*/
@mixin cuba-login-window {
.cuba-login-bottom {
.cuba-login-panel {
background: $cuba-loginform-background-color;
border: thin solid $cuba-loginform-border-color;
box-shadow: 0 0 5px rgba(0,0,0,0.25);
-moz-box-shadow: 0 0 5px rgba(0,0,0,0.25);
-webkit-box-shadow: 0 0 5px rgba(0,0,0,0.25);
.cuba-login-username-field.v-caption {
font-weight: bold;
font-size: 10pt;
color: #2B4666;
}
.v-captiontext {
padding-right: 5px;
float: none;
}
padding: 10px 40px 10px 40px;
}
.cuba-login-main-layout {
background-color: #fff;
}
.cuba-login-title {
padding-top: 3px;
.cuba-login-icon > img {
height: 32px;
}
.cuba-login-form {
font-weight: normal;
.v-formlayout {
float: left;
}
legend {
display: none;
}
}
.cuba-login-form-caption {
padding-top: 5px;
}
.cuba-login-bottom > div:first-child {
padding-top: 5px;
.login-form-label {
font-weight: bold;
font-size: 10pt;
color: #2B4666;
}
}

View File

@ -31,12 +31,6 @@
</param-value>
</context-param>
<context-param>
<description>Vaadin production mode</description>
<param-name>productionMode</param-name>
<param-value>false</param-value>
</context-param>
<context-param>
<description>Web resources version for correct caching in browser</description>
<param-name>webResourcesTs</param-name>
@ -53,26 +47,6 @@
<servlet>
<servlet-name>cuba_servlet</servlet-name>
<servlet-class>com.haulmont.cuba.web.sys.CubaApplicationServlet</servlet-class>
<init-param>
<description>UI class</description>
<param-name>UI</param-name>
<param-value>com.haulmont.cuba.web.AppUI</param-value>
</init-param>
<init-param>
<description>UIProvider class</description>
<param-name>UIProvider</param-name>
<param-value>com.haulmont.cuba.web.sys.CubaUIProvider</param-value>
</init-param>
<init-param>
<description>Application class</description>
<param-name>Application</param-name>
<param-value>com.haulmont.cuba.web.DefaultApp</param-value>
</init-param>
<init-param>
<description>Widgetset name</description>
<param-name>widgetset</param-name>
<param-value>com.haulmont.cuba.web.toolkit.ui.WidgetSet</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
<async-supported>true</async-supported>
</servlet>