Skip to content

Commit

Permalink
feat(sso-oauth): added possibility to get oauth access tokens when pr…
Browse files Browse the repository at this point in the history
…e authenticated

* added a Filter that checks pre authentication headers and manipulates the request so that it can be processed by standard spring oauth mechanics
* added a AuthenticationProvider that is able to state for such a manipulated request that the user has been authenticated
* added some configuration to wire it all together
* added some tooling for this to work
* added some tests and documentation

closes eclipse-sw360#542

Signed-off-by: Andreas Klett <[email protected]>
  • Loading branch information
imaykay authored and maxhbr committed Jul 24, 2019
1 parent a401857 commit b21bedd
Show file tree
Hide file tree
Showing 17 changed files with 880 additions and 97 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright Siemens AG, 2017-2018. Part of the SW360 Portal Project.
* Copyright Siemens AG, 2017-2019. Part of the SW360 Portal Project.
*
* SPDX-License-Identifier: EPL-1.0
*
Expand All @@ -11,12 +11,20 @@

package org.eclipse.sw360.rest.authserver.security;

import org.eclipse.sw360.datahandler.thrift.ThriftClients;
import org.eclipse.sw360.rest.authserver.security.basicauth.Sw360LiferayAuthenticationProvider;
import org.eclipse.sw360.rest.authserver.security.customheaderauth.Sw360CustomHeaderAuthenticationFilter;
import org.eclipse.sw360.rest.authserver.security.customheaderauth.Sw360CustomHeaderAuthenticationProvider;
import org.eclipse.sw360.rest.authserver.security.customheaderauth.Sw360CustomHeaderUserDetailsProvider;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
Expand All @@ -28,39 +36,47 @@
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import javax.annotation.PostConstruct;
import javax.servlet.Filter;

import java.io.IOException;

import static org.eclipse.sw360.rest.authserver.Sw360AuthorizationServer.*;
import static org.eclipse.sw360.rest.authserver.Sw360AuthorizationServer.CONFIG_ACCESS_TOKEN_VALIDITY_SECONDS;
import static org.eclipse.sw360.rest.authserver.Sw360AuthorizationServer.CONFIG_CLIENT_ID;
import static org.eclipse.sw360.rest.authserver.Sw360AuthorizationServer.CONFIG_CLIENT_SECRET;
import static org.eclipse.sw360.rest.authserver.security.Sw360GrantedAuthority.BASIC;
import static org.eclipse.sw360.rest.authserver.security.Sw360SecurityEncryptor.decrypt;

/**
* This class configures the oauth2 authorization server specialities for the
* authorization parts of this server. Only exception is that the
* {@link AuthenticationManager} is shared with the standard security and this
* one is configured with our oauth2 {@link AuthenticationProvider}s in
* {@link Sw360WebSecurityConfiguration}.
*/
@Configuration
@EnableAuthorizationServer
public class Sw360AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;

@Value("${security.oauth2.client.client-id}")
private String clientId;

@Value("${security.oauth2.client.client-secret}")
private String clientSecret;

@Value("${security.oauth2.client.scope}")
private String[] scopes;

@Value("${security.oauth2.client.authorized-grant-types}")
private String[] authorizedGrantTypes;

@Value("${security.oauth2.client.resource-ids}")
private String resourceIds;

@Value("${security.oauth2.client.scope}")
private String[] scopes;

@Value("${security.oauth2.client.client-secret}")
private String clientSecret;

@Value("${security.oauth2.client.access-token-validity-seconds}")
private Integer accessTokenValiditySeconds;

@Autowired
public Sw360AuthorizationServerConfiguration(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
private AuthenticationManager authenticationManager;

@PostConstruct
public void postSw360AuthorizationServerConfiguration() throws IOException {
Expand All @@ -78,6 +94,7 @@ public void postSw360AuthorizationServerConfiguration() throws IOException {
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
.tokenStore(tokenStore())
.tokenEnhancer(jwtAccessTokenConverter())
.authenticationManager(authenticationManager);
Expand All @@ -87,7 +104,8 @@ public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws E
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
String serverAuthority = BASIC.getAuthority();
oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('" + serverAuthority + "')")
.checkTokenAccess("hasAuthority('" + serverAuthority + "')");
.checkTokenAccess("hasAuthority('" + serverAuthority + "')")
.addTokenEndpointAuthenticationFilter(sw360CustomHeaderAuthenticationFilter());
}

@Override
Expand Down Expand Up @@ -115,4 +133,29 @@ protected JwtAccessTokenConverter jwtAccessTokenConverter() {
jwtAccessTokenConverter.setKeyPair(keyStoreKeyFactory.getKeyPair("jwt"));
return jwtAccessTokenConverter;
}

@Bean
protected Filter sw360CustomHeaderAuthenticationFilter() {
return new Sw360CustomHeaderAuthenticationFilter();
}

@Bean
protected Sw360LiferayAuthenticationProvider sw360LiferayAuthenticationProvider() {
return new Sw360LiferayAuthenticationProvider();
}

@Bean
protected Sw360CustomHeaderAuthenticationProvider sw360CustomHeaderAuthenticationProvider() {
return new Sw360CustomHeaderAuthenticationProvider();
}

@Bean
protected Sw360CustomHeaderUserDetailsProvider principalProvider() {
return new Sw360CustomHeaderUserDetailsProvider();
}

@Bean
protected ThriftClients thriftClients() {
return new ThriftClients();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright Siemens AG, 2017. Part of the SW360 Portal Project.
* Copyright Siemens AG, 2017, 2019. Part of the SW360 Portal Project.
*
* SPDX-License-Identifier: EPL-1.0
*
Expand All @@ -11,35 +11,68 @@

package org.eclipse.sw360.rest.authserver.security;

import org.eclipse.sw360.rest.authserver.security.basicauth.Sw360LiferayAuthenticationProvider;
import org.eclipse.sw360.rest.authserver.security.customheaderauth.Sw360CustomHeaderAuthenticationProvider;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.AuthenticationManagerConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
* This class configures the standard spring security for this server. Only
* exception is that the {@link AuthenticationManager} is shared with the oauth2
* security and this one is configured with the oauth2
* {@link AuthenticationProvider}s from
* {@link Sw360AuthorizationServerConfiguration}.
*/
@Configuration
@EnableWebSecurity
public class Sw360WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

private final Sw360AuthenticationProvider sw360AuthenticationProvider;
@Autowired
private Sw360LiferayAuthenticationProvider sw360LiferayAuthenticationProvider;

@Autowired
private Sw360CustomHeaderAuthenticationProvider sw360CustomHeaderAuthenticationProvider;

public Sw360WebSecurityConfiguration(Sw360AuthenticationProvider sw360AuthenticationProvider) {
this.sw360AuthenticationProvider = sw360AuthenticationProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS)
.permitAll() // some JS frameworks make HTTP OPTIONS requests
.anyRequest()
.authenticated()
.and()
.httpBasic()
.and()
.csrf().disable();
}

@Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) {
authenticationManagerBuilder.authenticationProvider(this.sw360AuthenticationProvider);
authenticationManagerBuilder
.authenticationProvider(sw360LiferayAuthenticationProvider)
.authenticationProvider(sw360CustomHeaderAuthenticationProvider);
}

/**
* We have to publish our configured authentication manager because otherwise
* some boot default manager will be populated from
* {@link AuthenticationManagerConfiguration}.
*/
@Bean(name = "authenticationManager")
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll() // some JS frameworks make HTTP OPTIONS requests
.anyRequest().authenticated()
.and().httpBasic()
.and().csrf().disable();
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@
* http://www.eclipse.org/legal/epl-v10.html
*/

package org.eclipse.sw360.rest.authserver.security;
package org.eclipse.sw360.rest.authserver.security.basicauth;

import com.google.common.base.Strings;
import org.apache.thrift.TException;

import org.eclipse.sw360.datahandler.permissions.PermissionUtils;
import org.eclipse.sw360.datahandler.thrift.ThriftClients;
import org.eclipse.sw360.datahandler.thrift.users.User;
import org.eclipse.sw360.datahandler.thrift.users.UserService;

import org.apache.thrift.TException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.client.RestTemplateBuilder;
Expand All @@ -28,7 +30,6 @@
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.io.UnsupportedEncodingException;
Expand All @@ -41,8 +42,11 @@
import static org.eclipse.sw360.rest.authserver.security.Sw360GrantedAuthority.READ;
import static org.eclipse.sw360.rest.authserver.security.Sw360GrantedAuthority.WRITE;

@Component
public class Sw360AuthenticationProvider implements AuthenticationProvider {
/**
* This {@link AuthenticationProvider} is able to verify the given credentials
* of the {@link Authentication} object against a configured Liferay instance.
*/
public class Sw360LiferayAuthenticationProvider implements AuthenticationProvider {

@Value("${sw360.test-user-id:#{null}}")
private String testUserId;
Expand All @@ -64,8 +68,14 @@ public class Sw360AuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String userIdentifier = authentication.getName();
String password = authentication.getCredentials().toString();
Object possiblePassword = authentication.getCredentials();
if (possiblePassword == null) {
return null;
}
String password = possiblePassword.toString();

// FIXME: we should definitely find a way to remove this test code in production
// code!
if (isDevEnvironment() && testUserId != null && testUserPassword != null) {
// For easy testing without having a Liferay portal running, we mock an existing sw360 user
if (userIdentifier.equals(testUserId) && password.equals(testUserPassword)) {
Expand Down
Loading

0 comments on commit b21bedd

Please sign in to comment.