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 AuthAdmin #1318

Merged
merged 9 commits into from
Dec 1, 2023
Merged

Add AuthAdmin #1318

merged 9 commits into from
Dec 1, 2023

Conversation

brfrn169
Copy link
Collaborator

Description

This PR adds the AuthAdmin interface for administrative operations related to ScalarDB Auth (authentication and authorization). As ScalarDB Auth is an enterprise feature, this PR only introduces the interface without any implementations in this repository.

Related issues and/or PRs

N/A

Changes made

  • Added the AuthAdmin interface for administrative operations for ScalarDB
  • Modify exceptions to support authentication and authorization errors

Checklist

  • I have commented my code, particularly in hard-to-understand areas.
  • I have updated the documentation to reflect the changes.
  • Any remaining open issues linked to this PR are documented and up-to-date (Jira, GitHub, etc.).
  • Tests (unit, integration, etc.) have been added for the changes.
  • My changes generate no new warnings.
  • Any dependent changes in other PRs have been merged and published.

Additional notes (optional)

  • Documentation for this will be added in a separate PR.

Release notes

N/A

@brfrn169 brfrn169 added the enhancement New feature or request label Nov 23, 2023
@brfrn169 brfrn169 self-assigned this Nov 23, 2023
* An interface for administrative operations for authentication and authorization. This interface
* is used for creating and dropping users, and granting and revoking privileges.
*/
public interface AuthAdmin {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added AuthAdmin for administrative operations for ScalarDB Auth.

@@ -8,7 +8,7 @@
* An administrative interface for distributed transaction implementations. The user can execute
* administrative operations with it like createNamespace/createTable/getTableMetadata.
*/
public interface DistributedTransactionAdmin extends Admin {
public interface DistributedTransactionAdmin extends Admin, AuthAdmin {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Made DistributedTransactionAdmin extend AuthAdmin.

Comment on lines +9 to +12
private final boolean authenticationError;
private final boolean authorizationError;
private final boolean superuserRequired;
@Nullable private final AuthAdmin.Privilege requiredPrivilege;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added flags to the exceptions to indicate authentication and authorization errors. Initially, I considered introducing new exceptions for these errors, but to maintain backward compatibility, I opted for this approach.

Comment on lines 246 to 296
@Override
public void createUser(String username, @Nullable String password, UserOption... userOption) {
throw new UnsupportedOperationException("Not supported in the community edition");
}

@Override
public void alterUser(String username, @Nullable String password, UserOption... userOption) {
throw new UnsupportedOperationException("Not supported in the community edition");
}

@Override
public void dropUser(String username) {
throw new UnsupportedOperationException("Not supported in the community edition");
}

@Override
public void grant(String username, String namespaceName, Privilege... privileges) {
throw new UnsupportedOperationException("Not supported in the community edition");
}

@Override
public void grant(
String username, String namespaceName, String tableName, Privilege... privileges) {
throw new UnsupportedOperationException("Not supported in the community edition");
}

@Override
public void revoke(String username, String namespaceName, Privilege... privileges) {
throw new UnsupportedOperationException("Not supported in the community edition");
}

@Override
public void revoke(
String username, String namespaceName, String tableName, Privilege... privileges) {
throw new UnsupportedOperationException("Not supported in the community edition");
}

@Override
public List<User> getUsers() {
throw new UnsupportedOperationException("Not supported in the community edition");
}

@Override
public EnumSet<Privilege> getPrivileges(String username, String namespaceName) {
throw new UnsupportedOperationException("Not supported in the community edition");
}

@Override
public EnumSet<Privilege> getPrivileges(String username, String namespaceName, String tableName) {
throw new UnsupportedOperationException("Not supported in the community edition");
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No implementations are added here as it's an enterprise feature.

@@ -240,6 +243,58 @@ public void upgrade(Map<String, String> options) throws ExecutionException {
admin.upgrade(options);
}

@Override
public void createUser(String username, @Nullable String password, UserOption... userOption) {
throw new UnsupportedOperationException("Not supported in the community edition");
Copy link
Contributor

Choose a reason for hiding this comment

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

How about moving these default implementations throwing the exception to AuthAdmin as default methods so that all classes which inherit AuthAdmin don't need to implement these ones?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I had the same thought, but I didn't do that because I was not sure for which methods we should add a default implementation. In other words, should we add default implementations for the existing methods (createTable(), getTableMetadata(), etc.) as well? What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think only the methods of AuthAdmin should have default methods to throw the exception since those features aren't supported in the community edition. As for the existing methods coming from DistributedTransactionAdmin, we should keep them available in the community edition and don't need default impl for them. But I might be missing something on the background of this auth feature.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Okay, I think that's reasonable. Let me add default implementations to the methods in AuthAdmin. Thanks.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in ca46a9d. Thanks.


@Override
public void createUser(String username, @Nullable String password, UserOption... userOption) {
throw new UnsupportedOperationException();
Copy link
Contributor

Choose a reason for hiding this comment

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

This exception should have the same error message as the exceptions ConsensusCommitAdmin's methods throw?

Copy link
Collaborator Author

@brfrn169 brfrn169 Nov 24, 2023

Choose a reason for hiding this comment

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

Initially, I didn't add any message here because we were going to remove the JDBC transaction and ScalarDB Server components. However, recent discussions have raised the possibility of retaining the JDBC transaction. Therefore, I will add a message depending on the outcome of this discussion:
#1318 (comment)

Thanks.

}

/** The privileges for ScalarDB operations. */
enum Privilege {
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems like an union of privileges for user (admin) operations, namespace operations and table operations. For instance, GRANT is for user (admin) operations and TRUNCATE is for table operations. Another option is to separate this set of privileges into each type of operations like UserPrivilege, NamespacePrivilege and TablePrivilege. This option requires additional enum definitions, but it might beneficial in the long run to avoid wrong privilege usage (e.g, TRUNCATE for a user). What do you think?

Copy link
Collaborator Author

@brfrn169 brfrn169 Nov 24, 2023

Choose a reason for hiding this comment

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

Let me clarify the specification to ensure we are on the same page.

Currently, we can grant privileges to a user for either a specific namespace or a table. I think we can imagine the SQL GRANT statement as follows:

For table privileges:

GRANT READ, WRITE, CREATE ON <TABLE> TO <USER> WITH GRANT OPTION

In this case, the user has READ, WRITE, CREATE and GRANT privileges for the specified table.

  • READ: The user can read the table.
  • WRITE: The user can write the table.
  • CREATE: The user can create indexes on the table.
  • GRANT: The user can grant privileges on the table to other users.

For namespace privileges:

GRANT READ, WRITE, CREATE ON NAMESPACE <NAMESPACE> TO <USER> WITH GRANT OPTION

In this case, the user has READ, WRITE, CREATE and GRANT privileges for all tables in the namespace.

  • READ: The user can read all tables in the namespace.
  • WRITE: The user can write all tables in the namespace.
  • CREATE: The user can create a table in the namespace or create indexes on all tables in the namespace.
  • GRANT: The user can grant privileges on all tables in the namespace to other users.

Given this, I believe the approach is consistent and there's no need for separate privilege types for tables, namespaces, and users. What are your thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for clarifying the usage. I understand how to use GRANT.

GRANT privilege seems a bit different type (more metadata-like?) attribute to me from other privileges (e.g. CREATE). It's like between UserOption and Privilege? It might fit PrivilegeOption.

In this case, the user has READ, WRITE, CREATE and GRANT privileges for all tables in the namespace.

Understood. So, the enum is like TablePrivilege. BTW, we don't need to another type of privileges to allow or prohibit operations for namespaces like GRANT CREATE ON DATABASE?

Copy link
Collaborator Author

@brfrn169 brfrn169 Nov 24, 2023

Choose a reason for hiding this comment

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

Ah sorry, the explanation of GRANT for namespace privileges was wrong.

The correct explanation is as follows

  • GRANT: The user can grant/revoke privileges on all tables in the namespace to/from other users. Also the user can grant/revoke privileges on the namespace to/from other users.

So for example, if a superuser issues the following:

GRANT ... ON NAMESPACE ns TO user1 WITH GRANT OPTION

Then user1 can issue GRANT... ON NAMESPACE ns to ... for other users. Also, user1 can issue GRANT on all tables in the ns namespace for other users.

What do you think?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

One more thing, if a superuser issues the following GRANT command:

GRANT DROP ON NAMESPACE ns TO user1

Then, user1 can drop the ns namespace as well as all tables in the ns namespace.

That sounds inconsistent to you?

Copy link
Contributor

Choose a reason for hiding this comment

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

GRANT DROP ON NAMESPACE ns TO user1

As we talked offline, this semantics of dropping a specific namespace seems inconsistent with other GRANT statements that affect tables. Perhaps, we can defer considering how to permit/prohibit manipulation of namespace.

As for Privilege.GRANT, I still think it might belong to a different layer, but I don't have a so strong opinion on it. Let's proceed with the current design.

Copy link
Collaborator Author

@brfrn169 brfrn169 Nov 28, 2023

Choose a reason for hiding this comment

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

Thank you for summarizing the discussion. I've fixed the Javadoc slightly and renamed several methods.

I think we can support something like the following grammar in ScalarDB SQL:

GRANT { { SELECT | INSERT | UPDATE | DELETE | CREATE | DROP | TRUNCATE | ALTER } [, ...] | ALL [ PRIVILEGES ] }
    ON { [ TABLE ] table_name [, ...] | ON NAMESPACE namespace_name [, ...] }
    TO user_name [, ...] [ WITH GRANT OPTION ]

If a user specified ON [TABLE] table_name in the GRANT statement, we call the AuthAdmin.grant(String username, String namespaceName, String tableName, Privilege... privileges) method. And if a user specified ON NAMESPACE namespace_name in the GRANT statement, we call the AuthAdmin.grant(String username, String namespaceName, Privilege... privileges) method.

Also, if a user specified WITH GRANT OPTION and ON [TABLE] table_name in the GRANT statement, the granted user can issue GRANT statements, and they can grant privileges on the specified table to other users.

And if a user specified WITH GRANT OPTION and ALL TABLES IN NAMESPACE namespace_name in the GRANT statement, they can grant privileges on all tables in the specified namespace to other users.

Note that users can only grant privileges that they have to other users.

Does that make sense?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes. The syntax of the GRANT statement and the behavior looks good 👍

As for Privilege.GRANT, I still think it might belong to a different layer, but I don't have a so strong opinion on it. Let's proceed with the current design.

This comment only considers the internal design and implementation since I still think WITH GRANT OPTION is a sort of option not a normal privilege. The above GRANT statement syntax itself also seems to handle GRANT option differently from other privileges (e.g., UPDATE). I checked the PostgreSQL source code, and it doesn't handle GRANT OPTION as well as other privileges https://github.com/postgres/postgres/blob/5ad49322e50459444b201e624d5df706751c9d41/src/include/nodes/parsenodes.h#L74-L93. So, IMO, maybe Privilege.GRANT should be removed from the enum and handled differently, although it might be a matter of taste.

Copy link
Collaborator Author

@brfrn169 brfrn169 Nov 28, 2023

Choose a reason for hiding this comment

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

As discussed offline, we have reached a conclusion regarding the behavior mentioned in the following comment:
#1318 (comment)

I've also revised the Javadoc and revered the method names in ad52e62.

@komamitsu Can you please take a quick look at it? Thanks.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks. Looks good!

@brfrn169 brfrn169 requested a review from komamitsu November 24, 2023 09:40
Copy link
Contributor

@komamitsu komamitsu left a comment

Choose a reason for hiding this comment

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

LGTM! 👍

Copy link
Contributor

@feeblefakie feeblefakie left a comment

Choose a reason for hiding this comment

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

Overall, looking good. Thank you!
I left one comment. PTAL!

String message,
Throwable cause,
@Nullable String transactionId,
boolean authenticationError) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of adding an argument only for authentication, it might be better to define an error code and add it here or something?
Since we don't define error code yet, I understand it's hard to do so now...

Copy link
Collaborator Author

@brfrn169 brfrn169 Nov 28, 2023

Choose a reason for hiding this comment

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

Yeah, at the moment, it's hard to do so since we need to define error codes for all errors for that. Anyway, I think we can add error codes later on.

@brfrn169 brfrn169 requested a review from feeblefakie November 28, 2023 07:37
Copy link
Contributor

@feeblefakie feeblefakie left a comment

Choose a reason for hiding this comment

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

LGTM! Thank you!

@brfrn169 brfrn169 merged commit 2de8433 into master Dec 1, 2023
23 checks passed
feeblefakie pushed a commit that referenced this pull request Dec 1, 2023
@brfrn169 brfrn169 deleted the add-auth-admin branch December 1, 2023 07:58
@brfrn169 brfrn169 mentioned this pull request Dec 12, 2023
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants