Skip to content

Commit 38bd347

Browse files
committed
forbidden exception handling + owner of share authorized
1 parent 46b8b15 commit 38bd347

File tree

9 files changed

+91
-35
lines changed

9 files changed

+91
-35
lines changed

server/app/src/main/java/io/whitefox/api/deltasharing/server/DeltaSharesApiImpl.java

+16-24
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,10 @@ public DeltaSharesApiImpl(
4848
@Override
4949
public Response getShare(String share) {
5050
return wrapExceptions(
51-
() -> optionalToNotFound(
52-
shareService.getShare(share),
53-
foundShare -> shareToForbidden(foundShare, s -> {
54-
var resultShare = new Share().name(s.name()).id(s.id());
55-
return Response.ok(resultShare).build();
56-
})),
51+
() -> optionalToNotFound(shareService.getShare(share), s -> {
52+
var resultShare = new Share().name(s.name()).id(s.id());
53+
return Response.ok(resultShare).build();
54+
}),
5755
exceptionToResponse);
5856
}
5957

@@ -126,24 +124,18 @@ share, schema, table, startingTimestamp, getRequestPrincipal()),
126124
public Response listALLTables(String share, Integer maxResults, String pageToken) {
127125
return wrapExceptions(
128126
() -> optionalToNotFound(
129-
shareService.getShare(share),
130-
foundShare -> shareToForbidden(
131-
foundShare,
132-
s -> optionalToNotFound(
133-
deltaSharesService.listTablesOfShare(
134-
share,
135-
parseToken(pageToken),
136-
Optional.ofNullable(maxResults),
137-
getRequestPrincipal()),
138-
c -> Response.ok(c.getToken()
139-
.map(
140-
t -> new ListTablesResponse()
141-
.items(mapList(c.getContent(), DeltaMappers::table2api))
142-
.nextPageToken(tokenEncoder.encodePageToken(t)))
143-
.orElse(
144-
new ListTablesResponse()
145-
.items(mapList(c.getContent(), DeltaMappers::table2api))))
146-
.build()))),
127+
deltaSharesService.listTablesOfShare(
128+
share,
129+
parseToken(pageToken),
130+
Optional.ofNullable(maxResults),
131+
getRequestPrincipal()),
132+
c -> Response.ok(c.getToken()
133+
.map(t -> new ListTablesResponse()
134+
.items(mapList(c.getContent(), DeltaMappers::table2api))
135+
.nextPageToken(tokenEncoder.encodePageToken(t)))
136+
.orElse(new ListTablesResponse()
137+
.items(mapList(c.getContent(), DeltaMappers::table2api))))
138+
.build()),
147139
exceptionToResponse);
148140
}
149141

server/app/src/main/java/io/whitefox/api/server/ApiUtils.java

+13-6
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
import io.quarkus.runtime.util.ExceptionUtil;
44
import io.whitefox.api.deltasharing.model.v1.generated.CommonErrorResponse;
55
import io.whitefox.core.Principal;
6-
import io.whitefox.core.Share;
76
import io.whitefox.core.services.DeltaSharedTable;
87
import io.whitefox.core.services.exceptions.AlreadyExists;
98
import io.whitefox.core.services.exceptions.NotFound;
9+
import jakarta.ws.rs.ForbiddenException;
1010
import jakarta.ws.rs.core.Response;
1111
import java.sql.Timestamp;
1212
import java.time.OffsetDateTime;
@@ -44,6 +44,12 @@ public interface ApiUtils extends DeltaHeaders {
4444
.errorCode("BAD REQUEST - timestamp provided is not formatted correctly")
4545
.message(ExceptionUtil.generateStackTrace(t)))
4646
.build();
47+
} else if (t instanceof ForbiddenException) {
48+
return Response.status(Response.Status.FORBIDDEN)
49+
.entity(new CommonErrorResponse()
50+
.errorCode("FORBIDDEN ACCESS")
51+
.message(ExceptionUtil.generateStackTrace(t)))
52+
.build();
4753
} else {
4854
return Response.status(Response.Status.BAD_GATEWAY)
4955
.entity(new CommonErrorResponse()
@@ -67,13 +73,14 @@ default Response notFoundResponse() {
6773
.build();
6874
}
6975

70-
default <T> Response optionalToNotFound(Optional<T> opt, Function<T, Response> fn) {
71-
return opt.map(fn).orElse(notFoundResponse());
76+
default Response forbiddenResponse() {
77+
return Response.status(Response.Status.FORBIDDEN)
78+
.entity(new CommonErrorResponse().errorCode("2").message("UNAUTHORIZED ACCESS"))
79+
.build();
7280
}
7381

74-
default Response shareToForbidden(Share value, Function<Share, Response> fn) {
75-
if (value.recipients().contains(getRequestPrincipal())) return fn.apply(value);
76-
else return Response.status(Response.Status.FORBIDDEN).build();
82+
default <T> Response optionalToNotFound(Optional<T> opt, Function<T, Response> fn) {
83+
return opt.map(fn).orElse(notFoundResponse());
7784
}
7885

7986
default Principal getRequestPrincipal() {

server/app/src/test/java/io/whitefox/api/deltasharing/SampleTables.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,23 @@ public static StorageManager createStorageManager() {
7070
new SharedTable("icebergtable2", "default", "name", icebergtable2)),
7171
"name")),
7272
testPrincipal,
73-
0L)));
73+
0L),
74+
new io.whitefox.core.Share(
75+
"noauthShare",
76+
"key",
77+
Map.of(
78+
"default",
79+
new io.whitefox.core.Schema(
80+
"default",
81+
List.of(
82+
new SharedTable("table1", "default", "name", deltaTable1),
83+
new SharedTable(
84+
"table-with-history", "default", "name", deltaTableWithHistory1),
85+
new SharedTable("icebergtable1", "default", "name", icebergtable1),
86+
new SharedTable("icebergtable2", "default", "name", icebergtable2)),
87+
"name")),
88+
new Principal("Mr. White"),
89+
0L)));
7490
}
7591

7692
public static final ParquetMetadata deltaTable1Metadata = ParquetMetadata.builder()

server/app/src/test/java/io/whitefox/api/deltasharing/server/DeltaSharesApiImplTest.java

+10
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,16 @@ public void listNotFoundSchemas() {
108108
.body("message", is("NOT FOUND"));
109109
}
110110

111+
@Test
112+
public void listSchemasNoAuth() {
113+
given()
114+
.when()
115+
.filter(deltaFilter)
116+
.get("delta-api/v1/shares/{share}/schemas", "noauthShare")
117+
.then()
118+
.statusCode(403);
119+
}
120+
111121
@Test
112122
public void listSchemas() {
113123
given()
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
quarkus.http.test-port=8080
1+
quarkus.http.test-port=8080
2+
quarkus.test.arg-line=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005

server/core/src/main/java/io/whitefox/core/Share.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public Share(
5050
id,
5151
schemas,
5252
Optional.empty(),
53-
Set.of(createPrincipal),
53+
Set.of(),
5454
createTime,
5555
createPrincipal,
5656
createTime,

server/core/src/main/java/io/whitefox/core/WhitefoxAuthorization.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class WhitefoxSimpleAuthorization implements WhitefoxAuthorization {
1313

1414
@Override
1515
public Boolean authorize(Share share, Principal principal) {
16-
return share.recipients().contains(principal);
16+
return share.recipients().contains(principal) || share.owner().equals(principal);
1717
}
1818
}
1919
}

server/core/src/main/java/io/whitefox/core/services/DeltaSharesServiceImpl.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ public ContentAndToken<List<Share>> listShares(
7373
Optional<ContentAndToken.Token> optionalToken =
7474
end < pageContent.size() ? Optional.of(new ContentAndToken.Token(end)) : Optional.empty();
7575
var content = pageContent.result().stream()
76-
.filter(s -> s.recipients().contains(currentPrincipal))
76+
.filter(
77+
s -> s.recipients().contains(currentPrincipal) || s.owner().equals(currentPrincipal))
7778
.collect(Collectors.toList());
7879
return optionalToken
7980
.map(t -> ContentAndToken.of(content, t))

server/core/src/test/java/io/whitefox/core/services/DeltaShareServiceTest.java

+29
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import io.whitefox.core.services.exceptions.TableNotFound;
1414
import io.whitefox.persistence.StorageManager;
1515
import io.whitefox.persistence.memory.InMemoryStorageManager;
16+
import jakarta.ws.rs.ForbiddenException;
1617
import java.util.Collections;
1718
import java.util.List;
1819
import java.util.Map;
@@ -31,6 +32,7 @@ public class DeltaShareServiceTest {
3132
WhitefoxAuthorization whitefoxAuthorization = new WhitefoxSimpleAuthorization();
3233

3334
private static final Principal testPrincipal = new Principal("Mr. Fox");
35+
private static final Principal badPrincipal = new Principal("Mr. White");
3436

3537
private static Share createShare(String name, String key, Map<String, Schema> schemas) {
3638
return new Share(name, key, schemas, testPrincipal, 0L);
@@ -52,6 +54,21 @@ public void listShares() {
5254
Assertions.assertTrue(sharesWithNextToken.getToken().isEmpty());
5355
}
5456

57+
@Test
58+
public void listSharesUnauthorized() {
59+
var shares = List.of(new Share("name", "key", Collections.emptyMap(), badPrincipal, 0L));
60+
StorageManager storageManager = new InMemoryStorageManager(shares);
61+
DeltaSharesService deltaSharesService = new DeltaSharesServiceImpl(
62+
storageManager,
63+
defaultMaxResults,
64+
tableLoaderFactory,
65+
fileSignerFactory,
66+
whitefoxAuthorization);
67+
var sharesWithNextToken =
68+
deltaSharesService.listShares(Optional.empty(), Optional.of(30), testPrincipal);
69+
Assertions.assertEquals(0, sharesWithNextToken.getContent().size());
70+
}
71+
5572
@Test
5673
public void listSharesWithToken() {
5774
var shares = List.of(createShare("name", "key", Collections.emptyMap()));
@@ -81,6 +98,18 @@ public void listSchemasOfEmptyShare() {
8198
Assertions.assertTrue(resultSchemas.get().getToken().isEmpty());
8299
}
83100

101+
@Test
102+
public void listSchemasNoAuth() {
103+
var shares = List.of(new Share("name", "key", Collections.emptyMap(), badPrincipal, 0L));
104+
StorageManager storageManager = new InMemoryStorageManager(shares);
105+
DeltaSharesService deltaSharesService = new DeltaSharesServiceImpl(
106+
storageManager, 100, tableLoaderFactory, fileSignerFactory, whitefoxAuthorization);
107+
assertThrows(
108+
ForbiddenException.class,
109+
() -> deltaSharesService.listSchemas(
110+
"name", Optional.empty(), Optional.empty(), testPrincipal));
111+
}
112+
84113
@Test
85114
public void listSchemas() {
86115
var shares = List.of(createShare(

0 commit comments

Comments
 (0)