mchades commented on code in PR #12366:
URL: https://github.com/apache/gravitino/pull/12366#discussion_r3733336388


##########
core/src/main/java/org/apache/gravitino/catalog/SchemaOperationDispatcher.java:
##########
@@ -101,84 +106,99 @@ public NameIdentifier[] listSchemas(Namespace namespace) 
throws NoSuchCatalogExc
    * @throws SchemaAlreadyExistsException If a schema with the same identifier 
already exists.
    */
   @Override
-  public Schema createSchema(NameIdentifier ident, String comment, Map<String, 
String> properties)
+  public Schema createSchema(
+      NameIdentifier ident,
+      String comment,
+      Map<String, String> properties,
+      Map<String, SecretBinding> secretBindings,
+      Map<String, SecretReference> secretReferences)
       throws NoSuchCatalogException, SchemaAlreadyExistsException {
     NameIdentifier catalogIdent = getCatalogIdentifier(ident);
 
+    long uid = idGenerator.nextId();
+    Map<String, String> entityProperties = 
SecretPropertyUtils.copyEntityProperties(properties);
+    List<SecretUrn> secretUrns =
+        secretManager.assembleSecretUrns(
+            properties, entityProperties, "schema", uid, secretBindings, 
secretReferences);
     doWithCatalog(
         catalogIdent,
         c ->
             c.doWithPropertiesMeta(
                 p -> {
-                  validatePropertyForCreate(p.schemaPropertiesMetadata(), 
properties);
+                  validatePropertyForCreate(p.schemaPropertiesMetadata(), 
entityProperties);
                   return null;
                 }),
         IllegalArgumentException.class);
-    long uid = idGenerator.nextId();
+    secretManager.writeSecrets(secretBindings, secretUrns);
     // Add StringIdentifier to the properties, the specific catalog will 
handle this
     // StringIdentifier to make sure only when the operation is successful, 
the related
     // SchemaEntity will be visible.
     StringIdentifier stringId = StringIdentifier.fromId(uid);
     Map<String, String> updatedProperties =
-        StringIdentifier.newPropertiesWithId(stringId, properties);
-
-    return TreeLockUtils.doWithTreeLock(
-        catalogIdent,
-        LockType.WRITE,
-        () -> {
-          // we do not retrieve the schema again (to obtain some values 
generated by underlying
-          // catalog)
-          // since some catalogs' API is async and the schema may not be 
created immediately
-          Schema schema =
-              doWithCatalog(
-                  catalogIdent,
-                  c -> c.doWithSchemaOps(s -> s.createSchema(ident, comment, 
updatedProperties)),
-                  NoSuchCatalogException.class,
-                  SchemaAlreadyExistsException.class);
+        StringIdentifier.newPropertiesWithId(stringId, entityProperties);
 
-          // If the Schema is maintained by the Gravitino's store, we don't 
have to store again.
-          boolean isManagedSchema = isManagedEntity(catalogIdent, 
Capability.Scope.SCHEMA);
-          if (isManagedSchema) {
-            return EntityCombinedSchema.of(schema)
-                .withHiddenProperties(
-                    getHiddenPropertyNames(
-                        catalogIdent,
-                        HasPropertyMetadata::schemaPropertiesMetadata,
-                        schema.properties()));
-          }
+    try {
+      return TreeLockUtils.doWithTreeLock(
+          catalogIdent,
+          LockType.WRITE,
+          () -> {
+            // we do not retrieve the schema again (to obtain some values 
generated by underlying
+            // catalog)
+            // since some catalogs' API is async and the schema may not be 
created immediately
+            Schema schema =
+                doWithCatalog(
+                    catalogIdent,
+                    c -> c.doWithSchemaOps(s -> s.createSchema(ident, comment, 
updatedProperties)),

Review Comment:
   `updatedProperties` contains the secret URNs assembled above, but it is 
passed unchanged to the schema connector. There is no schema/fileset equivalent 
of `CatalogManager.toPlaintextProperties`; the fileset catalog later merges 
schema/fileset properties into filesystem configuration, so a credential 
remains a `urn:...` string and authentication fails. Please provide a resolved 
copy at the connector/runtime consumption boundary while keeping persisted 
metadata as URNs, and apply the same fix to `FilesetOperationDispatcher`.



##########
core/src/main/java/org/apache/gravitino/catalog/CatalogManager.java:
##########
@@ -593,12 +596,20 @@ public Catalog createCatalog(
       Catalog.Type type,
       String provider,
       String comment,
-      Map<String, String> properties)
+      Map<String, String> properties,
+      Map<String, SecretBinding> secretBindings,
+      Map<String, SecretReference> secretReferences)
       throws NoSuchMetalakeException, CatalogAlreadyExistsException {
     NameIdentifier metalakeIdent = 
NameIdentifier.of(ident.namespace().levels());
 
-    Map<String, String> mergedConfig = buildCatalogConf(provider, properties);
+    final Map<String, String> mergedConfig = new 
HashMap<>(buildCatalogConf(provider, properties));
     long uid = idGenerator.nextId();
+
+    List<SecretUrn> secretUrns =
+        secretManager.assembleSecretUrns(
+            properties, mergedConfig, "catalog", uid, secretBindings, 
secretReferences);
+    secretManager.writeSecrets(secretBindings, secretUrns);

Review Comment:
   `writeSecrets` executes before `checkMetalake` and before the cleanup 
`try/finally`. If the metalake is missing or concurrently deleted, this method 
exits at `checkMetalake` without rollback and leaves provider material 
orphaned. Move the write under the lock, after metalake validation, and keep it 
inside the cleanup scope.



##########
core/src/main/java/org/apache/gravitino/catalog/SchemaOperationDispatcher.java:
##########
@@ -101,84 +106,99 @@ public NameIdentifier[] listSchemas(Namespace namespace) 
throws NoSuchCatalogExc
    * @throws SchemaAlreadyExistsException If a schema with the same identifier 
already exists.
    */
   @Override
-  public Schema createSchema(NameIdentifier ident, String comment, Map<String, 
String> properties)
+  public Schema createSchema(
+      NameIdentifier ident,
+      String comment,
+      Map<String, String> properties,
+      Map<String, SecretBinding> secretBindings,
+      Map<String, SecretReference> secretReferences)
       throws NoSuchCatalogException, SchemaAlreadyExistsException {
     NameIdentifier catalogIdent = getCatalogIdentifier(ident);
 
+    long uid = idGenerator.nextId();
+    Map<String, String> entityProperties = 
SecretPropertyUtils.copyEntityProperties(properties);
+    List<SecretUrn> secretUrns =
+        secretManager.assembleSecretUrns(
+            properties, entityProperties, "schema", uid, secretBindings, 
secretReferences);
     doWithCatalog(
         catalogIdent,
         c ->
             c.doWithPropertiesMeta(
                 p -> {
-                  validatePropertyForCreate(p.schemaPropertiesMetadata(), 
properties);
+                  validatePropertyForCreate(p.schemaPropertiesMetadata(), 
entityProperties);
                   return null;
                 }),
         IllegalArgumentException.class);
-    long uid = idGenerator.nextId();
+    secretManager.writeSecrets(secretBindings, secretUrns);

Review Comment:
   Once this write succeeds, a successful `dropSchema` has no corresponding 
path that reads the stored URNs and deletes the write-through secret. A 
create-then-drop sequence therefore permanently leaves provider material 
behind. Please add drop cleanup for write-through URNs owned by the schema; 
`FilesetOperationDispatcher` has the same gap. External-reference URNs must 
remain untouched.



##########
core/src/main/java/org/apache/gravitino/hook/SchemaHookDispatcher.java:
##########
@@ -91,7 +98,8 @@ public Schema createSchema(NameIdentifier ident, String 
comment, Map<String, Str
           // ancestor's owner is never overwritten.
           List<NameIdentifier> newAncestors = 
findMissingAncestors(normalizedIdent);
 
-          Schema schema = dispatcher.createSchema(ident, comment, properties);
+          Schema schema =
+              dispatcher.createSchema(ident, comment, properties, 
secretBindings, secretReferences);

Review Comment:
   `dispatcher.createSchema` has already persisted the entity and written its 
secrets when it returns. If the later `ownerManager.setOwners` call throws, the 
API reports a create failure but this hook has no compensating drop or secret 
cleanup, so the entity and secret remain and a retry sees already-exists. 
Please add hook-level compensation; `FilesetHookDispatcher` has the same 
sequence.



##########
design-docs/gravitino-entity-secrets.md:
##########
@@ -650,12 +650,12 @@ sibling maps). Existing **`setProperty`** stays a 
**string** `value` (plaintext
 | `@type`              | Fields (flat; not nested under `value`)               
                                   | Behavior                                   
                                                                                
                               |
 | -------------------- | 
----------------------------------------------------------------------------------------
 | 
---------------------------------------------------------------------------------------------------------------------------------------------------------
 |
 | `setProperty`        | `value` (**string** plaintext)                        
                                   | Today’s plaintext set. If the **current** 
value matches the URN recognition rule, in-place `writeSecret` via provider in 
the current URN; persist new URN |
-| `setSecretBinding`   | `provider` (instance name) + `value` (plaintext 
string)                                  | Write-through bind/re-bind 
(`writeSecret`); persist returned URN                                           
                                               |
+| `setSecretBinding`   | `provider` (instance name) + `plaintext` (plaintext 
string)                              | Write-through bind/re-bind 
(`writeSecret`); persist returned URN                                           
                                               |

Review Comment:
   This alter row now correctly uses typed `provider` plus `plaintext`, but the 
create contract in §5.9.2/TC-1 still says `secretBindings` is 
`map<string,string>` and places plaintext in `properties`. The current 
DTO/OpenAPI require `property -> {provider, plaintext}` and reject overlap with 
`properties`, so requests following the document fail. Please update the create 
rules and examples in the same change.



##########
api/src/main/java/org/apache/gravitino/file/FilesetCatalog.java:
##########
@@ -113,6 +116,10 @@ default Fileset createFileset(
    * @param type The type of the fileset.
    * @param storageLocations The location names and storage locations of the 
fileset.
    * @param properties The properties of the fileset.
+   * @param secretBindings optional property key → binding ({@code provider} + 
{@code plaintext})

Review Comment:
   The new secret-aware entry point exists only for 
`createMultipleLocationFileset`. The common single-location `createFileset` 
overload cannot accept either secret map, so Java callers must switch APIs and 
manufacture a singleton location map. Please add a symmetric secret-aware 
overload and delegate internally.



##########
core/src/main/java/org/apache/gravitino/secret/SecretManager.java:
##########
@@ -87,7 +155,7 @@ public List<SecretUrn> getSecretReferenceUrns(Map<String, 
SecretReference> secre
       SecretProvider provider = registry.getProvider(providerName);
       try {
         SecretUrn urn = provider.buildReferenceUrn(key, locator.attributes());

Review Comment:
   For an external-reference provider that reads a shared secret namespace with 
a service identity, this accepts caller-controlled `provider`/`attributes` 
without authorizing the referenced secret for the current principal or target 
entity. A catalog creator could therefore make Gravitino read or use a secret 
outside their scope; a caller-controlled connector target may then disclose or 
misuse it. Please add reference-level authorization/scoping when binding and 
again when reading or using the URN.



##########
api/src/main/java/org/apache/gravitino/SupportsCatalogs.java:
##########
@@ -84,23 +88,58 @@ default boolean catalogExists(String catalogName) {
    * the created catalog is the managed catalog, like model, fileset catalog. 
For the details of the
    * provider definition, see {@link CatalogProvider}.
    *
-   * @param catalogName the name of the catalog.
-   * @param type the type of the catalog.
-   * @param provider the provider of the catalog, or null if the catalog is a 
managed catalog.
-   * @param comment the comment of the catalog.
-   * @param properties the properties of the catalog.
-   * @return The created catalog.
-   * @throws NoSuchMetalakeException If the metalake does not exist.
-   * @throws CatalogAlreadyExistsException If the catalog already exists.
+   * @param catalogName the name of the catalog
+   * @param type the type of the catalog
+   * @param provider the provider of the catalog, or null if the catalog is a 
managed catalog
+   * @param comment the comment of the catalog
+   * @param properties the properties of the catalog
+   * @param secretBindings optional property key → binding ({@code provider} + 
{@code plaintext})
+   *     for write-through
+   * @param secretReferences optional property key → secret locator ({@code 
provider} plus
+   *     provider-specific attributes)
+   * @return the created catalog
+   * @throws NoSuchMetalakeException if the metalake does not exist
+   * @throws CatalogAlreadyExistsException if the catalog already exists
    */
   Catalog createCatalog(
       String catalogName,
       Catalog.Type type,
       String provider,
       String comment,
-      Map<String, String> properties)
+      Map<String, String> properties,
+      Map<String, SecretBinding> secretBindings,

Review Comment:
   I realize this interface is `@Evolving`, but making the new secret-aware 
overload abstract while turning the old method into a default forces every 
existing third-party implementation to add the new method; invoking the new API 
on an old implementation can produce `AbstractMethodError`. Could we preserve 
the old abstract method and make the new overload a safe default that delegates 
only for empty secret maps, or explicitly rejects non-empty maps? 
`SupportsSchemas` has the same compatibility reversal.



##########
common/src/main/java/org/apache/gravitino/dto/requests/CatalogCreateRequest.java:
##########
@@ -62,18 +94,26 @@ public class CatalogCreateRequest implements RESTRequest {
    * @param provider The provider of the catalog.
    * @param comment The comment for the catalog.
    * @param properties The properties for the catalog.
+   * @param secretBindings Optional property key → binding DTO ({@code 
provider} + {@code
+   *     plaintext}) for write-through secrets.
+   * @param secretReferences Optional property key → secret locator DTO 
({@code provider} plus
+   *     provider-specific attributes).
    */
   @JsonCreator
   public CatalogCreateRequest(
       @JsonProperty("name") String name,
       @JsonProperty("type") Catalog.Type type,
       @JsonProperty("provider") String provider,
       @JsonProperty("comment") String comment,
-      @JsonProperty("properties") Map<String, String> properties) {
+      @JsonProperty("properties") Map<String, String> properties,
+      @JsonProperty("secretBindings") Map<String, SecretBindingDTO> 
secretBindings,
+      @JsonProperty("secretReferences") Map<String, SecretReferenceDTO> 
secretReferences) {
     this.name = name;
     this.type = type;
     this.comment = comment;
     this.properties = properties;
+    this.secretBindings = secretBindings;
+    this.secretReferences = secretReferences;

Review Comment:
   `NON_EMPTY` omits the empty maps created by the legacy constructor, but 
missing JSON fields arrive as `null` and are stored unchanged here. A JSON 
round trip therefore changes empty maps to `null`, currently failing 
`TestCatalogCreateRequest.testCatalogCreateRequestSerDe` and 
`TestRequestJsonSerDe.testCatalogCreateRequestSerDe` in `build (17)`. Normalize 
absent secret maps to empty here, or otherwise make the serialization/equality 
contract consistent.



##########
core/src/main/java/org/apache/gravitino/catalog/CatalogManager.java:
##########
@@ -1303,7 +1320,10 @@ private Map<String, String> 
getResolvedProperties(CatalogEntity entity) {
   private BaseCatalog<?> createBaseCatalog(IsolatedClassLoader classLoader, 
CatalogEntity entity) {
     // Load Catalog class instance
     BaseCatalog<?> catalog = createCatalogInstance(classLoader, 
entity.getProvider());
-    catalog.withCatalogConf(entity.getProperties()).withCatalogEntity(entity);
+    // Resolve secret URNs to plaintext for connector init only; entity 
storage keeps URNs.
+    catalog
+        
.withCatalogConf(secretManager.toPlaintextProperties(entity.getProperties()))

Review Comment:
   `GET .../catalogs?details=true` calls `listCatalogsInfo` for every catalog 
before the REST layer performs per-item authorization filtering. This eager 
resolution therefore performs provider reads and initializes cached catalog 
configuration with plaintext even for catalogs filtered from the response; one 
provider failure can also fail the entire listing. Filter authorized 
identifiers before loading/resolving wrappers, and avoid retaining plaintext 
longer than connector use.



##########
common/src/main/java/org/apache/gravitino/dto/requests/FilesetCreateRequest.java:
##########
@@ -64,6 +67,16 @@ public class FilesetCreateRequest implements RESTRequest {
   @JsonProperty("properties")
   private Map<String, String> properties;
 
+  @Nullable
+  @JsonInclude(JsonInclude.Include.NON_EMPTY)
+  @JsonProperty("secretBindings")
+  private Map<String, SecretBindingDTO> secretBindings;

Review Comment:
   Because this class exposes Lombok's `@AllArgsConstructor`, adding these 
fields changes the public constructor from six parameters to eight. Existing 
source calls stop compiling and existing binaries can fail with 
`NoSuchMethodError`. Please preserve an explicit six-argument overload, as the 
catalog/schema request DTOs do, or avoid treating the generated all-args 
constructor as the compatibility contract.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to