Skip to content

[ISSUE #15476] Add plugin-owned AI visibility grant API#15513

Open
Zhengcy05 wants to merge 7 commits into
alibaba:developfrom
Zhengcy05:feat/ai-visibility-grant-api
Open

[ISSUE #15476] Add plugin-owned AI visibility grant API#15513
Zhengcy05 wants to merge 7 commits into
alibaba:developfrom
Zhengcy05:feat/ai-visibility-grant-api

Conversation

@Zhengcy05

Copy link
Copy Markdown
Contributor

Special note: Since there are many design changes this time, I sincerely hope someone will carefully review this PR. Thx😘
fixes #15476

What is the purpose of the change

This PR adds plugin-owned AI visibility grant management to the default auth plugin so resource owners or global admins can grant, list, and revoke explicit visibility permissions for AI resources.

It also wires those explicit grants into AI visibility queries, while keeping the API ownership and persistence model inside the auth plugin boundary.

Brief changelog

  • add /v3/auth/ai/visibility grant/list/revoke APIs in the default auth plugin
  • introduce VisibilityResourceLocator so the auth plugin can resolve AI resources and verify owner-based management authority
  • reuse the existing RBAC persistence model for explicit AI visibility grants and apply them during AI query authorization
  • validate grantee existence when creating grants, while still allowing revoke to clean up stale bindings
  • add unit coverage, auth API IT coverage, and update related specs and API test coverage docs

Verifying this change

  • mvn -pl plugin-default-impl/nacos-default-auth-plugin,test/openapi-test spotless:apply
  • mvn -pl plugin-default-impl/nacos-default-auth-plugin,test/openapi-test spotless:check
  • mvn -pl plugin-default-impl/nacos-default-auth-plugin -Dtest=DefaultAiVisibilityGrantServiceTest,NacosAuthPluginControllerConfigTest test
  • mvn -pl test/openapi-test test-compile

Note:

  • AiVisibilityGrantAuthApiITCase was added for the new auth API flow.

Follow this checklist to help us incorporate your contribution quickly and easily:

  • Make sure there is a Github issue filed for the change (usually before you start working on it). Trivial changes like typos do not require a Github issue. Your pull request should address just this issue, without pulling in other changes - one PR resolves one issue.
  • Format the pull request title like [ISSUE #123] Fix UnknownException when host config not exist. Each commit in the pull request should have a meaningful subject line and body.
  • Write a pull request description that is detailed enough to understand what the pull request does, how, and why.
  • Write necessary unit-test to verify your logic correction, more mock a little better when cross module dependency exist. If the new feature or significant change is committed, please remember to add integration-test in test module.
  • Run mvn -B clean package apache-rat:check spotbugs:check -DskipTests to make sure basic checks pass. Run mvn clean install to make sure unit-test pass. Run mvn clean test-compile failsafe:integration-test to make sure integration-test pass.

@github-actions

Copy link
Copy Markdown

Thanks for your this PR. 🙏
Please check again for your PR changes whether contains any usage/api/configuration change such as Add new API , Add new configuration, Change default value of configuration.
If so, please add or update documents(markdown type) in docs/next/ for repository nacos-group/nacos-group.github.io


感谢您提交的PR。 🙏
请再次查看您的PR内容,确认是否包含任何使用方式/API/配置参数的变更,如:新增API新增配置参数修改默认配置等操作。
如果是,请确保在提交之前,在仓库nacos-group/nacos-group.github.io中的docs/next/目录下添加或更新文档(markdown格式)。

@KomachiSion

Copy link
Copy Markdown
Collaborator

@Zhengcy05 Thanks for the contribution. The overall direction—keeping visibility grant management inside the auth plugin and resolving domain resource metadata through an SPI—is reasonable.

However, I think the RBAC mapping, permission lookup, database compatibility, and authentication scope need to be revised before this feature can be merged. Requesting changes for the following items.

1. Create one dedicated visibility role per user

The current implementation creates one internal role for every resource/action pair and encodes the namespace, resource type, resource name, and action into the role name.

This causes several problems:

  • the number of distinct roles grows with the resource count;
  • normal resource names already exceed the existing roles.role varchar(50) limit;
  • permission information is duplicated in the role name;
  • list authorization later needs to decode resources from role names.

For this user-specific grant model, please create at most one dedicated internal visibility role for each user when the user receives their first visibility grant:

identity
  -> dedicated visibility role
      -> permission(resource A, r)
      -> permission(resource B, rw)
      -> ...

The intended persistence model should be similar to:

roles:
username -> dedicated visibility role

permissions:
dedicated visibility role
    -> @@visibility/{namespaceId}/{resourceType}/{resourceName} : r|rw

The dedicated role name must be deterministic, reserved for visibility grants, unique per user, and bounded to the existing 50-character column.

Please do not attach these permissions to an arbitrary existing role. Existing roles may be shared by multiple users, so adding a user-specific visibility permission to such a role would grant access to every user bound to that role.

Grant should:

  1. resolve or create the user's dedicated visibility role;
  2. ensure the user-to-role binding exists;
  3. add the raw resource/action permission idempotently.

Revoke should delete the corresponding permission. The empty internal role may either be retained or cleaned up.

A partially created role without a permission must never grant visibility.

2. Keep the authorization chain as identity -> role -> permission

findAuthorizedResourceNames currently derives authorized resources directly from encoded role names.

A role binding only establishes identity-to-role membership. It must not be treated as a permission by itself. Otherwise, an orphaned or manually created role can expose a private resource in list/search results even when the backing permission does not exist.

Please derive visible resources from the actual permissions attached to the current user's dedicated visibility role:

identity
  -> visibility role
  -> actual permissions
  -> filter namespace/resource type/action
  -> extract resource names
  -> build list/search/count condition

This keeps single-resource authorization and list/search/count authorization on the same permission source of truth.

The resource grant-list API also needs the reverse lookup:

resource permission -> visibility role -> user

Please add an exact persistence query and the required indexes for this path. It should not scan all roles and permissions or depend on decoding role names.

Please also ensure that grant and revoke invalidate or refresh the relevant role/permission caches so that a successful API response has clearly defined visibility semantics.

3. Store the raw resource identifier and expand it to 512 characters

The permission should store the original resource identifier:

@@visibility/{namespaceId}/{resourceType}/{resourceName}

Using the current AI resource field limits, the complete identifier can require approximately 431 characters:

13 + 128 + 1 + 32 + 1 + 256 = 431

The current built-in schemas are inconsistent:

Database Current resource length
MySQL 128
Oracle 255
Derby 512
PostgreSQL 512

For this PR, expanding permissions.resource to 512 is an acceptable short-term solution.

Please update:

  • the MySQL initialization schema;
  • the Oracle initialization schema, using VARCHAR2(512 CHAR);
  • the corresponding schema tests;
  • migration SQL for existing MySQL and Oracle deployments;
  • the upgrade/release documentation.

The MySQL permissions table currently does not declare a charset, engine, or row format and therefore inherits the user's database defaults. The absence of an explicit utf8mb4 declaration does not guarantee that the table uses a single-byte charset.

With utf8mb4, the current unique index has a maximum declared size of approximately:

(role 50 + resource 512 + action 8) * 4 = 2280 bytes

This fits the 3072-byte InnoDB index limit only under a compatible configuration, such as:

MySQL >= 5.7.9
innodb_page_size = 16KB
ROW_FORMAT = DYNAMIC

The field was previously reduced from 255 to 128 because the unique index exceeded the historical 767-byte limit: #11306

Therefore, the migration documentation must include preflight checks such as:

SELECT VERSION();
SHOW VARIABLES LIKE 'innodb_page_size';
SHOW VARIABLES LIKE 'innodb_default_row_format';
SHOW CREATE TABLE permissions;

The MySQL migration should explicitly rebuild the table with a compatible row format while changing the column, for example:

ALTER TABLE permissions
    ROW_FORMAT=DYNAMIC,
    MODIFY COLUMN resource VARCHAR(512) NOT NULL;

The exact charset and collation should also be explicitly defined instead of silently inheriting the database default. Please confirm whether permission resource comparison is intended to be case-sensitive before selecting the collation.

Users whose MySQL configuration cannot support the resulting index must be warned before upgrading rather than encountering a runtime permission-insert failure.

4. Handle database-independent indexing as a follow-up

A database-independent SHA-256 index is the preferred long-term design, but it does not need to expand the scope of this PR.

Please create a follow-up issue/design for a model such as:

role
resource          -- raw resource identifier
resource_hash     -- SHA-256 of the canonical UTF-8 resource
action

The unique index would become:

UNIQUE(role, resource_hash, action)

To reduce database-type differences, resource_hash can be stored as a normalized lowercase hexadecimal CHAR(64) value calculated in Java rather than using database-specific binary types or database hash functions.

The raw resource must remain the final authorization value. After looking up by hash, the implementation should still compare the raw resource using exact equality.

The follow-up design should define the canonicalization rules for:

  • default namespace normalization;
  • resource type normalization;
  • resource name case sensitivity;
  • UTF-8 encoding;
  • migration and backfill of existing permission rows.

5. Match visibility resources exactly in memory

Visibility grants target one exact resource.

The current default RBAC matcher treats stored permission resources as regular expressions. A resource name containing ., [], +, (, or other regex metacharacters may therefore match a different resource.

Please keep the original raw resource string in storage, but use exact in-memory matching for the reserved @@visibility/ resource prefix.

For example:

if (permissionResource.startsWith(VISIBILITY_RESOURCE_PREFIX)) {
    matched = permissionResource.equals(targetResource);
} else {
    matched = existingWildcardMatch(permissionResource, targetResource);
}

The persisted resource must not be regex-escaped or otherwise transformed. Existing wildcard behavior for Config/Naming permissions should remain unchanged.

6. Explicitly select the authentication scope

The visibility grant endpoints rely on the service layer to resolve the authenticated NacosUser and allow only the resource owner or a global administrator.

However, the current @Secured annotations default to ApiType.OPEN_API.

With the distribution defaults:

nacos.core.auth.enabled=false
nacos.core.auth.admin.enabled=true

the open API filter skips identity validation, while isAnyAuthEnabled() still returns true. Consequently, resolveCurrentUsername() returns null and even owner/global-admin requests are rejected.

Please explicitly assign the intended authentication scope, likely ApiType.ADMIN_API, and verify that identity-only authentication still allows the service layer to distinguish:

  • the resource owner;
  • a global administrator;
  • an authenticated non-owner;
  • an unauthenticated caller.

This behavior must be covered by auth-enabled integration tests. The current auth-disabled IT cannot validate the most important authorization semantics of this API.

7. Keep Prompt visibility integration in a separate issue/PR

Prompt has not yet fully joined the standard visibility flow. Its read/detail/version/download and list paths do not consistently apply standard readable checks and pre-pagination visibility conditions.

Refactoring Prompt visibility is outside the scope of this PR and should be handled by a separate issue and PR.

However, this PR must not accept a Prompt visibility grant and return success when that permission cannot affect Prompt runtime behavior.

Please introduce or validate a supported-resource-type capability so that the grant API only accepts resource types whose single-resource and list/search/count paths already support the standard visibility flow.

Unsupported resource types should return a clear unsupported error until their visibility integration is complete. The currently supported resource types must also be documented in the visibility/auth specs.

The same rule should apply to any other AI resource type that has not yet joined the standard visibility flow.

Grant cleanup when a resource is deleted is not required in this PR and can remain a later lifecycle enhancement.

8. Align the API and specification text

Please use the same endpoint consistently in:

  • controller mappings;
  • PR description;
  • English and Chinese specs;
  • V3 API surface;
  • API test coverage documents.

The implementation currently uses:

/v3/auth/visibility

but some text still refers to /v3/auth/ai/visibility or /ai/visibility.

9. Required tests

In addition to the current API contract tests, please cover:

  • one user receiving grants for multiple resources while retaining one dedicated visibility role;
  • multiple users receiving grants for the same resource;
  • idempotent repeated grant and revoke;
  • a visibility role without a backing permission granting no access;
  • list/search/count being derived from actual permissions rather than role names;
  • read grants not implying write;
  • rw grants implying read;
  • exact matching for resource names containing regex metacharacters;
  • maximum supported resource identifier length;
  • MySQL schema creation and migration under the documented configuration;
  • Oracle VARCHAR2(512 CHAR) schema behavior;
  • rejection of resource types that have not joined the standard visibility flow;
  • auth-enabled owner, global-admin, non-owner, and unauthenticated behavior;
  • permission/cache visibility after successful grant and revoke.

Once these items are addressed, the plugin-owned API and locator SPI should provide a reasonable base for explicit visibility grants without coupling the generic resource controllers directly to the default auth implementation.

@KomachiSion

Copy link
Copy Markdown
Collaborator

One more need todo after this PR:

Follow-up proposal: use a SHA-256 resource index for database-independent permission storage

The current PR can use resource VARCHAR(512) as a short-term solution, with the required MySQL configuration and migration documentation.

For the long term, I suggest creating a separate issue/design to replace the raw resource string in permission indexes with a Java-computed SHA-256 digest. This would remove the index-length dependency on database charset, collation, page size, and database-specific index limits while retaining the original resource value for authorization.

Proposed data model

The permissions table should continue storing the original resource identifier and add a fixed-length hash column:

role
resource          -- original canonical resource identifier
resource_hash     -- SHA-256(resource), lowercase hexadecimal
action

A cross-database logical schema could use:

resource      VARCHAR(512) NOT NULL,
resource_hash CHAR(64) NOT NULL

Using lowercase hexadecimal CHAR(64) is preferable to database-specific binary types:

MySQL       BINARY(32)
Oracle      RAW(32)
PostgreSQL  BYTEA
Derby       CHAR FOR BIT DATA

The hash must be calculated in Java, not with database-specific SQL functions.

Canonical hash input

The implementation should first build the canonical raw resource identifier:

@@visibility/{normalizedNamespaceId}/{normalizedResourceType}/{resourceName}

It should then calculate:

resource_hash = lowercaseHex(
    SHA-256(canonicalResource.getBytes(StandardCharsets.UTF_8))
)

The canonicalization rules must be defined in the visibility/auth specification:

  • a blank namespace is converted to the default namespace;
  • resource type uses the same normalization as authorization;
  • resource name preserves its defined case-sensitive identity;
  • no additional Unicode normalization is applied unless the resource model explicitly requires it;
  • UTF-8 is always used;
  • role and action remain separate index fields and are not included in resource_hash.

The hash should always be calculated from the exact canonical string that is stored in resource.

Indexes

The current unique index:

UNIQUE(role, resource, action)

should eventually be replaced with:

UNIQUE(role, resource_hash, action)

The grant-list API also needs efficient reverse lookup from a resource to users, so an additional index should be considered:

INDEX(resource_hash, action, role)

This supports both directions:

authorization:
identity -> role -> resource_hash/action permission


** Of cource if you has better way to solve this problem or better design **

@KomachiSion

Copy link
Copy Markdown
Collaborator

And @Zhengcy05 Would you like provide your dingtalk number?

@Zhengcy05

Copy link
Copy Markdown
Contributor Author

Understood. I will focus on this PR first, which might take a bit longer. 🫡

@Zhengcy05

Copy link
Copy Markdown
Contributor Author

And @Zhengcy05 Would you like provide your dingtalk number?

My DingTalk account : 1ex-76mml35v5b

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.3] Add plugin-owned AI resource visibility authorization APIs

2 participants