Mikhail Mazursky (Migrated from SEC-1908) said:

I was updating spring 3.0.x to 3.1.0 and one of my tests (with Mockito) showed a compilation error. The problem i encountered was the same as described here 1:

LdapAuthoritiesPopulator authoritiesPopulator = mock(LdapAuthoritiesPopulator.class);

when(authoritiesPopulator.getGrantedAuthorities(userData, USERNAME)).thenReturn(AuthorityUtils.NO_AUTHORITIES);

This is not type safe but that problem can be worked around by:

doReturn(AuthorityUtils.NO_AUTHORITIES).when(authoritiesPopulator).getGrantedAuthorities(userData, USERNAME);

The question is should the LdapAuthoritiesPopulator interface's method getGrantedAuthorities have a bounded wildcard return type or not? I think it's better to remove wildcard as it gives nothing to the calling code.

Spring 3.0.x have it as:

Collection getGrantedAuthorities(DirContextOperations userData, String username);

and 3.1.0 as:

Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username);

The same problem with GrantedAuthoritiesMapper, GrantedAuthoritiesContainer, Attributes2GrantedAuthoritiesMapper, WebSpherePreAuthenticatedWebAuthenticationDetailsSource and possibly some other classes/ifaces.

Interesting reading on the topic 2.

Methods with bounded wildcard return types

PUBLIC

  • [ ] ~.FindBoundedWildcardsTest$Here#here
  • [x] ~.access.annotation.AnnotationMetadataExtractor#extractAttributes
  • [x] ~.access.annotation.BusinessService#methodReturningAList
  • [x] ~.access.annotation.BusinessServiceImpl#methodReturningAList
  • [x] ~.access.annotation.ExpressionProtectedBusinessServiceImpl#methodReturningAList
  • [x] ~.access.annotation.Jsr250BusinessServiceImpl#methodReturningAList
  • [x] ~.access.annotation.SecuredAnnotationSecurityMetadataSourceTests$CustomSecurityAnnotationMetadataExtractor#extractAttributes
  • [x] ~.access.expression.method.PrePostAnnotationSecurityMetadataSourceTests$ReturnAList#doSomething
  • [x] ~.access.expression.method.PrePostAnnotationSecurityMetadataSourceTests$ReturnAListImpl1#doSomething
  • [x] ~.access.expression.method.PrePostAnnotationSecurityMetadataSourceTests$ReturnAListImpl2#doSomething
  • [x] ~.access.expression.method.PrePostAnnotationSecurityMetadataSourceTests$ReturnAnotherList#doSomething
  • [x] ~.access.expression.method.PrePostAnnotationSecurityMetadataSourceTests$ReturnAnotherListImpl1#doSomething
  • [x] ~.access.expression.method.PrePostAnnotationSecurityMetadataSourceTests$ReturnAnotherListImpl2#doSomething
  • [x] ~.access.hierarchicalroles.NullRoleHierarchy#getReachableGrantedAuthorities
  • [x] ~.access.hierarchicalroles.RoleHierarchy#getReachableGrantedAuthorities
  • [x] ~.access.hierarchicalroles.RoleHierarchyAuthoritiesMapper#mapAuthorities
  • [x] ~.access.intercept.AbstractSecurityInterceptor#getSecureObjectClass
  • [x] ~.access.intercept.AbstractSecurityInterceptorTests$MockSecurityInterceptorReturnsNull#getSecureObjectClass
  • [x] ~.access.intercept.AbstractSecurityInterceptorTests$MockSecurityInterceptorWhichOnlySupportsStrings#getSecureObjectClass
  • [x] ~.access.intercept.RunAsUserToken#getOriginalAuthentication
  • [x] ~.access.intercept.aopalliance.MethodSecurityInterceptor#getSecureObjectClass
  • [x] ~.access.vote.AbstractAccessDecisionManager#getDecisionVoters
  • [x] ~.access.vote.AbstractAclVoter#getProcessDomainObjectClass
  • [x] ~.authentication.event.InteractiveAuthenticationSuccessEvent#getGeneratedBy
  • [x] ~.authorization.method.PreAuthorizeAuthorizationManagerTests$TestTargetClassAware#getTargetClass
  • [x] ~.authorization.method.SecuredAuthorizationManagerTests$TestTargetClassAware#getTargetClass
  • [x] ~.concurrent.DelegatingSecurityContextExecutorService#submit
  • [x] ~.concurrent.DelegatingSecurityContextScheduledExecutorService#schedule
  • [x] ~.concurrent.DelegatingSecurityContextScheduledExecutorService#scheduleAtFixedRate
  • [x] ~.concurrent.DelegatingSecurityContextScheduledExecutorService#scheduleWithFixedDelay
  • [x] ~.config.annotation.AbstractConfiguredSecurityBuilder#getSharedObjects
  • [x] ~.config.annotation.web.FormLoginDsl#getAuthenticationDetailsSource
  • [x] ~.config.annotation.web.HttpBasicDsl#getAuthenticationDetailsSource
  • [x] ~.config.annotation.web.OAuth2LoginDsl#getAuthenticationDetailsSource
  • [x] ~.config.annotation.web.messaging.MessageSecurityMetadataSourceRegistry$MatcherBuilder#build
  • [x] ~.config.annotation.web.messaging.MessageSecurityMetadataSourceRegistry$PathMatcherMessageMatcherBuilder#build
  • [x] ~.config.annotation.web.messaging.MessageSecurityMetadataSourceRegistry$PreBuiltMatcherBuilder#build
  • [x] ~.config.annotation.web.oauth2.resourceserver.JwtDsl#getJwtAuthenticationConverter
  • [x] ~.config.annotation.web.oauth2.resourceserver.JwtDslTests$CustomJwtAuthenticationConverterConfig$Companion#getCONVERTER
  • [x] ~.config.authentication.AuthenticationManagerFactoryBean#getObjectType
  • [x] ~.config.core.userdetails.ReactiveUserDetailsServiceResourceFactoryBean#getObjectType
  • [x] ~.config.core.userdetails.UserDetailsMapFactoryBean#getObjectType
  • [x] ~.config.core.userdetails.UserDetailsResourceFactoryBean#getObjectType
  • [x] ~.config.http.HandlerMappingIntrospectorFactoryBean#getObjectType
  • [x] ~.config.http.HttpConfigurationBuilder$SecurityContextHolderStrategyFactory#getObjectType
  • [x] ~.config.http.OAuth2ResourceServerBeanDefinitionParser$NimbusJwtDecoderJwkSetUriFactoryBean#getObjectType
  • [x] ~.config.http.OAuth2ResourceServerBeanDefinitionParserTests$ClockFactoryBean#getObjectType
  • [x] ~.config.http.OAuth2ResourceServerBeanDefinitionParserTests$JwtDecoderFactoryBean#getObjectType
  • [x] ~.config.http.OAuth2ResourceServerBeanDefinitionParserTests$MockWebServerFactoryBean#getObjectType
  • [x] ~.config.http.OAuth2ResourceServerBeanDefinitionParserTests$MockWebServerPropertiesFactoryBean#getObjectType
  • [x] ~.config.http.OAuth2ResourceServerBeanDefinitionParserTests$OpaqueTokenIntrospectorFactoryBean#getObjectType
  • [x] ~.config.ldap.EmbeddedLdapServerContextSourceFactoryBean#getObjectType
  • [x] #11805
  • [x] #11804
  • [x] #11803
  • [x] #11802
  • [x] #11801
  • [x] ~.config.provisioning.UserDetailsManagerResourceFactoryBean#getObjectType
  • [ ] ~.config.web.server.ServerJwtDsl#getJwtAuthenticationConverter
  • [ ] ~.config.web.server.ServerJwtDslTests$CustomJwtAuthenticationConverterConfig$Companion#getCONVERTER
  • [ ] ~.config.websocket.WebSocketMessageBrokerConfigTests$ExceptingInterceptor#preSend
  • [ ] ~.config.websocket.WebSocketMessageBrokerSecurityBeanDefinitionParser$SecurityContextHolderStrategyFactory#getObjectType
  • [x] ~.core.Authentication#getAuthorities
  • [x] ~.core.authority.GrantedAuthoritiesContainer#getGrantedAuthorities
  • [x] ~.core.authority.mapping.Attributes2GrantedAuthoritiesMapper#getGrantedAuthorities
  • [x] ~.core.authority.mapping.GrantedAuthoritiesMapper#mapAuthorities
  • [x] ~.core.authority.mapping.NullAuthoritiesMapper#mapAuthorities
  • [x] ~.core.userdetails.UserDetails#getAuthorities
  • [ ] ~.jackson2.UnmodifiableMapDeserializer#deserialize
  • [x] ~.ldap.authentication.UserDetailsServiceLdapAuthoritiesPopulator#getGrantedAuthorities
  • [x] ~.ldap.userdetails.LdapAuthoritiesPopulator#getGrantedAuthorities
  • [ ] ~.messaging.access.intercept.AuthorizationChannelInterceptor#preSend
  • [ ] ~.messaging.access.intercept.ChannelSecurityInterceptor#getSecureObjectClass
  • [ ] ~.messaging.access.intercept.ChannelSecurityInterceptor#postReceive
  • [ ] ~.messaging.access.intercept.ChannelSecurityInterceptor#preSend
  • [ ] ~.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager$Builder#build
  • [ ] ~.messaging.context.SecurityContextChannelInterceptor#beforeHandle
  • [ ] ~.messaging.context.SecurityContextChannelInterceptor#preSend
  • [ ] ~.messaging.web.csrf.CsrfChannelInterceptor#preSend
  • [ ] ~.oauth2.client.endpoint.AbstractOAuth2AuthorizationGrantRequestEntityConverter#convert
  • [ ] ~.oauth2.client.oidc.authentication.OidcIdTokenDecoderFactory#createDefaultClaimTypeConverters
  • [ ] ~.oauth2.client.oidc.authentication.ReactiveOidcIdTokenDecoderFactory#createDefaultClaimTypeConverters
  • [ ] ~.oauth2.client.oidc.userinfo.OidcReactiveOAuth2UserService#createDefaultClaimTypeConverters
  • [ ] ~.oauth2.client.oidc.userinfo.OidcUserService#createDefaultClaimTypeConverters
  • [ ] ~.oauth2.client.userinfo.OAuth2UserRequestEntityConverter#convert
  • [ ] ~.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction#defaultRequest
  • [x] ~.oauth2.core.DefaultOAuth2AuthenticatedPrincipal#getAuthorities
  • [x] ~.oauth2.core.OAuth2AuthenticatedPrincipal#getAuthorities
  • [x] ~.oauth2.core.user.DefaultOAuth2User#getAuthorities
  • [x] ~.oauth2.server.resource.introspection.OAuth2IntrospectionAuthenticatedPrincipal#getAuthorities
  • [x] ~.provisioning.MutableUser#getAuthorities
  • [ ] ~.scheduling.DelegatingSecurityContextTaskScheduler#schedule
  • [ ] ~.scheduling.DelegatingSecurityContextTaskScheduler#scheduleAtFixedRate
  • [ ] ~.scheduling.DelegatingSecurityContextTaskScheduler#scheduleWithFixedDelay
  • [ ] ~.task.DelegatingSecurityContextAsyncTaskExecutor#submit
  • [x] ~.test.context.showcase.CustomUserDetails#getAuthorities
  • [ ] ~.test.context.support.WithSecurityContext#factory
  • [ ] ~.web.access.intercept.FilterSecurityInterceptor#getSecureObjectClass
  • [x] ~.web.authentication.switchuser.SwitchUserAuthorityChanger#modifyGrantedAuthorities
  • [ ] ~.web.authentication.www.BasicAuthenticationConverter#getAuthenticationDetailsSource

PROTECTED

  • [ ] ~.acls.afterinvocation.AbstractAclProvider#getProcessDomainObjectClass
  • [ ] ~.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer$AuthorizedUrl#getMatchers
  • [ ] ~.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer$AuthorizedUrl#getMatchers
  • [ ] ~.config.annotation.web.configurers.UrlAuthorizationConfigurer$AuthorizedUrl#getMatchers
  • [ ] ~.config.web.server.ServerHttpSecurity$OAuth2ResourceServerSpec$JwtSpec#getJwtAuthenticationConverter
  • [x] ~.ldap.authentication.AbstractLdapAuthenticationProvider#loadUserAuthorities
  • [x] ~.ldap.authentication.LdapAuthenticationProvider#loadUserAuthorities
  • [x] ~.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider#loadUserAuthorities
  • [ ] ~.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter#getAuthenticationDetailsSource
  • [ ] ~.web.authentication.rememberme.AbstractRememberMeServices#getAuthenticationDetailsSource

DEFAULT

  • [ ] ~.access.vote.RoleHierarchyVoter#extractAuthorities
  • [ ] ~.access.vote.RoleVoter#extractAuthorities
  • [ ] ~.authentication.AuthenticationTrustResolverImpl#getAnonymousClass
  • [ ] ~.authentication.AuthenticationTrustResolverImpl#getRememberMeClass
  • [ ] ~.config.annotation.ObjectPostProcessorTests$PerformConversion#perform
  • [ ] ~.config.annotation.web.configurers.AbstractInterceptUrlConfigurer#getDecisionVoters
  • [ ] ~.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer#getDecisionVoters
  • [ ] ~.config.annotation.web.configurers.NamespaceHttpBasicTests$AuthenticationDetailsSourceHttpBasicConfig#authenticationDetailsSource
  • [ ] ~.config.annotation.web.configurers.NamespaceHttpBasicTests$AuthenticationDetailsSourceHttpBasicLambdaConfig#authenticationDetailsSource
  • [ ] ~.config.annotation.web.configurers.UrlAuthorizationConfigurer#getDecisionVoters
  • [ ] ~.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer$JwtConfigurer#getJwtAuthenticationConverter
  • [ ] ~.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurerTests$CustomAuthenticationDetailsSource#authenticationDetailsSource
  • [ ] ~.config.annotation.web.socket.WebSocketMessageBrokerSecurityConfigurationDocTests$WebSocketSecurityConfig#authorizationManager
  • [ ] ~.config.annotation.web.socket.WebSocketMessageBrokerSecurityConfigurationTests$DefaultPatternMatcherConfig#authorizationManager
  • [ ] ~.config.annotation.web.socket.WebSocketMessageBrokerSecurityConfigurationTests$MsmsRegistryCustomPatternMatcherConfig#authorizationManager
  • [ ] ~.config.annotation.web.socket.WebSocketMessageBrokerSecurityConfigurationTests$OverrideMsmsRegistryCustomPatternMatcherConfig#authorizationManager
  • [ ] ~.config.annotation.web.socket.WebSocketMessageBrokerSecurityConfigurationTests$SockJsProxylessSecurityConfig#authorizationManager
  • [ ] ~.config.annotation.web.socket.WebSocketMessageBrokerSecurityConfigurationTests$SockJsSecurityConfig#authorizationManager
  • [ ] ~.config.annotation.web.socket.WebSocketMessageBrokerSecurityConfigurationTests$WebSocketSecurityConfig#authorizationManager
  • [ ] ~.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager$Entry#getMessageMatcher

PRIVATE

  • [ ] ~.aot.hint.CoreSecurityRuntimeHintsTests#getAuthenticationEvents
  • [ ] ~.aot.hint.CoreSecurityRuntimeHintsTests#getAuthenticationExceptions
  • [ ] ~.authentication.DefaultAuthenticationEventPublisher#getEventConstructor
  • [ ] ~.authorization.AuthorityAuthorizationManager#getGrantedAuthorities
  • [ ] ~.authorization.method.AuthorizationManagerAfterReactiveMethodInterceptor#postAuthorize
  • [ ] ~.authorization.method.PostFilterAuthorizationReactiveMethodInterceptor#filterMultiValue
  • [ ] ~.authorization.method.PostFilterAuthorizationReactiveMethodInterceptor#filterSingleValue
  • [ ] ~.authorization.method.PostFilterAuthorizationReactiveMethodInterceptor#postFilter
  • [ ] ~.authorization.method.PreFilterAuthorizationReactiveMethodInterceptor#filterMultiValue
  • [ ] ~.authorization.method.PreFilterAuthorizationReactiveMethodInterceptor#filterSingleValue
  • [ ] ~.config.annotation.web.builders.HttpSecurityAddFilterTest#assertThatFilters
  • [ ] ~.config.annotation.web.configuration.HttpSecurityConfiguration#createSharedObjects
  • [ ] ~.config.annotation.web.configuration.WebSecurityConfigurerAdapter#createSharedObjects
  • [ ] ~.config.annotation.web.configurers.NamespaceDebugTests#filterChainClass
  • [ ] ~.config.annotation.web.configurers.NamespaceHttpCustomFilterTests#assertThatFilters
  • [ ] ~.config.http.UserDetailsServiceFactoryBean#getBeansOfType
  • [ ] ~.config.ldap.ContextSourceSettingPostProcessor#getContextSourceClass
  • [ ] ~.config.websocket.WebSocketMessageBrokerConfigTests#message
  • [ ] ~.config.websocket.WebSocketMessageBrokerSecurityBeanDefinitionParser$MessageMatcherDelegatingAuthorizationManagerFactory#createMessageMatcherDelegatingAuthorizationManager
  • [ ] ~.htmlunit.server.HtmlUnitWebTestClient#content
  • [ ] ~.jackson2.SecurityJackson2Modules#createAllowlistedDefaultTyping
  • [ ] ~.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager#authorizationContext
  • [ ] ~.oauth2.client.oidc.authentication.OidcIdTokenDecoderFactory#getConverter
  • [ ] ~.oauth2.client.oidc.authentication.ReactiveOidcIdTokenDecoderFactory#getConverter
  • [ ] ~.oauth2.client.oidc.userinfo.OidcReactiveOAuth2UserService#getConverter
  • [ ] ~.oauth2.client.oidc.userinfo.OidcUserService#getConverter
  • [ ] ~.oauth2.client.userinfo.DefaultReactiveOAuth2UserService#getRequestHeaderSpec
  • [ ] ~.oauth2.core.converter.ClaimTypeConverterTests#getConverter
  • [ ] ~.oauth2.core.oidc.user.TestOidcUsers#authorities
  • [ ] ~.oauth2.jwt.MappedJwtClaimSetConverter#getConverter
  • [ ] ~.oauth2.server.resource.authentication.JwtAuthenticationProviderTests#errorCode
  • [ ] ~.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector#defaultRequestEntityConverter
  • [ ] ~.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector#defaultRequestEntityConverter
  • [ ] ~.saml2.provider.service.authentication.OpenSamlAuthenticationProvider#getAssertionAuthorities
  • [ ] ~.test.context.support.WithSecurityContextTestExecutionListener#createFactory
  • [ ] ~.web.authentication.preauth.websphere.DefaultWASUsernameAndGroupsExtractor#getClass
  • [ ] ~.web.authentication.preauth.websphere.DefaultWASUsernameAndGroupsExtractor#getWSCredentialClass
  • [ ] ~.web.authentication.preauth.websphere.WebSpherePreAuthenticatedWebAuthenticationDetailsSource#getWebSphereGroupsBasedGrantedAuthorities

Comment From: spring-projects-issues

Luke Taylor said:

This was requested in SEC-1550, to cater for classes which use custom GrantedAuthority implementations.

Comment From: spring-projects-issues

Mikhail Mazursky said:

From the mentioned issue i still can't see any benefit to return bounded wildcad types. When i use ??void test(List<? super T> arg)?? method signature that allows me to safely pass lists of child types (U extends T) to method. What's the point to return bounded wildcard types? As you said in those issue ??You can still only read the upper bound of the wildcard from the collection??. Wildcard only helps to avoid in-method cast from ??List?? to ??List?? but it's 100% safe as everything that is ??Authority?? can be used as ??GrantedAuthority??.