Skip to content

Commit

Permalink
feat(amphora-service): protect public endpoints
Browse files Browse the repository at this point in the history
Signed-off-by: Sebastian Becker <[email protected]>
  • Loading branch information
sbckr committed Nov 14, 2024
1 parent 5b4321d commit e683198
Show file tree
Hide file tree
Showing 27 changed files with 864 additions and 397 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 - for information on the respective copyright owner
* Copyright (c) 2021-2024 - for information on the respective copyright owner
* see the NOTICE file and/or the repository https://github.com/carbynestack/amphora.
*
* SPDX-License-Identifier: Apache-2.0
Expand All @@ -13,6 +13,7 @@
import io.carbynestack.amphora.common.MaskedInput;
import io.carbynestack.amphora.common.MaskedInputData;
import io.carbynestack.amphora.common.SecretShare;
import io.carbynestack.amphora.common.Tag;
import io.carbynestack.castor.common.entities.Field;
import io.carbynestack.castor.common.entities.InputMask;
import io.carbynestack.castor.common.entities.Share;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (c) 2024 - for information on the respective copyright owner
* see the NOTICE file and/or the repository https://github.com/carbynestack/amphora.
*
* SPDX-License-Identifier: Apache-2.0
*/

package io.carbynestack.amphora.service.config;


import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@ConfigurationProperties(prefix = "carbynestack.auth")
@Component
@Data
@Accessors(chain = true)
public class AuthProperties {
private String userIdFieldName;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,21 @@

package io.carbynestack.amphora.service.config;

import io.carbynestack.amphora.service.opa.JwtReader;
import io.carbynestack.amphora.service.opa.OpaClient;
import io.carbynestack.castor.client.download.CastorIntraVcpClient;
import io.carbynestack.castor.client.download.DefaultCastorIntraVcpClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

import java.io.File;
import java.net.URI;

@Configuration
public class OpaConfig {

@Bean
JwtReader jwtReader(AuthProperties authProperties) {
return new JwtReader(authProperties.getUserIdFieldName());
}

@Bean
OpaClient opaClient(OpaProperties opaProperties) {
return OpaClient.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@ConfigurationProperties(prefix = "carbynestack.amphora.opa")
@ConfigurationProperties(prefix = "carbynestack.opa")
@Component
@Data
@Accessors(chain = true)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright (c) 2024 - for information on the respective copyright owner
* see the NOTICE file and/or the repository https://github.com/carbynestack/amphora.
*
* SPDX-License-Identifier: Apache-2.0
*/

package io.carbynestack.amphora.service.exceptions;

public class UnauthorizedException extends Exception {
public UnauthorizedException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2024 - for information on the respective copyright owner
* see the NOTICE file and/or the repository https://github.com/carbynestack/amphora.
*
* SPDX-License-Identifier: Apache-2.0
*/

package io.carbynestack.amphora.service.opa;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.carbynestack.amphora.service.exceptions.UnauthorizedException;
import io.vavr.control.Option;

import java.util.Base64;

public class JwtReader {
static ObjectMapper mapper = new ObjectMapper();
private final String userIdField;

public JwtReader(String userIdField) {
this.userIdField = userIdField;
}

public String extractUserIdFromAuthHeader(String header) throws UnauthorizedException {
return extractFieldFromAuthHeader(header, userIdField);
}

private static String extractFieldFromAuthHeader(String header, String field) throws UnauthorizedException {
return tokenFromHeader(header)
.flatMap(JwtReader::dataNodeFromToken)
.flatMap(node -> fieldFromNode(node, field))
.getOrElseThrow(() -> new UnauthorizedException("No token provided"));
}

private static Option<JsonNode> dataNodeFromToken(String token) {
String[] parts = token.split("\\.");
if (parts.length != 3) {
return Option.none();

Check warning on line 40 in amphora-service/src/main/java/io/carbynestack/amphora/service/opa/JwtReader.java

View check run for this annotation

Codecov / codecov/patch

amphora-service/src/main/java/io/carbynestack/amphora/service/opa/JwtReader.java#L40

Added line #L40 was not covered by tests
}
try {
String jwt = new String(Base64.getDecoder().decode(parts[1]));
return Option.of(mapper.reader().readTree(jwt));
} catch (JsonProcessingException e) {
return Option.none();

Check warning on line 46 in amphora-service/src/main/java/io/carbynestack/amphora/service/opa/JwtReader.java

View check run for this annotation

Codecov / codecov/patch

amphora-service/src/main/java/io/carbynestack/amphora/service/opa/JwtReader.java#L45-L46

Added lines #L45 - L46 were not covered by tests
}
}

private static Option<String> tokenFromHeader(String header) {
if (header != null && header.startsWith("Bearer ")) {
return Option.of(header.substring(7));
}
return Option.none();

Check warning on line 54 in amphora-service/src/main/java/io/carbynestack/amphora/service/opa/JwtReader.java

View check run for this annotation

Codecov / codecov/patch

amphora-service/src/main/java/io/carbynestack/amphora/service/opa/JwtReader.java#L54

Added line #L54 was not covered by tests
}

private static Option<String> fieldFromNode(JsonNode node, String field) {
try {
for(String f : field.split("\\.")) {
node = node.get(f);

Check warning on line 60 in amphora-service/src/main/java/io/carbynestack/amphora/service/opa/JwtReader.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

amphora-service/src/main/java/io/carbynestack/amphora/service/opa/JwtReader.java#L60

Avoid reassigning parameters such as 'node'
}
return Option.of(node.asText());
} catch (NullPointerException e) {
return Option.none();
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public OpaClient(URI opaServiceUri, String defaultPolicyPackage) {
* @param tags The tags describing the accessed object.
* @return True if the subject can perform the action, false otherwise (or if an error occurred).
*/
boolean isAllowed(String policyPackage, String action, String subject, List<Tag> tags) {
public boolean isAllowed(String policyPackage, String action, String subject, List<Tag> tags) {
OpaRequestBody body = OpaRequestBody.builder()
.subject(subject)
.tags(tags)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
@Service
public class OpaService {
public static final String POLICY_PACKAGE_TAG_KEY = "accessPolicy";
public static final String OWNER_TAG_KEY = "owner";

static final String READ_SECRET_ACTION_NAME = "read";
static final String DELETE_SECRET_ACTION_NAME = "delete";
static final String CREATE_TAG_ACTION_NAME = "tag/create";
Expand Down
Loading

0 comments on commit e683198

Please sign in to comment.