Skip to content

Commit

Permalink
feat: add permission override for super-user/admin role (#255)
Browse files Browse the repository at this point in the history
feat: add permission override for supser-user/admin role
  • Loading branch information
paullatzelsperger authored Feb 5, 2024
1 parent 51e737a commit 3f7471c
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 111 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;

import java.util.Arrays;

import static io.restassured.http.ContentType.JSON;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;

Expand Down Expand Up @@ -81,27 +83,34 @@ void publishDid() {

var user = "test-user";
var token = createParticipant(user);
RUNTIME_CONFIGURATION.getManagementEndpoint().baseRequest()
.contentType(JSON)
.header(new Header("x-api-key", token))
.body("""
{
"did": "did:web:test-user"

assertThat(Arrays.asList(token, getSuperUserApiKey()))
.allSatisfy(t -> {
reset(subscriber);
RUNTIME_CONFIGURATION.getManagementEndpoint().baseRequest()
.contentType(JSON)
.header(new Header("x-api-key", t))
.body("""
{
"did": "did:web:test-user"
}
""")
.post("/v1/participants/%s/dids/publish".formatted(user))
.then()
.log().ifValidationFails()
.statusCode(204)
.body(Matchers.notNullValue());

// verify that the publish event was fired twice
verify(subscriber).on(argThat(env -> {
if (env.getPayload() instanceof DidDocumentPublished event) {
return event.getDid().equals("did:web:test-user");
}
""")
.post("/v1/participants/%s/dids/publish".formatted(user))
.then()
.log().ifValidationFails()
.statusCode(204)
.body(Matchers.notNullValue());
return false;
}));

});

// verify that the publish event was fired twice
verify(subscriber, times(2)).on(argThat(env -> {
if (env.getPayload() instanceof DidDocumentPublished event) {
return event.getDid().equals("did:web:test-user");
}
return false;
}));
}

@Test
Expand Down Expand Up @@ -150,27 +159,33 @@ void unpublishDid() {

var user = "test-user";
var token = createParticipant(user);
RUNTIME_CONFIGURATION.getManagementEndpoint().baseRequest()
.contentType(JSON)
.header(new Header("x-api-key", token))
.body("""
{
"did": "did:web:test-user"

assertThat(Arrays.asList(token, getSuperUserApiKey()))
.allSatisfy(t -> {
reset(subscriber);
RUNTIME_CONFIGURATION.getManagementEndpoint().baseRequest()
.contentType(JSON)
.header(new Header("x-api-key", t))
.body("""
{
"did": "did:web:test-user"
}
""")
.post("/v1/participants/%s/dids/unpublish".formatted(user))
.then()
.log().ifValidationFails()
.statusCode(204)
.body(Matchers.notNullValue());

// verify that the publish event was fired twice
verify(subscriber).on(argThat(env -> {
if (env.getPayload() instanceof DidDocumentUnpublished event) {
return event.getDid().equals("did:web:test-user");
}
""")
.post("/v1/participants/%s/dids/unpublish".formatted(user))
.then()
.log().ifValidationFails()
.statusCode(204)
.body(Matchers.notNullValue());
return false;
}));
});

// verify that the publish event was fired twice
verify(subscriber).on(argThat(env -> {
if (env.getPayload() instanceof DidDocumentUnpublished event) {
return event.getDid().equals("did:web:test-user");
}
return false;
}));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.eclipse.edc.spi.event.EventSubscriber;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.Map;
import java.util.UUID;

Expand All @@ -37,6 +38,7 @@
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;

@EndToEndTest
Expand Down Expand Up @@ -78,15 +80,15 @@ void findById() {

var key = createKeyPair(user1);

// attempt to publish user1's DID document, which should fail
RUNTIME_CONFIGURATION.getManagementEndpoint().baseRequest()
.contentType(JSON)
.header(new Header("x-api-key", token))
.get("/v1/participants/%s/keypairs/%s".formatted(user1, key))
.then()
.log().ifValidationFails()
.statusCode(200)
.body(notNullValue());
assertThat(Arrays.asList(token, getSuperUserApiKey()))
.allSatisfy(t -> RUNTIME_CONFIGURATION.getManagementEndpoint().baseRequest()
.contentType(JSON)
.header(new Header("x-api-key", t))
.get("/v1/participants/%s/keypairs/%s".formatted(user1, key))
.then()
.log().ifValidationFails()
.statusCode(200)
.body(notNullValue()));
}

@Test
Expand Down Expand Up @@ -128,15 +130,16 @@ void findForParticipant() {

var key = createKeyPair(user1);

// attempt to publish user1's DID document, which should fail
RUNTIME_CONFIGURATION.getManagementEndpoint().baseRequest()
.contentType(JSON)
.header(new Header("x-api-key", token))
.get("/v1/participants/%s/keypairs".formatted(user1))
.then()
.log().ifValidationFails()
.statusCode(200)
.body(notNullValue());
assertThat(Arrays.asList(token, getSuperUserApiKey()))
.allSatisfy(t -> RUNTIME_CONFIGURATION.getManagementEndpoint().baseRequest()
.contentType(JSON)
.header(new Header("x-api-key", t))
.get("/v1/participants/%s/keypairs".formatted(user1))
.then()
.log().ifValidationFails()
.statusCode(200)
.body(notNullValue()));

}

@Test
Expand All @@ -147,22 +150,24 @@ void addKeyPair() {
var user1 = "user1";
var token = createParticipant(user1);


// attempt to publish user1's DID document, which should fail
var keyDesc = createKeyDescriptor(user1).build();
RUNTIME_CONFIGURATION.getManagementEndpoint().baseRequest()
.contentType(JSON)
.header(new Header("x-api-key", token))
.body(keyDesc)
.put("/v1/participants/%s/keypairs?participantId=%s".formatted(user1, user1))
.then()
.log().ifValidationFails()
.statusCode(204)
.body(notNullValue());
verify(subscriber).on(argThat(env -> {
var evt = (KeyPairAdded) env.getPayload();
return evt.getParticipantId().equals(user1) && evt.getKeyId().equals(keyDesc.getKeyId());
}));
assertThat(Arrays.asList(token, getSuperUserApiKey()))
.allSatisfy(t -> {
var keyDesc = createKeyDescriptor(user1).build();
RUNTIME_CONFIGURATION.getManagementEndpoint().baseRequest()
.contentType(JSON)
.header(new Header("x-api-key", t))
.body(keyDesc)
.put("/v1/participants/%s/keypairs?participantId=%s".formatted(user1, user1))
.then()
.log().ifValidationFails()
.statusCode(204)
.body(notNullValue());

verify(subscriber).on(argThat(env -> {
var evt = (KeyPairAdded) env.getPayload();
return evt.getParticipantId().equals(user1) && evt.getKeyId().equals(keyDesc.getKeyId());
}));
});
}

@Test
Expand Down Expand Up @@ -208,32 +213,36 @@ void rotate() {

var keyId = createKeyPair(user1);

// attempt to publish user1's DID document, which should fail
var keyDesc = createKeyDescriptor(user1).build();
RUNTIME_CONFIGURATION.getManagementEndpoint().baseRequest()
.contentType(JSON)
.header(new Header("x-api-key", token))
.body(keyDesc)
.post("/v1/participants/%s/keypairs/%s/rotate".formatted(user1, keyId))
.then()
.log().ifValidationFails()
.statusCode(204)
.body(notNullValue());

// verify that the "rotated" event fired once
verify(subscriber).on(argThat(env -> {
if (env.getPayload() instanceof KeyPairRotated evt) {
return evt.getParticipantId().equals(user1);
}
return false;
}));
// verify that the correct "added" event fired
verify(subscriber).on(argThat(env -> {
if (env.getPayload() instanceof KeyPairAdded evt) {
return evt.getKeyId().equals(keyDesc.getKeyId());
}
return false;
}));
assertThat(Arrays.asList(token, getSuperUserApiKey()))
.allSatisfy(t -> {
reset(subscriber);
// attempt to publish user1's DID document, which should fail
var keyDesc = createKeyDescriptor(user1).build();
RUNTIME_CONFIGURATION.getManagementEndpoint().baseRequest()
.contentType(JSON)
.header(new Header("x-api-key", t))
.body(keyDesc)
.post("/v1/participants/%s/keypairs/%s/rotate".formatted(user1, keyId))
.then()
.log().ifValidationFails()
.statusCode(204)
.body(notNullValue());

// verify that the "rotated" event fired once
verify(subscriber).on(argThat(env -> {
if (env.getPayload() instanceof KeyPairRotated evt) {
return evt.getParticipantId().equals(user1);
}
return false;
}));
// verify that the correct "added" event fired
verify(subscriber).on(argThat(env -> {
if (env.getPayload() instanceof KeyPairAdded evt) {
return evt.getKeyId().equals(keyDesc.getKeyId());
}
return false;
}));
});
}

@Test
Expand Down Expand Up @@ -277,18 +286,20 @@ void revoke() {

var keyId = createKeyPair(user1);

// attempt to publish user1's DID document, which should fail
RUNTIME_CONFIGURATION.getManagementEndpoint().baseRequest()
.contentType(JSON)
.header(new Header("x-api-key", token))
.post("/v1/participants/%s/keypairs/%s/revoke".formatted(user1, keyId))
.then()
.log().ifValidationFails()
.statusCode(204)
.body(notNullValue());

assertThat(getDidForParticipant(user1)).hasSize(1)
.allSatisfy(dd -> assertThat(dd.getVerificationMethod()).noneMatch(vm -> vm.getId().equals(keyId)));
assertThat(Arrays.asList(token, getSuperUserApiKey()))
.allSatisfy(t -> {
RUNTIME_CONFIGURATION.getManagementEndpoint().baseRequest()
.contentType(JSON)
.header(new Header("x-api-key", t))
.post("/v1/participants/%s/keypairs/%s/revoke".formatted(user1, keyId))
.then()
.log().ifValidationFails()
.statusCode(204)
.body(notNullValue());

assertThat(getDidForParticipant(user1)).hasSize(1)
.allSatisfy(dd -> assertThat(dd.getVerificationMethod()).noneMatch(vm -> vm.getId().equals(keyId)));
});
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import org.eclipse.edc.spi.event.EventSubscriber;
import org.junit.jupiter.api.Test;

import java.util.Arrays;

import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.equalTo;
Expand Down Expand Up @@ -205,4 +207,21 @@ void deleteParticipant() {
assertThat(getDidForParticipant(participantId)).isEmpty();
}

@Test
void regenerateToken() {

var participantId = "another-user";
var userToken = createParticipant(participantId);

assertThat(Arrays.asList(userToken, getSuperUserApiKey()))
.allSatisfy(t -> RUNTIME_CONFIGURATION.getManagementEndpoint().baseRequest()
.header(new Header("x-api-key", t))
.contentType(ContentType.JSON)
.post("/v1/participants/%s/token".formatted(participantId))
.then()
.log().ifError()
.statusCode(200)
.body(notNullValue()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import jakarta.ws.rs.core.SecurityContext;
import org.eclipse.edc.identityhub.spi.AuthorizationService;
import org.eclipse.edc.identityhub.spi.authentication.ServicePrincipal;
import org.eclipse.edc.identityhub.spi.model.ParticipantResource;
import org.eclipse.edc.spi.result.ServiceResult;

Expand All @@ -31,6 +32,11 @@ public class AuthorizationServiceImpl implements AuthorizationService {

@Override
public ServiceResult<Void> isAuthorized(SecurityContext securityContext, String resourceId, Class<? extends ParticipantResource> resourceClass) {

if (securityContext.isUserInRole(ServicePrincipal.ROLE_ADMIN)) {
return ServiceResult.success();
}

var function = resourceLookupFunctions.get(resourceClass);
var name = securityContext.getUserPrincipal().getName();
if (function == null) {
Expand Down
Loading

0 comments on commit 3f7471c

Please sign in to comment.