Skip to content

Commit

Permalink
[#36] Adding HTTP Headers to OPA Auth request (#45)
Browse files Browse the repository at this point in the history
* [#36] Adding Headers to OPA request
  • Loading branch information
massenz authored Nov 19, 2022
1 parent 12db937 commit 208703f
Show file tree
Hide file tree
Showing 17 changed files with 235 additions and 87 deletions.
40 changes: 40 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright AlertAvert.com (c) 2022. All rights reserved.

version: '3.2'

services:
opa:
container_name: opa
hostname: opa
image: openpolicyagent/opa:0.42.2
command: run --server --addr :8181
ports:
- "8181:8181"
networks:
- backend

mongo:
container_name: "mongo"
image: "mongo:4"
hostname: mongo
ports:
- "27017:27017"
networks:
- backend
volumes:
- mongo_data:/data

### INFRASTRUCTURE

volumes:
mongo_data:

# To connect to the servers in this stack, from a container run
# via Docker, use `--network docker_backend`.
# The hosts listed above will then be reachable at the given names,
# on whatever ports are exposed.
networks:
backend:
ipam:
config:
- subnet: 172.1.2.0/24
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ public class JwtSecurityConfiguration {
JwtAuthenticationWebFilter jwtAuthenticationWebFilter;



@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

package com.alertavert.opa.configuration;

import com.alertavert.opa.security.crypto.KeypairFileReader;
import com.alertavert.opa.security.crypto.KeypairReader;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
Expand All @@ -29,14 +28,12 @@
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;

import static com.alertavert.opa.Constants.ELLIPTIC_CURVE;
import static com.alertavert.opa.Constants.PASSPHRASE;
import static com.alertavert.opa.Constants.UNDEFINED_KEYPAIR;

@Slf4j
@Configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;

import java.util.List;

/**
* <h2>OpaServerConfiguration</h2>
*
Expand All @@ -41,8 +43,7 @@ public class OpaServerConfiguration {

public OpaServerConfiguration(
OpaServerProperties opaServerProperties,
RoutesConfiguration configuration
) {
RoutesConfiguration configuration) {
this.opaServerProperties = opaServerProperties;
this.configuration = configuration;
}
Expand Down Expand Up @@ -73,9 +74,14 @@ public WebClient client() {
.build();
}

@Bean
public List<String> requiredHeaders() {
return opaServerProperties.getHeaders();
}

@Bean
public OpaReactiveAuthorizationManager authorizationManager(WebClient client) {
return new OpaReactiveAuthorizationManager(client, configuration);
public OpaReactiveAuthorizationManager authorizationManager() {
return new OpaReactiveAuthorizationManager(client(), configuration,
opaServerProperties.getHeaders());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@

import com.alertavert.opa.Constants;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.http.HttpHeaders;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;


/**
Expand All @@ -45,15 +52,33 @@
* @see OpaServerConfiguration
* @author M. Massenzio, 2020-11-22
*/
@Data
@Data @Slf4j
@ConfigurationProperties(prefix = "opa")
public class OpaServerProperties {

public static final Collection<String> DEFAULT_HEADERS = List.of(
HttpHeaders.HOST,
HttpHeaders.USER_AGENT);

Boolean secure = false;
String server;
String policy;
String rule;

/**
* The list of headers to be sent to OPA to evaluate for authorization.
*
* <p> {@link #DEFAULT_HEADERS default headers} are always sent</p>
*/
List<String> headers = new ArrayList<>();


@PostConstruct
public void log() {
headers.addAll(DEFAULT_HEADERS);
log.info("Headers configured: headers = {}", headers);
}

protected String versionedApi(String api) {
return String.format("/%s/%s", Constants.OPA_VERSION, api);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.alertavert.opa.jwt.JwtTokenProvider;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
Expand All @@ -38,7 +39,10 @@
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import javax.sound.midi.Soundbank;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand All @@ -61,16 +65,20 @@
*
* @author M. Massenzio, 2020-11-22
*/
@Component
@Slf4j
@RequiredArgsConstructor
@Slf4j @RequiredArgsConstructor
public class OpaReactiveAuthorizationManager
implements ReactiveAuthorizationManager<AuthorizationContext> {

private final WebClient client;
private final RoutesConfiguration configuration;
private final List<String> requiredHeaders;
private final AntPathMatcher pathMatcher = new AntPathMatcher();

@PostConstruct
private void info() {
log.info("Configured Headers, headers = {}", requiredHeaders);
}

/**
* Determines if access is granted for a specific request, given a user's credentials (API
* token).
Expand Down Expand Up @@ -134,12 +142,26 @@ private TokenBasedAuthorizationRequest makeRequestBody(
Object credentials,
ServerHttpRequest request
) {
Map<String, String> authnHeaders = new HashMap<>();
HttpHeaders requestHeaders = request.getHeaders();
log.debug("Adding headers, request = {}, required = {}", requestHeaders,
requiredHeaders);
if (requestHeaders != null) {
requiredHeaders.forEach(key -> {
var value = requestHeaders.getFirst(key);
if (value != null) {
authnHeaders.put(key, value);
}
});
}

String token = Objects.requireNonNull(credentials).toString();
return TokenBasedAuthorizationRequest.builder()
.input(new TokenBasedAuthorizationRequest.AuthRequestBody(token,
new TokenBasedAuthorizationRequest.Resource(
request.getMethodValue(),
request.getPath().toString()
request.getPath().toString(),
authnHeaders
)
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import lombok.Value;
import lombok.extern.jackson.Jacksonized;

import java.util.Map;

import static com.alertavert.opa.Constants.MAPPER;
import static com.alertavert.opa.Constants.MAX_TOKEN_LEN_LOG;

Expand Down Expand Up @@ -62,7 +64,7 @@
@Jacksonized
public class TokenBasedAuthorizationRequest {

public record Resource(String method, String path) {
public record Resource(String method, String path, Map<String, ?> headers) {
}

public record AuthRequestBody(@JsonProperty("api_token") String token, Resource resource) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

package com.alertavert.opa.security.aws;

import com.alertavert.opa.ExcludeFromCoverageGenerated;
import com.alertavert.opa.security.crypto.KeyLoadException;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import com.alertavert.opa.AbstractTestBase;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;

import static org.assertj.core.api.Assertions.assertThat;

Expand All @@ -29,27 +31,40 @@ class OpaServerPropertiesTest extends AbstractTestBase {
@Autowired
OpaServerProperties opaServerProperties;

@Value("${opa.policy}")
String policy;
@Value("${opa.rule}")
String rule;

@Test
public void endpoint() {
assertThat(opaServerProperties.endpoint("foo"))
.isEqualTo("http://localhost:8181/v1/foo/com.alertavert.policies");
String api = "foo";
assertThat(opaServerProperties.endpoint(api))
.isEqualTo(String.format("http://localhost:8181/v1/%s/%s", api, policy));
}

@Test
public void policy() {
assertThat(opaServerProperties.policyEndpoint())
.isEqualTo("http://localhost:8181/v1/policies/com.alertavert.policies");
.isEqualTo(String.format("http://localhost:8181/v1/policies/%s", policy));
}

@Test
public void data() {
assertThat(opaServerProperties.dataEndpoint())
.isEqualTo("http://localhost:8181/v1/data/com.alertavert.policies");
.isEqualTo(String.format("http://localhost:8181/v1/data/%s", policy));
}

@Test
public void authorizationEndpoint() {
assertThat(opaServerProperties.authorization())
.isEqualTo("http://localhost:8181/v1/data/com.alertavert.policies/allow");
.isEqualTo(String.format("http://localhost:8181/v1/data/%s/%s",policy, rule));
}

@Test
public void testHeaders() {
assertThat(opaServerProperties.getHeaders()).containsExactlyInAnyOrder(
HttpHeaders.HOST, HttpHeaders.USER_AGENT, "x-test-header"
);
}
}
Loading

0 comments on commit 208703f

Please sign in to comment.