I'm trying to add security headers to my Spring Boot application.
It already had a Java class with multiple filters extending from WebSecurityConfigurerAdapter. But whenever I try to add the annotation #EnableWebSecurity to this class or even with a new custom one I always receive NullPointerException for the bean springSecurityFilterChain.
Changing the order to add some filters seems to solve this problem but whenever I try to enter the app I can't because it seems the HTTP Authorization header field is null (which I recover inside one of my custom filters).
Do any have a clue of what is happening?
EDIT: After some days of cheking this I noted that the Authorization header was not the problem as the code is built to let that call enter without it and before any change it was already sent without header.
Still with the same call and the changes I'm receiving a 403 FORBIDDEN (before any change this call was receiving 302 FOUND).
This happens before even reaching the controller and I can only get debugging until the filter.
As there were no other changes in the code except the #EnableWebSecurity and the way to add one filter I suspect the problem is around here but i can't find what is causing it exactly.
EDIT: I'm adding the code in case anyone need to see it.
This is the class that has the multiple filters:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity //ADDED THIS ONE
public class MultipleEntryPointsSecurityConfig {
#Configuration
#Order(1)
public class OauthSecurityAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private OAuth2RestTemplate restTemplate;
#Bean
public CustomFilterOneFilter customFilterOneFilter() {
final CustomFilterOneFilter filter = new CustomFilterOneFilter ("/testLogin");
filter.setRestTemplate(restTemplate);
return filter;
}
#Bean
public FilterRegistrationBean<OAuth2ClientContextFilter> oauth2ClientFilterRegistration(
OAuth2ClientContextFilter filter) {
FilterRegistrationBean<OAuth2ClientContextFilter> registration = new FilterRegistrationBean<OAuth2ClientContextFilter>();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.antMatcher("/login")
.cors()
.and()
.csrf().disable()
//CHANGED THIS
// .addFilterAfter(openIdConnectFilter(), OAuth2ClientContextFilter.class)
//FOR THESE TWO
.addFilterAfter(new OAuth2ClientContextFilter(), AbstractPreAuthenticatedProcessingFilter.class)
.addFilterAfter(openIdConnectFilter(), OAuth2ClientContextFilter.class)
.httpBasic()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/testLogin"))
.and()
.logout()
.logoutSuccessUrl("/logout")
.permitAll()
.and()
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated();
// #formatter:on
}
}
#Configuration
#Order(2)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public JwtSecurityFilter authenticationJwtTokenFilter() {
return new JwtSecurityFilter();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.cors()
.and()
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/**").authenticated();
http
.addFilterAfter(new UsernamePasswordAuthenticationFilter(), AbstractPreAuthenticatedProcessingFilter.class)
.addFilterAfter(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
//CHANGED THE BELOW ONE FOR THE TWO ABOVE
//http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
#Configuration
#Order(3)
public static class PublicConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/**").permitAll()
.antMatchers("/api/v1/login/**").permitAll();
}
}
}
And this is the custom filter where I try to recover the Authorization header:
#Component
public class JwtSecurityFilter extends OncePerRequestFilter{
#Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
//FAILS HERE!
if(authHeader == null || !authHeader.startsWith("Bearer ")) {
SecurityContextHolder.getContext().setAuthentication(null);
chain.doFilter(request, response);
return;
}
...
}
}
Related
So I have a two login pages. One for Customer and one for AM. I configured 2 login pages in my WebSecurityConfig class. When I try to login in AM using an admin account it works but when I try to login in Customer using a user account the loginProcessingUrl can't be found.
In my WebSecurityConfig class:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig
{
#Autowired
MyDBAuthenticationService myDBAuthenticationService;
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(myDBAuthenticationService);
}
#Configuration
#Order(1)
public static class WebConfigurationAdapter1 extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests().antMatchers("/am/**").access("hasRole('ROLE_AM')")
.and()
.exceptionHandling()
.accessDeniedPage("/403")
.and()
.formLogin()
.loginPage("/amLogin")
.loginProcessingUrl("/am/postLogin")
.defaultSuccessUrl("/amChatPage")
.failureUrl("/amLogin?error")
.and().logout().logoutUrl("/amLogout").logoutSuccessUrl("/amLogoutSuccessful")
.deleteCookies("JSESSIONID")
.and().csrf().disable();
System.out.println("1st Configurer");
}
}
#Configuration
#Order(2)
public static class WebConfigurationAdapter2 extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests().antMatchers("/customer/**").access("hasRole('ROLE_CUSTOMER')")
.and()
.exceptionHandling()
.accessDeniedPage("/403")
.and()
.formLogin()
.loginPage("/customerLogin")
.loginProcessingUrl("/customer/postLogin")
.defaultSuccessUrl("/customerChatPage")
.failureUrl("/customerLogin?error")
.and().logout().logoutUrl("/customerLogout").logoutSuccessUrl("/customerLogoutSuccessful")
.and().csrf().disable();
System.out.println("2nd Configurer");
}
}
}
Here's my SpringWebAppInitializer class:
#Configuration
public class SpringWebAppInitializer implements WebApplicationInitializer{
#Override
public void onStartup(ServletContext sc) throws ServletException {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(ApplicationContextConfig.class);
ServletRegistration.Dynamic dispatcher = sc.addServlet("dispatcher", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
Things that I have done so far are:
Putting sout() in each static class to know if it gets run. Both did display the sout().
Change name of both processing url and make it unique. Also change in my jsp file.
Spring boot - how to configure multiple login pages?
Here is the result when I login as AM:
Result in netbeans. It enters the Controller but says "null"
Here is the result when I login as Customer:
Since there is no differentiation pattern between both http configuration Spring Security is taking the first one login as default, that is the reason why only admin login works, because it is part of the web security adapter declared as Order(1).
In order to separate both configuration properly it is necessary to define a pattern separation with .antMatcher.
here is one small example to give you an idea of how define both configuration
For admins (see the .antMatcher definition it forces to apply the http configuration only to admin/** urls.
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.antMatcher("/admin/**").authorizeRequests().anyRequest().authenticated().anyRequest().hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/adminLogin")
.loginProcessingUrl("/admin/postLogin")
.defaultSuccessUrl("/admin/home")
.failureUrl("/adminLogin?error")
.and().logout().logoutUrl("/admin/logout").logoutSuccessUrl("/home")
.and()
.csrf().disable();
}
For customers (see the .antMatcher definition it forces to apply the http configuration only to customer/** urls.
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.antMatcher("/customer/**").authorizeRequests().anyRequest().authenticated().anyRequest().hasRole("USER")
.and()
.formLogin()
.loginPage("/customerLogin")
.loginProcessingUrl("/customer/postLogin")
.defaultSuccessUrl("/customer/home")
.failureUrl("/customerLogin?error")
.and()
.logout().logoutUrl("/customer/logout").logoutSuccessUrl("/home")
.and()
.csrf().disable();
}
There are other examples here: Example two login pages and visit the spring security documentation Multiple Http Security
Hope this information helps you.
According to the new configuration you have to do some changes, please review the following configuration, and compare with yours and you will see what is the difference (antMatcher is different of antMatchers)
#Configuration
#EnableWebSecurity
public class WebSecurityConfig
{
#Autowired
MyDBAuthenticationService myDBAuthenticationService;
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(myDBAuthenticationService);
}
#Configuration
#Order(1)
public static class WebConfigurationAdapter1 extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
//.authorizeRequests().antMatchers("/am/**").access("hasRole('ROLE_AM')")
.antMatcher("/am/**").authorizeRequests().anyRequest().hasRole("AM")
.and()
.exceptionHandling()
.accessDeniedPage("/403")
.and()
.formLogin()
.loginPage("/amLogin")
.loginProcessingUrl("/am/postLogin")
.defaultSuccessUrl("/am/chatPage")
.failureUrl("/amLogin?error")
.and().logout().logoutUrl("/am/logout").logoutSuccessUrl("/amLogoutSuccessful")
.deleteCookies("JSESSIONID")
.and().csrf().disable();
System.out.println("1st Configurer");
}
}
#Configuration
#Order(2)
public static class WebConfigurationAdapter2 extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
//.authorizeRequests().antMatchers("/customer/**").access("hasRole('ROLE_CUSTOMER')")
.antMatcher("/admin/**").authorizeRequests().anyRequest().hasRole("CUSTOMER")
.and()
.exceptionHandling()
.accessDeniedPage("/403")
.and()
.formLogin()
.loginPage("/customerLogin")
.loginProcessingUrl("/customer/postLogin")
.defaultSuccessUrl("/customer/chatPage")
.failureUrl("/customerLogin?error")
.and().logout().logoutUrl("/customer/logout").logoutSuccessUrl("/customerLogoutSuccessful")
.and().csrf().disable();
System.out.println("2nd Configurer");
}
}
}
And finally remember the controller, you should have the following RequestMapping definition at least
#RequestMapping("/adminLogin"), #RequestMapping("/customerLogin"), #RequestMapping("/am/chatPage"), #RequestMapping("/customer/chatPage")
In the following class that extends WebSecurityConfigurerAdapter i've overwritten the configure(HttpSecurity) method.
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
public void configureAuth(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("fabio")
.password("123")
.roles("ADMIN")
.and()
.withUser("joe")
.password("123")
.roles("GUEST");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/post/list").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.logout();
}
}
With this i should be able to get to localhost:8080/post/list page without having to commit to a user login since it has the .permitAll() , but when i try to getting into it it always prompts the login page before, only after i enter the previous credentials i'm able to view it. How can i fix this ?
controller class
#RestController
#RequestMapping("/post")
public class HomeController {
#Secured("ROLE_GUEST")
#RequestMapping("/list")
public String list(){
return "list...";
}
#Secured("ROLE_USER")
#RequestMapping("/drafts")
public String drafts(){
return "drafts...";
}
#Secured({"ROLE_ADMIN","ROLE_USER"})
#RequestMapping("/add")
public String add(){
return "adding...";
}
}
According to the #RequestMapping definition there is a conflict because it is secured by annotation #Secured("ROLE_GUEST") but also you need to access it with .permitAll() configuration.
Option 1: Just remove the #Secured("ROLE_GUEST") in order to let .permitAll() do the work.
Option 2: use #Secured("ROLE_ANONYMOUS") on the #RequestMapping("/list") instead of #Secured("ROLE_GUEST"). You can see the definition of ROLE_ANONYMOUS in the Spring Documentation
It will depends on the path value after /post/list. Please see the following examples of how to define antMatchers depending on the path value.
localhost:8080/post/list = .antMatchers( "/post/list").permitAll()
localhost:8080/post/list/stuff = .antMatchers( "/post/list/**").permitAll()
localhost:8080/post/list, localhost:8080/post/list1234
= .antMatchers( "/post/list**").permitAll()
For more information visit the AnthPathMatcher documentation and HttpSecurity
I've got a problem with my Spring Boot application in which im trying to implement dynamically configured multiple login pages.
In the database are stored prefixes for pages and I'm trying to get multiple paths for separate login pages.
I'm trying to do it in the following SecurityConfig class
(global.getPath() returns a plain prefix String):
#Configuration
#EnableGlobalMethodSecurity( securedEnabled = true )
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private GlobalSettingsService globalSettingsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
List<GlobalSettings> globals = globalSettingsService.findAll();
http
.authorizeRequests()
.antMatchers("/css/**", "/index").permitAll()
.antMatchers("/js/**", "/").permitAll()
.antMatchers("/fonts/**", "/img/**").permitAll();
for (GlobalSettings global : globals) {
http.authorizeRequests()
.antMatchers("/"+global.getPath()+"/**").permitAll()
.antMatchers("/"+global.getPath()+"/admin/**").hasAnyRole("USER","ADMIN")
.and()
.formLogin().loginProcessingUrl("/"+global.getPath()+"/login")
.loginPage("/"+global.getPath()+"/login").permitAll()
.defaultSuccessUrl("/"+global.getPath()+"/admin")
.failureUrl("/"+global.getPath()+"/login?error").permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/"+global.getPath()+"/login?logout"))
.logoutSuccessUrl("/"+global.getPath()+"/login?logout")
.permitAll();
}
}
}
Login forms views are configured in another class (WebConfig) which extends WebMvcConfigureAdapter:
for (GlobalSettings global : globals) {
registry.addViewController("/"+global.getPath()+"/login").setViewName(global.getPath()+"-/login-form");
}
I've got 3 prefixes to configure and the last one configured in the loop is working correctly (propably with the highest order?) - I can log in and out without any troubles.
So the configuration works only for the last prefix which is called in the loop. Other prefixes are rendering proper login forms but returning 405 code after login attempt with following message:
o.s.web.servlet.PageNotFound: Request method 'POST' not supported
So I made custom POST method definition in one of my controllers:
#RequestMapping(value = "/{path}/login", method = RequestMethod.POST)
public ModelAndView getLoginPage(#PathVariable("path") String path, #RequestParam Optional<String> error) {
return new ModelAndView(path+"-front/login-form", "error", error);
}
But unfortunately it's not being recognized, still getting 'Method not supported' message, while the login request path is correct.
Besides, the paths are mapping correctly during application initialization so I don't know where the problem is.
I tried many ways such as adding hidden _csrf field to the login form and still getting nothing.
Is it possible to do it avoiding prefixes hardcoding?
Is it related with #Order annotation or something like this?
Thanks for your time
I resolved it by rebuilding SecurityConfig class making separate WebSecurityConfigurerAdapters for each of my prefix:
#Configuration
#EnableGlobalMethodSecurity( securedEnabled = true )
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Autowired
private GlobalSettingsService globalSettingsService;
#Configuration
#Order(1)
public static class FirstPrefixWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/app1/**")
.authorizeRequests()
.antMatchers("/app1/**").permitAll()
.antMatchers("/app1/login").permitAll()
.antMatchers("/app1/admin/**")
.hasAnyRole("ADMIN","USER")
.and()
.formLogin().loginProcessingUrl("/app1/login")
.loginPage("/app1/login").permitAll()
.defaultSuccessUrl("/app1/admin")
.failureUrl("/app1/login?error").permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/app1/login?logout"))
.logoutSuccessUrl("/app1/login?logout")
.permitAll();
}
}
#Configuration
#Order(2)
public static class SecondPrefixWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/app2/**")
.authorizeRequests()
.antMatchers("/app2/**").permitAll()
.antMatchers("/app2/login").permitAll()
.antMatchers("/app2/admin/**")
.hasAnyRole("ADMIN","USER")
.and()
.formLogin().loginProcessingUrl("/app2/login")
.loginPage("/app2/login").permitAll()
.defaultSuccessUrl("/app2/admin")
.failureUrl("/app2/login?error").permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/app2/login?logout"))
.logoutSuccessUrl("/app2/login?logout")
.permitAll();
}
}
#Configuration
#Order(3)
public static class ThirdPrefixWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/app3/**")
.authorizeRequests()
.antMatchers("/app3/**").permitAll()
.antMatchers("/app3/login").permitAll()
.antMatchers("/app3/admin/**")
.hasAnyRole("ADMIN","USER")
.and()
.formLogin().loginProcessingUrl("/app3/login")
.loginPage("/app3/login").permitAll()
.defaultSuccessUrl("/app3/admin")
.failureUrl("/app3/login?error").permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/app3/login?logout"))
.logoutSuccessUrl("/app3/login?logout")
.permitAll();
}
}
}
It isn't a dynamic solution but it's alright for now.
I am trying to give role based authorization for resources. It works with out roles if I do it like
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(new OrRequestMatcher(
new AntPathRequestMatcher("/hello"),
new AntPathRequestMatcher("/user")
))
.authorizeRequests()
.anyRequest().access("#oauth2.hasScope('read')");
}
#Override
public void configure(ResourceServerSecurityConfigurer resources)
throws Exception {
resources.resourceId("openid");
}
}
If I use below method it won't work for test resources.
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.requestMatcher(new OrRequestMatcher(
new AntPathRequestMatcher("/hello"),
new AntPathRequestMatcher("/user")
))
.authorizeRequests()
.antMatchers("/test").hasRole("ADMIN")
.anyRequest().access("#oauth2.hasScope('read')");
}
It completely ignores token based authorization. How can I implement this? Another issue I am getting is if I remove requestMatcher block, Oauth client can not get the authorization code, after submitting user credentials to login form it reloads login page again. But with the previous block of code it works fine. What I am doing wrong here?
Here is my security configuration class
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**").permitAll()
.antMatchers("/js/**").permitAll()
.antMatchers("/img/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.defaultSuccessUrl("/hello")
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout");
}
}
When you use roles in spring you have to use prefix ROLE (for example ROLE_ADMIN) to make it work with default settings.
In Specific
I want to have HTTP Basic authentication ONLY for a specific URL pattern.
In Detail
I'm creating an API interface for my application and that needs to be authenticated by simple HTTP basic authentication. But other web pages should not be using HTTP basic but rather a the normal form login.
Current Configuration - NOT Working
#Override
protected void configure(HttpSecurity http) throws Exception {
http //HTTP Security
.csrf().disable() //Disable CSRF
.authorizeRequests() //Authorize Request Configuration
.antMatchers("/connect/**").permitAll()
.antMatchers("/", "/register").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/api/**").hasRole("API")
.anyRequest().authenticated()
.and() //HTTP basic Authentication only for API
.antMatcher("/api/**").httpBasic()
.and() //Login Form configuration for all others
.formLogin().loginPage("/login").permitAll()
.and() //Logout Form configuration
.logout().permitAll();
}
Waited for 2 days and didn't get any help here. But my research provided me a solution :)
Solution
#Configuration
#EnableWebMvcSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
private AuthenticationProvider authenticationProvider;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().hasAnyRole("ADMIN", "API")
.and()
.httpBasic();
}
}
#Configuration
#Order(2)
public static class FormWebSecurityConfig extends WebSecurityConfigurerAdapter{
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/js/**", "/img/**", "/lib/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() //HTTP with Disable CSRF
.authorizeRequests() //Authorize Request Configuration
.antMatchers("/connect/**").permitAll()
.antMatchers("/", "/register").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and() //Login Form configuration for all others
.formLogin()
.loginPage("/login").permitAll()
.and() //Logout Form configuration
.logout().permitAll();
}
}
}
I dunno if it can be helpful but I couldn't implement the above solution. I found a workaround defining a single Security
#Configuration class
extending
WebSecurityConfigurerAdapter
with both httpBasic() and formLogin() configured. Then I created a custom
CustomAuthEntryPoint implements AuthenticationEntryPoint
that has this logic in the commence method:
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException
{
String urlContext = UtilityClass.extractUrlContext(request);
if (!urlContext.equals(API_URL_PREFIX))
{
String redirectUrl = "urlOfFormLogin"
response.sendRedirect(request.getContextPath() + redirectUrl);
}
else
{
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
Dunno which is the "best practice strategy" about this issue