SpelEvaluationException interpreting "access" string in ResourceServerConfigurerAdapter - nullpointerexception

Any ideas on this?
From Tomcat:
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1011E:(pos 8): Method call: Attempted to call method throwOnError(java.lang.Boolean) on null context object
Returned to Client:
java.lang.IllegalArgumentException: Failed to evaluate expression '#oauth2.throwOnError(#oauth2.hasScope('read') and #oauth2.hasScope('write') and #oauth2.hasAnyRole('ROLE_USER','ROLE_ADMIN'))'
org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:13)
org.springframework.security.web.access.expression.WebExpressionVoter.vote(WebExpressionVoter.java:34)
org.springframework.security.web.access.expression.WebExpressionVoter.vote(WebExpressionVoter.java:18)
org.springframework.security.access.vote.UnanimousBased.decide(UnanimousBased.java:77)
I do a POST to my authorization server /oauth/token and get a token.
If I take that token and add a Authorization: Bearer header to a GET request to the resource server, I get that error.
In my subclass of ResourceServerConfigurerAdapter, the line it blows up on is here:
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**")
.access("#oauth2.hasScope('read') and #oauth2.hasScope('write') and #oauth2.hasAnyRole('ROLE_USER','ROLE_ADMIN')")
.accessDecisionManager(accessDecisionManager())
.anyRequest()
.fullyAuthenticated();
I know that the resource server recognizes the token because if i leave it out, I get the proper error. If I make up a fake one then I get the "invalid token" message, which is expected. If I use the actual token Spring is jumps in and blows up on the .access()
Thanks in advance for any help. I'm putting the code for my ResourceReserver below:
#Configuration
#EnableWebSecurity
#EnableResourceServer
public class ResourceServerConfigurer extends ResourceServerConfigurerAdapter {
#Autowired
private OAuth2AuthenticationEntryPoint oAuth2AuthenticationEntryPoint;
#Autowired
private ResourceServerTokenServices tokenServices;
#Autowired
private TokenStore tokenStore;
#Autowired
#Qualifier("oauth2ResourceId")
private String oauth2ResourceId;
#Autowired
#Qualifier("oauth2Realm")
private String oauth2Realm;
#Bean
OAuth2AuthenticationEntryPoint oAuth2AuthenticationEntryPoint() {
final OAuth2AuthenticationEntryPoint entryPoint = new OAuth2AuthenticationEntryPoint();
entryPoint.setRealmName(oauth2Realm);
entryPoint.setTypeName("Basic");
return entryPoint;
}
private AccessDecisionManager accessDecisionManager() {
return new UnanimousBased(Arrays.<AccessDecisionVoter>asList(new ScopeVoter(),
new AuthenticatedVoter(),
new WebExpressionVoter()));
}
private AuthenticationManager getAuthenticationManager() {
final OAuth2AuthenticationManager oAuth2AuthenticationManager = new OAuth2AuthenticationManager();
oAuth2AuthenticationManager.setTokenServices(tokenServices);
return oAuth2AuthenticationManager;
}
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**")
.access("#oauth2.hasScope('read') and #oauth2.hasScope('write') and #oauth2.hasAnyRole('ROLE_USER','ROLE_ADMIN')")
.accessDecisionManager(accessDecisionManager())
.anyRequest()
.fullyAuthenticated();
http
.anonymous()
.disable();
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER);
http
.logout()
.logoutUrl("/oauth/logout")
.logoutSuccessHandler(logoutSuccessHandler())
.invalidateHttpSession(true);
/*
http
.requiresChannel()
.antMatchers("/oauth/api/**")
.requiresSecure();
http
.portMapper()
.http(8080)
.mapsTo(8443);
*/
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources
.authenticationManager(getAuthenticationManager())
.tokenServices(tokenServices)
.tokenStore(tokenStore)
.resourceId(oauth2ResourceId);
}
private LogoutSuccessHandler logoutSuccessHandler() {
return new OAuth2SuccessLogoutHandler(tokenStore);
}
static final class OAuth2SuccessLogoutHandler implements LogoutSuccessHandler {
private final TokenStore tokenStore;
public OAuth2SuccessLogoutHandler(final TokenStore tokenStore) {
this.tokenStore = tokenStore;
}
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
request.toString();
}
}
}

The hasAnyRole() method is not OAuth2 related and therefore is not on the #oauth2 variable (it's on the root so you don't need to qualify it).

Related

How can I provide my custom OAuth2LoginAuthenticationProvider in spring boot

I am trying to setup OAuth2 login in spring boot, every thing is working good in my dev environment, however when I deploy it on production behind the Apache Http server using proxy configuration, authentication fails on Invalid Redirect URI check.
As suggested in some other post I already tried
1. use-forward-headers: true
2. Rewriting the request redirect URI
with no luck.
So I decided to hook my own custom OAuth2LoginAuthenticationProvider so that I can override this check.
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Value("${baseUrl}")
private String domainPath;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(new MyCustomFilter(domainPath), OAuth2LoginAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated().and()
.logout().logoutSuccessUrl("/")
.and()
.oauth2Login().userInfoEndpoint().customUserType(CmmOAuth2User.class, "custom");
}
#Bean
public MyOAuth2AuthenticationProvider authenticationProvider() {
MyOAuth2AuthenticationProvider authProvider
= new MyOAuth2AuthenticationProvider();
return authProvider;
}
#Autowired
public void configureProviderManager(ProviderManager providerManager) {
providerManager.getProviders().add(0,authenticationProvider());
}
#Bean
public ProviderManager authenticationManager() {
List<AuthenticationProvider> authProviderList = new ArrayList<AuthenticationProvider>();
authProviderList.add(0,authenticationProvider());
ProviderManager providerManager = new ProviderManager(authProviderList);
return providerManager;
}
}
My custom MyOAuth2AuthenticationProvider is not being called.

Spring security authentication How to get rid of

Trying to do simple spring boot security test.
I can pass the test only with deprecated NoOpPasswordEncoder in
globalConfigure() method in SpringSecurityConfig.
it works fine, but is it possible get rid of deprecated NoOpPasswordEncoder?
#Configuration
#EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest().fullyAuthenticated()
.antMatchers("/").permitAll()
.and()
.formLogin().defaultSuccessUrl("/", true)
.loginPage("/login").permitAll().and().logout().permitAll()
.and().httpBasic();
}
#SuppressWarnings("deprecated")
#Autowired
public void configureGlobal(AuthenticationManagerBuilder authBuilder) throws Exception{
authBuilder.inMemoryAuthentication()
.passwordEncoder(NoOpPasswordEncoder.getInstance())
.withUser("user").password("user").roles("USER")
.and()
.withUser("admin").password("admin").roles("USER", "ADMIN");
}
}
Testing spring security
#RunWith(SpringJUnit4ClassRunner.class)
#AutoConfigureMockMvc
#SpringBootTest
public class SpringSecurityConfigTest {
#Autowired
MockMvc mockMvc;
#Test
public void userIsAuthenticatedTest() throws Exception {
mockMvc.perform(formLogin().user("admin").password("admin"))
.andExpect(authenticated());
}
}
It depends what you want to do exactly. If you just want your test to pass and get rid of the deprecation you can remove the password encoder and add the {noop} prefix to the passwords in your configureGlobal method:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder authBuilder) throws Exception {
authBuilder.inMemoryAuthentication()
.withUser("user").password("{noop}user").roles("USER")
.and()
.withUser("admin").password("{noop}admin").roles("USER", "ADMIN");
}
Spring Security 5 changed the default password encoder to Delegating Password Encoder which uses the prefix in curly braces to determine which password encoder to use, s. https://docs.spring.io/spring-security/site/docs/5.0.5.RELEASE/reference/htmlsingle/#pe-dpe-format
However, if you want to use this security config in production you should probably use a different encoder anyway.

Resteasy client : How to set Context Params

I'm using Resteasy client to run test cases for my Service. In application We set context Params in a session check filter,(which implements ContainerRequestFilter). I'm trying to set the same, in Resteasy client, using by adding a ClientRequestFilter implementation, but the property is not recognized, in the service call.
//Resteasy client calling logic
ResteasyClient resteasyClient = new ResteasyClientBuilder().build();
resteasyClient.register(new MyClientRequestFilter());
resteasyClient.target("http://localhost:" + port + "/myPath").request()
.post(Entity.json(authorization_reqParams)).readEntity(String.class));
//filter
public class MyClientRequestFilter implements ClientRequestFilter
{
#Override
public void filter(ClientRequestContext requestContext) throws IOException
{
requestContext.setProperty("CUSTOMER_ATTRIBUTE", "myCustomValue");
}
}
//Rest service method
#POST
#Path("/myPath")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response subpartner(Authorization_ReqParams authorizationReqParams, #Context HttpHeaders headers,
#Context HttpServletRequest request, #Context HttpServletResponse response)
{
String myAttribute= request.getAttribute("CUSTOMER_ATTRIBUTE");
//myAttribute is returned as null always
//additional logic
}
I'm able to set&get Header paramets with the same implementation, but Request param is always read as null.
How can I set the request context params ?
In MyClientRequestFilter you add a propery to the request object. What you really want is to send a header instead.
Try this instead:
#Override
public void filter(ClientRequestContext requestContext) {
MultivaluedMap<String, Object> headers = requestContext.getHeaders();
headers.add("CUSTOMER_ATTRIBUTE", "myCustomValue");
}
And read it like this:
#POST
#Path("/myPath")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response subpartner(Authorization_ReqParams authorizationReqParams, #Context HttpHeaders headers,
#Context HttpServletRequest request, #Context HttpServletResponse response)
{
String myAttribute= headers.getRequestHeader("CUSTOMER_ATTRIBUTE");
//additional logic
}

Oauth2 server example with all 4 grant types

Is it possible to have an auth server defining all four types of grant types?
And if yes then how can I test them? A sample auth client for grant type password would be helpful.
I am presuming that you want to implement it using Spring OAuth server implementation. One example by Dave Syer is hosted on GitHub, and it uses JDBC as the token store.
Now for the grant types, you have the option to configure that per client that you register with your OAuth server. You will notice that it has been setup using "authorizedGrantTypes" in the example below.
For testing - password grant type is very easy to test using a REST client like Postman and making a call to the OAuth server.
Feel free to comment on this if you need more help. All the best!
Resource Server
#Configuration
#EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Override
public void configure(ResourceServerSecurityConfigurer resources)
throws Exception {
resources.tokenStore(tokenStore);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
}
Authorization Server
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager auth;
#Autowired
private DataSource dataSource;
private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
#Bean
public JdbcTokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Bean
protected AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security)
throws Exception {
security.passwordEncoder(passwordEncoder);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.authorizationCodeServices(authorizationCodeServices())
.authenticationManager(auth).tokenStore(tokenStore())
.approvalStoreDisabled();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// #formatter:off
clients.jdbc(dataSource)
.passwordEncoder(passwordEncoder)
.withClient("my-trusted-client")
.authorizedGrantTypes("password", "authorization_code",
"refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.resourceIds("oauth2-resource")
.accessTokenValiditySeconds(60).and()
.withClient("my-client-with-registered-redirect")
.authorizedGrantTypes("authorization_code")
.authorities("ROLE_CLIENT").scopes("read", "trust")
.resourceIds("oauth2-resource")
.redirectUris("http://anywhere?key=value").and()
.withClient("my-client-with-secret")
.authorizedGrantTypes("client_credentials", "password")
.authorities("ROLE_CLIENT").scopes("read")
.resourceIds("oauth2-resource").secret("secret");
// #formatter:on
}
}

In WCF how do I remove the 404 response body?

I have a WCF service configured and I'm using routing to configure it. Everything is working the way I want it, except the 404 messages have a body stating Service Endpoint not found.
I'd like the 404 to have an empty response body.
Here is my route registration:
public class Global : HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
}
private void RegisterRoutes(RouteCollection routes)
{
routes.Add(new ServiceRoute("RootService", new WebServiceHostFactory(), typeof(ServiceProvider)));
}
Here is my service class:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ServiceContract]
public class ServiceProvider
{
[WebGet]
public Test ValidUrl()
{
return new Test();
}
}
How do I make the response for this url http://localhost/RootService have an empty 404 body?
I found a few ways to do this and I've listed two below. They key is having the UriTemplate set as *. This makes the method match all routes that aren't explicitly matched otherwise.
[WebGet(UriTemplate="*")]
public void ErrorForGet()
{
throw new WebFaultException(HttpStatusCode.NotFound);
}
I don't like this way as well, but it works:
[WebGet(UriTemplate="*")]
public void ErrorForGet()
{
WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound();
}
Both of these methods have overloads that take a string as a message to provide to the requesting client. The WebFaultException needs to be like this going that route though: throw new WebFaultException<string>("Resource not found", HttpStatusCode.NotFound);