SEC-2216: Add withObjectPostProcessor

This commit is contained in:
Rob Winch 2013-08-16 08:34:49 -05:00
parent d62c2e0835
commit 5fe32bb3c8
22 changed files with 86 additions and 19 deletions

View File

@ -20,8 +20,10 @@ import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.encoding.PasswordEncoder; import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.authentication.encoding.PlaintextPasswordEncoder; import org.springframework.security.authentication.encoding.PlaintextPasswordEncoder;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter; import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder; import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
import org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource; import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.authentication.AbstractLdapAuthenticator; import org.springframework.security.ldap.authentication.AbstractLdapAuthenticator;
@ -81,6 +83,17 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
return ldapAuthenticationProvider; return ldapAuthenticationProvider;
} }
/**
* Adds an {@link ObjectPostProcessor} for this class.
*
* @param objectPostProcessor
* @return the {@link ChannelSecurityConfigurer} for further customizations
*/
public LdapAuthenticationProviderConfigurer<B> withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return this;
}
/** /**
* Creates the {@link LdapAuthenticator} to use * Creates the {@link LdapAuthenticator} to use
* *

View File

@ -16,6 +16,7 @@
package org.springframework.security.config.annotation.authentication.configurers.userdetails; package org.springframework.security.config.annotation.authentication.configurers.userdetails;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityBuilder; import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.SecurityConfigurer; import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder; import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
@ -47,6 +48,18 @@ abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuil
provider.setUserDetailsService(userDetailsService); provider.setUserDetailsService(userDetailsService);
} }
/**
* Adds an {@link ObjectPostProcessor} for this class.
*
* @param objectPostProcessor
* @return the {@link AbstractDaoAuthenticationConfigurer} for further customizations
*/
@SuppressWarnings("unchecked")
public C withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return (C) this;
}
/** /**
* Allows specifying the {@link PasswordEncoder} to use with the {@link DaoAuthenticationProvider}. The default is * Allows specifying the {@link PasswordEncoder} to use with the {@link DaoAuthenticationProvider}. The default is
* is to use plain text. * is to use plain text.

View File

@ -50,7 +50,7 @@ import org.springframework.web.accept.HeaderContentNegotiationStrategy;
* @author Rob Winch * @author Rob Winch
* @since 3.2 * @since 3.2
*/ */
public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecurityBuilder<B>,T extends AbstractAuthenticationFilterConfigurer<B,T, F>, F extends AbstractAuthenticationProcessingFilter> extends AbstractHttpConfigurer<B> { public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecurityBuilder<B>,T extends AbstractAuthenticationFilterConfigurer<B,T, F>, F extends AbstractAuthenticationProcessingFilter> extends AbstractHttpConfigurer<T,B> {
private final F authFilter; private final F authFilter;

View File

@ -15,6 +15,7 @@
*/ */
package org.springframework.security.config.annotation.web.configurers; package org.springframework.security.config.annotation.web.configurers;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityConfigurer; import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter; import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
@ -28,7 +29,7 @@ import org.springframework.security.web.DefaultSecurityFilterChain;
* @author Rob Winch * @author Rob Winch
* *
*/ */
abstract class AbstractHttpConfigurer<B extends HttpSecurityBuilder<B>> extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> { abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>,B extends HttpSecurityBuilder<B>> extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {
/** /**
* Disables the {@link AbstractHttpConfigurer} by removing it. After doing * Disables the {@link AbstractHttpConfigurer} by removing it. After doing
@ -41,4 +42,10 @@ abstract class AbstractHttpConfigurer<B extends HttpSecurityBuilder<B>> extends
getBuilder().removeConfigurer(getClass()); getBuilder().removeConfigurer(getClass());
return getBuilder(); return getBuilder();
} }
@SuppressWarnings("unchecked")
public T withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return (T) this;
}
} }

View File

@ -21,13 +21,11 @@ import java.util.UUID;
import org.springframework.security.authentication.AnonymousAuthenticationProvider; import org.springframework.security.authentication.AnonymousAuthenticationProvider;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.SecurityConfigurer; import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
/** /**
@ -39,7 +37,7 @@ import org.springframework.security.web.authentication.AnonymousAuthenticationFi
* @author Rob Winch * @author Rob Winch
* @since 3.2 * @since 3.2
*/ */
public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> { public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<AnonymousConfigurer<H>,H> {
private String key; private String key;
private AuthenticationProvider authenticationProvider; private AuthenticationProvider authenticationProvider;
private AnonymousAuthenticationFilter authenticationFilter; private AnonymousAuthenticationFilter authenticationFilter;

View File

@ -22,6 +22,7 @@ import java.util.List;
import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig; import org.springframework.security.access.SecurityConfig;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain; import org.springframework.security.web.DefaultSecurityFilterChain;
@ -99,6 +100,17 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> e
http.addFilter(channelFilter); http.addFilter(channelFilter);
} }
/**
* Adds an {@link ObjectPostProcessor} for this class.
*
* @param objectPostProcessor
* @return the {@link ChannelSecurityConfigurer} for further customizations
*/
public ChannelSecurityConfigurer<H> withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return this;
}
/** /**
* Sets the {@link ChannelProcessor} instances to use in {@link ChannelDecisionManagerImpl} * Sets the {@link ChannelProcessor} instances to use in {@link ChannelDecisionManagerImpl}
* @param channelProcessors * @param channelProcessors

View File

@ -55,7 +55,7 @@ import org.springframework.util.Assert;
* @author Rob Winch * @author Rob Winch
* @since 3.2 * @since 3.2
*/ */
public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> { public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<CsrfConfigurer<H>,H> {
private CsrfTokenRepository csrfTokenRepository = new HttpSessionCsrfTokenRepository(); private CsrfTokenRepository csrfTokenRepository = new HttpSessionCsrfTokenRepository();
private RequestMatcher requireCsrfProtectionMatcher; private RequestMatcher requireCsrfProtectionMatcher;

View File

@ -54,7 +54,7 @@ import org.springframework.security.web.authentication.ui.DefaultLoginPageViewFi
* @since 3.2 * @since 3.2
*/ */
public final class DefaultLoginPageConfigurer<H extends HttpSecurityBuilder<H>> extends public final class DefaultLoginPageConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<H> { AbstractHttpConfigurer<DefaultLoginPageConfigurer<H>,H> {
private DefaultLoginPageViewFilter loginPageGeneratingFilter = new DefaultLoginPageViewFilter(); private DefaultLoginPageViewFilter loginPageGeneratingFilter = new DefaultLoginPageViewFilter();

View File

@ -59,7 +59,7 @@ import org.springframework.security.web.util.RequestMatcher;
* @author Rob Winch * @author Rob Winch
* @since 3.2 * @since 3.2
*/ */
public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> { public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<ExceptionHandlingConfigurer<H>,H> {
private AuthenticationEntryPoint authenticationEntryPoint; private AuthenticationEntryPoint authenticationEntryPoint;

View File

@ -24,6 +24,7 @@ import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig; import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.expression.SecurityExpressionHandler; import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.FilterInvocation; import org.springframework.security.web.FilterInvocation;
@ -95,6 +96,17 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
return this; return this;
} }
/**
* Adds an {@link ObjectPostProcessor} for this class.
*
* @param objectPostProcessor
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further customizations
*/
public ExpressionUrlAuthorizationConfigurer<H> withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return this;
}
@Override @Override
protected final AuthorizedUrl chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) { protected final AuthorizedUrl chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {
return new AuthorizedUrl(requestMatchers); return new AuthorizedUrl(requestMatchers);

View File

@ -34,7 +34,7 @@ import org.springframework.util.Assert;
* @since 3.2 * @since 3.2
* @see RememberMeConfigurer * @see RememberMeConfigurer
*/ */
public final class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> { public final class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<HeadersConfigurer<H>,H> {
private List<HeaderWriter> headerWriters = new ArrayList<HeaderWriter>(); private List<HeaderWriter> headerWriters = new ArrayList<HeaderWriter>();
/** /**

View File

@ -65,7 +65,7 @@ import org.springframework.web.accept.HeaderContentNegotiationStrategy;
* @author Rob Winch * @author Rob Winch
* @since 3.2 * @since 3.2
*/ */
public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends AbstractHttpConfigurer<B> { public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends AbstractHttpConfigurer<HttpBasicConfigurer<B>,B> {
private static final String DEFAULT_REALM = "Spring Security Application"; private static final String DEFAULT_REALM = "Spring Security Application";
private AuthenticationEntryPoint authenticationEntryPoint = new BasicAuthenticationEntryPoint(); private AuthenticationEntryPoint authenticationEntryPoint = new BasicAuthenticationEntryPoint();

View File

@ -68,7 +68,7 @@ import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthe
* @author Rob Winch * @author Rob Winch
* @since 3.2 * @since 3.2
*/ */
public final class JeeConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> { public final class JeeConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<JeeConfigurer<H>,H> {
private J2eePreAuthenticatedProcessingFilter j2eePreAuthenticatedProcessingFilter; private J2eePreAuthenticatedProcessingFilter j2eePreAuthenticatedProcessingFilter;
private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService; private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService;
private Set<String> mappableRoles = new HashSet<String>(); private Set<String> mappableRoles = new HashSet<String>();

View File

@ -59,7 +59,7 @@ import org.springframework.security.web.util.RequestMatcher;
* @since 3.2 * @since 3.2
* @see RememberMeConfigurer * @see RememberMeConfigurer
*/ */
public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> { public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<LogoutConfigurer<H>,H> {
private List<LogoutHandler> logoutHandlers = new ArrayList<LogoutHandler>(); private List<LogoutHandler> logoutHandlers = new ArrayList<LogoutHandler>();
private SecurityContextLogoutHandler contextLogoutHandler = new SecurityContextLogoutHandler(); private SecurityContextLogoutHandler contextLogoutHandler = new SecurityContextLogoutHandler();
private String logoutSuccessUrl = "/login?logout"; private String logoutSuccessUrl = "/login?logout";

View File

@ -33,7 +33,7 @@ import org.springframework.security.web.PortMapperImpl;
* @author Rob Winch * @author Rob Winch
* @since 3.2 * @since 3.2
*/ */
public final class PortMapperConfigurer<H extends HttpSecurityBuilder<H>> extends SecurityConfigurerAdapter<DefaultSecurityFilterChain,H> { public final class PortMapperConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<PortMapperConfigurer<H>,H> {
private PortMapper portMapper; private PortMapper portMapper;
private Map<String, String> httpsPortMappings = new HashMap<String,String>(); private Map<String, String> httpsPortMappings = new HashMap<String,String>();

View File

@ -72,7 +72,7 @@ import org.springframework.security.web.authentication.ui.DefaultLoginPageViewFi
* @author Rob Winch * @author Rob Winch
* @since 3.2 * @since 3.2
*/ */
public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> { public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<RememberMeConfigurer<H>,H> {
private AuthenticationSuccessHandler authenticationSuccessHandler; private AuthenticationSuccessHandler authenticationSuccessHandler;
private String key; private String key;
private RememberMeServices rememberMeServices; private RememberMeServices rememberMeServices;

View File

@ -55,7 +55,7 @@ import org.springframework.security.web.util.AntPathRequestMatcher;
* @since 3.2 * @since 3.2
* @see RequestCache * @see RequestCache
*/ */
public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> { public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<RequestCacheConfigurer<H>,H> {
public RequestCacheConfigurer() { public RequestCacheConfigurer() {
} }

View File

@ -58,7 +58,7 @@ import org.springframework.security.web.context.SecurityContextRepository;
* @author Rob Winch * @author Rob Winch
* @since 3.2 * @since 3.2
*/ */
public final class SecurityContextConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> { public final class SecurityContextConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<SecurityContextConfigurer<H>,H> {
/** /**
* Creates a new instance * Creates a new instance

View File

@ -49,7 +49,7 @@ import org.springframework.security.web.servletapi.SecurityContextHolderAwareReq
* @author Rob Winch * @author Rob Winch
* @since 3.2 * @since 3.2
*/ */
public final class ServletApiConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> { public final class ServletApiConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<ServletApiConfigurer<H>,H> {
private SecurityContextHolderAwareRequestFilter securityContextRequestFilter = new SecurityContextHolderAwareRequestFilter(); private SecurityContextHolderAwareRequestFilter securityContextRequestFilter = new SecurityContextHolderAwareRequestFilter();
/** /**

View File

@ -78,7 +78,7 @@ import org.springframework.util.Assert;
* @see SessionManagementFilter * @see SessionManagementFilter
* @see ConcurrentSessionFilter * @see ConcurrentSessionFilter
*/ */
public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> { public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<SessionManagementConfigurer<H>,H> {
private SessionAuthenticationStrategy sessionFixationAuthenticationStrategy = createDefaultSessionFixationProtectionStrategy(); private SessionAuthenticationStrategy sessionFixationAuthenticationStrategy = createDefaultSessionFixationProtectionStrategy();
private SessionAuthenticationStrategy sessionAuthenticationStrategy; private SessionAuthenticationStrategy sessionAuthenticationStrategy;
private List<SessionAuthenticationStrategy> sessionAuthenticationStrategies = new ArrayList<SessionAuthenticationStrategy>(); private List<SessionAuthenticationStrategy> sessionAuthenticationStrategies = new ArrayList<SessionAuthenticationStrategy>();

View File

@ -25,6 +25,7 @@ import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig; import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.vote.AuthenticatedVoter; import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter; import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource; import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
@ -68,6 +69,17 @@ import org.springframework.util.Assert;
*/ */
public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>, C> extends AbstractInterceptUrlConfigurer<H,C,UrlAuthorizationConfigurer<H,C>.AuthorizedUrl> { public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>, C> extends AbstractInterceptUrlConfigurer<H,C,UrlAuthorizationConfigurer<H,C>.AuthorizedUrl> {
/**
* Adds an {@link ObjectPostProcessor} for this class.
*
* @param objectPostProcessor
* @return the {@link UrlAuthorizationConfigurer} for further customizations
*/
public UrlAuthorizationConfigurer<H,C> withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return this;
}
/** /**
* Creates the default {@link AccessDecisionVoter} instances used if an * Creates the default {@link AccessDecisionVoter} instances used if an
* {@link AccessDecisionManager} was not specified using * {@link AccessDecisionManager} was not specified using

View File

@ -71,7 +71,7 @@ import org.springframework.security.web.authentication.preauth.x509.X509Authenti
* @author Rob Winch * @author Rob Winch
* @since 3.2 * @since 3.2
*/ */
public final class X509Configurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> { public final class X509Configurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<X509Configurer<H>,H> {
private X509AuthenticationFilter x509AuthenticationFilter; private X509AuthenticationFilter x509AuthenticationFilter;
private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService; private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService;
private String subjectPrincipalRegex; private String subjectPrincipalRegex;