Skip to content

Commit 06659a1

Browse files
authored
Merge pull request #629 from overture-stack/rc/5.1.0
5.1.0 use spring security oauth2 client replace OAuth2SsoFilter with OAuth2AuthorizationRequestResolver bean: - use oauth2 DSL in SecureServerConfig - add custom oauth2 and open id connect user info service - add oauth2 request resolver replace legacy spring oauth2 beans add docker compose to run ego stack locally update docker compose to update keycloak replace legacy oauth2 lib exceptions update tests with new changes
2 parents bc906d5 + 78d9f11 commit 06659a1

40 files changed

+816
-963
lines changed

docker-compose-all.yml

+24-10
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,45 @@
11
version: '3.7'
22
services:
3+
ego-ui:
4+
image: overture/ego-ui:edge
5+
expose:
6+
- "8080"
7+
ports:
8+
- "8080:8080"
9+
environment:
10+
REACT_APP_API: http://localhost:8081
11+
REACT_APP_EGO_CLIENT_ID: ego-ui
312
api:
4-
build:
5-
context: ./
6-
dockerfile: Dockerfile
7-
restart: always
13+
# change the image tag to the target image as needed
14+
image: overture/ego:4c1969bf
815
environment:
9-
SERVER_PORT: 8080
16+
SERVER_PORT: 8081
1017
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/ego?stringtype=unspecified
1118
SPRING_DATASOURCE_USERNAME: postgres
1219
SPRING_DATASOURCE_PASSWORD: password
1320
SPRING_FLYWAY_ENABLED: "true"
1421
SPRING_FLYWAY_LOCATIONS: "classpath:flyway/sql,classpath:db/migration"
15-
SPRING_PROFILES: demo, auth
22+
SPRING_PROFILES_ACTIVE: auth
23+
google.client.clientId: $EGO_GOOGLE_CLIENT_ID
24+
google.client.clientSecret: $EGO_GOOGLE_SECRET
25+
default.user.firstUserAsAdmin: "true"
26+
logging.level.root: INFO
1627
expose:
17-
- "8080"
28+
- "8081"
1829
ports:
19-
- "$API_HOST_PORT:8080"
30+
- "8081:8081"
2031
depends_on:
2132
- postgres
2233
postgres:
2334
image: postgres:12.6
24-
restart: always
2535
environment:
2636
- POSTGRES_DB=ego
2737
- POSTGRES_PASSWORD=password
2838
expose:
2939
- "5432"
3040
ports:
31-
- "8432:5432"
41+
- "5432:5432"
42+
volumes:
43+
- "ego_data:/var/lib/postgresql/data"
44+
volumes:
45+
ego_data:

docker-compose.yml

+20-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,26 @@
11
version: '3.7'
22
services:
3+
keycloak:
4+
image: quay.io/keycloak/keycloak:16.1.0
5+
ports:
6+
- 8083:8080
7+
environment:
8+
KEYCLOAK_USER: admin
9+
KEYCLOAK_PASSWORD: admin
10+
DB_VENDOR: POSTGRES
11+
DB_ADDR: postgres
12+
DB_DATABASE: ego
13+
DB_USER: postgres
14+
DB_SCHEMA: public
15+
DB_PASSWORD: password
316
ego-ui:
417
image: overture/ego-ui:edge
5-
network_mode: host
18+
ports:
19+
- "8080:8080"
620
environment:
721
REACT_APP_API: http://localhost:8081
822
REACT_APP_EGO_CLIENT_ID: ego-ui
9-
23+
REACT_APP_KEYCLOAK_ENABLED: "true"
1024
postgres:
1125
image: postgres:12.6
1226
restart: always
@@ -17,4 +31,7 @@ services:
1731
- "5432"
1832
ports:
1933
- "5432:5432"
20-
34+
volumes:
35+
- "psql_data:/var/lib/postgresql/data"
36+
volumes:
37+
psql_data:

makefile

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
start:
2+
docker-compose -f docker-compose-all.yml up -d
3+
sleep 10;
4+
make init-db
5+
6+
up:
7+
docker-compose -f docker-compose-all.yml up -d
8+
9+
down:
10+
docker-compose -f docker-compose-all.yml down
11+
12+
nuke:
13+
docker-compose -f docker-compose-all.yml down --volumes
14+
15+
# needed to insert the ego ui client in ego db
16+
init-db:
17+
docker exec ego_postgres_1 psql -h localhost -p 5432 -U postgres -d ego --command "INSERT INTO EGOAPPLICATION (name, clientId, clientSecret, redirectUri, description, status, errorredirecturi) VALUES ('ego ui', 'ego-ui', 'secret', 'http://localhost:8080/', '...', 'APPROVED', 'http://localhost:8080/error') on conflict do nothing"

pom.xml

+13-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
<groupId>bio.overture</groupId>
77
<artifactId>ego</artifactId>
8-
<version>5.0.0</version>
8+
<version>5.1.0</version>
99

1010
<name>ego</name>
1111
<description>OAuth 2.0 Authorization service that supports multiple OpenID Connect Providers</description>
@@ -48,6 +48,18 @@
4848
<groupId>org.springframework.boot</groupId>
4949
<artifactId>spring-boot-starter-security</artifactId>
5050
</dependency>
51+
52+
<!-- spring security -->
53+
<dependency>
54+
<groupId>org.springframework.boot</groupId>
55+
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
56+
</dependency>
57+
58+
<dependency>
59+
<groupId>org.springframework.boot</groupId>
60+
<artifactId>spring-boot-starter-oauth2-client</artifactId>
61+
</dependency>
62+
5163
<dependency>
5264
<groupId>org.springframework.boot</groupId>
5365
<artifactId>spring-boot-starter-jdbc</artifactId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package bio.overture.ego.config;
2+
3+
import java.util.*;
4+
import java.util.stream.Collectors;
5+
import java.util.stream.Stream;
6+
import org.springframework.core.convert.converter.Converter;
7+
import org.springframework.security.oauth2.core.OAuth2AccessToken;
8+
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
9+
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
10+
import org.springframework.util.Assert;
11+
import org.springframework.util.StringUtils;
12+
13+
// needed for linked in since it doesn't return tokenType in the access token response violating
14+
// oauth2 spec.
15+
// https://github.com/spring-projects/spring-security/issues/5983
16+
public class OAuth2AccessTokenResponseConverterWithDefaults
17+
implements Converter<Map<String, String>, OAuth2AccessTokenResponse> {
18+
private static final Set<String> TOKEN_RESPONSE_PARAMETER_NAMES =
19+
Stream.of(
20+
OAuth2ParameterNames.ACCESS_TOKEN,
21+
OAuth2ParameterNames.TOKEN_TYPE,
22+
OAuth2ParameterNames.EXPIRES_IN,
23+
OAuth2ParameterNames.REFRESH_TOKEN,
24+
OAuth2ParameterNames.SCOPE)
25+
.collect(Collectors.toSet());
26+
27+
private OAuth2AccessToken.TokenType defaultAccessTokenType = OAuth2AccessToken.TokenType.BEARER;
28+
29+
@Override
30+
public OAuth2AccessTokenResponse convert(Map<String, String> tokenResponseParameters) {
31+
String accessToken = tokenResponseParameters.get(OAuth2ParameterNames.ACCESS_TOKEN);
32+
33+
OAuth2AccessToken.TokenType accessTokenType = this.defaultAccessTokenType;
34+
if (OAuth2AccessToken.TokenType.BEARER
35+
.getValue()
36+
.equalsIgnoreCase(tokenResponseParameters.get(OAuth2ParameterNames.TOKEN_TYPE))) {
37+
accessTokenType = OAuth2AccessToken.TokenType.BEARER;
38+
}
39+
40+
long expiresIn = 0;
41+
if (tokenResponseParameters.containsKey(OAuth2ParameterNames.EXPIRES_IN)) {
42+
try {
43+
expiresIn = Long.parseLong(tokenResponseParameters.get(OAuth2ParameterNames.EXPIRES_IN));
44+
} catch (NumberFormatException ignored) {
45+
}
46+
}
47+
48+
Set<String> scopes = Collections.emptySet();
49+
if (tokenResponseParameters.containsKey(OAuth2ParameterNames.SCOPE)) {
50+
String scope = tokenResponseParameters.get(OAuth2ParameterNames.SCOPE);
51+
scopes =
52+
Arrays.stream(StringUtils.delimitedListToStringArray(scope, " "))
53+
.collect(Collectors.toSet());
54+
}
55+
56+
Map<String, Object> additionalParameters = new LinkedHashMap<>();
57+
tokenResponseParameters.entrySet().stream()
58+
.filter(e -> !TOKEN_RESPONSE_PARAMETER_NAMES.contains(e.getKey()))
59+
.forEach(e -> additionalParameters.put(e.getKey(), e.getValue()));
60+
61+
return OAuth2AccessTokenResponse.withToken(accessToken)
62+
.tokenType(accessTokenType)
63+
.expiresIn(expiresIn)
64+
.scopes(scopes)
65+
.additionalParameters(additionalParameters)
66+
.build();
67+
}
68+
69+
public final void setDefaultAccessTokenType(OAuth2AccessToken.TokenType defaultAccessTokenType) {
70+
Assert.notNull(defaultAccessTokenType, "defaultAccessTokenType cannot be null");
71+
this.defaultAccessTokenType = defaultAccessTokenType;
72+
}
73+
}

src/main/java/bio/overture/ego/config/OAuth2ClientConfig.java

+11-33
Original file line numberDiff line numberDiff line change
@@ -17,47 +17,25 @@
1717

1818
package bio.overture.ego.config;
1919

20-
import bio.overture.ego.security.OAuth2ClientResources;
21-
import org.springframework.boot.context.properties.ConfigurationProperties;
20+
import bio.overture.ego.security.CorsFilter;
21+
import bio.overture.ego.security.OAuth2RequestResolver;
2222
import org.springframework.context.annotation.Bean;
2323
import org.springframework.context.annotation.Configuration;
24+
import org.springframework.context.annotation.Primary;
25+
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
26+
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
2427

2528
@Configuration
2629
public class OAuth2ClientConfig {
27-
28-
@Bean
29-
@ConfigurationProperties("google")
30-
public OAuth2ClientResources google() {
31-
return new OAuth2ClientResources();
32-
}
33-
34-
@Bean
35-
@ConfigurationProperties("facebook")
36-
public OAuth2ClientResources facebook() {
37-
return new OAuth2ClientResources();
38-
}
39-
40-
@Bean
41-
@ConfigurationProperties("github")
42-
public OAuth2ClientResources github() {
43-
return new OAuth2ClientResources();
44-
}
45-
46-
@Bean
47-
@ConfigurationProperties("linkedin")
48-
public OAuth2ClientResources linkedin() {
49-
return new OAuth2ClientResources();
50-
}
51-
5230
@Bean
53-
@ConfigurationProperties("orcid")
54-
public OAuth2ClientResources orcid() {
55-
return new OAuth2ClientResources();
31+
public OAuth2AuthorizationRequestResolver oAuth2AuthorizationRequestResolver(
32+
ClientRegistrationRepository clientRegistrationRepository) {
33+
return new OAuth2RequestResolver(clientRegistrationRepository, "/oauth/login/");
5634
}
5735

5836
@Bean
59-
@ConfigurationProperties("keycloak")
60-
public OAuth2ClientResources keycloak() {
61-
return new OAuth2ClientResources();
37+
@Primary
38+
public CorsFilter corsFilter() {
39+
return new CorsFilter();
6240
}
6341
}

0 commit comments

Comments
 (0)