Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add @ManualAuthorization annotation for non-standard endpoints #9252

Merged
merged 11 commits into from
Aug 25, 2022

Conversation

apucher
Copy link
Contributor

@apucher apucher commented Aug 19, 2022

Several endpoints in pinot aren't served well by the AuthFilter's heuristics for extracting table names. In particular, this includes /tableConfigs and /schema endpoints that accept opaque payloads without a table or schema name in the parameters. This causes problems with fine-grained access control, such as non-admin users creating new tables via self-serve.

This PR adds a new annotation @ManualAuthorization for REST endpoints, which allows developers to skip the default authorization and deserialize payloads before manually invoking authorization, e.g. via AccessControlUtils.validatePermissions(). This annotation comes with obvious risks and should be used sparingly, as it enables requests to bypass most of the AuthFilter.

@codecov-commenter
Copy link

codecov-commenter commented Aug 19, 2022

Codecov Report

Merging #9252 (8c1aa68) into master (8bf94e7) will increase coverage by 1.52%.
The diff coverage is 85.18%.

@@             Coverage Diff              @@
##             master    #9252      +/-   ##
============================================
+ Coverage     68.44%   69.97%   +1.52%     
- Complexity     5008     5012       +4     
============================================
  Files          1861     1861              
  Lines         99218    99231      +13     
  Branches      15092    15096       +4     
============================================
+ Hits          67911    69432    +1521     
+ Misses        26460    24890    -1570     
- Partials       4847     4909      +62     
Flag Coverage Δ
integration1 26.31% <22.22%> (?)
integration2 24.78% <31.48%> (+0.04%) ⬆️
unittests1 67.12% <0.00%> (+0.05%) ⬆️
unittests2 15.35% <74.07%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
...er/api/access/ZkBasicAuthAccessControlFactory.java 0.00% <ø> (ø)
...sources/PinotAccessControlUserRestletResource.java 57.14% <ø> (-3.47%) ⬇️
...ler/api/resources/PinotControllerAuthResource.java 0.00% <ø> (ø)
...ces/PinotSegmentUploadDownloadRestletResource.java 54.16% <ø> (+4.16%) ⬆️
...ache/pinot/spi/utils/builder/TableNameBuilder.java 83.87% <0.00%> (-5.79%) ⬇️
...t/controller/api/resources/PinotQueryResource.java 54.22% <25.00%> (+2.81%) ⬆️
...che/pinot/controller/api/access/AccessControl.java 27.27% <50.00%> (-9.10%) ⬇️
...oller/api/resources/PinotTableRestletResource.java 63.41% <87.50%> (+0.60%) ⬆️
...inot/controller/api/access/AccessControlUtils.java 86.66% <91.66%> (+23.03%) ⬆️
...ot/controller/api/access/AuthenticationFilter.java 92.30% <100.00%> (+0.64%) ⬆️
... and 147 more

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@apucher apucher force-pushed the manual-authorization-annotation branch 2 times, most recently from a569240 to c43fdad Compare August 22, 2022 18:32
@@ -51,7 +52,8 @@ public interface AccessControl {
* @param endpointUrl the request url for which this access control is called
* @return whether the client has permission
*/
default boolean hasAccess(String tableName, AccessType accessType, HttpHeaders httpHeaders, String endpointUrl) {
default boolean hasAccess(@Nullable String tableName, AccessType accessType, HttpHeaders httpHeaders,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we shouldn't make these nullable. The existing implementations might assume these are not null.

Copy link
Contributor Author

@apucher apucher Aug 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have scenarios where null is passed in here "by accident" if tableName (schemaName) happens to come out to null. This merely acknowledges that reality. Do you know of an existing implementation that has issues with this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1. table name can already be null. also the access control doesn't distinguish between table or schema name

Comment on lines 141 to 150
@Authenticate(AccessType.READ)
public Response downloadSegment(
@ApiParam(value = "Name of the table", required = true) @PathParam("tableName") String tableName,
@ApiParam(value = "Name of the segment", required = true) @PathParam("segmentName") @Encoded String segmentName,
@Context HttpHeaders httpHeaders)
throws Exception {
// Validate data access
boolean hasDataAccess;
try {
AccessControl accessControl = _accessControlFactory.create();
hasDataAccess = accessControl.hasDataAccess(httpHeaders, tableName);
} catch (Exception e) {
throw new ControllerApplicationException(LOGGER,
"Caught exception while validating access to table: " + tableName, Response.Status.INTERNAL_SERVER_ERROR, e);
}
if (!hasDataAccess) {
throw new ControllerApplicationException(LOGGER, "No data access to table: " + tableName,
Response.Status.FORBIDDEN);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't delete the manual authentication here. Although the method is GET, but we need to only allow ppl who are authorized to be able to download the data.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same strictness as before. The endpoint provides a table name, therefore, we'll perform auth based on headers and table name. The only change is removing the call to a deprecated method.

Comment on lines 196 to 201
if (!accessControl.hasDataAccess(httpHeaders, rawTableName)) {
if (!accessControl.hasAccess(rawTableName, AccessType.READ, httpHeaders, endpointUrl)) {
Copy link
Contributor

@sajjad-moradi sajjad-moradi Aug 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not change this. Although we're reading data and AccessType.READ looks appropriate, we need to only allow ppl who are authorized to be able to query the data that might contain sensitive info like member information. It's different than reading table config or some other cluster configs that might be ok with weaker authentication level like AccessType.READ.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the above, this has similar strictness to the original implementation. The difference here is removing the use of a deprecated method.

On a tangent, this resource shouldn't be in the controller in the first place, rather, it should be controlled by the broker's access control.

@apucher apucher force-pushed the manual-authorization-annotation branch from c43fdad to 22d0609 Compare August 23, 2022 02:44
@apucher apucher force-pushed the manual-authorization-annotation branch from 7feb69b to 890aa3c Compare August 24, 2022 06:19
@apucher apucher merged commit f6e26c2 into master Aug 25, 2022
@apucher apucher deleted the manual-authorization-annotation branch August 25, 2022 04:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants