Skip to content

Commit

Permalink
Add authorization test and minor refactoring.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmelinav committed Jul 28, 2020
1 parent 2461ac1 commit 59bf780
Show file tree
Hide file tree
Showing 11 changed files with 358 additions and 21 deletions.
6 changes: 6 additions & 0 deletions serving/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,12 @@
<version>3.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>sh.ory.keto</groupId>
<artifactId>keto-client</artifactId>
<version>0.4.4-alpha.1</version>
<scope>test</scope>
</dependency>
</dependencies>

<profiles>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
import io.opentracing.Span;
import io.opentracing.Tracer;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import net.devh.boot.grpc.server.service.GrpcService;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -83,11 +85,15 @@ public void getOnlineFeatures(
Span span = tracer.buildSpan("getOnlineFeatures").start();
try (Scope scope = tracer.scopeManager().activate(span, false)) {
// authorize for the project in request object.
this.authorizationService.authorizeRequest(
SecurityContextHolder.getContext(), request.getProject());
// authorize for projects set in feature list, backward compatibility for
// <=v0.5.X
this.checkProjectAccess(request.getFeaturesList());
if (request.getProject() != null && !request.getProject().isEmpty()) {
// project set at root level overrides the project set at feature set level
this.authorizationService.authorizeRequest(
SecurityContextHolder.getContext(), request.getProject());
} else {
// authorize for projects set in feature list, backward compatibility for
// <=v0.5.X
this.checkProjectAccess(request.getFeaturesList());
}
RequestHelper.validateOnlineRequest(request);
GetOnlineFeaturesResponse onlineFeatures = servingService.getOnlineFeatures(request);
responseObserver.onNext(onlineFeatures);
Expand Down Expand Up @@ -149,11 +155,17 @@ public void getJob(GetJobRequest request, StreamObserver<GetJobResponse> respons
}

private void checkProjectAccess(List<FeatureReference> featureList) {
featureList.stream()
.forEach(
featureRef -> {
this.authorizationService.authorizeRequest(
SecurityContextHolder.getContext(), featureRef.getProject());
});
Set<String> projectList =
featureList.stream().map(FeatureReference::getProject).collect(Collectors.toSet());
if (projectList.isEmpty()) {
authorizationService.authorizeRequest(SecurityContextHolder.getContext(), "default");
} else {
projectList.stream()
.forEach(
project -> {
this.authorizationService.authorizeRequest(
SecurityContextHolder.getContext(), project);
});
}
}
}
2 changes: 1 addition & 1 deletion serving/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ feast:
jwkEndpointURI: "https://www.googleapis.com/oauth2/v3/certs"
authorization:
enabled: false
provider: none
provider: http
options:
basePath: http://localhost:3000

Expand Down
72 changes: 72 additions & 0 deletions serving/src/test/java/feast/serving/it/AuthTestUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
import io.grpc.ManagedChannelBuilder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
Expand All @@ -51,9 +53,17 @@
import okhttp3.Response;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.runners.model.InitializationError;
import sh.ory.keto.ApiClient;
import sh.ory.keto.ApiException;
import sh.ory.keto.Configuration;
import sh.ory.keto.api.EnginesApi;
import sh.ory.keto.model.OryAccessControlPolicy;
import sh.ory.keto.model.OryAccessControlPolicyRole;

public class AuthTestUtils {

private static final String DEFAULT_FLAVOR = "glob";

static SourceProto.Source defaultSource =
createSource("kafka:9092,localhost:9094", "feast-features");

Expand Down Expand Up @@ -208,4 +218,66 @@ public static void seedHydra(
throw new InitializationError(response.message());
}
}

public static void seedKeto(String url, String project, String subjectInProject, String admin)
throws ApiException {
ApiClient ketoClient = Configuration.getDefaultApiClient();
ketoClient.setBasePath(url);
EnginesApi enginesApi = new EnginesApi(ketoClient);

// Add policies
OryAccessControlPolicy adminPolicy = getAdminPolicy();
enginesApi.upsertOryAccessControlPolicy(DEFAULT_FLAVOR, adminPolicy);

OryAccessControlPolicy projectPolicy = getMyProjectMemberPolicy(project);
enginesApi.upsertOryAccessControlPolicy(DEFAULT_FLAVOR, projectPolicy);

// Add policy roles
OryAccessControlPolicyRole adminPolicyRole = getAdminPolicyRole(admin);
enginesApi.upsertOryAccessControlPolicyRole(DEFAULT_FLAVOR, adminPolicyRole);

OryAccessControlPolicyRole myProjectMemberPolicyRole =
getMyProjectMemberPolicyRole(project, subjectInProject);
enginesApi.upsertOryAccessControlPolicyRole(DEFAULT_FLAVOR, myProjectMemberPolicyRole);
}

private static OryAccessControlPolicyRole getMyProjectMemberPolicyRole(
String project, String subjectInProject) {
OryAccessControlPolicyRole role = new OryAccessControlPolicyRole();
role.setId(String.format("roles:%s-project-members", project));
role.setMembers(Collections.singletonList("users:" + subjectInProject));
return role;
}

private static OryAccessControlPolicyRole getAdminPolicyRole(String subjectIsAdmin) {
OryAccessControlPolicyRole role = new OryAccessControlPolicyRole();
role.setId("roles:admin");
role.setMembers(Collections.singletonList("users:" + subjectIsAdmin));
return role;
}

private static OryAccessControlPolicy getAdminPolicy() {
OryAccessControlPolicy policy = new OryAccessControlPolicy();
policy.setId("policies:admin");
policy.subjects(Collections.singletonList("roles:admin"));
policy.resources(Collections.singletonList("resources:**"));
policy.actions(Collections.singletonList("actions:**"));
policy.effect("allow");
policy.conditions(null);
return policy;
}

private static OryAccessControlPolicy getMyProjectMemberPolicy(String project) {
OryAccessControlPolicy policy = new OryAccessControlPolicy();
policy.setId(String.format("policies:%s-project-members-policy", project));
policy.subjects(Collections.singletonList(String.format("roles:%s-project-members", project)));
policy.resources(
Arrays.asList(
String.format("resources:projects:%s", project),
String.format("resources:projects:%s:**", project)));
policy.actions(Collections.singletonList("actions:**"));
policy.effect("allow");
policy.conditions(null);
return policy;
}
}
6 changes: 1 addition & 5 deletions serving/src/test/java/feast/serving/it/BaseAuthIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.DynamicPropertyRegistry;
Expand All @@ -45,7 +43,6 @@ public class BaseAuthIT {
static final String CORE = "core_1";

static final String HYDRA = "hydra_1";
static final Map<String, String> options = new HashMap<>();
static final int HYDRA_PORT = 4445;

static CoreSimpleAPIClient insecureApiClient;
Expand All @@ -56,7 +53,7 @@ public class BaseAuthIT {
static final int FEAST_SERVING_PORT = 6566;

@DynamicPropertySource
static void initialize(DynamicPropertyRegistry registry) throws UnknownHostException {
static void properties(DynamicPropertyRegistry registry) {
registry.add("feast.stores[0].name", () -> "online");
registry.add("feast.stores[0].type", () -> "REDIS");
// Redis needs to accessible by both core and serving, hence using host address
Expand All @@ -66,7 +63,6 @@ static void initialize(DynamicPropertyRegistry registry) throws UnknownHostExcep
try {
return InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.io.File;
import java.io.IOException;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import org.junit.ClassRule;
import org.junit.jupiter.api.BeforeAll;
Expand All @@ -52,6 +53,8 @@
@Testcontainers
public class ServingServiceOauthAuthenticationIT extends BaseAuthIT {

static final Map<String, String> options = new HashMap<>();

@ClassRule @Container
public static DockerComposeContainer environment =
new DockerComposeContainer(
Expand Down
Loading

0 comments on commit 59bf780

Please sign in to comment.