Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(DHIS2-18994): support OAuth2 client credentials flow for Route API #19953

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
8 changes: 8 additions & 0 deletions dhis-2/dhis-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,14 @@
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,8 @@ enum Status {
BAD_REQUEST(400),
FORBIDDEN(403),
NOT_FOUND(404),
CONFLICT(409);
CONFLICT(409),
BAD_GATEWAY(502);

private final int code;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,32 @@
import java.util.Map;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.springframework.context.ApplicationContext;

@Getter
@Setter
@Accessors(chain = true)
@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class ApiHeadersAuthScheme implements AuthScheme {
public static final String API_HEADERS_TYPE = "api-headers";

@JsonProperty(required = true, access = JsonProperty.Access.WRITE_ONLY)
private Map<String, String> headers = new HashMap<>();

@Override
public void apply(Map<String, List<String>> headers, Map<String, List<String>> queryParams) {
public void apply(
ApplicationContext applicationContext,
Map<String, List<String>> headers,
Map<String, List<String>> queryParams) {
for (Map.Entry<String, String> header : this.headers.entrySet()) {
headers.computeIfAbsent(header.getKey(), v -> new LinkedList<>()).add(header.getValue());
}
Expand All @@ -61,7 +72,7 @@ public ApiHeadersAuthScheme encrypt(UnaryOperator<String> encryptFunc) {
headers.entrySet().stream()
.map(e -> new AbstractMap.SimpleEntry<>(e.getKey(), encryptFunc.apply(e.getValue())))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
return copy(encryptedHeaders);
return this.toBuilder().headers(encryptedHeaders).build();
}

@Override
Expand All @@ -70,18 +81,12 @@ public ApiHeadersAuthScheme decrypt(UnaryOperator<String> decryptFunc) {
headers.entrySet().stream()
.map(e -> new AbstractMap.SimpleEntry<>(e.getKey(), decryptFunc.apply(e.getValue())))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
return copy(decryptedHeaders);

return this.toBuilder().headers(decryptedHeaders).build();
}

@Override
public String getType() {
return API_HEADERS_TYPE;
}

protected ApiHeadersAuthScheme copy(Map<String, String> headers) {
ApiHeadersAuthScheme apiHeadersAuth = new ApiHeadersAuthScheme();
apiHeadersAuth.setHeaders(headers);

return apiHeadersAuth;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,32 @@
import java.util.Map;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.springframework.context.ApplicationContext;

@Getter
@Setter
@Accessors(chain = true)
@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class ApiQueryParamsAuthScheme implements AuthScheme {
public static final String API_QUERY_PARAMS_TYPE = "api-query-params";

@JsonProperty(required = true, access = JsonProperty.Access.WRITE_ONLY)
private Map<String, String> queryParams = new HashMap<>();

@Override
public void apply(Map<String, List<String>> headers, Map<String, List<String>> queryParams) {
public void apply(
ApplicationContext applicationContext,
Map<String, List<String>> headers,
Map<String, List<String>> queryParams) {
for (Map.Entry<String, String> queryParam : this.queryParams.entrySet()) {
queryParams
.computeIfAbsent(queryParam.getKey(), v -> new LinkedList<>())
Expand All @@ -63,7 +74,8 @@ public ApiQueryParamsAuthScheme encrypt(UnaryOperator<String> encryptFunc) {
queryParams.entrySet().stream()
.map(e -> new AbstractMap.SimpleEntry<>(e.getKey(), encryptFunc.apply(e.getValue())))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
return copy(encryptedQueryParams);

return this.toBuilder().queryParams(encryptedQueryParams).build();
}

@Override
Expand All @@ -72,18 +84,12 @@ public ApiQueryParamsAuthScheme decrypt(UnaryOperator<String> decryptFunc) {
queryParams.entrySet().stream()
.map(e -> new AbstractMap.SimpleEntry<>(e.getKey(), decryptFunc.apply(e.getValue())))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
return copy(decryptedQueryParams);

return this.toBuilder().queryParams(decryptedQueryParams).build();
}

@Override
public String getType() {
return API_QUERY_PARAMS_TYPE;
}

protected ApiQueryParamsAuthScheme copy(Map<String, String> queryParams) {
ApiQueryParamsAuthScheme apiQueryParamsAuth = new ApiQueryParamsAuthScheme();
apiQueryParamsAuth.setQueryParams(queryParams);

return apiQueryParamsAuth;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,14 @@
import java.util.List;
import java.util.Map;
import java.util.function.UnaryOperator;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.springframework.context.ApplicationContext;
import org.springframework.util.StringUtils;

/**
Expand All @@ -46,14 +51,20 @@
@Getter
@Setter
@Accessors(chain = true)
@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class ApiTokenAuthScheme implements AuthScheme {
public static final String API_TOKEN_TYPE = "api-token";

@JsonProperty(required = true, access = JsonProperty.Access.WRITE_ONLY)
private String token;

@Override
public void apply(Map<String, List<String>> headers, Map<String, List<String>> queryParams) {
public void apply(
ApplicationContext applicationContext,
Map<String, List<String>> headers,
Map<String, List<String>> queryParams) {
if (!StringUtils.hasText(token)) {
return;
}
Expand All @@ -63,23 +74,16 @@ public void apply(Map<String, List<String>> headers, Map<String, List<String>> q

@Override
public ApiTokenAuthScheme encrypt(UnaryOperator<String> encryptFunc) {
return copy(encryptFunc.apply(token));
return this.toBuilder().token(encryptFunc.apply(token)).build();
}

@Override
public AuthScheme decrypt(UnaryOperator<String> decryptFunc) {
return copy(decryptFunc.apply(token));
return this.toBuilder().token(decryptFunc.apply(token)).build();
}

@Override
public String getType() {
return API_TOKEN_TYPE;
}

protected ApiTokenAuthScheme copy(String token) {
ApiTokenAuthScheme newApiTokenAuth = new ApiTokenAuthScheme();
newApiTokenAuth.setToken(token);

return newApiTokenAuth;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import java.util.Map;
import java.util.function.UnaryOperator;
import lombok.experimental.Accessors;
import org.springframework.context.ApplicationContext;

/**
* @author Morten Olav Hansen
Expand All @@ -52,10 +53,17 @@
name = ApiHeadersAuthScheme.API_HEADERS_TYPE),
@JsonSubTypes.Type(
value = ApiQueryParamsAuthScheme.class,
name = ApiQueryParamsAuthScheme.API_QUERY_PARAMS_TYPE)
name = ApiQueryParamsAuthScheme.API_QUERY_PARAMS_TYPE),
@JsonSubTypes.Type(
value = OAuth2ClientCredentialsAuthScheme.class,
name = OAuth2ClientCredentialsAuthScheme.OAUTH2_CLIENT_CREDENTIALS_TYPE)
})
public interface AuthScheme extends Serializable {
void apply(Map<String, List<String>> headers, Map<String, List<String>> queryParams);
void apply(
ApplicationContext applicationContext,
Map<String, List<String>> headers,
Map<String, List<String>> queryParams)
throws Exception;

AuthScheme encrypt(UnaryOperator<String> encryptFunc);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,14 @@
import java.util.List;
import java.util.Map;
import java.util.function.UnaryOperator;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.springframework.context.ApplicationContext;
import org.springframework.util.StringUtils;

/**
Expand All @@ -44,6 +49,9 @@
@Getter
@Setter
@Accessors(chain = true)
@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class HttpBasicAuthScheme implements AuthScheme {
public static final String HTTP_BASIC_TYPE = "http-basic";

Expand All @@ -54,7 +62,10 @@ public class HttpBasicAuthScheme implements AuthScheme {
private String password;

@Override
public void apply(Map<String, List<String>> headers, Map<String, List<String>> queryParams) {
public void apply(
ApplicationContext applicationContext,
Map<String, List<String>> headers,
Map<String, List<String>> queryParams) {
if (!(StringUtils.hasText(username) && StringUtils.hasText(password))) {
return;
}
Expand All @@ -66,27 +77,19 @@ public void apply(Map<String, List<String>> headers, Map<String, List<String>> q

@Override
public HttpBasicAuthScheme encrypt(UnaryOperator<String> encryptFunc) {
return copy(encryptFunc.apply(password));
return this.toBuilder().password(encryptFunc.apply(password)).build();
}

@Override
public HttpBasicAuthScheme decrypt(UnaryOperator<String> decryptFunc) {
return copy(decryptFunc.apply(password));
return this.toBuilder().password(decryptFunc.apply(password)).build();
}

@Override
public String getType() {
return HTTP_BASIC_TYPE;
}

protected HttpBasicAuthScheme copy(String password) {
HttpBasicAuthScheme newHttpBasicAuth = new HttpBasicAuthScheme();
newHttpBasicAuth.setUsername(username);
newHttpBasicAuth.setPassword(password);

return newHttpBasicAuth;
}

private String getBasicAuth(String username, String password) {
String string = String.format("%s:%s", username, password);
return "Basic " + Base64.getEncoder().encodeToString(string.getBytes());
Expand Down
Loading
Loading