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

[Feature/Identity] Permissions check API #4516

Conversation

cwperks
Copy link
Member

@cwperks cwperks commented Sep 14, 2022

Description

Companion PR in opensearch-sdk-java that shows this in use: opensearch-project/opensearch-sdk-java#118

This PR introduces an AuthorizationRequest that is intended to be used to evaluate whether a user is permitted to perform an action. This is experimental and used for prototyping AuthN/AuthZ. The AuthorizationRequest is intended to encapsulate all parameters needed to perform authorization on any request in OpenSearch. Where authorization is performed is being determined and this PR proposes a simple interface that encapsulates the information needed for authorization.

The AuthenticationRequest is a minimal interface that contains information about the requester (PrincipalIdentifierToken introduced in: #4299), a permissionId and a map of parameters that can be checked to see if the request fits within a user's permitted actions and constraints. PermissionId is a new concept and will be expanded on in future PRs. PermissionId can be thought of as action name for this PR.

The AuthorizationResponse is also minimal and contains a message and an AuthorizationStatus of granted or denied.

I would welcome any feedback on the design of the interface to see if this is suitable for most requests.

Issues Resolved

opensearch-project/opensearch-sdk-java#40

Check List

  • New functionality includes testing.
    • All tests pass
  • New functionality has been documented.
    • New functionality has javadoc added
  • Commits are signed per the DCO using --signoff
  • Commit changes are listed out in CHANGELOG.md file (See: Changelog)

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

@github-actions
Copy link
Contributor

Gradle Check (Jenkins) Run Completed with:

@github-actions
Copy link
Contributor

Gradle Check (Jenkins) Run Completed with:

Comment on lines 33 to 34
this.extensionUniqueId = extensionUniqueId;
this.requestIssuerIdentity = requestIssuerIdentity;
Copy link
Member

Choose a reason for hiding this comment

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

These pieces of information have been added into the request body so the caller could alter they as they saw fit. What about changing the usage so this request is only allowed for the current Subject?

Copy link
Member Author

Choose a reason for hiding this comment

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

What's the current mechanism for sending PrincipalIdentifierToken from extension -> cluster? I added this in when creating my own extension to pass back the PrincipalIdentifierToken that was sent to the extension.

You're right though, this shouldn't be exposed to the caller so I will remove it.

This class encapsulates the information needed to perform authorization, but currently isn't used. I am thinking that most of the time authorization will happen at the point in time that an action is being performed and we will permit/deny the action just in time and may not use this construct. We will resolve to a subject based on a token embedded in the request/message as you pointed out here (opensearch-project/opensearch-sdk-java#40 (comment)).

In the extensions world, I think we will want to perform authorization in RestSendToExtensionAction.prepareRequest before the transport service sends the request to the extension node. Is there a central place we can move authorization to in core similar to how all requests go through the security plugin when security is installed today?

Copy link
Member Author

Choose a reason for hiding this comment

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

@peternied I removed the reference to the requestIssuerIdentity from this class.

@peternied peternied changed the base branch from feature/extensions to feature/identity September 15, 2022 21:38
@peternied
Copy link
Member

@cwperks I moved this PR to target feature/identity where we have more flexiblity

@cwperks
Copy link
Member Author

cwperks commented Sep 16, 2022

@cwperks I moved this PR to target feature/identity where we have more flexiblity

@peternied Its hard to see the changes this PR is introducing now. Would it be possible to update the feature/identity branch with the latest changes in feature/extensions?

@cwperks cwperks force-pushed the extensions-authorize-request branch from edbfbc1 to 46fb6f9 Compare September 27, 2022 16:47
@github-actions
Copy link
Contributor

Gradle Check (Jenkins) Run Completed with:

@github-actions
Copy link
Contributor

Gradle Check (Jenkins) Run Completed with:

@cwperks cwperks changed the title [Feature/Extensions] Permissions check API for extensions [Feature/Identity] Permissions check API for extensions Sep 27, 2022
@cwperks cwperks marked this pull request as ready for review September 27, 2022 17:57
@cwperks cwperks changed the title [Feature/Identity] Permissions check API for extensions [Feature/Identity] Permissions check API Sep 27, 2022
@github-actions
Copy link
Contributor

Gradle Check (Jenkins) Run Completed with:

@github-actions
Copy link
Contributor

Gradle Check (Jenkins) Run Completed with:

Signed-off-by: Craig Perkins <[email protected]>
@github-actions
Copy link
Contributor

Gradle Check (Jenkins) Run Completed with:

Copy link
Member

@peternied peternied left a comment

Choose a reason for hiding this comment

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

I'd like to work out the new package before merging, everything else is optional IMO

*
* @opensearch.experimental
*/
package org.opensearch.authz;
Copy link
Member

Choose a reason for hiding this comment

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

When should this package be used vs the identity package?

Copy link
Member Author

Choose a reason for hiding this comment

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

Never. You can't have authorization without identity. I didn't want to put these in the base level of the identity package, can I move this package into identity as a sub-package?

Copy link
Member

Choose a reason for hiding this comment

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

Yup. lets do it!

/**
* Core classes responsible for handling identity in OpenSearch
*
* @opensearch.internal
Copy link
Member

Choose a reason for hiding this comment

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

👍

Comment on lines 28 to 30
private PrincipalIdentifierToken requestIssuerIdentity;
private String permissionId;
private Map<String, CheckableParameter> params;
Copy link
Member

Choose a reason for hiding this comment

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

It looks like with this design the resource id is contained within this map. This means that this API could be used for cluster level permission or specific resources. 👍

* @opensearch.experimental
*/
public class AuthorizationResponse extends TransportResponse {
private String response;
Copy link
Member

Choose a reason for hiding this comment

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

This object is a response, what about changing the name to message? I take this field is meant to represent a human readable message


@Override
public String toString() {
return "CheckableParameter{key=" + key + ", value=" + value + "}";
Copy link
Member

Choose a reason for hiding this comment

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

Should the typename also be included?

}

@SuppressWarnings("unchecked")
private CheckableParameter<?> createParameter(ParameterType type, String key, Object value) {
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't make sure callers use the correct value with the correct ParameterType so they can get an ugly runtime exception. Instead what about could create a set of overloads like createParameter(String key, TimeValue value) and createParameter(String key, ByteSizeValue value) that create the background implementations.

I'm uncertain the worth going back and forth on this, but I think we might want to revisit after we've implemented more classes

String key = in.readString();
String className = in.readString();
Object value = in.readGenericValue();
return new CheckableParameter(key, value, Class.forName(className));
Copy link
Member

Choose a reason for hiding this comment

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

Potential performance issue https://stackoverflow.com/q/18231991/533057

Copy link
Member

Choose a reason for hiding this comment

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

Maybe just make a comment around this line and we can revisit, what do you think?

Copy link
Member Author

Choose a reason for hiding this comment

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

Sounds good, this class and WriteableParameter were inspired by similar work on extensions with settings here: https://github.com/opensearch-project/OpenSearch/blob/feature/extensions/server/src/main/java/org/opensearch/common/settings/WriteableSetting.java

The different between Setting and here is that setting can rely on the type of the default value when determining its data type.

@github-actions
Copy link
Contributor

Gradle Check (Jenkins) Run Completed with:

* compatible open source license.
*/

/*
Copy link
Member

Choose a reason for hiding this comment

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

is this license correct?

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for catching that.

Copy link
Member

@DarshitChanpura DarshitChanpura left a comment

Choose a reason for hiding this comment

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

Looks good overall. Thanks Craig. One ask is to add more doc in CheckableParameter.java explaining why is this needed/how is this used?

*
* @opensearch.experimental
*/
public class CheckableParameter<T> {
Copy link
Member

Choose a reason for hiding this comment

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

More javadoc explaining how will this be used. Thoughts?

Copy link
Member Author

Choose a reason for hiding this comment

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

I expect to iterate on this idea a bit. The AuthorizationRequest is a class that encapsulates all of the information needed to perform authorization which includes the permissionId of the action being performed and any parameters needed to evaluate privileges.

When asking for permission such as: "Can I read documents on index X?", we need to know:

  1. Who is making the request (current subject)
  2. What action is being performed?
  3. On what resource?

@github-actions
Copy link
Contributor

Gradle Check (Jenkins) Run Completed with:

return new CheckableParameter<>(key, (TimeValue) value, TimeValue.class);
case ByteSizeValue:
return new CheckableParameter<>(key, (ByteSizeValue) value, ByteSizeValue.class);
default:
Copy link
Member

Choose a reason for hiding this comment

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

Are we planning on supporting custom user object types here? (i.e. have a case that supports a generic type)

Copy link
Member Author

Choose a reason for hiding this comment

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

No, I expect we will define a list of parameters that are checkable. We need to support index patterns to support existing index permissions. Other parameters that are needed to perform authorization could be resource identifiers.

@github-actions
Copy link
Contributor

Gradle Check (Jenkins) Run Completed with:

Signed-off-by: Craig Perkins <[email protected]>
@github-actions
Copy link
Contributor

Gradle Check (Jenkins) Run Completed with:

Signed-off-by: Craig Perkins <[email protected]>
@github-actions
Copy link
Contributor

Gradle Check (Jenkins) Run Completed with:

@cwperks
Copy link
Member Author

cwperks commented Sep 29, 2022

@peternied Any idea what's going on with the CI check here?

I see BUILD SUCCESSFUL in 22m 14s in jenkins, but this is saying UNSTABLE.

@cwperks
Copy link
Member Author

cwperks commented Sep 29, 2022

I see 2 test failures:

Tests with failures:
 - org.opensearch.action.admin.cluster.node.tasks.ResourceAwareTasksTests.testTaskResourceTrackingDuringTaskCancellation
 - org.opensearch.index.ShardIndexingPressureConcurrentExecutionTests.testReplicaThreadedUpdateToShardLimitsAndRejections

Are these known flaky tests?

@peternied
Copy link
Member

peternied commented Sep 29, 2022

Any idea what's going on with the CI check here?

@cwperks Nope! But I'm not going to work about it, as they look supremely unrelated to what we are doing. I am going to merge without passing CI... and I'll fix it if this is actually busted instead of an intermittent failure

@peternied peternied merged commit 1b403cd into opensearch-project:feature/identity Sep 29, 2022
@github-actions
Copy link
Contributor

Gradle Check (Jenkins) Run Completed with:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants